[SF#614]Don't follow symlink ~/.icons/default or we can recurse inheritance.
[lxde/lxappearance.git] / src / lxappearance.c
index a6c6025..2993fe8 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
+#include <glib/gstdio.h>
 
 #include <X11/X.h>
 #include <X11/Xatom.h>
 #include <gdk/gdkx.h>
 #include <string.h>
 
+#if ENABLE_DBUS
+#include <dbus/dbus.h>
+#endif
+
 #include "widget-theme.h"
 #include "color-scheme.h"
 #include "icon-theme.h"
 #include "cursor-theme.h"
+#include "font.h"
 #include "other.h"
 #include "plugin.h"
 
@@ -46,19 +52,59 @@ LXAppearance app = {0};
 Atom lxsession_atom = 0;
 static const char* lxsession_name = NULL;
 
+/*  Dbus functions Copy from lxsession-logout
+    TODO Create a library fro this ?
+*/
+
+static gboolean check_lxde_dbus()
+{
+#if ENABLE_DBUS
+    DBusError error;
+    dbus_error_init(&error);
+    DBusConnection * connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
+    if (connection == NULL)
+    {
+        g_warning(G_STRLOC ": Failed to connect to the session message bus: %s", error.message);
+        dbus_error_free(&error);
+        return FALSE;
+    }
+
+    dbus_bool_t ret = dbus_bus_name_has_owner(connection,"org.lxde.SessionManager",NULL);
+
+    if (ret == TRUE)
+    {
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+#else
+    return FALSE;
+#endif
+}
+
 static void check_lxsession()
 {
-    lxsession_atom = XInternAtom( GDK_DISPLAY(), "_LXSESSION", True );
+    lxsession_atom = XInternAtom( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), "_LXSESSION", True );
     if( lxsession_atom != None )
     {
-        XGrabServer( GDK_DISPLAY() );
-        if( XGetSelectionOwner( GDK_DISPLAY(), lxsession_atom ) )
+        XGrabServer( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) );
+        if( XGetSelectionOwner( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), lxsession_atom ) )
         {
             app.use_lxsession = TRUE;
             lxsession_name = g_getenv("DESKTOP_SESSION");
         }
-        XUngrabServer( GDK_DISPLAY() );
+        XUngrabServer( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) );
     }
+
+    /* Check Lxsession also with dbus */
+    if (check_lxde_dbus())
+    {
+        app.use_lxsession = TRUE;
+        lxsession_name = g_getenv("DESKTOP_SESSION");
+    }
+
 }
 
 static GOptionEntry option_entries[] =
@@ -66,26 +112,76 @@ static GOptionEntry option_entries[] =
     { NULL }
 };
 
+static gboolean verify_cursor_theme(GKeyFile *kf, const char *cursor_theme,
+                                    const char *test)
+{
+    char *fpath;
+    gboolean ret;
+
+    /* get the inherited theme name. */
+    fpath = g_build_filename(g_get_home_dir(), ".icons", cursor_theme,
+                             "index.theme", NULL);
+    ret = g_key_file_load_from_file(kf, fpath, 0, NULL);
+    g_free(fpath);
+
+    fpath = g_build_filename("icons", cursor_theme, "index.theme", NULL);
+    if (!ret)
+        ret = g_key_file_load_from_data_dirs(kf, fpath, NULL, 0, NULL);
+    g_free(fpath);
+
+    if (ret)
+    {
+        fpath = g_key_file_get_string(kf, "Icon Theme", "Inherits", NULL);
+        if (fpath == NULL) /* end of chain, success */
+            return TRUE;
+        if (strcmp(fpath, test) == 0) /* recursion */
+            ret = FALSE;
+        else if (!verify_cursor_theme(kf, fpath, test)) /* recursion */
+            ret = FALSE;
+        else /* check recursion against this one too */
+            ret = verify_cursor_theme(kf, fpath, cursor_theme);
+        g_free(fpath);
+    }
+    return ret;
+}
+
 static void save_cursor_theme_name()
 {
     char* dir_path;
-    if(!app.cursor_theme || !g_strcmp0(app.cursor_theme, "default"))
+    GKeyFile* kf;
+
+    if (app.cursor_theme == NULL || strcmp(app.cursor_theme, "default") == 0)
         return;
 
     dir_path = g_build_filename(g_get_home_dir(), ".icons/default", NULL);
-    if(0 == g_mkdir_with_parents(dir_path, 0700))
+    kf = g_key_file_new();
+    /* test if cursor theme isn't recursed and don't use it otherwise */
+    if (!verify_cursor_theme(kf, app.cursor_theme, "default"))
+    {
+        g_free(app.cursor_theme);
+        app.cursor_theme = NULL; /* FIXME: replace with "default"? */
+        /* FIXME: show an error message */
+    }
+    /* SF bug #614: ~/.icons/default may be symlink so remove symlink */
+    else if (g_file_test(dir_path, G_FILE_TEST_IS_SYMLINK) &&
+                         g_unlink(dir_path) != 0)
+    {
+        /* FIXME: show an error message */
+    }
+    else if (0 == g_mkdir_with_parents(dir_path, 0700))
     {
         char* index_theme = g_build_filename(dir_path, "index.theme", NULL);
         char* content = g_strdup_printf(
-            "# This file is written by LXAppearance. Do not edit."
-            "[Icon Theme]\n"
-            "Name=Default\n"
-            "Comment=Default Cursor Theme\n"
-            "Inherits=%s\n", app.cursor_theme);
+                        "# This file is written by LXAppearance. Do not edit.\n"
+                        "[Icon Theme]\n"
+                        "Name=Default\n"
+                        "Comment=Default Cursor Theme\n"
+                        "Inherits=%s\n", app.cursor_theme);
         g_file_set_contents(index_theme, content, -1, NULL);
         g_free(content);
         g_free(index_theme);
     }
+    g_key_file_free(kf);
     g_free(dir_path);
 
     /*
@@ -99,6 +195,11 @@ static void save_cursor_theme_name()
 
 static void reload_all_programs()
 {
+#if GTK_CHECK_VERSION(3, 0, 0)
+
+/* TODO Port this to something else than gdk_event_send_clientmessage_toall */
+
+#else
     GdkEventClient event;
     event.type = GDK_CLIENT_EVENT;
     event.send_event = TRUE;
@@ -118,11 +219,7 @@ static void reload_all_programs()
     }
     event.data_format = 8;
     gdk_event_send_clientmessage_toall((GdkEvent *)&event);
-}
-
-static const char* bool2str(gboolean val)
-{
-    return val ? "TRUE" : "FALSE";
+#endif
 }
 
 static void lxappearance_save_gtkrc()
@@ -145,52 +242,165 @@ static void lxappearance_save_gtkrc()
 
     char* file_path = g_build_filename(g_get_home_dir(), ".gtkrc-2.0", NULL);
     GString* content = g_string_sized_new(512);
-    g_string_append_printf(content,
+    g_string_append(content,
         "# DO NOT EDIT! This file will be overwritten by LXAppearance.\n"
-        "# Any customization should be done in ~/.gtkrc-2.0.mine instead.\n\n"
-        "gtk-theme-name=\"%s\"\n"
-        "gtk-icon-theme-name=\"%s\"\n"
-        "gtk-font-name=\"%s\"\n"
+        "# Any customization should be done in ~/.gtkrc-2.0.mine instead.\n\n");
+    if(app.widget_theme)
+        g_string_append_printf(content,
+            "gtk-theme-name=\"%s\"\n", app.widget_theme);
+    if(app.icon_theme)
+        g_string_append_printf(content,
+            "gtk-icon-theme-name=\"%s\"\n", app.icon_theme);
+    if(app.default_font)
+        g_string_append_printf(content,
+            "gtk-font-name=\"%s\"\n", app.default_font);
+    if(app.cursor_theme)
+        g_string_append_printf(content,
+            "gtk-cursor-theme-name=\"%s\"\n", app.cursor_theme);
+    save_cursor_theme_name();
+
+    g_string_append_printf(content,
+        "gtk-cursor-theme-size=%d\n"
         "gtk-toolbar-style=%s\n"
         "gtk-toolbar-icon-size=%s\n"
-        "gtk-cursor-theme-name=\"%s\"\n"
-        "gtk-cursor-theme-size=%d\n"
-        "gtk-button-images=%s\n"
-        "gtk-menu-images=%s\n"
+        "gtk-button-images=%d\n"
+        "gtk-menu-images=%d\n"
 #if GTK_CHECK_VERSION(2, 14, 0)
-        "gtk-enable-event-sounds=%s\n"
-        "gtk-enable-input-feedback-sounds=%s\n",
+        "gtk-enable-event-sounds=%d\n"
+        "gtk-enable-input-feedback-sounds=%d\n"
 #endif
-        app.widget_theme,
-        app.icon_theme,
-        app.default_font,
+        "gtk-xft-antialias=%d\n"
+        "gtk-xft-hinting=%d\n"
+
+        , app.cursor_theme_size,
         tb_styles[app.toolbar_style],
         tb_icon_sizes[app.toolbar_icon_size],
-        app.cursor_theme,
-        app.cursor_theme_size,
-        bool2str(app.button_images),
-        bool2str(app.menu_images),
+        app.button_images ? 1 : 0,
+        app.menu_images ? 1 : 0,
 #if GTK_CHECK_VERSION(2, 14, 0)
-        bool2str(app.enable_event_sound),
-        bool2str(app.enable_input_feedback)
+        app.enable_event_sound ? 1 : 0,
+        app.enable_input_feedback ? 1 : 0,
 #endif
+        app.enable_antialising ? 1 : 0,
+        app.enable_hinting ? 1 : 0
         );
 
+    if(app.hinting_style)
+        g_string_append_printf(content,
+            "gtk-xft-hintstyle=\"%s\"\n", app.hinting_style);
+
+    if(app.font_rgba)
+        g_string_append_printf(content,
+            "gtk-xft-rgba=\"%s\"\n", app.font_rgba);
+
+#if 0
+    /* unfortunately we cannot set colors without XSETTINGS daemon,
+       themes will override any custom settings in .gtkrc-2.0 file */
+    /* FIXME: we should support other xsettings daemons too */
     if(app.color_scheme)
     {
         char* escaped = g_strescape(app.color_scheme, NULL);
         g_string_append_printf(content,
-            "gtk-color-scheme=\"\"\n",
+            "gtk-color-scheme=\"%s\"\n",
             escaped);
         g_free(escaped);
     }
+#endif
 
     g_string_append_printf(content,
         "include \"%s/.gtkrc-2.0.mine\"\n",
         g_get_home_dir());
 
     g_file_set_contents(file_path, content->str, content->len, NULL);
+
+    /* Save also in GTK3 folder
+       Content shold be different from the gtk2 one
+    */
+    GKeyFile *content_gtk3 = g_key_file_new();
+    char* file_path_gtk3 = g_build_filename(g_get_user_config_dir(), "gtk-3.0", NULL);
+    char* file_path_settings = g_build_filename(file_path_gtk3, "settings.ini", NULL);
+
+    if (!g_file_test(file_path_gtk3, G_FILE_TEST_IS_DIR))
+    {
+        g_mkdir_with_parents(file_path_gtk3, 0755);
+    }
+
+    g_key_file_load_from_file(content_gtk3, file_path_settings, /* ignoring errors */
+                              G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
+
+    if(app.widget_theme)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-theme-name", app.widget_theme);
+    if(app.icon_theme)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-icon-theme-name", app.icon_theme);
+    if(app.default_font)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-font-name", app.default_font);
+    if(app.cursor_theme)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-cursor-theme-name", app.cursor_theme);
+    save_cursor_theme_name();
+
+    g_key_file_set_integer(content_gtk3, "Settings",
+                           "gtk-cursor-theme-size", app.cursor_theme_size);
+    g_key_file_set_string(content_gtk3, "Settings",
+                          "gtk-toolbar-style", tb_styles[app.toolbar_style]);
+    g_key_file_set_string(content_gtk3, "Settings",
+                          "gtk-toolbar-icon-size", tb_icon_sizes[app.toolbar_icon_size]);
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-button-images", app.button_images);
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-menu-images", app.menu_images);
+#if GTK_CHECK_VERSION(2, 14, 0)
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-enable-event-sounds", app.enable_event_sound);
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-enable-input-feedback-sounds", app.enable_input_feedback);
+#endif
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-xft-antialias", app.enable_antialising);
+    g_key_file_set_boolean(content_gtk3, "Settings",
+                           "gtk-xft-hinting", app.enable_hinting);
+
+    if(app.hinting_style)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-xft-hintstyle", app.hinting_style);
+
+    if(app.font_rgba)
+        g_key_file_set_string(content_gtk3, "Settings",
+                              "gtk-xft-rgba", app.font_rgba);
+
+#if 0
+    /* unfortunately we cannot set colors without XSETTINGS daemon,
+       themes will override any custom settings in .gtkrc-2.0 file */
+    /* FIXME: we should support other xsettings daemons too */
+    if(app.color_scheme)
+    {
+        char* escaped = g_strescape(app.color_scheme, NULL);
+        g_string_append_printf(content_gtk3,
+            "gtk-color-scheme=%s\n",
+            escaped);
+        g_free(escaped);
+    }
+#endif
+
+#if GLIB_CHECK_VERSION(2, 40, 0)
+    g_key_file_save_to_file(content_gtk3, file_path_settings, NULL);
+#else
+    char *contents;
+    gsize s;
+
+    contents = g_key_file_to_data(content_gtk3, &s, NULL);
+    if (contents)
+        g_file_set_contents(file_path_settings, contents, s, NULL);
+    g_free(contents);
+#endif
+
+    g_free(file_path_gtk3);
+    g_free(file_path_settings);
     g_string_free(content, TRUE);
+    g_key_file_free(content_gtk3);
     g_free(file_path);
 }
 
@@ -199,7 +409,7 @@ static void lxappearance_save_lxsession()
     char* rel_path = g_strconcat("lxsession/", lxsession_name, "/desktop.conf", NULL);
     char* user_config_file = g_build_filename(g_get_user_config_dir(), rel_path, NULL);
     char* buf;
-    int len;
+    gsize len;
     GKeyFile* kf = g_key_file_new();
 
     if(!g_key_file_load_from_file(kf, user_config_file, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
@@ -211,7 +421,8 @@ static void lxappearance_save_lxsession()
         g_mkdir_with_parents(user_config_file, 0700);
         user_config_file[len] = '/';
 
-        g_key_file_load_from_dirs(kf, rel_path, g_get_system_config_dirs(), NULL, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
+        g_key_file_load_from_dirs(kf, rel_path, (const char**)g_get_system_config_dirs(), NULL,
+                                  G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
     }
 
     g_free(rel_path);
@@ -241,6 +452,10 @@ static void lxappearance_save_lxsession()
     g_key_file_set_integer( kf, "GTK", "iNet/EnableEventSounds", app.enable_event_sound);
     g_key_file_set_integer( kf, "GTK", "iNet/EnableInputFeedbackSounds", app.enable_input_feedback);
 #endif
+    g_key_file_set_integer( kf, "GTK", "iXft/Antialias", app.enable_antialising);
+    g_key_file_set_integer( kf, "GTK", "iXft/Hinting", app.enable_hinting);
+    g_key_file_set_string( kf, "GTK", "sXft/HintStyle", app.hinting_style);
+    g_key_file_set_string( kf, "GTK", "sXft/RGBA", app.font_rgba);
 
     buf = g_key_file_to_data( kf, &len, NULL );
     g_key_file_free(kf);
@@ -264,7 +479,7 @@ static void on_dlg_response(GtkDialog* dlg, int res, gpointer user_data)
         reload_all_programs();
 
         app.changed = FALSE;
-        gtk_dialog_set_response_sensitive(app.dlg, GTK_RESPONSE_APPLY, FALSE);
+        gtk_dialog_set_response_sensitive(GTK_DIALOG(app.dlg), GTK_RESPONSE_APPLY, FALSE);
         break;
     case 1: /* about dialog */
         {
@@ -272,7 +487,7 @@ static void on_dlg_response(GtkDialog* dlg, int res, gpointer user_data)
             if(gtk_builder_add_from_file(b, PACKAGE_UI_DIR "/about.ui", NULL))
             {
                 GtkWidget* dlg = GTK_WIDGET(gtk_builder_get_object(b, "dlg"));
-                gtk_dialog_run(dlg);
+                gtk_dialog_run(GTK_DIALOG(dlg));
                 gtk_widget_destroy(dlg);
             }
             g_object_unref(b);
@@ -300,6 +515,10 @@ static void settings_init()
                 "gtk-enable-event-sounds", &app.enable_event_sound,
                 "gtk-enable-input-feedback-sounds", &app.enable_input_feedback,
 #endif
+                "gtk-xft-antialias", &app.enable_antialising,
+                "gtk-xft-hinting", &app.enable_hinting,
+                "gtk-xft-hintstyle", &app.hinting_style,
+                "gtk-xft-rgba", &app.font_rgba,
                 NULL);
     /* try to figure out cursor theme used. */
     if(!app.cursor_theme || g_strcmp0(app.cursor_theme, "default") == 0)
@@ -331,7 +550,7 @@ static void settings_init()
         GKeyFile* kf = g_key_file_new();
         if(g_key_file_load_from_file(kf, user_config_file, 0, NULL))
             app.color_scheme = g_key_file_get_string(kf, "GTK", "sGtk/ColorScheme", NULL);
-        else if(g_key_file_load_from_dirs(kf, rel_path, g_get_system_config_dirs(), NULL, 0, NULL))
+        else if(g_key_file_load_from_dirs(kf, rel_path, (const char**)g_get_system_config_dirs(), NULL, 0, NULL))
             app.color_scheme = g_key_file_get_string(kf, "GTK", "sGtk/ColorScheme", NULL);
         g_key_file_free(kf);
         g_free(rel_path);
@@ -348,6 +567,10 @@ static void settings_init()
             }
         }
     }
+#if 0
+    /* unfortunately we cannot set colors without XSETTINGS daemon,
+       themes will override any custom settings in .gtkrc-2.0 file */
+    /* FIXME: we should support other xsettings daemons too */
     else
     {
         char* gtkrc_file = g_build_filename(g_get_home_dir(), ".gtkrc-2.0", NULL);
@@ -356,6 +579,7 @@ static void settings_init()
         if(g_hash_table_size(app.color_scheme_hash) > 0)
             app.color_scheme = color_scheme_hash_to_str(app.color_scheme_hash);
     }
+#endif
 }
 
 int main(int argc, char** argv)
@@ -370,7 +594,9 @@ int main(int argc, char** argv)
     textdomain ( GETTEXT_PACKAGE );
 #endif
 
+#if !GLIB_CHECK_VERSION(2, 32, 0)
     g_thread_init(NULL);
+#endif
     /* initialize GTK+ and parse the command line arguments */
     if( G_UNLIKELY( ! gtk_init_with_args( &argc, &argv, "", option_entries, GETTEXT_PACKAGE, &err ) ) )
     {
@@ -403,6 +629,7 @@ int main(int argc, char** argv)
     color_scheme_init(b);
     icon_theme_init(b);
     cursor_theme_init(b);
+    font_init(b);
     other_init(b);
     /* the page for window manager plugins */
     app.wm_page = GTK_WIDGET(gtk_builder_get_object(b, "wm_page"));
@@ -426,6 +653,17 @@ void lxappearance_changed()
     if(!app.changed)
     {
         app.changed = TRUE;
-        gtk_dialog_set_response_sensitive(app.dlg, GTK_RESPONSE_APPLY, TRUE);
+        gtk_dialog_set_response_sensitive(GTK_DIALOG(app.dlg), GTK_RESPONSE_APPLY, TRUE);
+    }
+}
+
+void on_check_button_toggled(GtkToggleButton* btn, gpointer user_data)
+{
+    gboolean* val = (gboolean*)user_data;
+    gboolean new_val = gtk_toggle_button_get_active(btn);
+    if(new_val != *val)
+    {
+        *val = new_val;
+        lxappearance_changed();
     }
 }