gnome-control-center/gnome-settings-daemon/gnome-settings-xsettings.c
Federico Mena Quintero 30c1207620 Fix the gnome-settings-daemon part of
2007-03-21  Federico Mena Quintero  <federico@novell.com>

	Fix the gnome-settings-daemon part of
	https://bugzilla.novell.com/show_bug.cgi?id=217790 and
	http://bugzilla.gnome.org/show_bug.cgi?id=378338:  try to figure
	out the DPI value from the X server or the user's GConf settings.

	Should also fix https://bugzilla.novell.com/show_bug.cgi?id=240246.

	* gnome-settings-daemon/gnome-settings-xsettings.c (gnome_xft_settings_get): Call
	get_dpi_from_gconf_or_server() to figure out a reasonable DPI
	value; don't unconditionally get it from GConf.
	(get_dpi_from_gconf_or_server): New function.  If the user has
	ever set the /desktop/gnome/font_rendering/dpi value in GConf, we
	use its value.  Otherwise, we ask the X server.  We constrain the
	X server's response to a range of reasonable DPI values, since
	some servers lie about the screen's phisical dimensions --- the
	user would get unusably huge or tiny fonts otherwise.

	* capplets/font/main.c (dpi_load): First, see if the DPI value is actually set
	in GConf.  If it is, it means that the user has changed it at
	least once.  In that case, just use the value.  Otherwise, find
	the value from the X server in a similar way to what we do in
	gnome-settings-daemon.

svn path=/trunk/; revision=7409
2007-03-21 17:05:06 +00:00

544 lines
15 KiB
C

#include <config.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gconf/gconf-client.h>
#include "gnome-settings-daemon.h"
#include "gnome-settings-xsettings.h"
#include "xsettings-manager.h"
extern XSettingsManager **managers;
#ifdef HAVE_XFT2
#define FONT_RENDER_DIR "/desktop/gnome/font_rendering"
#define FONT_ANTIALIASING_KEY FONT_RENDER_DIR "/antialiasing"
#define FONT_HINTING_KEY FONT_RENDER_DIR "/hinting"
#define FONT_RGBA_ORDER_KEY FONT_RENDER_DIR "/rgba_order"
#define FONT_DPI_KEY FONT_RENDER_DIR "/dpi"
#endif /* HAVE_XFT2 */
typedef struct _TranslationEntry TranslationEntry;
typedef void (* TranslationFunc) (TranslationEntry *trans,
GConfValue *value);
struct _TranslationEntry
{
const char *gconf_key;
const char *xsetting_name;
GConfValueType gconf_type;
TranslationFunc translate;
};
#ifdef HAVE_XFT2
/* X servers sometimes lie about the screen's physical dimensions, so we cannot
* compute an accurate DPI value. When this happens, the user gets fonts that
* are too huge or too tiny. So, we see what the server returns: if it reports
* something outside of the range [DPI_LOW_REASONABLE_VALUE,
* DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use
* DPI_FALLBACK instead.
*
* See get_dpi_from_gconf_or_server() below, and also
* https://bugzilla.novell.com/show_bug.cgi?id=217790
*/
#define DPI_FALLBACK 96
#define DPI_LOW_REASONABLE_VALUE 50
#define DPI_HIGH_REASONABLE_VALUE 500
static void gnome_settings_update_xft (GConfClient *client);
static void xft_callback (GConfEntry *entry);
#endif /* HAVE_XFT2 */
static void
translate_bool_int (TranslationEntry *trans,
GConfValue *value)
{
int i;
g_assert (value->type == trans->gconf_type);
for (i = 0; managers [i]; i++)
xsettings_manager_set_int (managers [i], trans->xsetting_name,
gconf_value_get_bool (value));
}
static void
translate_int_int (TranslationEntry *trans,
GConfValue *value)
{
int i;
g_assert (value->type == trans->gconf_type);
for (i = 0; managers [i]; i++)
xsettings_manager_set_int (managers [i], trans->xsetting_name,
gconf_value_get_int (value));
}
static void
translate_string_string (TranslationEntry *trans,
GConfValue *value)
{
int i;
g_assert (value->type == trans->gconf_type);
for (i = 0; managers [i]; i++)
xsettings_manager_set_string (managers [i],
trans->xsetting_name,
gconf_value_get_string (value));
}
static void
translate_string_string_toolbar (TranslationEntry *trans,
GConfValue *value)
{
int i;
const char *tmp;
g_assert (value->type == trans->gconf_type);
/* This is kind of a workaround since GNOME expects the key value to be
* "both_horiz" and gtk+ wants the XSetting to be "both-horiz".
*/
tmp = gconf_value_get_string (value);
if (tmp && strcmp (tmp, "both_horiz") == 0)
tmp = "both-horiz";
for (i = 0; managers [i]; i++)
xsettings_manager_set_string (managers [i],
trans->xsetting_name,
tmp);
}
static TranslationEntry translations [] = {
{ "/desktop/gnome/peripherals/mouse/double_click", "Net/DoubleClickTime",
GCONF_VALUE_INT, translate_int_int },
{ "/desktop/gnome/peripherals/mouse/drag_threshold", "Net/DndDragThreshold",
GCONF_VALUE_INT, translate_int_int },
{ "/desktop/gnome/gtk-color-palette", "Gtk/ColorPalette",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/font_name", "Gtk/FontName",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/gtk_key_theme", "Gtk/KeyThemeName",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/toolbar_style", "Gtk/ToolbarStyle",
GCONF_VALUE_STRING, translate_string_string_toolbar },
{ "/desktop/gnome/interface/toolbar_icon_size", "Gtk/ToolbarIconSize",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/can_change_accels", "Gtk/CanChangeAccels",
GCONF_VALUE_BOOL, translate_bool_int },
{ "/desktop/gnome/interface/cursor_blink", "Net/CursorBlink",
GCONF_VALUE_BOOL, translate_bool_int },
{ "/desktop/gnome/interface/cursor_blink_time", "Net/CursorBlinkTime",
GCONF_VALUE_INT, translate_int_int },
{ "/desktop/gnome/interface/gtk_theme", "Net/ThemeName",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/gtk_color_scheme", "Gtk/ColorScheme",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/gtk-im-preedit-style", "Gtk/IMPreeditStyle",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/gtk-im-status-style", "Gtk/IMStatusStyle",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/icon_theme", "Net/IconThemeName",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/file_chooser_backend", "Gtk/FileChooserBackend",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/interface/menus_have_icons", "Gtk/MenuImages",
GCONF_VALUE_BOOL, translate_bool_int },
{ "/desktop/gnome/interface/menubar_accel", "Gtk/MenuBarAccel",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/peripherals/mouse/cursor_theme", "Gtk/CursorThemeName",
GCONF_VALUE_STRING, translate_string_string },
{ "/desktop/gnome/peripherals/mouse/cursor_size", "Gtk/CursorThemeSize",
GCONF_VALUE_INT, translate_int_int },
{ "/desktop/gnome/interface/show_input_method_menu", "Gtk/ShowInputMethodMenu",
GCONF_VALUE_BOOL, translate_bool_int },
{ "/desktop/gnome/interface/show_unicode_menu", "Gtk/ShowUnicodeMenu",
GCONF_VALUE_BOOL, translate_bool_int },
};
static TranslationEntry*
find_translation_entry (const char *gconf_key)
{
int i;
i = 0;
while (i < G_N_ELEMENTS (translations))
{
if (strcmp (translations[i].gconf_key, gconf_key) == 0)
return &translations[i];
++i;
}
return NULL;
}
static const gchar*
type_to_string (GConfValueType type)
{
switch (type)
{
case GCONF_VALUE_INT:
return "int";
case GCONF_VALUE_STRING:
return "string";
case GCONF_VALUE_FLOAT:
return "float";
case GCONF_VALUE_BOOL:
return "bool";
case GCONF_VALUE_SCHEMA:
return "schema";
case GCONF_VALUE_LIST:
return "list";
case GCONF_VALUE_PAIR:
return "pair";
case GCONF_VALUE_INVALID:
return "*invalid*";
default:
g_assert_not_reached();
return NULL; /* for warnings */
}
}
static void
process_value (TranslationEntry *trans,
GConfValue *val)
{
if (val == NULL)
{
int i;
for (i = 0; managers [i]; i++)
xsettings_manager_delete_setting (managers [i], trans->xsetting_name);
}
else
{
if (val->type == trans->gconf_type)
{
(* trans->translate) (trans, val);
}
else
{
g_warning (_("GConf key %s set to type %s but its expected type was %s\n"),
trans->gconf_key,
type_to_string (val->type),
type_to_string (trans->gconf_type));
}
}
}
static void
xsettings_callback (GConfEntry *entry)
{
TranslationEntry *trans;
int i;
trans = find_translation_entry (entry->key);
if (trans == NULL)
return;
process_value (trans, entry->value);
for (i = 0; managers [i]; i++)
xsettings_manager_set_string (managers [i], "Net/FallbackIconTheme",
"gnome");
for (i = 0; managers [i]; i++)
xsettings_manager_notify (managers [i]);
}
void
gnome_settings_xsettings_init (GConfClient *client)
{
gnome_settings_daemon_register_callback ("/desktop/gnome/peripherals/mouse", xsettings_callback);
gnome_settings_daemon_register_callback ("/desktop/gtk", xsettings_callback);
gnome_settings_daemon_register_callback ("/desktop/gnome/interface", xsettings_callback);
#ifdef HAVE_XFT2
gnome_settings_daemon_register_callback (FONT_RENDER_DIR, xft_callback);
#endif /* HAVE_XFT2 */
}
#ifdef HAVE_XFT2
static void
xft_callback (GConfEntry *entry)
{
GConfClient *client;
int i;
client = gnome_settings_daemon_get_conf_client ();
gnome_settings_update_xft (client);
for (i = 0; managers [i]; i++)
xsettings_manager_notify (managers [i]);
}
static double
dpi_from_pixels_and_mm (int pixels, int mm)
{
double dpi;
if (mm >= 1)
dpi = pixels / (mm / 25.4);
else
dpi = 0;
return dpi;
}
static double
get_dpi_from_x_server (void)
{
GdkScreen *screen;
double dpi;
screen = gdk_screen_get_default ();
if (screen)
{
double width_dpi, height_dpi;
width_dpi = dpi_from_pixels_and_mm (gdk_screen_get_width (screen), gdk_screen_get_width_mm (screen));
height_dpi = dpi_from_pixels_and_mm (gdk_screen_get_height (screen), gdk_screen_get_height_mm (screen));
if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE
|| height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE)
dpi = DPI_FALLBACK;
else
dpi = (width_dpi + height_dpi) / 2.0;
}
else
{
/* Huh!? No screen? */
dpi = DPI_FALLBACK;
}
return dpi;
}
static double
get_dpi_from_gconf_or_x_server (GConfClient *client)
{
GConfValue *value;
double dpi;
value = gconf_client_get_without_default (client, FONT_DPI_KEY, NULL);
/* If the user has ever set the DPI preference in GConf, we use that.
* Otherwise, we see if the X server reports a reasonable DPI value: some X
* servers report completely bogus values, and the user gets huge or tiny
* fonts which are unusable.
*/
if (value)
{
dpi = gconf_value_get_float (value);
gconf_value_free (value);
}
else
dpi = get_dpi_from_x_server ();
return dpi;
}
typedef struct
{
gboolean antialias;
gboolean hinting;
int dpi;
const char *rgba;
const char *hintstyle;
} GnomeXftSettings;
static const char *rgba_types[] = { "rgb", "bgr", "vbgr", "vrgb" };
/* Read GConf settings and determine the appropriate Xft settings based on them
* This probably could be done a bit more cleanly with gconf_string_to_enum
*/
static void
gnome_xft_settings_get (GConfClient *client,
GnomeXftSettings *settings)
{
char *antialiasing = gconf_client_get_string (client, FONT_ANTIALIASING_KEY, NULL);
char *hinting = gconf_client_get_string (client, FONT_HINTING_KEY, NULL);
char *rgba_order = gconf_client_get_string (client, FONT_RGBA_ORDER_KEY, NULL);
double dpi = get_dpi_from_gconf_or_x_server (client);
settings->antialias = TRUE;
settings->hinting = TRUE;
settings->hintstyle = "hintfull";
settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */
settings->rgba = "rgb";
if (rgba_order)
{
int i;
gboolean found = FALSE;
for (i = 0; i < G_N_ELEMENTS (rgba_types) && !found; i++)
if (strcmp (rgba_order, rgba_types[i]) == 0)
{
settings->rgba = rgba_types[i];
found = TRUE;
}
if (!found)
g_warning ("Invalid value for " FONT_RGBA_ORDER_KEY ": '%s'",
rgba_order);
}
if (hinting)
{
if (strcmp (hinting, "none") == 0)
{
settings->hinting = 0;
settings->hintstyle = "hintnone";
}
else if (strcmp (hinting, "slight") == 0)
{
settings->hinting = 1;
settings->hintstyle = "hintslight";
}
else if (strcmp (hinting, "medium") == 0)
{
settings->hinting = 1;
settings->hintstyle = "hintmedium";
}
else if (strcmp (hinting, "full") == 0)
{
settings->hinting = 1;
settings->hintstyle = "hintfull";
}
else
g_warning ("Invalid value for " FONT_HINTING_KEY ": '%s'",
hinting);
}
if (antialiasing)
{
gboolean use_rgba = FALSE;
if (strcmp (antialiasing, "none") == 0)
settings->antialias = 0;
else if (strcmp (antialiasing, "grayscale") == 0)
settings->antialias = 1;
else if (strcmp (antialiasing, "rgba") == 0)
{
settings->antialias = 1;
use_rgba = TRUE;
}
else
g_warning ("Invalid value for " FONT_ANTIALIASING_KEY " : '%s'",
antialiasing);
if (!use_rgba)
settings->rgba = "none";
}
g_free (rgba_order);
g_free (hinting);
g_free (antialiasing);
}
static void
gnome_xft_settings_set_xsettings (GnomeXftSettings *settings)
{
int i;
for (i = 0; managers [i]; i++)
{
xsettings_manager_set_int (managers [i], "Xft/Antialias", settings->antialias);
xsettings_manager_set_int (managers [i], "Xft/Hinting", settings->hinting);
xsettings_manager_set_string (managers [i], "Xft/HintStyle", settings->hintstyle);
xsettings_manager_set_int (managers [i], "Xft/DPI", settings->dpi);
xsettings_manager_set_string (managers [i], "Xft/RGBA", settings->rgba);
}
}
static void
gnome_xft_settings_set_xresources (GnomeXftSettings *settings)
{
char *add[] = { "xrdb", "-nocpp", "-merge", NULL };
GString *add_string = g_string_new (NULL);
char *old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
setlocale (LC_NUMERIC, "C");
g_string_append_printf (add_string,
"Xft.dpi: %f\n", settings->dpi / 1024.);
g_string_append_printf (add_string,
"Xft.antialias: %d\n", settings->antialias);
g_string_append_printf (add_string,
"Xft.hinting: %d\n", settings->hinting);
g_string_append_printf (add_string,
"Xft.hintstyle: %s\n", settings->hintstyle);
g_string_append_printf (add_string,
"Xft.rgba: %s\n", settings->rgba);
gnome_settings_daemon_spawn_with_input (add, add_string->str);
g_string_free (add_string, TRUE);
setlocale (LC_NUMERIC, old_locale);
g_free (old_locale);
}
/* We mirror the Xft properties both through XSETTINGS and through
* X resources
*/
static void
gnome_settings_update_xft (GConfClient *client)
{
GnomeXftSettings settings;
gnome_xft_settings_get (client, &settings);
gnome_xft_settings_set_xsettings (&settings);
gnome_xft_settings_set_xresources (&settings);
}
#endif /* HAVE_XFT2 */
void
gnome_settings_xsettings_load (GConfClient *client)
{
int i;
i = 0;
while (i < G_N_ELEMENTS (translations))
{
GConfValue *val;
GError *err;
err = NULL;
val = gconf_client_get (client,
translations[i].gconf_key,
&err);
if (err != NULL)
{
fprintf (stderr, "Error getting value for %s: %s\n",
translations[i].gconf_key, err->message);
g_error_free (err);
}
else
{
process_value (&translations[i], val);
if (val != NULL)
gconf_value_free (val);
}
++i;
}
#ifdef HAVE_XFT2
gnome_settings_update_xft (client);
#endif /* HAVE_XFT */
for (i = 0; managers [i]; i++)
xsettings_manager_set_string (managers [i], "Net/FallbackIconTheme",
"gnome");
for (i = 0; managers [i]; i++)
xsettings_manager_notify (managers [i]);
}