gsd-xsettings-manager.c 42.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2007 Rodrigo Moya
 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
Behdad Esfahbod's avatar
Behdad Esfahbod committed
31
#include <time.h>
32

Martin Pitt's avatar
Martin Pitt committed
33
34
#include <X11/Xatom.h>

35
36
37
38
39
40
#include <glib.h>
#include <glib/gi18n.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>

41
#include "gnome-settings-profile.h"
42
#include "gsd-enums.h"
43
#include "gsd-xsettings-manager.h"
44
#include "gsd-xsettings-gtk.h"
45
#include "xsettings-manager.h"
Behdad Esfahbod's avatar
Behdad Esfahbod committed
46
#include "fontconfig-monitor.h"
47
48
49

#define GNOME_XSETTINGS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManagerPrivate))

50
51
52
#define MOUSE_SETTINGS_SCHEMA     "org.gnome.settings-daemon.peripherals.mouse"
#define INTERFACE_SETTINGS_SCHEMA "org.gnome.desktop.interface"
#define SOUND_SETTINGS_SCHEMA     "org.gnome.desktop.sound"
53
#define PRIVACY_SETTINGS_SCHEMA     "org.gnome.desktop.privacy"
54

55
#define XSETTINGS_PLUGIN_SCHEMA "org.gnome.settings-daemon.plugins.xsettings"
56
#define XSETTINGS_OVERRIDE_KEY  "overrides"
57
58
59

#define GTK_MODULES_DISABLED_KEY "disabled-gtk-modules"
#define GTK_MODULES_ENABLED_KEY  "enabled-gtk-modules"
60

61
#define TEXT_SCALING_FACTOR_KEY "text-scaling-factor"
62
63
#define SCALING_FACTOR_KEY "scaling-factor"
#define CURSOR_SIZE_KEY "cursor-size"
64

65
66
#define FONT_ANTIALIASING_KEY "antialiasing"
#define FONT_HINTING_KEY      "hinting"
67
#define FONT_RGBA_ORDER_KEY   "rgba-order"
68

69
70
71
/* As we cannot rely on the X server giving us good DPI information, and
 * that we don't want multi-monitor screens to have different DPIs (thus
 * different text sizes), we'll hard-code the value of the DPI
72
 *
73
74
75
 * See also:
 * https://bugzilla.novell.com/show_bug.cgi?id=217790•
 * https://bugzilla.gnome.org/show_bug.cgi?id=643704
Colin Walters's avatar
Colin Walters committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
 *
 * http://lists.fedoraproject.org/pipermail/devel/2011-October/157671.html
 * Why EDID is not trustworthy for DPI
 * Adam Jackson ajax at redhat.com
 * Tue Oct 4 17:54:57 UTC 2011
 * 
 *     Previous message: GNOME 3 - font point sizes now scaled?
 *     Next message: Why EDID is not trustworthy for DPI
 *     Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
 * 
 * On Tue, 2011-10-04 at 11:46 -0400, Kaleb S. KEITHLEY wrote:
 * 
 * > Grovelling around in the F15 xorg-server sources and reviewing the Xorg 
 * > log file on my F15 box, I see, with _modern hardware_ at least, that we 
 * > do have the monitor geometry available from DDC or EDIC, and obviously 
 * > it is trivial to compute the actual, correct DPI for each screen.
 * 
 * I am clearly going to have to explain this one more time, forever.
 * Let's see if I can't write it authoritatively once and simply answer
 * with a URL from here out.  (As always, use of the second person "you"
 * herein is plural, not singular.)
 * 
 * EDID does not reliably give you the size of the display.
 * 
 * Base EDID has at least two different places where you can give a
 * physical size (before considering extensions that aren't widely deployed
 * so whatever).  The first is a global property, measured in centimeters,
 * of the physical size of the glass.  The second is attached to your (zero
 * or more) detailed timing specifications, and reflects the size of the
 * mode, in millimeters.
 * 
 * So, how does this screw you?
 * 
 * a) Glass size is too coarse.  On a large display that cm roundoff isn't
 * a big deal, but on subnotebooks it's a different game.  The 11" MBA is
 * 25.68x14.44 cm, so that gives you a range of 52.54-54.64 dpcm horizontal
 * and 51.20-54.86 dpcm vertical (133.4-138.8 dpi h and 130.0-139.3 dpi v).
 * Which is optimistic, because that's doing the math forward from knowing
 * the actual size, and you as the EDID parser can't know which way the
 * manufacturer rounded.
 * 
 * b) Glass size need not be non-zero.  This is in fact the usual case for
 * projectors, which don't have a fixed display size since it's a function
 * of how far away the wall is from the lens.
 * 
 * c) Glass size could be partially non-zero.  Yes, really.  EDID 1.4
 * defines a method of using these two bytes to encode aspect ratio, where
 * if vertical size is 0 then the aspect ratio is computed as (horizontal
 * value + 99) / 100 in portrait mode (and the obvious reverse thing if
 * horizontal is zero).  Admittedly, unlike every other item in this list,
 * I've never seen this in the wild.  But it's legal.
 * 
 * d) Glass size could be a direct encoding of the aspect ratio.  Base EDID
 * doesn't condone this behaviour, but the CEA spec (to which all HDMI
 * monitors must conform) does allow-but-not-require it, which means your
 * 1920x1080 TV could claim to be 16 "cm" by 9 "cm".  So of course that's
 * what TV manufacturers do because that way they don't have to modify the
 * EDID info when physical construction changes, and that's cheaper.
 * 
 * e) You could use mode size to get size in millimeters, but you might not
 * have any detailed timings.
 * 
 * f) You could use mode size, but mode size is explicitly _not_ glass
 * size.  It's the size that the display chooses to present that mode.
 * Sometimes those are the same, and sometimes they're not.  You could be
 * scaled or {letter,pillar}boxed, and that's not necessarily something you
 * can control from the host side.
 * 
 * g) You could use mode size, but it could be an encoded aspect ratio, as
 * in case d above, because CEA says that's okay.
 * 
 * h) You could use mode size, but it could be the aspect ratio from case d
 * multiplied by 10 in each direction (because, of course, you gave size in
 * centimeters and so your authoring tool just multiplied it up).
 * 
 * i) Any or all of the above could be complete and utter garbage, because
 * - and I really, really need you to understand this - there is no
 * requirements program for any commercial OS or industry standard that
 * requires honesty here, as far as I'm aware.  There is every incentive
 * for there to _never_ be one, because it would make the manufacturing
 * process more expensive.
 * 
 * So from this point the suggestion is usually "well come up with some
 * heuristic to make a good guess assuming there's some correlation between
 * the various numbers you're given".  I have in fact written heuristics
 * for this, and they're in your kernel and your X server, and they still
 * encounter a huge number of cases where we simply _cannot_ know from EDID
 * anything like a physical size, because - to pick only one example - the
 * consumer electronics industry are cheap bastards, because you the
 * consumer demanded that they be cheap.
 * 
 * And then your only recourse is to an external database, and now you're
 * up the creek again because the identifying information here is a
 * vendor/model/serial tuple, and the vendor can and does change physical
 * construction without changing model number.  Now you get to play the
 * guessing game of how big the serial number range is for each subvariant,
 * assuming they bothered to encode a serial number - and they didn't.  Or,
 * if they bothered to encode week/year of manufacturer correctly - and
 * they didn't - which weeks meant which models.  And then you still have
 * to go out and buy one of every TV at Fry's, and that covers you for one
 * market, for three months.
 * 
 * If someone wants to write something better, please, by all means.  If
 * it's kernel code, send it to dri-devel at lists.freedesktop.org and cc me
 * and I will happily review it.  Likewise xorg-devel@ for X server
 * changes.
 * 
 * I gently suggest that doing so is a waste of time.
 * 
 * But if there's one thing free software has taught me, it's that you can
 * not tell people something is a bad idea and have any expectation they
 * will believe you.
 * 
 * > Obviously in a multi-screen set-up using Xinerama this has the potential 
 * > to be a Hard Problem if the monitors differ greatly in their DPI.
 * > 
 * > If the major resistance is over what to do with older hardware that 
 * > doesn't have this data available, then yes, punt; use a hard-coded 
 * > default. Likewise, if the two monitors really differ greatly, then punt.
 * 
 * I'm going to limit myself to observing that "greatly" is a matter of
 * opinion, and that in order to be really useful you'd need some way of
 * communicating "I punted" to the desktop.
 * 
 * Beyond that, sure, pick a heuristic, accept that it's going to be
 * insufficient for someone, and then sit back and wait to get
 * second-guessed on it over and over.
 * 
 * > And it wouldn't be so hard to to add something like -dpi:0, -dpi:1, 
 * > -dpi:2 command line options to specify per-screen dpi. I kinda thought I 
 * > did that a long, long time ago, but maybe I only thought about doing it 
 * > and never actually got around to it.
 * 
 * The RANDR extension as of version 1.2 does allow you to override
 * physical size on a per-output basis at runtime.  We even try pretty hard
 * to set them as honestly as we can up front.  The 96dpi thing people
 * complain about is from the per-screen info, which is simply a default
 * because of all the tl;dr above; because you have N outputs per screen
 * which means a single number is in general useless; and because there is
 * no way to refresh the per-screen info at runtime, as it's only ever sent
 * in the initial connection handshake.
 * 
 * - ajax
 * 
220
221
222
 */
#define DPI_FALLBACK 96

223
224
#define HIDPI_LIMIT (DPI_FALLBACK * 2)

225
226
227
typedef struct _TranslationEntry TranslationEntry;
typedef void (* TranslationFunc) (GnomeXSettingsManager *manager,
                                  TranslationEntry      *trans,
228
                                  GVariant              *value);
229
230

struct _TranslationEntry {
231
232
        const char     *gsettings_schema;
        const char     *gsettings_key;
233
234
235
236
237
        const char     *xsetting_name;

        TranslationFunc translate;
};

238
239
240
241
242
243
244
245
typedef struct _FixedEntry FixedEntry;
typedef void (* FixedFunc) (GnomeXSettingsManager *manager,
                            FixedEntry            *fixed);
struct _FixedEntry {
        const char     *xsetting_name;
        FixedFunc       func;
};

246
247
struct GnomeXSettingsManagerPrivate
{
248
        guint              start_idle_id;
249
        XSettingsManager **managers;
250
251
        GHashTable        *settings;

252
        GSettings         *plugin_settings;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
253
        fontconfig_monitor_handle_t *fontconfig_handle;
254
255

        GsdXSettingsGtk   *gtk;
256
257

        guint              shell_name_watch_id;
258
259
260
        gboolean           have_shell;

        guint              notify_idle_id;
261
262
263
264
265
266
};

#define GSD_XSETTINGS_ERROR gsd_xsettings_error_quark ()

enum {
        GSD_XSETTINGS_ERROR_INIT
267
268
269
270
271
272
273
274
275
276
};

static void     gnome_xsettings_manager_class_init  (GnomeXSettingsManagerClass *klass);
static void     gnome_xsettings_manager_init        (GnomeXSettingsManager      *xsettings_manager);
static void     gnome_xsettings_manager_finalize    (GObject                  *object);

G_DEFINE_TYPE (GnomeXSettingsManager, gnome_xsettings_manager, G_TYPE_OBJECT)

static gpointer manager_object = NULL;

277
278
279
280
281
282
static GQuark
gsd_xsettings_error_quark (void)
{
        return g_quark_from_static_string ("gsd-xsettings-error-quark");
}

283
284
285
static void
translate_bool_int (GnomeXSettingsManager *manager,
                    TranslationEntry      *trans,
286
                    GVariant              *value)
287
288
289
290
291
{
        int i;

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
292
                                           g_variant_get_boolean (value));
293
294
295
296
297
298
        }
}

static void
translate_int_int (GnomeXSettingsManager *manager,
                   TranslationEntry      *trans,
299
                   GVariant              *value)
300
301
302
303
304
{
        int i;

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
305
                                           g_variant_get_int32 (value));
306
307
308
309
310
311
        }
}

static void
translate_string_string (GnomeXSettingsManager *manager,
                         TranslationEntry      *trans,
312
                         GVariant              *value)
313
314
315
316
317
318
{
        int i;

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_string (manager->priv->managers [i],
                                              trans->xsetting_name,
319
                                              g_variant_get_string (value, NULL));
320
321
322
        }
}

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
static void
fixed_false_int (GnomeXSettingsManager *manager,
                 FixedEntry            *fixed)
{
        int i;

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], fixed->xsetting_name, FALSE);
        }
}

static void
fixed_true_int (GnomeXSettingsManager *manager,
                FixedEntry            *fixed)
{
        int i;

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], fixed->xsetting_name, TRUE);
        }
}

static FixedEntry fixed_entries [] = {
        { "Gtk/MenuImages",          fixed_false_int },
        { "Gtk/ButtonImages",        fixed_false_int },
        { "Gtk/ShowInputMethodMenu", fixed_false_int },
        { "Gtk/ShowUnicodeMenu",     fixed_false_int },
        { "Gtk/AutoMnemonics",       fixed_true_int },
351
        { "Gtk/EnablePrimaryPaste",  fixed_true_int },
352
353
};

354
static TranslationEntry translations [] = {
355
356
357
358
359
360
        { "org.gnome.settings-daemon.peripherals.mouse", "double-click",   "Net/DoubleClickTime",  translate_int_int },
        { "org.gnome.settings-daemon.peripherals.mouse", "drag-threshold", "Net/DndDragThreshold", translate_int_int },

        { "org.gnome.desktop.interface", "gtk-color-palette",      "Gtk/ColorPalette",        translate_string_string },
        { "org.gnome.desktop.interface", "font-name",              "Gtk/FontName",            translate_string_string },
        { "org.gnome.desktop.interface", "gtk-key-theme",          "Gtk/KeyThemeName",        translate_string_string },
361
        { "org.gnome.desktop.interface", "toolbar-style",          "Gtk/ToolbarStyle",        translate_string_string },
362
363
364
365
        { "org.gnome.desktop.interface", "toolbar-icons-size",     "Gtk/ToolbarIconSize",     translate_string_string },
        { "org.gnome.desktop.interface", "can-change-accels",      "Gtk/CanChangeAccels",     translate_bool_int },
        { "org.gnome.desktop.interface", "cursor-blink",           "Net/CursorBlink",         translate_bool_int },
        { "org.gnome.desktop.interface", "cursor-blink-time",      "Net/CursorBlinkTime",     translate_int_int },
366
        { "org.gnome.desktop.interface", "cursor-blink-timeout",   "Gtk/CursorBlinkTimeout",  translate_int_int },
367
368
369
370
371
372
373
374
375
376
377
        { "org.gnome.desktop.interface", "gtk-theme",              "Net/ThemeName",           translate_string_string },
        { "org.gnome.desktop.interface", "gtk-timeout-initial",    "Gtk/TimeoutInitial",      translate_int_int },
        { "org.gnome.desktop.interface", "gtk-timeout-repeat",     "Gtk/TimeoutRepeat",       translate_int_int },
        { "org.gnome.desktop.interface", "gtk-color-scheme",       "Gtk/ColorScheme",         translate_string_string },
        { "org.gnome.desktop.interface", "gtk-im-preedit-style",   "Gtk/IMPreeditStyle",      translate_string_string },
        { "org.gnome.desktop.interface", "gtk-im-status-style",    "Gtk/IMStatusStyle",       translate_string_string },
        { "org.gnome.desktop.interface", "gtk-im-module",          "Gtk/IMModule",            translate_string_string },
        { "org.gnome.desktop.interface", "icon-theme",             "Net/IconThemeName",       translate_string_string },
        { "org.gnome.desktop.interface", "menubar-accel",          "Gtk/MenuBarAccel",        translate_string_string },
        { "org.gnome.desktop.interface", "enable-animations",      "Gtk/EnableAnimations",    translate_bool_int },
        { "org.gnome.desktop.interface", "cursor-theme",           "Gtk/CursorThemeName",     translate_string_string },
378
        /* cursor-size is handled via the Xft side as it needs the scaling factor */
379
380
381

        { "org.gnome.desktop.sound", "theme-name",                 "Net/SoundThemeName",            translate_string_string },
        { "org.gnome.desktop.sound", "event-sounds",               "Net/EnableEventSounds" ,        translate_bool_int },
382
383
        { "org.gnome.desktop.sound", "input-feedback-sounds",      "Net/EnableInputFeedbackSounds", translate_bool_int },

384
385
        { "org.gnome.desktop.privacy", "recent-files-max-age",      "Gtk/RecentFilesMaxAge", translate_int_int },
        { "org.gnome.desktop.privacy", "remember-recent-files",    "Gtk/RecentFilesEnabled", translate_bool_int }
386
387
};

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
static gboolean
notify_idle (gpointer data)
{
        GnomeXSettingsManager *manager = data;
        gint i;
        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_notify (manager->priv->managers[i]);
        }
        manager->priv->notify_idle_id = 0;
        return G_SOURCE_REMOVE;
}

static void
queue_notify (GnomeXSettingsManager *manager)
{
        if (manager->priv->notify_idle_id != 0)
                return;

        manager->priv->notify_idle_id = g_idle_add (notify_idle, manager);
}

409
static double
410
get_dpi_from_gsettings (GnomeXSettingsManager *manager)
411
{
412
	GSettings  *interface_settings;
413
        double      dpi;
414
        double      factor;
415

416
417
	interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA);
        factor = g_settings_get_double (interface_settings, TEXT_SCALING_FACTOR_KEY);
418

419
	dpi = DPI_FALLBACK;
420
421

        return dpi * factor;
422
423
}

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
static int
get_window_scale (GnomeXSettingsManager *manager)
{
	GSettings  *interface_settings;
        int window_scale;
        GdkRectangle rect;
        GdkDisplay *display;
        GdkScreen *screen;
        int width_mm, height_mm;
        int monitor_scale;
        double dpi_x, dpi_y;

	interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA);
        window_scale =
                g_settings_get_uint (interface_settings, SCALING_FACTOR_KEY);
        if (window_scale == 0) {
                display = gdk_display_get_default ();
                screen = gdk_display_get_default_screen (display);
                gdk_screen_get_monitor_geometry (screen, 0, &rect);
                width_mm = gdk_screen_get_monitor_width_mm (screen, 0);
                height_mm = gdk_screen_get_monitor_height_mm (screen, 0);
                monitor_scale = gdk_screen_get_monitor_scale_factor (screen, 0);

                window_scale = 1;
                if (width_mm > 0 && height_mm > 0) {
                        dpi_x = (double)rect.width * monitor_scale / (width_mm / 25.4);
                        dpi_y = (double)rect.height * monitor_scale / (height_mm / 25.4);
                        /* We don't completely trust these values so both
                           must be high, and never pick higher ratio than
                           2 automatically */
                        if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT)
                                window_scale = 2;
                }
        }

        return window_scale;
}

462
typedef struct {
463
464
        gboolean    antialias;
        gboolean    hinting;
465
        int         scaled_dpi;
466
        int         dpi;
467
468
        int         window_scale;
        int         cursor_size;
469
470
471
472
        const char *rgba;
        const char *hintstyle;
} GnomeXftSettings;

473
/* Read GSettings and determine the appropriate Xft settings based on them. */
474
static void
475
476
xft_settings_get (GnomeXSettingsManager *manager,
                  GnomeXftSettings      *settings)
477
{
478
	GSettings  *interface_settings;
479
480
        GsdFontAntialiasingMode antialiasing;
        GsdFontHinting hinting;
481
        GsdFontRgbaOrder order;
482
        gboolean use_rgba = FALSE;
483
484
485
486
        double dpi;
        int cursor_size;

	interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA);
487

488
489
        antialiasing = g_settings_get_enum (manager->priv->plugin_settings, FONT_ANTIALIASING_KEY);
        hinting = g_settings_get_enum (manager->priv->plugin_settings, FONT_HINTING_KEY);
490
        order = g_settings_get_enum (manager->priv->plugin_settings, FONT_RGBA_ORDER_KEY);
491

492
493
        settings->antialias = (antialiasing != GSD_FONT_ANTIALIASING_MODE_NONE);
        settings->hinting = (hinting != GSD_FONT_HINTING_NONE);
494
495
496
497
498
499
        settings->window_scale = get_window_scale (manager);
        dpi = get_dpi_from_gsettings (manager);
        settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */
        settings->scaled_dpi = dpi * settings->window_scale * 1024;
        cursor_size = g_settings_get_int (interface_settings, CURSOR_SIZE_KEY);
        settings->cursor_size = cursor_size * settings->window_scale;
500
        settings->rgba = "rgb";
501
        settings->hintstyle = "hintfull";
502

503
504
505
506
507
508
509
510
511
512
513
514
515
        switch (hinting) {
        case GSD_FONT_HINTING_NONE:
                settings->hintstyle = "hintnone";
                break;
        case GSD_FONT_HINTING_SLIGHT:
                settings->hintstyle = "hintslight";
                break;
        case GSD_FONT_HINTING_MEDIUM:
                settings->hintstyle = "hintmedium";
                break;
        case GSD_FONT_HINTING_FULL:
                settings->hintstyle = "hintfull";
                break;
516
517
        }

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
        switch (order) {
        case GSD_FONT_RGBA_ORDER_RGBA:
                settings->rgba = "rgba";
                break;
        case GSD_FONT_RGBA_ORDER_RGB:
                settings->rgba = "rgb";
                break;
        case GSD_FONT_RGBA_ORDER_BGR:
                settings->rgba = "bgr";
                break;
        case GSD_FONT_RGBA_ORDER_VRGB:
                settings->rgba = "vrgb";
                break;
        case GSD_FONT_RGBA_ORDER_VBGR:
                settings->rgba = "vbgr";
                break;
        }

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
        switch (antialiasing) {
        case GSD_FONT_ANTIALIASING_MODE_NONE:
                settings->antialias = 0;
                break;
        case GSD_FONT_ANTIALIASING_MODE_GRAYSCALE:
                settings->antialias = 1;
                break;
        case GSD_FONT_ANTIALIASING_MODE_RGBA:
                settings->antialias = 1;
                use_rgba = TRUE;
        }

        if (!use_rgba) {
                settings->rgba = "none";
        }
551
552
553
554
555
556
557
}

static void
xft_settings_set_xsettings (GnomeXSettingsManager *manager,
                            GnomeXftSettings      *settings)
{
        int i;
558
559
560

        gnome_settings_profile_start (NULL);

561
562
563
564
        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], "Xft/Antialias", settings->antialias);
                xsettings_manager_set_int (manager->priv->managers [i], "Xft/Hinting", settings->hinting);
                xsettings_manager_set_string (manager->priv->managers [i], "Xft/HintStyle", settings->hintstyle);
565
566
567
                xsettings_manager_set_int (manager->priv->managers [i], "Gdk/WindowScalingFactor", settings->window_scale);
                xsettings_manager_set_int (manager->priv->managers [i], "Gdk/UnscaledDPI", settings->dpi);
                xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->scaled_dpi);
568
                xsettings_manager_set_string (manager->priv->managers [i], "Xft/RGBA", settings->rgba);
569
                xsettings_manager_set_int (manager->priv->managers [i], "Gtk/CursorThemeSize", settings->cursor_size);
570
        }
571
        gnome_settings_profile_end (NULL);
572
573
574
}

static void
Martin Pitt's avatar
Martin Pitt committed
575
update_property (GString *props, const gchar* key, const gchar* value)
576
{
Martin Pitt's avatar
Martin Pitt committed
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
        gchar* needle;
        size_t needle_len;
        gchar* found = NULL;

        /* update an existing property */
        needle = g_strconcat (key, ":", NULL);
        needle_len = strlen (needle);
        if (g_str_has_prefix (props->str, needle))
                found = props->str;
        else 
            found = strstr (props->str, needle);

        if (found) {
                size_t value_index;
                gchar* end;

                end = strchr (found, '\n');
                value_index = (found - props->str) + needle_len + 1;
                g_string_erase (props, value_index, end ? (end - found - needle_len) : -1);
                g_string_insert (props, value_index, "\n");
                g_string_insert (props, value_index, value);
        } else {
                g_string_append_printf (props, "%s:\t%s\n", key, value);
600
        }
Christian Persch's avatar
Christian Persch committed
601
602

	g_free (needle);
603
604
605
606
607
608
}

static void
xft_settings_set_xresources (GnomeXftSettings *settings)
{
        GString    *add_string;
609
        char        dpibuf[G_ASCII_DTOSTR_BUF_SIZE];
Martin Pitt's avatar
Martin Pitt committed
610
        Display    *dpy;
611

612
613
        gnome_settings_profile_start (NULL);

Martin Pitt's avatar
Martin Pitt committed
614
615
616
617
        /* get existing properties */
        dpy = XOpenDisplay (NULL);
        g_return_if_fail (dpy != NULL);
        add_string = g_string_new (XResourceManagerString (dpy));
618

Martin Pitt's avatar
Martin Pitt committed
619
        g_debug("xft_settings_set_xresources: orig res '%s'", add_string->str);
620

Martin Pitt's avatar
Martin Pitt committed
621
        update_property (add_string, "Xft.dpi",
622
                                g_ascii_dtostr (dpibuf, sizeof (dpibuf), (double) settings->scaled_dpi / 1024.0));
Martin Pitt's avatar
Martin Pitt committed
623
624
625
626
627
        update_property (add_string, "Xft.antialias",
                                settings->antialias ? "1" : "0");
        update_property (add_string, "Xft.hinting",
                                settings->hinting ? "1" : "0");
        update_property (add_string, "Xft.hintstyle",
628
                                settings->hintstyle);
Martin Pitt's avatar
Martin Pitt committed
629
        update_property (add_string, "Xft.rgba",
630
631
                                settings->rgba);

Martin Pitt's avatar
Martin Pitt committed
632
633
634
635
        g_debug("xft_settings_set_xresources: new res '%s'", add_string->str);

        /* Set the new X property */
        XChangeProperty(dpy, RootWindow (dpy, 0),
636
                        XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, (const unsigned char *) add_string->str, add_string->len);
Martin Pitt's avatar
Martin Pitt committed
637
        XCloseDisplay (dpy);
638
639

        g_string_free (add_string, TRUE);
640
641

        gnome_settings_profile_end (NULL);
642
643
644
645
646
647
}

/* We mirror the Xft properties both through XSETTINGS and through
 * X resources
 */
static void
648
update_xft_settings (GnomeXSettingsManager *manager)
649
650
651
{
        GnomeXftSettings settings;

652
653
        gnome_settings_profile_start (NULL);

654
        xft_settings_get (manager, &settings);
655
656
        xft_settings_set_xsettings (manager, &settings);
        xft_settings_set_xresources (&settings);
657
658

        gnome_settings_profile_end (NULL);
659
660
661
}

static void
662
663
xft_callback (GSettings             *settings,
              const gchar           *key,
664
665
              GnomeXSettingsManager *manager)
{
666
        update_xft_settings (manager);
667
        queue_notify (manager);
668
669
}

670
671
672
673
674
675
676
677
678
679
680
681
682
static void
override_callback (GSettings             *settings,
                   const gchar           *key,
                   GnomeXSettingsManager *manager)
{
        GVariant *value;
        int i;

        value = g_settings_get_value (settings, XSETTINGS_OVERRIDE_KEY);

        for (i = 0; manager->priv->managers[i]; i++) {
                xsettings_manager_set_overrides (manager->priv->managers[i], value);
        }
683
        queue_notify (manager);
684
685
686
687

        g_variant_unref (value);
}

688
689
690
691
692
693
694
695
static void
plugin_callback (GSettings             *settings,
                 const char            *key,
                 GnomeXSettingsManager *manager)
{
        if (g_str_equal (key, GTK_MODULES_DISABLED_KEY) ||
            g_str_equal (key, GTK_MODULES_ENABLED_KEY)) {
                /* Do nothing, as GsdXsettingsGtk will handle it */
696
697
        } else if (g_str_equal (key, XSETTINGS_OVERRIDE_KEY)) {
                override_callback (settings, key, manager);
698
699
700
701
702
703
704
705
706
707
        } else {
                xft_callback (settings, key, manager);
        }
}

static void
gtk_modules_callback (GsdXSettingsGtk       *gtk,
                      GParamSpec            *spec,
                      GnomeXSettingsManager *manager)
{
708
        const char *modules = gsd_xsettings_gtk_get_modules (manager->priv->gtk);
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
        int i;

        if (modules == NULL) {
                for (i = 0; manager->priv->managers [i]; ++i) {
                        xsettings_manager_delete_setting (manager->priv->managers [i], "Gtk/Modules");
                }
        } else {
                g_debug ("Setting GTK modules '%s'", modules);
                for (i = 0; manager->priv->managers [i]; ++i) {
                        xsettings_manager_set_string (manager->priv->managers [i],
                                                      "Gtk/Modules",
                                                      modules);
                }
        }

724
        queue_notify (manager);
725
726
}

Behdad Esfahbod's avatar
Behdad Esfahbod committed
727
728
729
730
731
732
733
734
735
736
737
738
static void
fontconfig_callback (fontconfig_monitor_handle_t *handle,
                     GnomeXSettingsManager       *manager)
{
        int i;
        int timestamp = time (NULL);

        gnome_settings_profile_start (NULL);

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], "Fontconfig/Timestamp", timestamp);
        }
739
        queue_notify (manager);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
740
741
742
        gnome_settings_profile_end (NULL);
}

743
744
745
746
747
748
749
750
751
static gboolean
start_fontconfig_monitor_idle_cb (GnomeXSettingsManager *manager)
{
        gnome_settings_profile_start (NULL);

        manager->priv->fontconfig_handle = fontconfig_monitor_start ((GFunc) fontconfig_callback, manager);

        gnome_settings_profile_end (NULL);

752
753
        manager->priv->start_idle_id = 0;

754
755
756
        return FALSE;
}

757
758
759
760
761
static void
start_fontconfig_monitor (GnomeXSettingsManager  *manager)
{
        gnome_settings_profile_start (NULL);

762
        fontconfig_cache_init ();
763

764
        manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_fontconfig_monitor_idle_cb, manager);
765
766
767
768
769
770
771

        gnome_settings_profile_end (NULL);
}

static void
stop_fontconfig_monitor (GnomeXSettingsManager  *manager)
{
772
773
774
775
        if (manager->priv->fontconfig_handle) {
                fontconfig_monitor_stop (manager->priv->fontconfig_handle);
                manager->priv->fontconfig_handle = NULL;
        }
776
}
777

778
779
780
781
782
783
784
static void
notify_have_shell (GnomeXSettingsManager   *manager,
                   gboolean                 have_shell)
{
        int i;

        gnome_settings_profile_start (NULL);
785
786
787
        if (manager->priv->have_shell == have_shell)
                return;
        manager->priv->have_shell = have_shell;
788
789
790
        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_int (manager->priv->managers [i], "Gtk/ShellShowsAppMenu", have_shell);
        }
791
        queue_notify (manager);
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
        gnome_settings_profile_end (NULL);
}

static void
on_shell_appeared (GDBusConnection *connection,
                   const gchar     *name,
                   const gchar     *name_owner,
                   gpointer         user_data)
{
        notify_have_shell (user_data, TRUE);
}

static void
on_shell_disappeared (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
{
        notify_have_shell (user_data, FALSE);
}

812
813
814
static void
process_value (GnomeXSettingsManager *manager,
               TranslationEntry      *trans,
815
               GVariant              *value)
816
{
817
        (* trans->translate) (manager, trans, value);
818
819
}

820
static TranslationEntry *
821
find_translation_entry (GSettings *settings, const char *key)
822
{
823
824
        guint i;
        char *schema;
825

826
827
828
829
830
831
        g_object_get (settings, "schema", &schema, NULL);

        for (i = 0; i < G_N_ELEMENTS (translations); i++) {
                if (g_str_equal (schema, translations[i].gsettings_schema) &&
                    g_str_equal (key, translations[i].gsettings_key)) {
                            g_free (schema);
832
833
834
835
                        return &translations[i];
                }
        }

836
837
        g_free (schema);

838
839
840
841
        return NULL;
}

static void
842
843
xsettings_callback (GSettings             *settings,
                    const char            *key,
844
845
846
                    GnomeXSettingsManager *manager)
{
        TranslationEntry *trans;
847
848
        guint             i;
        GVariant         *value;
849

850
851
        if (g_str_equal (key, TEXT_SCALING_FACTOR_KEY) ||
            g_str_equal (key, SCALING_FACTOR_KEY)) {
852
853
854
855
        	xft_callback (NULL, key, manager);
        	return;
	}

856
        trans = find_translation_entry (settings, key);
857
858
859
860
        if (trans == NULL) {
                return;
        }

861
862
863
864
        value = g_settings_get_value (settings, key);

        process_value (manager, trans, value);

865
        g_variant_unref (value);
866
867
868
869
870
871

        for (i = 0; manager->priv->managers [i]; i++) {
                xsettings_manager_set_string (manager->priv->managers [i],
                                              "Net/FallbackIconTheme",
                                              "gnome");
        }
872
        queue_notify (manager);
873
874
875
876
877
878
879
880
881
882
883
884
}

static void
terminate_cb (void *data)
{
        gboolean *terminated = data;

        if (*terminated) {
                return;
        }

        *terminated = TRUE;
885
        g_warning ("X Settings Manager is terminating");
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
        gtk_main_quit ();
}

static gboolean
setup_xsettings_managers (GnomeXSettingsManager *manager)
{
        GdkDisplay *display;
        int         i;
        int         n_screens;
        gboolean    res;
        gboolean    terminated;

        display = gdk_display_get_default ();
        n_screens = gdk_display_get_n_screens (display);

901
902
        res = xsettings_manager_check_running (gdk_x11_display_get_xdisplay (display),
                                               gdk_screen_get_number (gdk_screen_get_default ()));
903

904
        if (res) {
905
                g_warning ("You can only run one xsettings manager at a time; exiting");
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
                return FALSE;
        }

        manager->priv->managers = g_new0 (XSettingsManager *, n_screens + 1);

        terminated = FALSE;
        for (i = 0; i < n_screens; i++) {
                GdkScreen *screen;

                screen = gdk_display_get_screen (display, i);

                manager->priv->managers [i] = xsettings_manager_new (gdk_x11_display_get_xdisplay (display),
                                                                     gdk_screen_get_number (screen),
                                                                     terminate_cb,
                                                                     &terminated);
                if (! manager->priv->managers [i]) {
922
                        g_warning ("Could not create xsettings manager for screen %d!", i);
923
924
925
926
927
928
929
                        return FALSE;
                }
        }

        return TRUE;
}

930
931
932
933
934
935
936
937
938
939
940
941
942
943
static void
start_shell_monitor (GnomeXSettingsManager *manager)
{
        notify_have_shell (manager, TRUE);
        manager->priv->have_shell = TRUE;
        manager->priv->shell_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
                                                               "org.gnome.Shell",
                                                               0,
                                                               on_shell_appeared,
                                                               on_shell_disappeared,
                                                               manager,
                                                               NULL);
}

944
945
946
947
gboolean
gnome_xsettings_manager_start (GnomeXSettingsManager *manager,
                               GError               **error)
{
948
        GVariant    *overrides;
949
950
        guint        i;
        GList       *list, *l;
951
952

        g_debug ("Starting xsettings manager");
953
        gnome_settings_profile_start (NULL);
954

955
956
957
958
959
960
961
        if (!setup_xsettings_managers (manager)) {
                g_set_error (error, GSD_XSETTINGS_ERROR,
                             GSD_XSETTINGS_ERROR_INIT,
                             "Could not initialize xsettings manager.");
                return FALSE;
        }

962
963
        manager->priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                         NULL, (GDestroyNotify) g_object_unref);
964

965
966
967
968
969
970
        g_hash_table_insert (manager->priv->settings,
                             MOUSE_SETTINGS_SCHEMA, g_settings_new (MOUSE_SETTINGS_SCHEMA));
        g_hash_table_insert (manager->priv->settings,
                             INTERFACE_SETTINGS_SCHEMA, g_settings_new (INTERFACE_SETTINGS_SCHEMA));
        g_hash_table_insert (manager->priv->settings,
                             SOUND_SETTINGS_SCHEMA, g_settings_new (SOUND_SETTINGS_SCHEMA));
971
972
        g_hash_table_insert (manager->priv->settings,
                             PRIVACY_SETTINGS_SCHEMA, g_settings_new (PRIVACY_SETTINGS_SCHEMA));
973

974
975
976
977
978
        for (i = 0; i < G_N_ELEMENTS (fixed_entries); i++) {
                FixedEntry *fixed = &fixed_entries[i];
                (* fixed->func) (manager, fixed);
        }

979
        for (i = 0; i < G_N_ELEMENTS (translations); i++) {
980
981
982
983
984
985
986
987
                GVariant *val;
                GSettings *settings;

                settings = g_hash_table_lookup (manager->priv->settings,
                                                translations[i].gsettings_schema);
                if (settings == NULL) {
                        g_warning ("Schemas '%s' has not been setup", translations[i].gsettings_schema);
                        continue;
988
                }
989
990
991
992

                val = g_settings_get_value (settings, translations[i].gsettings_key);

                process_value (manager, &translations[i], val);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
993
                g_variant_unref (val);
994
995
        }

996
        list = g_hash_table_get_values (manager->priv->settings);
997
        for (l = list; l != NULL; l = l->next) {
998
                g_signal_connect_object (G_OBJECT (l->data), "changed", G_CALLBACK (xsettings_callback), manager, 0);
999
1000
        }
        g_list_free (list);
For faster browsing, not all history is shown. View entire blame