diff options
Diffstat (limited to 'plugins/xrdb/msd-xrdb-manager.c')
-rw-r--r-- | plugins/xrdb/msd-xrdb-manager.c | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/plugins/xrdb/msd-xrdb-manager.c b/plugins/xrdb/msd-xrdb-manager.c new file mode 100644 index 0000000..776d1e7 --- /dev/null +++ b/plugins/xrdb/msd-xrdb-manager.c @@ -0,0 +1,637 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2003 Ross Burton <[email protected]> + * Copyright (C) 2007 William Jon McCann <[email protected]> + * + * 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> + +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "mate-settings-profile.h" +#include "msd-xrdb-manager.h" + +#define MSD_XRDB_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_XRDB_MANAGER, MsdXrdbManagerPrivate)) + +#define SYSTEM_AD_DIR DATADIR "/xrdb" +#define GENERAL_AD SYSTEM_AD_DIR "/General.ad" +#define USER_AD_DIR ".config/mate/xrdb" +#define USER_X_RESOURCES ".Xresources" +#define USER_X_DEFAULTS ".Xdefaults" + +#define GTK_THEME_KEY "/desktop/mate/interface/gtk_theme" + +struct MsdXrdbManagerPrivate { + GtkWidget* widget; +}; + +static void msd_xrdb_manager_class_init (MsdXrdbManagerClass *klass); +static void msd_xrdb_manager_init (MsdXrdbManager *xrdb_manager); +static void msd_xrdb_manager_finalize (GObject *object); + +G_DEFINE_TYPE (MsdXrdbManager, msd_xrdb_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; +static void + +append_color_define (GString *string, + const char *name, + const GdkColor *color) +{ + g_return_if_fail (string != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (color != NULL); + + g_string_append_printf (string, + "#define %s #%2.2hx%2.2hx%2.2hx\n", + name, + color->red>>8, + color->green>>8, + color->blue>>8); +} + +static GdkColor* +color_shade (GdkColor *a, + gdouble shade, + GdkColor *b) +{ + guint16 red, green, blue; + + red = CLAMP ((a->red) * shade, 0, 0xFFFF); + green = CLAMP ((a->green) * shade, 0, 0xFFFF); + blue = CLAMP ((a->blue) * shade, 0, 0xFFFF); + + b->red = red; + b->green = green; + b->blue = blue; + + return b; +} + +static void +append_theme_colors (GtkStyle *style, + GString *string) +{ + GdkColor tmp; + + g_return_if_fail (style != NULL); + g_return_if_fail (string != NULL); + + append_color_define (string, + "BACKGROUND", + &style->bg[GTK_STATE_NORMAL]); + append_color_define (string, + "FOREGROUND", + &style->fg[GTK_STATE_NORMAL]); + append_color_define (string, + "SELECT_BACKGROUND", + &style->bg[GTK_STATE_SELECTED]); + append_color_define (string, + "SELECT_FOREGROUND", + &style->text[GTK_STATE_SELECTED]); + append_color_define (string, + "WINDOW_BACKGROUND", + &style->base[GTK_STATE_NORMAL]); + append_color_define (string, + "WINDOW_FOREGROUND", + &style->text[GTK_STATE_NORMAL]); + append_color_define (string, + "INACTIVE_BACKGROUND", + &style->bg[GTK_STATE_INSENSITIVE]); + append_color_define (string, + "INACTIVE_FOREGROUND", + &style->text[GTK_STATE_INSENSITIVE]); + append_color_define (string, + "ACTIVE_BACKGROUND", + &style->bg[GTK_STATE_SELECTED]); + append_color_define (string, + "ACTIVE_FOREGROUND", + &style->text[GTK_STATE_SELECTED]); + + append_color_define (string, + "HIGHLIGHT", + color_shade (&style->bg[GTK_STATE_NORMAL], 1.2, &tmp)); + append_color_define (string, + "LOWLIGHT", + color_shade (&style->bg[GTK_STATE_NORMAL], 2.0/3.0, &tmp)); + return; +} + +/** + * Scan a single directory for .ad files, and return them all in a + * GSList* + */ +static GSList* +scan_ad_directory (const char *path, + GError **error) +{ + GSList *list; + GDir *dir; + const char *entry; + GError *local_error; + + list = NULL; + + g_return_val_if_fail (path != NULL, NULL); + + local_error = NULL; + dir = g_dir_open (path, 0, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + return NULL; + } + + while ((entry = g_dir_read_name (dir)) != NULL) { + if (g_str_has_suffix (entry, ".ad")) { + list = g_slist_prepend (list, g_strdup_printf ("%s/%s", path, entry)); + } + } + + g_dir_close (dir); + + /* TODO: sort still? */ + return g_slist_sort (list, (GCompareFunc)strcmp); +} + +/** + * Compare two file names on their base names. + */ +static gint +compare_basenames (gconstpointer a, + gconstpointer b) +{ + char *base_a; + char *base_b; + int res; + + base_a = g_path_get_basename (a); + base_b = g_path_get_basename (b); + res = strcmp (base_a, base_b); + g_free (base_a); + g_free (base_b); + + return res; +} + +/** + * Scan the user and system paths, and return a list of strings in the + * right order for processing. + */ +static GSList* +scan_for_files (MsdXrdbManager *manager, + GError **error) +{ + const char *home_dir; + GSList *user_list; + GSList *system_list; + GSList *list; + GSList *p; + GError *local_error; + + list = NULL; + user_list = NULL; + system_list = NULL; + + local_error = NULL; + system_list = scan_ad_directory (SYSTEM_AD_DIR, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + return NULL; + } + + home_dir = g_get_home_dir (); + if (home_dir != NULL) { + char *user_ad; + + user_ad = g_build_filename (home_dir, USER_AD_DIR, NULL); + + if (g_file_test (user_ad, G_FILE_TEST_IS_DIR)) { + + local_error = NULL; + user_list = scan_ad_directory (user_ad, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + + g_slist_foreach (system_list, (GFunc)g_free, NULL); + g_slist_free (system_list); + g_free (user_ad); + return NULL; + } + } + + g_free (user_ad); + + } else { + g_warning (_("Cannot determine user's home directory")); + } + + /* An alternative approach would be to strdup() the strings + and free the entire contents of these lists, but that is a + little inefficient for my liking - RB */ + for (p = system_list; p != NULL; p = g_slist_next (p)) { + if (strcmp (p->data, GENERAL_AD) == 0) { + /* We ignore this, free the data now */ + g_free (p->data); + continue; + } + if (g_slist_find_custom (user_list, p->data, compare_basenames)) { + /* Ditto */ + g_free (p->data); + continue; + } + list = g_slist_prepend (list, p->data); + } + g_slist_free (system_list); + + for (p = user_list; p != NULL; p = g_slist_next (p)) { + list = g_slist_prepend (list, p->data); + } + g_slist_free (user_list); + + /* Reverse the order so it is the correct way */ + list = g_slist_reverse (list); + + /* Add the initial file */ + list = g_slist_prepend (list, g_strdup (GENERAL_AD)); + + return list; +} + +/** + * Append the contents of a file onto the end of a GString + */ +static void +append_file (const char *file, + GString *string, + GError **error) +{ + char *contents; + + g_return_if_fail (string != NULL); + g_return_if_fail (file != NULL); + + if (g_file_get_contents (file, &contents, NULL, error)) { + g_string_append (string, contents); + g_free (contents); + } +} + +/** + * Append an X resources file, such as .Xresources, or .Xdefaults + */ +static void +append_xresource_file (const char *filename, + GString *string, + GError **error) +{ + const char *home_path; + char *xresources; + + g_return_if_fail (string != NULL); + + home_path = g_get_home_dir (); + if (home_path == NULL) { + g_warning (_("Cannot determine user's home directory")); + return; + } + + xresources = g_build_filename (home_path, filename, NULL); + if (g_file_test (xresources, G_FILE_TEST_EXISTS)) { + GError *local_error; + + local_error = NULL; + + append_file (xresources, string, &local_error); + if (local_error != NULL) { + g_warning ("%s", local_error->message); + g_propagate_error (error, local_error); + } + } + g_free (xresources); +} + +static gboolean +write_all (int fd, + const char *buf, + gsize to_write) +{ + while (to_write > 0) { + gssize count = write (fd, buf, to_write); + if (count < 0) { + if (errno != EINTR) + return FALSE; + } else { + to_write -= count; + buf += count; + } + } + + return TRUE; +} + +static void +child_watch_cb (GPid pid, + int status, + gpointer user_data) +{ + char *command = user_data; + + if (!WIFEXITED (status) || WEXITSTATUS (status)) { + g_warning ("Command %s failed", command); + } +} + +static void +spawn_with_input (const char *command, + const char *input) +{ + char **argv; + int child_pid; + int inpipe; + GError *error; + gboolean res; + + argv = NULL; + res = g_shell_parse_argv (command, NULL, &argv, NULL); + if (! res) { + g_warning ("Unable to parse command: %s", command); + return; + } + + error = NULL; + res = g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &child_pid, + &inpipe, + NULL, + NULL, + &error); + g_strfreev (argv); + + if (! res) { + g_warning ("Could not execute %s: %s", command, error->message); + g_error_free (error); + + return; + } + + if (input != NULL) { + if (! write_all (inpipe, input, strlen (input))) { + g_warning ("Could not write input to %s", command); + } + + close (inpipe); + } + + g_child_watch_add (child_pid, (GChildWatchFunc) child_watch_cb, (gpointer)command); +} + +static void +apply_settings (MsdXrdbManager *manager, + GtkStyle *style) +{ + const char *command; + GString *string; + GSList *list; + GSList *p; + GError *error; + + mate_settings_profile_start (NULL); + + command = "xrdb -merge -quiet"; + + string = g_string_sized_new (256); + append_theme_colors (style, string); + + error = NULL; + list = scan_for_files (manager, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + for (p = list; p != NULL; p = p->next) { + error = NULL; + append_file (p->data, string, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + } + + g_slist_foreach (list, (GFunc)g_free, NULL); + g_slist_free (list); + + error = NULL; + append_xresource_file (USER_X_RESOURCES, string, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + error = NULL; + append_xresource_file (USER_X_DEFAULTS, string, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + spawn_with_input (command, string->str); + g_string_free (string, TRUE); + + mate_settings_profile_end (NULL); + + return; +} + +static void +theme_changed (GtkSettings *settings, + GParamSpec *pspec, + MsdXrdbManager *manager) +{ + apply_settings (manager, gtk_widget_get_style (manager->priv->widget)); +} + +gboolean +msd_xrdb_manager_start (MsdXrdbManager *manager, + GError **error) +{ + mate_settings_profile_start (NULL); + + /* the initialization is done here otherwise + mate_settings_xsettings_load would generate + false hit as gtk-theme-name is set to Default in + mate_settings_xsettings_init */ + g_signal_connect (gtk_settings_get_default (), + "notify::gtk-theme-name", + G_CALLBACK (theme_changed), + manager); + + manager->priv->widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_ensure_style (manager->priv->widget); + + mate_settings_profile_end (NULL); + + return TRUE; +} + +void +msd_xrdb_manager_stop (MsdXrdbManager *manager) +{ + MsdXrdbManagerPrivate *p = manager->priv; + + g_debug ("Stopping xrdb manager"); + + g_signal_handlers_disconnect_by_func (gtk_settings_get_default (), + theme_changed, + manager); + + if (p->widget != NULL) { + gtk_widget_destroy (p->widget); + p->widget = NULL; + } +} + +static void +msd_xrdb_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MsdXrdbManager *self; + + self = MSD_XRDB_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +msd_xrdb_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MsdXrdbManager *self; + + self = MSD_XRDB_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +msd_xrdb_manager_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + MsdXrdbManager *xrdb_manager; + MsdXrdbManagerClass *klass; + + klass = MSD_XRDB_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_XRDB_MANAGER)); + + xrdb_manager = MSD_XRDB_MANAGER (G_OBJECT_CLASS (msd_xrdb_manager_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (xrdb_manager); +} + +static void +msd_xrdb_manager_dispose (GObject *object) +{ + MsdXrdbManager *xrdb_manager; + + xrdb_manager = MSD_XRDB_MANAGER (object); + + G_OBJECT_CLASS (msd_xrdb_manager_parent_class)->dispose (object); +} + +static void +msd_xrdb_manager_class_init (MsdXrdbManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = msd_xrdb_manager_get_property; + object_class->set_property = msd_xrdb_manager_set_property; + object_class->constructor = msd_xrdb_manager_constructor; + object_class->dispose = msd_xrdb_manager_dispose; + object_class->finalize = msd_xrdb_manager_finalize; + + g_type_class_add_private (klass, sizeof (MsdXrdbManagerPrivate)); +} + +static void +msd_xrdb_manager_init (MsdXrdbManager *manager) +{ + manager->priv = MSD_XRDB_MANAGER_GET_PRIVATE (manager); + +} + +static void +msd_xrdb_manager_finalize (GObject *object) +{ + MsdXrdbManager *xrdb_manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_XRDB_MANAGER (object)); + + xrdb_manager = MSD_XRDB_MANAGER (object); + + g_return_if_fail (xrdb_manager->priv != NULL); + + G_OBJECT_CLASS (msd_xrdb_manager_parent_class)->finalize (object); +} + +MsdXrdbManager * +msd_xrdb_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (MSD_TYPE_XRDB_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return MSD_XRDB_MANAGER (manager_object); +} |