~ubuntu-branches/ubuntu/vivid/gnome-desktop3/vivid-proposed

« back to all changes in this revision

Viewing changes to .pc/02_refuse_to_break_GL_compositors.patch/libgnome-desktop/gnome-rr-config.c

  • Committer: Package Import Robot
  • Author(s): Tim Lunn
  • Date: 2013-05-28 09:10:46 UTC
  • mfrom: (1.6.1) (21.1.10 experimental)
  • Revision ID: package-import@ubuntu.com-20130528091046-b0oc28za9l97fgq1
Tags: 3.8.2-0ubuntu1
* New upstream release
* Sync with Debian (LP: #1184812) Remaining changes:
  - debian/patches:
    + 04_compute_average_color.patch: Compute the avergage color in
      gnome-desktop itself, not in unity to fix some races (LP #963140)
    + tweak_color_computation.patch, Patch from Gord, no patch header,
      no bug link.
    + git_revert_draw_background.patch
    + ubuntu_language.patch, Ported relevant bits from g-c-c 
      52_region_language.patch, as required for gnome 3.8 region panel
    + ubuntu_language_list_from_SUPPORTED.patch,
      adds api to get list of available languages from SUPPORTED file.
      To be used by gnome 3.8 region panel language installation.
  - debian/control.in:
    + Don't break gnome-shell << 3.7.90
    + Use source:Version for gnome-desktop3-data Depend
    + Add epoch to gnome-desktop3-data's Breaks/Replaces, as our old
      gnome-desktop source package introduced an epoch. This needs to be
      kept until after 14.04 LTS.
 - Install helper tools into a versioned directory (by overriding
   libexecdir). They could alternatively be installed in a separate package
* Dropped changes:
  - 02_refuse_to_break_GL_compositors.patch:
    + Doesn't appear to be needed any more
 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* gnome-rr-config.c
 
2
 * -*- c-basic-offset: 4 -*-
 
3
 *
 
4
 * Copyright 2007, 2008, Red Hat, Inc.
 
5
 * Copyright 2010 Giovanni Campagna
 
6
 * 
 
7
 * This file is part of the Gnome Library.
 
8
 * 
 
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.
 
13
 *
 
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.
 
18
 * 
 
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.
 
23
 * 
 
24
 * Author: Soren Sandmann <sandmann@redhat.com>
 
25
 */
 
26
 
 
27
#define GNOME_DESKTOP_USE_UNSTABLE_API
 
28
 
 
29
#include <config.h>
 
30
#include <glib/gi18n-lib.h>
 
31
#include <stdlib.h>
 
32
#include <string.h>
 
33
#include <glib.h>
 
34
#include <glib/gstdio.h>
 
35
 
 
36
#include <X11/Xlib.h>
 
37
#include <gdk/gdkx.h>
 
38
 
 
39
#include "gnome-rr-config.h"
 
40
 
 
41
#include "edid.h"
 
42
#include "gnome-rr-private.h"
 
43
 
 
44
#define CONFIG_INTENDED_BASENAME "monitors.xml"
 
45
#define CONFIG_BACKUP_BASENAME "monitors.xml.backup"
 
46
 
 
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
 
51
 
 
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
 
54
 * like
 
55
 *
 
56
 *   <configuration>
 
57
 *     ...
 
58
 *   </configuration>
 
59
 *   <configuration>
 
60
 *     ...
 
61
 *   </configuration>
 
62
 *
 
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:
 
66
 *
 
67
 *   <monitors version="1">
 
68
 *     <configuration>
 
69
 *       ...
 
70
 *     </configuration>
 
71
 *     <configuration>
 
72
 *       ...
 
73
 *     </configuration>
 
74
 *   </monitors>
 
75
 */
 
76
 
 
77
/* A helper wrapper around the GMarkup parser stuff */
 
78
static gboolean parse_file_gmarkup (const gchar *file,
 
79
                                    const GMarkupParser *parser,
 
80
                                    gpointer data,
 
81
                                    GError **err);
 
82
 
 
83
typedef struct CrtcAssignment CrtcAssignment;
 
84
 
 
85
static gboolean         crtc_assignment_apply (CrtcAssignment   *assign,
 
86
                                               guint32           timestamp,
 
87
                                               GError          **error);
 
88
static CrtcAssignment  *crtc_assignment_new   (GnomeRRScreen      *screen,
 
89
                                               GnomeRROutputInfo **outputs,
 
90
                                               GError            **error);
 
91
static void             crtc_assignment_free  (CrtcAssignment   *assign);
 
92
 
 
93
enum {
 
94
  PROP_0,
 
95
  PROP_SCREEN,
 
96
  PROP_LAST
 
97
};
 
98
 
 
99
G_DEFINE_TYPE (GnomeRRConfig, gnome_rr_config, G_TYPE_OBJECT)
 
100
 
 
101
typedef struct Parser Parser;
 
102
 
 
103
/* Parser for monitor configurations */
 
104
struct Parser
 
105
{
 
106
    int                 config_file_version;
 
107
    GnomeRROutputInfo * output;
 
108
    GnomeRRConfig *     configuration;
 
109
    GPtrArray *         outputs;
 
110
    GPtrArray *         configurations;
 
111
    GQueue *            stack;
 
112
};
 
113
 
 
114
static int
 
115
parse_int (const char *text)
 
116
{
 
117
    return strtol (text, NULL, 0);
 
118
}
 
119
 
 
120
static guint
 
121
parse_uint (const char *text)
 
122
{
 
123
    return strtoul (text, NULL, 0);
 
124
}
 
125
 
 
126
static gboolean
 
127
stack_is (Parser *parser,
 
128
          const char *s1,
 
129
          ...)
 
130
{
 
131
    GList *stack = NULL;
 
132
    const char *s;
 
133
    GList *l1, *l2;
 
134
    va_list args;
 
135
    
 
136
    stack = g_list_prepend (stack, (gpointer)s1);
 
137
    
 
138
    va_start (args, s1);
 
139
    
 
140
    s = va_arg (args, const char *);
 
141
    while (s)
 
142
    {
 
143
        stack = g_list_prepend (stack, (gpointer)s);
 
144
        s = va_arg (args, const char *);
 
145
    }
 
146
        
 
147
    l1 = stack;
 
148
    l2 = parser->stack->head;
 
149
    
 
150
    while (l1 && l2)
 
151
    {
 
152
        if (strcmp (l1->data, l2->data) != 0)
 
153
        {
 
154
            g_list_free (stack);
 
155
            return FALSE;
 
156
        }
 
157
        
 
158
        l1 = l1->next;
 
159
        l2 = l2->next;
 
160
    }
 
161
    
 
162
    g_list_free (stack);
 
163
    
 
164
    return (!l1 && !l2);
 
165
}
 
166
 
 
167
static void
 
168
handle_start_element (GMarkupParseContext *context,
 
169
                      const gchar         *name,
 
170
                      const gchar        **attr_names,
 
171
                      const gchar        **attr_values,
 
172
                      gpointer             user_data,
 
173
                      GError             **err)
 
174
{
 
175
    Parser *parser = user_data;
 
176
 
 
177
    if (strcmp (name, "output") == 0)
 
178
    {
 
179
        int i;
 
180
        g_assert (parser->output == NULL);
 
181
 
 
182
        parser->output = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL);
 
183
        parser->output->priv->rotation = 0;
 
184
        
 
185
        for (i = 0; attr_names[i] != NULL; ++i)
 
186
        {
 
187
            if (strcmp (attr_names[i], "name") == 0)
 
188
            {
 
189
                parser->output->priv->name = g_strdup (attr_values[i]);
 
190
                break;
 
191
            }
 
192
        }
 
193
 
 
194
        if (!parser->output->priv->name)
 
195
        {
 
196
            /* This really shouldn't happen, but it's better to make
 
197
             * something up than to crash later.
 
198
             */
 
199
            g_warning ("Malformed monitor configuration file");
 
200
            
 
201
            parser->output->priv->name = g_strdup ("default");
 
202
        }       
 
203
        parser->output->priv->connected = FALSE;
 
204
        parser->output->priv->on = FALSE;
 
205
        parser->output->priv->primary = FALSE;
 
206
    }
 
207
    else if (strcmp (name, "configuration") == 0)
 
208
    {
 
209
        g_assert (parser->configuration == NULL);
 
210
        
 
211
        parser->configuration = g_object_new (GNOME_TYPE_RR_CONFIG, NULL);
 
212
        parser->configuration->priv->clone = FALSE;
 
213
        parser->configuration->priv->outputs = NULL;
 
214
    }
 
215
    else if (strcmp (name, "monitors") == 0)
 
216
    {
 
217
        int i;
 
218
 
 
219
        for (i = 0; attr_names[i] != NULL; i++)
 
220
        {
 
221
            if (strcmp (attr_names[i], "version") == 0)
 
222
            {
 
223
                parser->config_file_version = parse_int (attr_values[i]);
 
224
                break;
 
225
            }
 
226
        }
 
227
    }
 
228
 
 
229
    g_queue_push_tail (parser->stack, g_strdup (name));
 
230
}
 
231
 
 
232
static void
 
233
handle_end_element (GMarkupParseContext *context,
 
234
                    const gchar         *name,
 
235
                    gpointer             user_data,
 
236
                    GError             **err)
 
237
{
 
238
    Parser *parser = user_data;
 
239
    
 
240
    if (strcmp (name, "output") == 0)
 
241
    {
 
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;
 
245
        
 
246
        g_ptr_array_add (parser->outputs, parser->output);
 
247
 
 
248
        parser->output = NULL;
 
249
    }
 
250
    else if (strcmp (name, "configuration") == 0)
 
251
    {
 
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;
 
258
    }
 
259
    
 
260
    g_free (g_queue_pop_tail (parser->stack));
 
261
}
 
262
 
 
263
#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
 
264
 
 
265
static void
 
266
handle_text (GMarkupParseContext *context,
 
267
             const gchar         *text,
 
268
             gsize                text_len,
 
269
             gpointer             user_data,
 
270
             GError             **err)
 
271
{
 
272
    Parser *parser = user_data;
 
273
    
 
274
    if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
275
    {
 
276
        parser->output->priv->connected = TRUE;
 
277
        
 
278
        strncpy ((gchar*) parser->output->priv->vendor, text, 3);
 
279
        parser->output->priv->vendor[3] = 0;
 
280
    }
 
281
    else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
 
282
    {
 
283
        if (strcmp (text, "yes") == 0)
 
284
            parser->configuration->priv->clone = TRUE;
 
285
    }
 
286
    else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
287
    {
 
288
        parser->output->priv->connected = TRUE;
 
289
 
 
290
        parser->output->priv->product = parse_int (text);
 
291
    }
 
292
    else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
293
    {
 
294
        parser->output->priv->connected = TRUE;
 
295
 
 
296
        parser->output->priv->serial = parse_uint (text);
 
297
    }
 
298
    else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
299
    {
 
300
        parser->output->priv->on = TRUE;
 
301
 
 
302
        parser->output->priv->width = parse_int (text);
 
303
    }
 
304
    else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
305
    {
 
306
        parser->output->priv->on = TRUE;
 
307
 
 
308
        parser->output->priv->x = parse_int (text);
 
309
    }
 
310
    else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
311
    {
 
312
        parser->output->priv->on = TRUE;
 
313
 
 
314
        parser->output->priv->y = parse_int (text);
 
315
    }
 
316
    else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
317
    {
 
318
        parser->output->priv->on = TRUE;
 
319
 
 
320
        parser->output->priv->height = parse_int (text);
 
321
    }
 
322
    else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
323
    {
 
324
        parser->output->priv->on = TRUE;
 
325
 
 
326
        parser->output->priv->rate = parse_int (text);
 
327
    }
 
328
    else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
329
    {
 
330
        if (strcmp (text, "normal") == 0)
 
331
        {
 
332
            parser->output->priv->rotation |= GNOME_RR_ROTATION_0;
 
333
        }
 
334
        else if (strcmp (text, "left") == 0)
 
335
        {
 
336
            parser->output->priv->rotation |= GNOME_RR_ROTATION_90;
 
337
        }
 
338
        else if (strcmp (text, "upside_down") == 0)
 
339
        {
 
340
            parser->output->priv->rotation |= GNOME_RR_ROTATION_180;
 
341
        }
 
342
        else if (strcmp (text, "right") == 0)
 
343
        {
 
344
            parser->output->priv->rotation |= GNOME_RR_ROTATION_270;
 
345
        }
 
346
    }
 
347
    else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
348
    {
 
349
        if (strcmp (text, "yes") == 0)
 
350
        {
 
351
            parser->output->priv->rotation |= GNOME_RR_REFLECT_X;
 
352
        }
 
353
    }
 
354
    else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
355
    {
 
356
        if (strcmp (text, "yes") == 0)
 
357
        {
 
358
            parser->output->priv->rotation |= GNOME_RR_REFLECT_Y;
 
359
        }
 
360
    }
 
361
    else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
 
362
    {
 
363
        if (strcmp (text, "yes") == 0)
 
364
        {
 
365
            parser->output->priv->primary = TRUE;
 
366
        }
 
367
    }
 
368
    else
 
369
    {
 
370
        /* Ignore other properties so we can expand the format in the future */
 
371
    }
 
372
}
 
373
 
 
374
static void
 
375
parser_free (Parser *parser)
 
376
{
 
377
    int i;
 
378
    GList *list;
 
379
 
 
380
    g_assert (parser != NULL);
 
381
 
 
382
    if (parser->output)
 
383
        g_object_unref (parser->output);
 
384
 
 
385
    if (parser->configuration)
 
386
        g_object_unref (parser->configuration);
 
387
 
 
388
    for (i = 0; i < parser->outputs->len; ++i)
 
389
    {
 
390
        GnomeRROutputInfo *output = parser->outputs->pdata[i];
 
391
 
 
392
        g_object_unref (output);
 
393
    }
 
394
 
 
395
    g_ptr_array_free (parser->outputs, TRUE);
 
396
 
 
397
    for (i = 0; i < parser->configurations->len; ++i)
 
398
    {
 
399
        GnomeRRConfig *config = parser->configurations->pdata[i];
 
400
 
 
401
        g_object_unref (config);
 
402
    }
 
403
 
 
404
    g_ptr_array_free (parser->configurations, TRUE);
 
405
 
 
406
    for (list = parser->stack->head; list; list = list->next)
 
407
        g_free (list->data);
 
408
    g_queue_free (parser->stack);
 
409
    
 
410
    g_free (parser);
 
411
}
 
412
 
 
413
static GnomeRRConfig **
 
414
configurations_read_from_file (const gchar *filename, GError **error)
 
415
{
 
416
    Parser *parser = g_new0 (Parser, 1);
 
417
    GnomeRRConfig **result;
 
418
    GMarkupParser callbacks = {
 
419
        handle_start_element,
 
420
        handle_end_element,
 
421
        handle_text,
 
422
        NULL, /* passthrough */
 
423
        NULL, /* error */
 
424
    };
 
425
 
 
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 ();
 
430
    
 
431
    if (!parse_file_gmarkup (filename, &callbacks, parser, error))
 
432
    {
 
433
        result = NULL;
 
434
        
 
435
        g_assert (parser->outputs);
 
436
        goto out;
 
437
    }
 
438
 
 
439
    g_assert (parser->outputs);
 
440
    
 
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 ();
 
444
    
 
445
    g_assert (parser->outputs);
 
446
out:
 
447
    parser_free (parser);
 
448
 
 
449
    return result;
 
450
}
 
451
 
 
452
static void
 
453
gnome_rr_config_init (GnomeRRConfig *self)
 
454
{
 
455
    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_RR_CONFIG, GnomeRRConfigPrivate);
 
456
 
 
457
    self->priv->clone = FALSE;
 
458
    self->priv->screen = NULL;
 
459
    self->priv->outputs = NULL;
 
460
}
 
461
 
 
462
static void
 
463
gnome_rr_config_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
 
464
{
 
465
    GnomeRRConfig *self = GNOME_RR_CONFIG (gobject);
 
466
 
 
467
    switch (property_id) {
 
468
        case PROP_SCREEN:
 
469
            self->priv->screen = g_value_dup_object (value);
 
470
            return;
 
471
        default:
 
472
            G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
 
473
    }
 
474
}
 
475
 
 
476
static void
 
477
gnome_rr_config_finalize (GObject *gobject)
 
478
{
 
479
    GnomeRRConfig *self = GNOME_RR_CONFIG (gobject);
 
480
 
 
481
    if (self->priv->screen)
 
482
        g_object_unref (self->priv->screen);
 
483
 
 
484
    if (self->priv->outputs) {
 
485
        int i;
 
486
 
 
487
        for (i = 0; self->priv->outputs[i] != NULL; i++) {
 
488
            GnomeRROutputInfo *output = self->priv->outputs[i];
 
489
            g_object_unref (output);
 
490
        }
 
491
        g_free (self->priv->outputs);
 
492
    }
 
493
 
 
494
    G_OBJECT_CLASS (gnome_rr_config_parent_class)->finalize (gobject);
 
495
}
 
496
 
 
497
gboolean
 
498
gnome_rr_config_load_current (GnomeRRConfig *config, GError **error)
 
499
{
 
500
    GPtrArray *a;
 
501
    GnomeRROutput **rr_outputs;
 
502
    int i;
 
503
    int clone_width = -1;
 
504
    int clone_height = -1;
 
505
    int last_x;
 
506
 
 
507
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), FALSE);
 
508
 
 
509
    a = g_ptr_array_new ();
 
510
    rr_outputs = gnome_rr_screen_list_outputs (config->priv->screen);
 
511
 
 
512
    config->priv->clone = FALSE;
 
513
    
 
514
    for (i = 0; rr_outputs[i] != NULL; ++i)
 
515
    {
 
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);
 
520
        GnomeRRCrtc *crtc;
 
521
 
 
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));
 
525
 
 
526
        if (!output->priv->connected)
 
527
        {
 
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;
 
534
        }
 
535
        else
 
536
        {
 
537
            MonitorInfo *info = NULL;
 
538
 
 
539
            if (edid_data)
 
540
                info = decode_edid (edid_data);
 
541
 
 
542
            if (info)
 
543
            {
 
544
                memcpy (output->priv->vendor, info->manufacturer_code,
 
545
                        sizeof (output->priv->vendor));
 
546
                
 
547
                output->priv->product = info->product_code;
 
548
                output->priv->serial = info->serial_number;
 
549
                output->priv->aspect = info->aspect_ratio;
 
550
            }
 
551
            else
 
552
            {
 
553
                strcpy (output->priv->vendor, "???");
 
554
                output->priv->product = 0;
 
555
                output->priv->serial = 0;
 
556
            }
 
557
            g_free (info);
 
558
                
 
559
            crtc = gnome_rr_output_get_crtc (rr_output);
 
560
            mode = crtc? gnome_rr_crtc_get_current_mode (crtc) : NULL;
 
561
            
 
562
            if (crtc && mode)
 
563
            {
 
564
                output->priv->on = TRUE;
 
565
                
 
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);
 
571
 
 
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;
 
579
                        }
 
580
                }
 
581
            }
 
582
            else
 
583
            {
 
584
                output->priv->on = FALSE;
 
585
                config->priv->clone = FALSE;
 
586
            }
 
587
 
 
588
            /* Get preferred size for the monitor */
 
589
            mode = gnome_rr_output_get_preferred_mode (rr_output);
 
590
            
 
591
            if (!mode)
 
592
            {
 
593
                GnomeRRMode **modes = gnome_rr_output_list_modes (rr_output);
 
594
                
 
595
                /* FIXME: we should pick the "best" mode here, where best is
 
596
                 * sorted wrt
 
597
                 *
 
598
                 * - closest aspect ratio
 
599
                 * - mode area
 
600
                 * - refresh rate
 
601
                 * - We may want to extend randrwrap so that get_preferred
 
602
                 *   returns that - although that could also depend on
 
603
                 *   the crtc.
 
604
                 */
 
605
                if (modes[0])
 
606
                    mode = modes[0];
 
607
            }
 
608
            
 
609
            if (mode)
 
610
            {
 
611
                output->priv->pref_width = gnome_rr_mode_get_width (mode);
 
612
                output->priv->pref_height = gnome_rr_mode_get_height (mode);
 
613
            }
 
614
            else
 
615
            {
 
616
                /* Pick some random numbers. This should basically never happen */
 
617
                output->priv->pref_width = 1024;
 
618
                output->priv->pref_height = 768;
 
619
            }
 
620
        }
 
621
 
 
622
        output->priv->primary = gnome_rr_output_get_is_primary (rr_output);
 
623
 
 
624
        g_ptr_array_add (a, output);
 
625
    }
 
626
 
 
627
    g_ptr_array_add (a, NULL);
 
628
    
 
629
    config->priv->outputs = (GnomeRROutputInfo **)g_ptr_array_free (a, FALSE);
 
630
 
 
631
    /* Walk the outputs computing the right-most edge of all
 
632
     * lit-up displays
 
633
     */
 
634
    last_x = 0;
 
635
    for (i = 0; config->priv->outputs[i] != NULL; ++i)
 
636
    {
 
637
        GnomeRROutputInfo *output = config->priv->outputs[i];
 
638
 
 
639
        if (output->priv->on)
 
640
        {
 
641
            last_x = MAX (last_x, output->priv->x + output->priv->width);
 
642
        }
 
643
    }
 
644
 
 
645
    /* Now position all off displays to the right of the
 
646
     * on displays
 
647
     */
 
648
    for (i = 0; config->priv->outputs[i] != NULL; ++i)
 
649
    {
 
650
        GnomeRROutputInfo *output = config->priv->outputs[i];
 
651
 
 
652
        if (output->priv->connected && !output->priv->on)
 
653
        {
 
654
            output->priv->x = last_x;
 
655
            last_x = output->priv->x + output->priv->width;
 
656
        }
 
657
    }
 
658
    
 
659
    g_assert (gnome_rr_config_match (config, config));
 
660
 
 
661
    return TRUE;
 
662
}
 
663
 
 
664
gboolean
 
665
gnome_rr_config_load_filename (GnomeRRConfig *result, const char *filename, GError **error)
 
666
{
 
667
    GnomeRRConfig *current;
 
668
    GnomeRRConfig **configs;
 
669
    gboolean found = FALSE;
 
670
 
 
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);
 
674
 
 
675
    current = gnome_rr_config_new_current (result->priv->screen, error);
 
676
 
 
677
    configs = configurations_read_from_file (filename, error);
 
678
 
 
679
    if (configs)
 
680
    {
 
681
        int i;
 
682
 
 
683
        for (i = 0; configs[i] != NULL; ++i)
 
684
        {
 
685
            if (gnome_rr_config_match (configs[i], current))
 
686
            {
 
687
                int j;
 
688
                GPtrArray *array;
 
689
                result->priv->clone = configs[i]->priv->clone;
 
690
 
 
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]);
 
695
                }
 
696
                g_ptr_array_add (array, NULL);
 
697
                result->priv->outputs = (GnomeRROutputInfo **) g_ptr_array_free (array, FALSE);
 
698
 
 
699
                found = TRUE;
 
700
                break;
 
701
            }
 
702
            g_object_unref (configs[i]);
 
703
        }
 
704
        g_free (configs);
 
705
 
 
706
        if (!found)
 
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"));
 
709
    }
 
710
 
 
711
    g_object_unref (current);
 
712
    return found;
 
713
}
 
714
 
 
715
static void
 
716
gnome_rr_config_class_init (GnomeRRConfigClass *klass)
 
717
{
 
718
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
719
 
 
720
    g_type_class_add_private (klass, sizeof (GnomeRROutputInfoPrivate));
 
721
 
 
722
    gobject_class->set_property = gnome_rr_config_set_property;
 
723
    gobject_class->finalize = gnome_rr_config_finalize;
 
724
 
 
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));
 
728
}
 
729
 
 
730
GnomeRRConfig *
 
731
gnome_rr_config_new_current (GnomeRRScreen *screen, GError **error)
 
732
{
 
733
    GnomeRRConfig *self = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
 
734
 
 
735
    if (gnome_rr_config_load_current (self, error))
 
736
      return self;
 
737
    else
 
738
      {
 
739
        g_object_unref (self);
 
740
        return NULL;
 
741
      }
 
742
}
 
743
 
 
744
GnomeRRConfig *
 
745
gnome_rr_config_new_stored (GnomeRRScreen *screen, GError **error)
 
746
{
 
747
    GnomeRRConfig *self = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
 
748
    char *filename;
 
749
    gboolean success;
 
750
 
 
751
    filename = gnome_rr_config_get_intended_filename ();
 
752
 
 
753
    success = gnome_rr_config_load_filename (self, filename, error);
 
754
 
 
755
    g_free (filename);
 
756
 
 
757
    if (success)
 
758
      return self;
 
759
    else
 
760
      {
 
761
        g_object_unref (self);
 
762
        return NULL;
 
763
      }
 
764
}
 
765
 
 
766
static gboolean
 
767
parse_file_gmarkup (const gchar          *filename,
 
768
                    const GMarkupParser  *parser,
 
769
                    gpointer             data,
 
770
                    GError              **err)
 
771
{
 
772
    GMarkupParseContext *context = NULL;
 
773
    gchar *contents = NULL;
 
774
    gboolean result = TRUE;
 
775
    gsize len;
 
776
 
 
777
    if (!g_file_get_contents (filename, &contents, &len, err))
 
778
    {
 
779
        result = FALSE;
 
780
        goto out;
 
781
    }
 
782
    
 
783
    context = g_markup_parse_context_new (parser, 0, data, NULL);
 
784
 
 
785
    if (!g_markup_parse_context_parse (context, contents, len, err))
 
786
    {
 
787
        result = FALSE;
 
788
        goto out;
 
789
    }
 
790
 
 
791
    if (!g_markup_parse_context_end_parse (context, err))
 
792
    {
 
793
        result = FALSE;
 
794
        goto out;
 
795
    }
 
796
 
 
797
out:
 
798
    if (contents)
 
799
        g_free (contents);
 
800
 
 
801
    if (context)
 
802
        g_markup_parse_context_free (context);
 
803
 
 
804
    return result;
 
805
}
 
806
 
 
807
static gboolean
 
808
output_match (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2)
 
809
{
 
810
    g_assert (GNOME_IS_RR_OUTPUT_INFO (output1));
 
811
    g_assert (GNOME_IS_RR_OUTPUT_INFO (output2));
 
812
 
 
813
    if (strcmp (output1->priv->name, output2->priv->name) != 0)
 
814
        return FALSE;
 
815
 
 
816
    if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0)
 
817
        return FALSE;
 
818
 
 
819
    if (output1->priv->product != output2->priv->product)
 
820
        return FALSE;
 
821
 
 
822
    if (output1->priv->serial != output2->priv->serial)
 
823
        return FALSE;
 
824
 
 
825
    if (output1->priv->connected != output2->priv->connected)
 
826
        return FALSE;
 
827
    
 
828
    return TRUE;
 
829
}
 
830
 
 
831
static gboolean
 
832
output_equal (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2)
 
833
{
 
834
    g_assert (GNOME_IS_RR_OUTPUT_INFO (output1));
 
835
    g_assert (GNOME_IS_RR_OUTPUT_INFO (output2));
 
836
 
 
837
    if (!output_match (output1, output2))
 
838
        return FALSE;
 
839
 
 
840
    if (output1->priv->on != output2->priv->on)
 
841
        return FALSE;
 
842
 
 
843
    if (output1->priv->on)
 
844
    {
 
845
        if (output1->priv->width != output2->priv->width)
 
846
            return FALSE;
 
847
        
 
848
        if (output1->priv->height != output2->priv->height)
 
849
            return FALSE;
 
850
        
 
851
        if (output1->priv->rate != output2->priv->rate)
 
852
            return FALSE;
 
853
        
 
854
        if (output1->priv->x != output2->priv->x)
 
855
            return FALSE;
 
856
        
 
857
        if (output1->priv->y != output2->priv->y)
 
858
            return FALSE;
 
859
        
 
860
        if (output1->priv->rotation != output2->priv->rotation)
 
861
            return FALSE;
 
862
    }
 
863
 
 
864
    return TRUE;
 
865
}
 
866
 
 
867
static GnomeRROutputInfo *
 
868
find_output (GnomeRRConfig *config, const char *name)
 
869
{
 
870
    int i;
 
871
 
 
872
    for (i = 0; config->priv->outputs[i] != NULL; ++i)
 
873
    {
 
874
        GnomeRROutputInfo *output = config->priv->outputs[i];
 
875
        
 
876
        if (strcmp (name, output->priv->name) == 0)
 
877
            return output;
 
878
    }
 
879
 
 
880
    return NULL;
 
881
}
 
882
 
 
883
/* Match means "these configurations apply to the same hardware
 
884
 * setups"
 
885
 */
 
886
gboolean
 
887
gnome_rr_config_match (GnomeRRConfig *c1, GnomeRRConfig *c2)
 
888
{
 
889
    int i;
 
890
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (c1), FALSE);
 
891
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (c2), FALSE);
 
892
 
 
893
    for (i = 0; c1->priv->outputs[i] != NULL; ++i)
 
894
    {
 
895
        GnomeRROutputInfo *output1 = c1->priv->outputs[i];
 
896
        GnomeRROutputInfo *output2;
 
897
 
 
898
        output2 = find_output (c2, output1->priv->name);
 
899
        if (!output2 || !output_match (output1, output2))
 
900
            return FALSE;
 
901
    }
 
902
    
 
903
    return TRUE;
 
904
}
 
905
 
 
906
/* Equal means "the configurations will result in the same
 
907
 * modes being set on the outputs"
 
908
 */
 
909
gboolean
 
910
gnome_rr_config_equal (GnomeRRConfig  *c1,
 
911
                       GnomeRRConfig  *c2)
 
912
{
 
913
    int i;
 
914
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (c1), FALSE);
 
915
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (c2), FALSE);
 
916
 
 
917
    for (i = 0; c1->priv->outputs[i] != NULL; ++i)
 
918
    {
 
919
        GnomeRROutputInfo *output1 = c1->priv->outputs[i];
 
920
        GnomeRROutputInfo *output2;
 
921
 
 
922
        output2 = find_output (c2, output1->priv->name);
 
923
        if (!output2 || !output_equal (output1, output2))
 
924
            return FALSE;
 
925
    }
 
926
    
 
927
    return TRUE;
 
928
}
 
929
 
 
930
static GnomeRROutputInfo **
 
931
make_outputs (GnomeRRConfig *config)
 
932
{
 
933
    GPtrArray *outputs;
 
934
    GnomeRROutputInfo *first_on;
 
935
    int i;
 
936
 
 
937
    outputs = g_ptr_array_new ();
 
938
 
 
939
    first_on = NULL;
 
940
    
 
941
    for (i = 0; config->priv->outputs[i] != NULL; ++i)
 
942
    {
 
943
        GnomeRROutputInfo *old = config->priv->outputs[i];
 
944
        GnomeRROutputInfo *new = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL);
 
945
        *(new->priv) = *(old->priv);
 
946
        if (old->priv->name)
 
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);
 
950
 
 
951
        if (old->priv->on && !first_on)
 
952
            first_on = old;
 
953
        
 
954
        if (config->priv->clone && new->priv->on)
 
955
        {
 
956
            g_assert (first_on);
 
957
 
 
958
            new->priv->width = first_on->priv->width;
 
959
            new->priv->height = first_on->priv->height;
 
960
            new->priv->rotation = first_on->priv->rotation;
 
961
            new->priv->x = 0;
 
962
            new->priv->y = 0;
 
963
        }
 
964
 
 
965
        g_ptr_array_add (outputs, new);
 
966
    }
 
967
 
 
968
    g_ptr_array_add (outputs, NULL);
 
969
 
 
970
    return (GnomeRROutputInfo **)g_ptr_array_free (outputs, FALSE);
 
971
}
 
972
 
 
973
gboolean
 
974
gnome_rr_config_applicable (GnomeRRConfig  *configuration,
 
975
                            GnomeRRScreen  *screen,
 
976
                            GError        **error)
 
977
{
 
978
    GnomeRROutputInfo **outputs;
 
979
    CrtcAssignment *assign;
 
980
    gboolean result;
 
981
    int i;
 
982
 
 
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);
 
986
 
 
987
    outputs = make_outputs (configuration);
 
988
    assign = crtc_assignment_new (screen, outputs, error);
 
989
 
 
990
    if (assign)
 
991
    {
 
992
        result = TRUE;
 
993
        crtc_assignment_free (assign);
 
994
    }
 
995
    else
 
996
    {
 
997
        result = FALSE;
 
998
    }
 
999
 
 
1000
    for (i = 0; outputs[i] != NULL; i++) {
 
1001
         g_object_unref (outputs[i]);
 
1002
    }
 
1003
 
 
1004
    return result;
 
1005
}
 
1006
 
 
1007
/* Database management */
 
1008
 
 
1009
static void
 
1010
ensure_config_directory (void)
 
1011
{
 
1012
    g_mkdir_with_parents (g_get_user_config_dir (), 0700);
 
1013
}
 
1014
 
 
1015
char *
 
1016
gnome_rr_config_get_backup_filename (void)
 
1017
{
 
1018
    ensure_config_directory ();
 
1019
    return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
 
1020
}
 
1021
 
 
1022
char *
 
1023
gnome_rr_config_get_intended_filename (void)
 
1024
{
 
1025
    ensure_config_directory ();
 
1026
    return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
 
1027
}
 
1028
 
 
1029
static const char *
 
1030
get_rotation_name (GnomeRRRotation r)
 
1031
{
 
1032
    if (r & GNOME_RR_ROTATION_0)
 
1033
        return "normal";
 
1034
    if (r & GNOME_RR_ROTATION_90)
 
1035
        return "left";
 
1036
    if (r & GNOME_RR_ROTATION_180)
 
1037
        return "upside_down";
 
1038
    if (r & GNOME_RR_ROTATION_270)
 
1039
        return "right";
 
1040
 
 
1041
    return "normal";
 
1042
}
 
1043
 
 
1044
static const char *
 
1045
yes_no (int x)
 
1046
{
 
1047
    return x? "yes" : "no";
 
1048
}
 
1049
 
 
1050
static const char *
 
1051
get_reflect_x (GnomeRRRotation r)
 
1052
{
 
1053
    return yes_no (r & GNOME_RR_REFLECT_X);
 
1054
}
 
1055
 
 
1056
static const char *
 
1057
get_reflect_y (GnomeRRRotation r)
 
1058
{
 
1059
    return yes_no (r & GNOME_RR_REFLECT_Y);
 
1060
}
 
1061
 
 
1062
static void
 
1063
emit_configuration (GnomeRRConfig *config,
 
1064
                    GString *string)
 
1065
{
 
1066
    int j;
 
1067
 
 
1068
    g_string_append_printf (string, "  <configuration>\n");
 
1069
 
 
1070
    g_string_append_printf (string, "      <clone>%s</clone>\n", yes_no (config->priv->clone));
 
1071
    
 
1072
    for (j = 0; config->priv->outputs[j] != NULL; ++j)
 
1073
    {
 
1074
        GnomeRROutputInfo *output = config->priv->outputs[j];
 
1075
        
 
1076
        g_string_append_printf (
 
1077
            string, "      <output name=\"%s\">\n", output->priv->name);
 
1078
        
 
1079
        if (output->priv->connected && *output->priv->vendor != '\0')
 
1080
        {
 
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);
 
1087
        }
 
1088
        
 
1089
        /* An unconnected output which is on does not make sense */
 
1090
        if (output->priv->connected && output->priv->on)
 
1091
        {
 
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));
 
1110
        }
 
1111
        
 
1112
        g_string_append_printf (string, "      </output>\n");
 
1113
    }
 
1114
    
 
1115
    g_string_append_printf (string, "  </configuration>\n");
 
1116
}
 
1117
 
 
1118
void
 
1119
gnome_rr_config_sanitize (GnomeRRConfig *config)
 
1120
{
 
1121
    int i;
 
1122
    int x_offset, y_offset;
 
1123
    gboolean found;
 
1124
 
 
1125
    /* Offset everything by the top/left-most coordinate to
 
1126
     * make sure the configuration starts at (0, 0)
 
1127
     */
 
1128
    x_offset = y_offset = G_MAXINT;
 
1129
    for (i = 0; config->priv->outputs[i]; ++i)
 
1130
    {
 
1131
        GnomeRROutputInfo *output = config->priv->outputs[i];
 
1132
 
 
1133
        if (output->priv->on)
 
1134
        {
 
1135
            x_offset = MIN (x_offset, output->priv->x);
 
1136
            y_offset = MIN (y_offset, output->priv->y);
 
1137
        }
 
1138
    }
 
1139
 
 
1140
    for (i = 0; config->priv->outputs[i]; ++i)
 
1141
    {
 
1142
        GnomeRROutputInfo *output = config->priv->outputs[i];
 
1143
        
 
1144
        if (output->priv->on)
 
1145
        {
 
1146
            output->priv->x -= x_offset;
 
1147
            output->priv->y -= y_offset;
 
1148
        }
 
1149
    }
 
1150
 
 
1151
    /* Only one primary, please */
 
1152
    found = FALSE;
 
1153
    for (i = 0; config->priv->outputs[i]; ++i)
 
1154
    {
 
1155
        if (config->priv->outputs[i]->priv->primary)
 
1156
        {
 
1157
            if (found)
 
1158
            {
 
1159
                config->priv->outputs[i]->priv->primary = FALSE;
 
1160
            }
 
1161
            else
 
1162
            {
 
1163
                found = TRUE;
 
1164
            }
 
1165
        }
 
1166
    }
 
1167
}
 
1168
 
 
1169
gboolean
 
1170
gnome_rr_config_ensure_primary (GnomeRRConfig *configuration)
 
1171
{
 
1172
        int i;
 
1173
        GnomeRROutputInfo  *laptop;
 
1174
        GnomeRROutputInfo  *top_left;
 
1175
        gboolean found;
 
1176
        GnomeRRConfigPrivate *priv;
 
1177
 
 
1178
        g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE);
 
1179
 
 
1180
        laptop = NULL;
 
1181
        top_left = NULL;
 
1182
        found = FALSE;
 
1183
        priv = configuration->priv;
 
1184
 
 
1185
        for (i = 0; priv->outputs[i] != NULL; ++i) {
 
1186
                GnomeRROutputInfo *info = priv->outputs[i];
 
1187
 
 
1188
                if (!info->priv->on) {
 
1189
                       info->priv->primary = FALSE;
 
1190
                       continue;
 
1191
                }
 
1192
 
 
1193
                /* ensure only one */
 
1194
                if (info->priv->primary) {
 
1195
                        if (found) {
 
1196
                                info->priv->primary = FALSE;
 
1197
                        } else {
 
1198
                                found = TRUE;
 
1199
                        }
 
1200
                }
 
1201
 
 
1202
                if (top_left == NULL
 
1203
                    || (info->priv->x < top_left->priv->x
 
1204
                        && info->priv->y < top_left->priv->y)) {
 
1205
                        top_left = info;
 
1206
                }
 
1207
                if (laptop == NULL
 
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 */
 
1211
                        laptop = info;
 
1212
                }
 
1213
        }
 
1214
 
 
1215
        if (!found) {
 
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;
 
1221
                }
 
1222
        }
 
1223
 
 
1224
        return !found;
 
1225
}
 
1226
 
 
1227
gboolean
 
1228
gnome_rr_config_save (GnomeRRConfig *configuration, GError **error)
 
1229
{
 
1230
    GnomeRRConfig **configurations;
 
1231
    GString *output;
 
1232
    int i;
 
1233
    gchar *intended_filename;
 
1234
    gchar *backup_filename;
 
1235
    gboolean result;
 
1236
 
 
1237
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE);
 
1238
    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
1239
 
 
1240
    output = g_string_new ("");
 
1241
 
 
1242
    backup_filename = gnome_rr_config_get_backup_filename ();
 
1243
    intended_filename = gnome_rr_config_get_intended_filename ();
 
1244
 
 
1245
    configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */
 
1246
    
 
1247
    g_string_append_printf (output, "<monitors version=\"1\">\n");
 
1248
 
 
1249
    if (configurations)
 
1250
    {
 
1251
        for (i = 0; configurations[i] != NULL; ++i)
 
1252
        {
 
1253
            if (!gnome_rr_config_match (configurations[i], configuration))
 
1254
                emit_configuration (configurations[i], output);
 
1255
            g_object_unref (configurations[i]);
 
1256
        }
 
1257
 
 
1258
        g_free (configurations);
 
1259
    }
 
1260
 
 
1261
    emit_configuration (configuration, output);
 
1262
 
 
1263
    g_string_append_printf (output, "</monitors>\n");
 
1264
 
 
1265
    /* backup the file first */
 
1266
    rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */
 
1267
 
 
1268
    result = g_file_set_contents (intended_filename, output->str, -1, error);
 
1269
 
 
1270
    if (!result)
 
1271
        rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */
 
1272
 
 
1273
    g_free (backup_filename);
 
1274
    g_free (intended_filename);
 
1275
 
 
1276
    return result;
 
1277
}
 
1278
 
 
1279
gboolean
 
1280
gnome_rr_config_apply_with_time (GnomeRRConfig *config,
 
1281
                                 GnomeRRScreen *screen,
 
1282
                                 guint32        timestamp,
 
1283
                                 GError       **error)
 
1284
{
 
1285
    CrtcAssignment *assignment;
 
1286
    GnomeRROutputInfo **outputs;
 
1287
    gboolean result = FALSE;
 
1288
    int i;
 
1289
 
 
1290
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), FALSE);
 
1291
    g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE);
 
1292
 
 
1293
    outputs = make_outputs (config);
 
1294
 
 
1295
    assignment = crtc_assignment_new (screen, outputs, error);
 
1296
 
 
1297
    for (i = 0; outputs[i] != NULL; i++)
 
1298
        g_object_unref (outputs[i]);
 
1299
    g_free (outputs);
 
1300
    
 
1301
    if (assignment)
 
1302
    {
 
1303
        if (crtc_assignment_apply (assignment, timestamp, error))
 
1304
            result = TRUE;
 
1305
 
 
1306
        crtc_assignment_free (assignment);
 
1307
 
 
1308
        gdk_flush ();
 
1309
    }
 
1310
 
 
1311
    return result;
 
1312
}
 
1313
 
 
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
 
1319
 *
 
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.
 
1323
 *
 
1324
 * Typically, @filename is the result of gnome_rr_config_get_intended_filename() or
 
1325
 * gnome_rr_config_get_backup_filename().
 
1326
 *
 
1327
 * Returns: TRUE if the RANDR configuration was loaded and applied from
 
1328
 * the specified file, or FALSE otherwise:
 
1329
 *
 
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.
 
1335
 *
 
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.
 
1340
 */
 
1341
gboolean
 
1342
gnome_rr_config_apply_from_filename_with_time (GnomeRRScreen *screen, const char *filename, guint32 timestamp, GError **error)
 
1343
{
 
1344
    GnomeRRConfig *stored;
 
1345
 
 
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);
 
1349
 
 
1350
    stored = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
 
1351
 
 
1352
    if (gnome_rr_config_load_filename (stored, filename, error))
 
1353
    {
 
1354
        gboolean result;
 
1355
 
 
1356
        gnome_rr_config_ensure_primary (stored);
 
1357
        result = gnome_rr_config_apply_with_time (stored, screen, timestamp, error);
 
1358
 
 
1359
        g_object_unref (stored);
 
1360
        return result;
 
1361
    }
 
1362
    else
 
1363
    {
 
1364
        g_object_unref (stored);
 
1365
        return FALSE;
 
1366
    }
 
1367
}
 
1368
 
 
1369
/**
 
1370
 * gnome_rr_config_get_outputs:
 
1371
 *
 
1372
 * Returns: (array zero-terminated=1) (element-type GnomeDesktop.RROutputInfo) (transfer none): the output configuration for this #GnomeRRConfig
 
1373
 */
 
1374
GnomeRROutputInfo **
 
1375
gnome_rr_config_get_outputs (GnomeRRConfig *self)
 
1376
{
 
1377
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), NULL);
 
1378
 
 
1379
    return self->priv->outputs;
 
1380
}
 
1381
 
 
1382
/**
 
1383
 * gnome_rr_config_get_clone:
 
1384
 *
 
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).
 
1388
 */
 
1389
gboolean
 
1390
gnome_rr_config_get_clone (GnomeRRConfig *self)
 
1391
{
 
1392
    g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), FALSE);
 
1393
 
 
1394
    return self->priv->clone;
 
1395
}
 
1396
 
 
1397
void
 
1398
gnome_rr_config_set_clone (GnomeRRConfig *self, gboolean clone)
 
1399
{
 
1400
    g_return_if_fail (GNOME_IS_RR_CONFIG (self));
 
1401
 
 
1402
    self->priv->clone = clone;
 
1403
}
 
1404
 
 
1405
/*
 
1406
 * CRTC assignment
 
1407
 */
 
1408
typedef struct CrtcInfo CrtcInfo;
 
1409
 
 
1410
struct CrtcInfo
 
1411
{
 
1412
    GnomeRRMode    *mode;
 
1413
    int        x;
 
1414
    int        y;
 
1415
    GnomeRRRotation rotation;
 
1416
    GPtrArray *outputs;
 
1417
};
 
1418
 
 
1419
struct CrtcAssignment
 
1420
{
 
1421
    GnomeRRScreen *screen;
 
1422
    GHashTable *info;
 
1423
    GnomeRROutput *primary;
 
1424
};
 
1425
 
 
1426
static gboolean
 
1427
can_clone (CrtcInfo *info,
 
1428
           GnomeRROutput *output)
 
1429
{
 
1430
    int i;
 
1431
 
 
1432
    for (i = 0; i < info->outputs->len; ++i)
 
1433
    {
 
1434
        GnomeRROutput *clone = info->outputs->pdata[i];
 
1435
 
 
1436
        if (!gnome_rr_output_can_clone (clone, output))
 
1437
            return FALSE;
 
1438
    }
 
1439
 
 
1440
    return TRUE;
 
1441
}
 
1442
 
 
1443
static gboolean
 
1444
crtc_assignment_assign (CrtcAssignment   *assign,
 
1445
                        GnomeRRCrtc      *crtc,
 
1446
                        GnomeRRMode      *mode,
 
1447
                        int               x,
 
1448
                        int               y,
 
1449
                        GnomeRRRotation   rotation,
 
1450
                        gboolean          primary,
 
1451
                        GnomeRROutput    *output,
 
1452
                        GError          **error)
 
1453
{
 
1454
    CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
 
1455
    guint32 crtc_id;
 
1456
    const char *output_name;
 
1457
 
 
1458
    crtc_id = gnome_rr_crtc_get_id (crtc);
 
1459
    output_name = gnome_rr_output_get_name (output);
 
1460
 
 
1461
    if (!gnome_rr_crtc_can_drive_output (crtc, output))
 
1462
    {
 
1463
        g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
 
1464
                     _("CRTC %d cannot drive output %s"), crtc_id, output_name);
 
1465
        return FALSE;
 
1466
    }
 
1467
 
 
1468
    if (!gnome_rr_output_supports_mode (output, mode))
 
1469
    {
 
1470
        g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
 
1471
                     _("output %s does not support mode %dx%d@%dHz"),
 
1472
                     output_name,
 
1473
                     gnome_rr_mode_get_width (mode),
 
1474
                     gnome_rr_mode_get_height (mode),
 
1475
                     gnome_rr_mode_get_freq (mode));
 
1476
        return FALSE;
 
1477
    }
 
1478
 
 
1479
    if (!gnome_rr_crtc_supports_rotation (crtc, rotation))
 
1480
    {
 
1481
        g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
 
1482
                     _("CRTC %d does not support rotation=%s"),
 
1483
                     crtc_id,
 
1484
                     get_rotation_name (rotation));
 
1485
        return FALSE;
 
1486
    }
 
1487
 
 
1488
    if (info)
 
1489
    {
 
1490
        if (!(info->mode == mode        &&
 
1491
              info->x == x              &&
 
1492
              info->y == y              &&
 
1493
              info->rotation == rotation))
 
1494
        {
 
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"),
 
1500
                         output_name,
 
1501
                         gnome_rr_mode_get_id (info->mode), gnome_rr_mode_get_id (mode),
 
1502
                         info->x, info->y,
 
1503
                         x, y,
 
1504
                         get_rotation_name (info->rotation), get_rotation_name (rotation));
 
1505
            return FALSE;
 
1506
        }
 
1507
 
 
1508
        if (!can_clone (info, output))
 
1509
        {
 
1510
            g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
 
1511
                         _("cannot clone to output %s"),
 
1512
                         output_name);
 
1513
            return FALSE;
 
1514
        }
 
1515
 
 
1516
        g_ptr_array_add (info->outputs, output);
 
1517
 
 
1518
        if (primary && !assign->primary)
 
1519
        {
 
1520
            assign->primary = output;
 
1521
        }
 
1522
 
 
1523
        return TRUE;
 
1524
    }
 
1525
    else
 
1526
    {   
 
1527
        CrtcInfo *info = g_new0 (CrtcInfo, 1);
 
1528
        
 
1529
        info->mode = mode;
 
1530
        info->x = x;
 
1531
        info->y = y;
 
1532
        info->rotation = rotation;
 
1533
        info->outputs = g_ptr_array_new ();
 
1534
        
 
1535
        g_ptr_array_add (info->outputs, output);
 
1536
        
 
1537
        g_hash_table_insert (assign->info, crtc, info);
 
1538
            
 
1539
        if (primary && !assign->primary)
 
1540
        {
 
1541
            assign->primary = output;
 
1542
        }
 
1543
 
 
1544
        return TRUE;
 
1545
    }
 
1546
}
 
1547
 
 
1548
static void
 
1549
crtc_assignment_unassign (CrtcAssignment *assign,
 
1550
                          GnomeRRCrtc         *crtc,
 
1551
                          GnomeRROutput       *output)
 
1552
{
 
1553
    CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
 
1554
 
 
1555
    if (info)
 
1556
    {
 
1557
        g_ptr_array_remove (info->outputs, output);
 
1558
 
 
1559
        if (assign->primary == output)
 
1560
        {
 
1561
            assign->primary = NULL;
 
1562
        }
 
1563
 
 
1564
        if (info->outputs->len == 0)
 
1565
            g_hash_table_remove (assign->info, crtc);
 
1566
    }
 
1567
}
 
1568
 
 
1569
static void
 
1570
crtc_assignment_free (CrtcAssignment *assign)
 
1571
{
 
1572
    g_hash_table_destroy (assign->info);
 
1573
 
 
1574
    g_free (assign);
 
1575
}
 
1576
 
 
1577
typedef struct {
 
1578
    guint32 timestamp;
 
1579
    gboolean has_error;
 
1580
    GError **error;
 
1581
} ConfigureCrtcState;
 
1582
 
 
1583
static void
 
1584
configure_crtc (gpointer key,
 
1585
                gpointer value,
 
1586
                gpointer data)
 
1587
{
 
1588
    GnomeRRCrtc *crtc = key;
 
1589
    CrtcInfo *info = value;
 
1590
    ConfigureCrtcState *state = data;
 
1591
 
 
1592
    if (state->has_error)
 
1593
        return;
 
1594
 
 
1595
    if (!gnome_rr_crtc_set_config_with_time (crtc,
 
1596
                                             state->timestamp,
 
1597
                                             info->x, info->y,
 
1598
                                             info->mode,
 
1599
                                             info->rotation,
 
1600
                                             (GnomeRROutput **)info->outputs->pdata,
 
1601
                                             info->outputs->len,
 
1602
                                             state->error))
 
1603
        state->has_error = TRUE;
 
1604
}
 
1605
 
 
1606
static gboolean
 
1607
mode_is_rotated (CrtcInfo *info)
 
1608
{
 
1609
    if ((info->rotation & GNOME_RR_ROTATION_270)                ||
 
1610
        (info->rotation & GNOME_RR_ROTATION_90))
 
1611
    {
 
1612
        return TRUE;
 
1613
    }
 
1614
    return FALSE;
 
1615
}
 
1616
 
 
1617
static gboolean
 
1618
crtc_is_rotated (GnomeRRCrtc *crtc)
 
1619
{
 
1620
    GnomeRRRotation r = gnome_rr_crtc_get_current_rotation (crtc);
 
1621
 
 
1622
    if ((r & GNOME_RR_ROTATION_270)             ||
 
1623
        (r & GNOME_RR_ROTATION_90))
 
1624
    {
 
1625
        return TRUE;
 
1626
    }
 
1627
 
 
1628
    return FALSE;
 
1629
}
 
1630
 
 
1631
static void
 
1632
accumulate_error (GString *accumulated_error, GError *error)
 
1633
{
 
1634
    g_string_append_printf (accumulated_error, "    %s\n", error->message);
 
1635
    g_error_free (error);
 
1636
}
 
1637
 
 
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.
 
1641
 *
 
1642
 * Brute force - the number of objects involved is small
 
1643
 * enough that it doesn't matter.
 
1644
 */
 
1645
static gboolean
 
1646
real_assign_crtcs (GnomeRRScreen *screen,
 
1647
                   GnomeRROutputInfo **outputs,
 
1648
                   CrtcAssignment *assignment,
 
1649
                   GError **error)
 
1650
{
 
1651
    GnomeRRCrtc **crtcs = gnome_rr_screen_list_crtcs (screen);
 
1652
    GnomeRROutputInfo *output;
 
1653
    int i;
 
1654
    gboolean tried_mode;
 
1655
    GError *my_error;
 
1656
    GString *accumulated_error;
 
1657
    gboolean success;
 
1658
 
 
1659
    output = *outputs;
 
1660
    if (!output)
 
1661
        return TRUE;
 
1662
 
 
1663
    /* It is always allowed for an output to be turned off */
 
1664
    if (!output->priv->on)
 
1665
    {
 
1666
        return real_assign_crtcs (screen, outputs + 1, assignment, error);
 
1667
    }
 
1668
 
 
1669
    success = FALSE;
 
1670
    tried_mode = FALSE;
 
1671
    accumulated_error = g_string_new (NULL);
 
1672
 
 
1673
    for (i = 0; crtcs[i] != NULL; ++i)
 
1674
    {
 
1675
        GnomeRRCrtc *crtc = crtcs[i];
 
1676
        int crtc_id = gnome_rr_crtc_get_id (crtc);
 
1677
        int pass;
 
1678
 
 
1679
        g_string_append_printf (accumulated_error,
 
1680
                                _("Trying modes for CRTC %d\n"),
 
1681
                                crtc_id);
 
1682
 
 
1683
        /* Make two passes, one where frequencies must match, then
 
1684
         * one where they don't have to
 
1685
         */
 
1686
        for (pass = 0; pass < 2; ++pass)
 
1687
        {
 
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);
 
1690
            int j;
 
1691
 
 
1692
            for (j = 0; modes[j] != NULL; ++j)
 
1693
            {
 
1694
                GnomeRRMode *mode = modes[j];
 
1695
                int mode_width;
 
1696
                int mode_height;
 
1697
                int mode_freq;
 
1698
 
 
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);
 
1702
 
 
1703
                g_string_append_printf (accumulated_error,
 
1704
                                        _("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"),
 
1705
                                        crtc_id,
 
1706
                                        mode_width, mode_height, mode_freq,
 
1707
                                        output->priv->width, output->priv->height, output->priv->rate,
 
1708
                                        pass);
 
1709
 
 
1710
                if (mode_width == output->priv->width   &&
 
1711
                    mode_height == output->priv->height &&
 
1712
                    (pass == 1 || mode_freq == output->priv->rate))
 
1713
                {
 
1714
                    tried_mode = TRUE;
 
1715
 
 
1716
                    my_error = NULL;
 
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,
 
1722
                            gnome_rr_output,
 
1723
                            &my_error))
 
1724
                    {
 
1725
                        my_error = NULL;
 
1726
                        if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) {
 
1727
                            success = TRUE;
 
1728
                            goto out;
 
1729
                        } else
 
1730
                            accumulate_error (accumulated_error, my_error);
 
1731
 
 
1732
                        crtc_assignment_unassign (assignment, crtc, gnome_rr_output);
 
1733
                    } else
 
1734
                        accumulate_error (accumulated_error, my_error);
 
1735
                }
 
1736
            }
 
1737
        }
 
1738
    }
 
1739
 
 
1740
out:
 
1741
 
 
1742
    if (success)
 
1743
        g_string_free (accumulated_error, TRUE);
 
1744
    else {
 
1745
        char *str;
 
1746
 
 
1747
        str = g_string_free (accumulated_error, FALSE);
 
1748
 
 
1749
        if (tried_mode)
 
1750
            g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
 
1751
                         _("could not assign CRTCs to outputs:\n%s"),
 
1752
                         str);
 
1753
        else
 
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"),
 
1756
                         str);
 
1757
 
 
1758
        g_free (str);
 
1759
    }
 
1760
 
 
1761
    return success;
 
1762
}
 
1763
 
 
1764
static void
 
1765
crtc_info_free (CrtcInfo *info)
 
1766
{
 
1767
    g_ptr_array_free (info->outputs, TRUE);
 
1768
    g_free (info);
 
1769
}
 
1770
 
 
1771
static void
 
1772
get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
 
1773
{
 
1774
    GList *active_crtcs = g_hash_table_get_keys (assign->info);
 
1775
    GList *list;
 
1776
    int d;
 
1777
 
 
1778
    if (!width)
 
1779
        width = &d;
 
1780
    if (!height)
 
1781
        height = &d;
 
1782
    
 
1783
    /* Compute size of the screen */
 
1784
    *width = *height = 1;
 
1785
    for (list = active_crtcs; list != NULL; list = list->next)
 
1786
    {
 
1787
        GnomeRRCrtc *crtc = list->data;
 
1788
        CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
 
1789
        int w, h;
 
1790
 
 
1791
        w = gnome_rr_mode_get_width (info->mode);
 
1792
        h = gnome_rr_mode_get_height (info->mode);
 
1793
        
 
1794
        if (mode_is_rotated (info))
 
1795
        {
 
1796
            int tmp = h;
 
1797
            h = w;
 
1798
            w = tmp;
 
1799
        }
 
1800
        
 
1801
        *width = MAX (*width, info->x + w);
 
1802
        *height = MAX (*height, info->y + h);
 
1803
    }
 
1804
 
 
1805
    g_list_free (active_crtcs);
 
1806
}
 
1807
 
 
1808
static CrtcAssignment *
 
1809
crtc_assignment_new (GnomeRRScreen *screen, GnomeRROutputInfo **outputs, GError **error)
 
1810
{
 
1811
    CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
 
1812
 
 
1813
    assignment->info = g_hash_table_new_full (
 
1814
        g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
 
1815
 
 
1816
    if (real_assign_crtcs (screen, outputs, assignment, error))
 
1817
    {
 
1818
        int width, height;
 
1819
        int min_width, max_width, min_height, max_height;
 
1820
 
 
1821
        get_required_virtual_size (assignment, &width, &height);
 
1822
 
 
1823
        gnome_rr_screen_get_ranges (
 
1824
            screen, &min_width, &max_width, &min_height, &max_height);
 
1825
    
 
1826
        if (width < min_width || width > max_width ||
 
1827
            height < min_height || height > max_height)
 
1828
        {
 
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)"),
 
1835
                         width, height,
 
1836
                         min_width, min_height,
 
1837
                         max_width, max_height);
 
1838
            goto fail;
 
1839
        }
 
1840
 
 
1841
        assignment->screen = screen;
 
1842
        
 
1843
        return assignment;
 
1844
    }
 
1845
 
 
1846
fail:
 
1847
    crtc_assignment_free (assignment);
 
1848
    
 
1849
    return NULL;
 
1850
}
 
1851
 
 
1852
static gboolean
 
1853
crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error)
 
1854
{
 
1855
    GnomeRRCrtc **all_crtcs = gnome_rr_screen_list_crtcs (assign->screen);
 
1856
    int width, height;
 
1857
    int i;
 
1858
    int min_width, max_width, min_height, max_height;
 
1859
    int width_mm, height_mm;
 
1860
    gboolean success = TRUE;
 
1861
 
 
1862
    /* Compute size of the screen */
 
1863
    get_required_virtual_size (assign, &width, &height);
 
1864
 
 
1865
    gnome_rr_screen_get_ranges (
 
1866
        assign->screen, &min_width, &max_width, &min_height, &max_height);
 
1867
 
 
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.
 
1870
     */
 
1871
    width = MAX (min_width, width);
 
1872
    width = MIN (max_width, width);
 
1873
    height = MAX (min_height, height);
 
1874
    height = MIN (max_height, height);
 
1875
 
 
1876
    /* FMQ: do we need to check the sizes instead of clamping them? */
 
1877
 
 
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
 
1880
     * status.
 
1881
     */
 
1882
 
 
1883
    gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
 
1884
 
 
1885
    /* Turn off all crtcs that are currently displaying outside the new screen,
 
1886
     * or are not used in the new setup
 
1887
     */
 
1888
    for (i = 0; all_crtcs[i] != NULL; ++i)
 
1889
    {
 
1890
        GnomeRRCrtc *crtc = all_crtcs[i];
 
1891
        GnomeRRMode *mode = gnome_rr_crtc_get_current_mode (crtc);
 
1892
        int x, y;
 
1893
 
 
1894
        if (mode)
 
1895
        {
 
1896
            int w, h;
 
1897
            gnome_rr_crtc_get_position (crtc, &x, &y);
 
1898
 
 
1899
            w = gnome_rr_mode_get_width (mode);
 
1900
            h = gnome_rr_mode_get_height (mode);
 
1901
 
 
1902
            if (crtc_is_rotated (crtc))
 
1903
            {
 
1904
                int tmp = h;
 
1905
                h = w;
 
1906
                w = tmp;
 
1907
            }
 
1908
            
 
1909
            if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
 
1910
            {
 
1911
                if (!gnome_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, GNOME_RR_ROTATION_0, NULL, 0, error))
 
1912
                {
 
1913
                    success = FALSE;
 
1914
                    break;
 
1915
                }
 
1916
                
 
1917
            }
 
1918
        }
 
1919
    }
 
1920
 
 
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
 
1923
     * dpi 96.
 
1924
     *
 
1925
     * Firefox and Evince apparently believe what X tells them.
 
1926
     */
 
1927
    width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
 
1928
    height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
 
1929
 
 
1930
    if (success)
 
1931
    {
 
1932
        ConfigureCrtcState state;
 
1933
 
 
1934
        gnome_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm);
 
1935
 
 
1936
        state.timestamp = timestamp;
 
1937
        state.has_error = FALSE;
 
1938
        state.error = error;
 
1939
        
 
1940
        g_hash_table_foreach (assign->info, configure_crtc, &state);
 
1941
 
 
1942
        success = !state.has_error;
 
1943
    }
 
1944
 
 
1945
    gnome_rr_screen_set_primary_output (assign->screen, assign->primary);
 
1946
 
 
1947
    gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
 
1948
 
 
1949
    return success;
 
1950
}