1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2007 Rodrigo Moya
4
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
#include <sys/types.h>
35
#include <glib/gi18n.h>
39
#include <gconf/gconf.h>
40
#include <gconf/gconf-client.h>
42
#include "gnome-xsettings-manager.h"
43
#include "xsettings-manager.h"
45
#define GNOME_XSETTINGS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManagerPrivate))
48
#define FONT_RENDER_DIR "/desktop/gnome/font_rendering"
49
#define FONT_ANTIALIASING_KEY FONT_RENDER_DIR "/antialiasing"
50
#define FONT_HINTING_KEY FONT_RENDER_DIR "/hinting"
51
#define FONT_RGBA_ORDER_KEY FONT_RENDER_DIR "/rgba_order"
52
#define FONT_DPI_KEY FONT_RENDER_DIR "/dpi"
54
/* X servers sometimes lie about the screen's physical dimensions, so we cannot
55
* compute an accurate DPI value. When this happens, the user gets fonts that
56
* are too huge or too tiny. So, we see what the server returns: if it reports
57
* something outside of the range [DPI_LOW_REASONABLE_VALUE,
58
* DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use
59
* DPI_FALLBACK instead.
61
* See get_dpi_from_gconf_or_server() below, and also
62
* https://bugzilla.novell.com/show_bug.cgi?id=217790
64
#define DPI_FALLBACK 96
65
#define DPI_LOW_REASONABLE_VALUE 50
66
#define DPI_HIGH_REASONABLE_VALUE 500
68
#endif /* HAVE_XFT2 */
70
typedef struct _TranslationEntry TranslationEntry;
71
typedef void (* TranslationFunc) (GnomeXSettingsManager *manager,
72
TranslationEntry *trans,
75
struct _TranslationEntry {
76
const char *gconf_key;
77
const char *xsetting_name;
79
GConfValueType gconf_type;
80
TranslationFunc translate;
83
struct GnomeXSettingsManagerPrivate
85
XSettingsManager **managers;
92
static void gnome_xsettings_manager_class_init (GnomeXSettingsManagerClass *klass);
93
static void gnome_xsettings_manager_init (GnomeXSettingsManager *xsettings_manager);
94
static void gnome_xsettings_manager_finalize (GObject *object);
96
G_DEFINE_TYPE (GnomeXSettingsManager, gnome_xsettings_manager, G_TYPE_OBJECT)
98
static gpointer manager_object = NULL;
101
translate_bool_int (GnomeXSettingsManager *manager,
102
TranslationEntry *trans,
107
g_assert (value->type == trans->gconf_type);
109
for (i = 0; manager->priv->managers [i]; i++) {
110
xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
111
gconf_value_get_bool (value));
116
translate_int_int (GnomeXSettingsManager *manager,
117
TranslationEntry *trans,
122
g_assert (value->type == trans->gconf_type);
124
for (i = 0; manager->priv->managers [i]; i++) {
125
xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
126
gconf_value_get_int (value));
131
translate_string_string (GnomeXSettingsManager *manager,
132
TranslationEntry *trans,
137
g_assert (value->type == trans->gconf_type);
139
for (i = 0; manager->priv->managers [i]; i++) {
140
xsettings_manager_set_string (manager->priv->managers [i],
141
trans->xsetting_name,
142
gconf_value_get_string (value));
147
translate_string_string_toolbar (GnomeXSettingsManager *manager,
148
TranslationEntry *trans,
154
g_assert (value->type == trans->gconf_type);
156
/* This is kind of a workaround since GNOME expects the key value to be
157
* "both_horiz" and gtk+ wants the XSetting to be "both-horiz".
159
tmp = gconf_value_get_string (value);
160
if (tmp && strcmp (tmp, "both_horiz") == 0) {
164
for (i = 0; manager->priv->managers [i]; i++) {
165
xsettings_manager_set_string (manager->priv->managers [i],
166
trans->xsetting_name,
171
static TranslationEntry translations [] = {
172
{ "/desktop/gnome/peripherals/mouse/double_click", "Net/DoubleClickTime", GCONF_VALUE_INT, translate_int_int },
173
{ "/desktop/gnome/peripherals/mouse/drag_threshold", "Net/DndDragThreshold", GCONF_VALUE_INT, translate_int_int },
174
{ "/desktop/gnome/gtk-color-palette", "Gtk/ColorPalette", GCONF_VALUE_STRING, translate_string_string },
175
{ "/desktop/gnome/interface/font_name", "Gtk/FontName", GCONF_VALUE_STRING, translate_string_string },
176
{ "/desktop/gnome/interface/gtk_key_theme", "Gtk/KeyThemeName", GCONF_VALUE_STRING, translate_string_string },
177
{ "/desktop/gnome/interface/toolbar_style", "Gtk/ToolbarStyle", GCONF_VALUE_STRING, translate_string_string_toolbar },
178
{ "/desktop/gnome/interface/toolbar_icon_size", "Gtk/ToolbarIconSize", GCONF_VALUE_STRING, translate_string_string },
179
{ "/desktop/gnome/interface/can_change_accels", "Gtk/CanChangeAccels", GCONF_VALUE_BOOL, translate_bool_int },
180
{ "/desktop/gnome/interface/cursor_blink", "Net/CursorBlink", GCONF_VALUE_BOOL, translate_bool_int },
181
{ "/desktop/gnome/interface/cursor_blink_time", "Net/CursorBlinkTime", GCONF_VALUE_INT, translate_int_int },
182
{ "/desktop/gnome/interface/gtk_theme", "Net/ThemeName", GCONF_VALUE_STRING, translate_string_string },
183
{ "/desktop/gnome/interface/gtk_color_scheme", "Gtk/ColorScheme", GCONF_VALUE_STRING, translate_string_string },
184
{ "/desktop/gnome/interface/gtk-im-preedit-style", "Gtk/IMPreeditStyle", GCONF_VALUE_STRING, translate_string_string },
185
{ "/desktop/gnome/interface/gtk-im-status-style", "Gtk/IMStatusStyle", GCONF_VALUE_STRING, translate_string_string },
186
{ "/desktop/gnome/interface/icon_theme", "Net/IconThemeName", GCONF_VALUE_STRING, translate_string_string },
187
{ "/desktop/gnome/interface/file_chooser_backend", "Gtk/FileChooserBackend", GCONF_VALUE_STRING, translate_string_string },
188
{ "/desktop/gnome/interface/menus_have_icons", "Gtk/MenuImages", GCONF_VALUE_BOOL, translate_bool_int },
189
{ "/desktop/gnome/interface/menubar_accel", "Gtk/MenuBarAccel", GCONF_VALUE_STRING, translate_string_string },
190
{ "/desktop/gnome/peripherals/mouse/cursor_theme", "Gtk/CursorThemeName", GCONF_VALUE_STRING, translate_string_string },
191
{ "/desktop/gnome/peripherals/mouse/cursor_size", "Gtk/CursorThemeSize", GCONF_VALUE_INT, translate_int_int },
192
{ "/desktop/gnome/interface/show_input_method_menu", "Gtk/ShowInputMethodMenu", GCONF_VALUE_BOOL, translate_bool_int },
193
{ "/desktop/gnome/interface/show_unicode_menu", "Gtk/ShowUnicodeMenu", GCONF_VALUE_BOOL, translate_bool_int },
198
dpi_from_pixels_and_mm (int pixels,
204
dpi = pixels / (mm / 25.4);
212
get_dpi_from_x_server (void)
217
screen = gdk_screen_get_default ();
218
if (screen != NULL) {
219
double width_dpi, height_dpi;
221
width_dpi = dpi_from_pixels_and_mm (gdk_screen_get_width (screen), gdk_screen_get_width_mm (screen));
222
height_dpi = dpi_from_pixels_and_mm (gdk_screen_get_height (screen), gdk_screen_get_height_mm (screen));
224
if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE
225
|| height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE) {
228
dpi = (width_dpi + height_dpi) / 2.0;
231
/* Huh!? No screen? */
240
get_dpi_from_gconf_or_x_server (GConfClient *client)
245
value = gconf_client_get_without_default (client, FONT_DPI_KEY, NULL);
247
/* If the user has ever set the DPI preference in GConf, we use that.
248
* Otherwise, we see if the X server reports a reasonable DPI value: some X
249
* servers report completely bogus values, and the user gets huge or tiny
250
* fonts which are unusable.
254
dpi = gconf_value_get_float (value);
255
gconf_value_free (value);
257
dpi = get_dpi_from_x_server ();
269
const char *hintstyle;
272
static const char *rgba_types[] = { "rgb", "bgr", "vbgr", "vrgb" };
274
/* Read GConf settings and determine the appropriate Xft settings based on them
275
* This probably could be done a bit more cleanly with gconf_string_to_enum
278
xft_settings_get (GConfClient *client,
279
GnomeXftSettings *settings)
286
antialiasing = gconf_client_get_string (client, FONT_ANTIALIASING_KEY, NULL);
287
hinting = gconf_client_get_string (client, FONT_HINTING_KEY, NULL);
288
rgba_order = gconf_client_get_string (client, FONT_RGBA_ORDER_KEY, NULL);
289
dpi = get_dpi_from_gconf_or_x_server (client);
291
settings->antialias = TRUE;
292
settings->hinting = TRUE;
293
settings->hintstyle = "hintfull";
294
settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */
295
settings->rgba = "rgb";
299
gboolean found = FALSE;
301
for (i = 0; i < G_N_ELEMENTS (rgba_types) && !found; i++) {
302
if (strcmp (rgba_order, rgba_types[i]) == 0) {
303
settings->rgba = rgba_types[i];
309
g_warning ("Invalid value for " FONT_RGBA_ORDER_KEY ": '%s'",
315
if (strcmp (hinting, "none") == 0) {
316
settings->hinting = 0;
317
settings->hintstyle = "hintnone";
318
} else if (strcmp (hinting, "slight") == 0) {
319
settings->hinting = 1;
320
settings->hintstyle = "hintslight";
321
} else if (strcmp (hinting, "medium") == 0) {
322
settings->hinting = 1;
323
settings->hintstyle = "hintmedium";
324
} else if (strcmp (hinting, "full") == 0) {
325
settings->hinting = 1;
326
settings->hintstyle = "hintfull";
328
g_warning ("Invalid value for " FONT_HINTING_KEY ": '%s'",
334
gboolean use_rgba = FALSE;
336
if (strcmp (antialiasing, "none") == 0) {
337
settings->antialias = 0;
338
} else if (strcmp (antialiasing, "grayscale") == 0) {
339
settings->antialias = 1;
340
} else if (strcmp (antialiasing, "rgba") == 0) {
341
settings->antialias = 1;
344
g_warning ("Invalid value for " FONT_ANTIALIASING_KEY " : '%s'",
349
settings->rgba = "none";
355
g_free (antialiasing);
359
xft_settings_set_xsettings (GnomeXSettingsManager *manager,
360
GnomeXftSettings *settings)
363
for (i = 0; manager->priv->managers [i]; i++) {
364
xsettings_manager_set_int (manager->priv->managers [i], "Xft/Antialias", settings->antialias);
365
xsettings_manager_set_int (manager->priv->managers [i], "Xft/Hinting", settings->hinting);
366
xsettings_manager_set_string (manager->priv->managers [i], "Xft/HintStyle", settings->hintstyle);
367
xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->dpi);
368
xsettings_manager_set_string (manager->priv->managers [i], "Xft/RGBA", settings->rgba);
377
while (to_write > 0) {
378
gssize count = write (fd, buf, to_write);
392
child_watch_cb (GPid pid,
396
char *command = user_data;
398
if (!WIFEXITED (status) || WEXITSTATUS (status)) {
399
g_warning ("Command %s failed", command);
404
spawn_with_input (const char *command,
414
res = g_shell_parse_argv (command, NULL, &argv, NULL);
416
g_warning ("Unable to parse command: %s", command);
421
res = g_spawn_async_with_pipes (NULL,
424
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
435
g_warning ("Could not execute %s: %s", command, error->message);
436
g_error_free (error);
442
if (! write_all (inpipe, input, strlen (input))) {
443
g_warning ("Could not write input to %s", command);
449
g_child_watch_add (child_pid, (GChildWatchFunc) child_watch_cb, (gpointer)command);
453
xft_settings_set_xresources (GnomeXftSettings *settings)
459
command = "xrdb -nocpp -merge";
461
add_string = g_string_new (NULL);
462
old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
464
setlocale (LC_NUMERIC, "C");
465
g_string_append_printf (add_string,
467
settings->dpi / 1024.0);
468
g_string_append_printf (add_string,
469
"Xft.antialias: %d\n",
470
settings->antialias);
471
g_string_append_printf (add_string,
474
g_string_append_printf (add_string,
475
"Xft.hintstyle: %s\n",
476
settings->hintstyle);
477
g_string_append_printf (add_string,
481
spawn_with_input (command, add_string->str);
483
g_string_free (add_string, TRUE);
484
setlocale (LC_NUMERIC, old_locale);
488
/* We mirror the Xft properties both through XSETTINGS and through
492
update_xft_settings (GnomeXSettingsManager *manager,
495
GnomeXftSettings settings;
497
xft_settings_get (client, &settings);
498
xft_settings_set_xsettings (manager, &settings);
499
xft_settings_set_xresources (&settings);
503
xft_callback (GConfClient *client,
506
GnomeXSettingsManager *manager)
510
update_xft_settings (manager, client);
512
for (i = 0; manager->priv->managers [i]; i++) {
513
xsettings_manager_notify (manager->priv->managers [i]);
517
#endif /* HAVE_XFT2 */
520
type_to_string (GConfValueType type)
523
case GCONF_VALUE_INT:
525
case GCONF_VALUE_STRING:
527
case GCONF_VALUE_FLOAT:
529
case GCONF_VALUE_BOOL:
531
case GCONF_VALUE_SCHEMA:
533
case GCONF_VALUE_LIST:
535
case GCONF_VALUE_PAIR:
537
case GCONF_VALUE_INVALID:
540
g_assert_not_reached();
541
return NULL; /* for warnings */
546
process_value (GnomeXSettingsManager *manager,
547
TranslationEntry *trans,
553
for (i = 0; manager->priv->managers [i]; i++) {
554
xsettings_manager_delete_setting (manager->priv->managers [i], trans->xsetting_name);
557
if (val->type == trans->gconf_type) {
558
(* trans->translate) (manager, trans, val);
560
g_warning (_("GConf key %s set to type %s but its expected type was %s\n"),
562
type_to_string (val->type),
563
type_to_string (trans->gconf_type));
569
gnome_xsettings_manager_start (GnomeXSettingsManager *manager,
575
g_debug ("Starting xsettings manager");
577
client = gconf_client_get_default ();
579
for (i = 0; i < G_N_ELEMENTS (translations); i++) {
584
val = gconf_client_get (client,
585
translations[i].gconf_key,
589
g_warning ("Error getting value for %s: %s\n",
590
translations[i].gconf_key,
594
process_value (manager, &translations[i], val);
596
gconf_value_free (val);
601
g_object_unref (client);
604
update_xft_settings (manager, client);
605
#endif /* HAVE_XFT */
607
for (i = 0; manager->priv->managers [i]; i++)
608
xsettings_manager_set_string (manager->priv->managers [i],
609
"Net/FallbackIconTheme",
612
for (i = 0; manager->priv->managers [i]; i++) {
613
xsettings_manager_notify (manager->priv->managers [i]);
620
gnome_xsettings_manager_stop (GnomeXSettingsManager *manager)
622
g_debug ("Stopping xsettings manager");
626
gnome_xsettings_manager_set_property (GObject *object,
631
GnomeXSettingsManager *self;
633
self = GNOME_XSETTINGS_MANAGER (object);
637
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643
gnome_xsettings_manager_get_property (GObject *object,
648
GnomeXSettingsManager *self;
650
self = GNOME_XSETTINGS_MANAGER (object);
654
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
660
gnome_xsettings_manager_constructor (GType type,
661
guint n_construct_properties,
662
GObjectConstructParam *construct_properties)
664
GnomeXSettingsManager *xsettings_manager;
665
GnomeXSettingsManagerClass *klass;
667
klass = GNOME_XSETTINGS_MANAGER_CLASS (g_type_class_peek (GNOME_TYPE_XSETTINGS_MANAGER));
669
xsettings_manager = GNOME_XSETTINGS_MANAGER (G_OBJECT_CLASS (gnome_xsettings_manager_parent_class)->constructor (type,
670
n_construct_properties,
671
construct_properties));
673
return G_OBJECT (xsettings_manager);
677
gnome_xsettings_manager_dispose (GObject *object)
679
GnomeXSettingsManager *xsettings_manager;
681
xsettings_manager = GNOME_XSETTINGS_MANAGER (object);
683
G_OBJECT_CLASS (gnome_xsettings_manager_parent_class)->dispose (object);
687
gnome_xsettings_manager_class_init (GnomeXSettingsManagerClass *klass)
689
GObjectClass *object_class = G_OBJECT_CLASS (klass);
691
object_class->get_property = gnome_xsettings_manager_get_property;
692
object_class->set_property = gnome_xsettings_manager_set_property;
693
object_class->constructor = gnome_xsettings_manager_constructor;
694
object_class->dispose = gnome_xsettings_manager_dispose;
695
object_class->finalize = gnome_xsettings_manager_finalize;
697
g_type_class_add_private (klass, sizeof (GnomeXSettingsManagerPrivate));
700
static TranslationEntry *
701
find_translation_entry (const char *gconf_key)
705
for (i =0; i < G_N_ELEMENTS (translations); i++) {
706
if (strcmp (translations[i].gconf_key, gconf_key) == 0) {
707
return &translations[i];
715
xsettings_callback (GConfClient *client,
718
GnomeXSettingsManager *manager)
720
TranslationEntry *trans;
723
trans = find_translation_entry (entry->key);
728
process_value (manager, trans, entry->value);
730
for (i = 0; manager->priv->managers [i]; i++) {
731
xsettings_manager_set_string (manager->priv->managers [i],
732
"Net/FallbackIconTheme",
736
for (i = 0; manager->priv->managers [i]; i++) {
737
xsettings_manager_notify (manager->priv->managers [i]);
742
register_config_callback (GnomeXSettingsManager *manager,
744
GConfClientNotifyFunc func)
748
client = gconf_client_get_default ();
750
gconf_client_add_dir (client, path, GCONF_CLIENT_PRELOAD_NONE, NULL);
751
gconf_client_notify_add (client, path, func, manager, NULL, NULL);
753
g_object_unref (client);
757
terminate_cb (void *data)
759
gboolean *terminated = data;
771
gnome_xsettings_manager_init (GnomeXSettingsManager *manager)
779
manager->priv = GNOME_XSETTINGS_MANAGER_GET_PRIVATE (manager);
781
display = gdk_display_get_default ();
782
n_screens = gdk_display_get_n_screens (display);
784
res = xsettings_manager_check_running (gdk_x11_display_get_xdisplay (display),
785
gdk_screen_get_number (gdk_screen_get_default ()));
787
g_error ("You can only run one xsettings manager at a time; exiting\n");
791
manager->priv->managers = g_new (XSettingsManager *, n_screens + 1);
794
for (i = 0; i < n_screens; i++) {
797
screen = gdk_display_get_screen (display, i);
799
manager->priv->managers [i] = xsettings_manager_new (gdk_x11_display_get_xdisplay (display),
800
gdk_screen_get_number (screen),
803
if (! manager->priv->managers [i]) {
804
g_error ("Could not create xsettings manager for screen %d!\n", i);
809
manager->priv->managers [i] = NULL;
811
register_config_callback (manager, "/desktop/gnome/peripherals/mouse", (GConfClientNotifyFunc)xsettings_callback);
812
register_config_callback (manager, "/desktop/gtk", (GConfClientNotifyFunc)xsettings_callback);
813
register_config_callback (manager, "/desktop/gnome/interface", (GConfClientNotifyFunc)xsettings_callback);
816
register_config_callback (manager, FONT_RENDER_DIR, (GConfClientNotifyFunc)xft_callback);
817
#endif /* HAVE_XFT2 */
822
gnome_xsettings_manager_finalize (GObject *object)
824
GnomeXSettingsManager *xsettings_manager;
826
g_return_if_fail (object != NULL);
827
g_return_if_fail (GNOME_IS_XSETTINGS_MANAGER (object));
829
xsettings_manager = GNOME_XSETTINGS_MANAGER (object);
831
g_return_if_fail (xsettings_manager->priv != NULL);
833
G_OBJECT_CLASS (gnome_xsettings_manager_parent_class)->finalize (object);
836
GnomeXSettingsManager *
837
gnome_xsettings_manager_new (void)
839
if (manager_object != NULL) {
840
g_object_ref (manager_object);
842
manager_object = g_object_new (GNOME_TYPE_XSETTINGS_MANAGER, NULL);
843
g_object_add_weak_pointer (manager_object,
844
(gpointer *) &manager_object);
847
return GNOME_XSETTINGS_MANAGER (manager_object);