guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCHv2] gtk: Patch GTK+ to look for themes in profiles.


From: Jookia
Subject: [PATCHv2] gtk: Patch GTK+ to look for themes in profiles.
Date: Sun, 13 Mar 2016 15:17:37 +1100

A long running complaint has been that GTK themes aren't found, thus making
GTK look terrible on GuixSD. To solve this, GTK+ now searches in XDG_DATA_DIRS
in themes.

* gnu/packages/gtk.scm (gtk+-2): Add gtk2-theme-paths.patch.
  (gtk+): Add gtk3-theme-paths.patch.
* gnu/packages/patches/gtk2-theme-paths.patch: Add patch.
* gnu/packages/patches/gtk3-theme-paths.patch: Add patch.
---

 gnu/packages/gtk.scm                        |  6 ++--
 gnu/packages/patches/gtk2-theme-paths.patch | 35 ++++++++++++++++++
 gnu/packages/patches/gtk3-theme-paths.patch | 55 +++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 2 deletions(-)
 create mode 100644 gnu/packages/patches/gtk2-theme-paths.patch
 create mode 100644 gnu/packages/patches/gtk3-theme-paths.patch

Hey there,

I've updated this patch to search XDG_DATA_DIRS. Whether this could get upstream
in to GTK+ would be a good discussion, so I'll lay out some appropriate
documentation for future reference below.

gtk+-2.24.28/gtk/gtkrc.c:
> gchar *
> gtk_rc_get_theme_dir (void)
> {
>   const gchar *var;
>   gchar *path;
>
>   var = g_getenv ("GTK_DATA_PREFIX");
>
>   if (var)
>     path = g_build_filename (var, "share", "themes", NULL);
>   else
>     path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
>
>   return path;
> }

gtk+-2.24.28/gtk/gtkrc.c:
> static void
> gtk_rc_parse_named (GtkRcContext *context,
>                     const gchar  *name,
>                     const gchar  *type)
> {
>   gchar *path = NULL;
>   const gchar *home_dir;
>   gchar *subpath;
>
>   if (type)
>     subpath = g_strconcat ("gtk-2.0-", type,
>                            G_DIR_SEPARATOR_S "gtkrc",
>                            NULL);
>   else
>     subpath = g_strdup ("gtk-2.0" G_DIR_SEPARATOR_S "gtkrc");
>
>   /* First look in the users home directory
>    */
>   home_dir = g_get_home_dir ();
>   if (home_dir)
>     {
>       path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
>       if (!g_file_test (path, G_FILE_TEST_EXISTS))
>         {
>           g_free (path);
>           path = NULL;
>         }
>     }
>
>   if (!path)
>     {
>       gchar *theme_dir = gtk_rc_get_theme_dir ();
>       path = g_build_filename (theme_dir, name, subpath, NULL);
>       g_free (theme_dir);
>
>       if (!g_file_test (path, G_FILE_TEST_EXISTS))
>         {
>           g_free (path);
>           path = NULL;
>         }
>     }
>
>   if (path)
>     {
>       gtk_rc_context_parse_file (context, path, GTK_PATH_PRIO_THEME, FALSE);
>       g_free (path);
>     }
>
>   g_free (subpath);
> }

First we have a public API function that helps you find the system's theme
directory, which is also where GTK+ keeps its default themes. Then we have the
code that finds the themes, first looking in the ~/.themes directory and then
the system's theme directory which can be overridden by GTK_DATA_PREFIX. The
API function is deprecated in GTK+3 though it still uses this logic internally.

The GTK2_RC_FILES environmental variable can be used to specify which GTK theme
files to load, and supports multiple files so I assume this could be used to
build a path to load themes outside of the locations in the first function.

gtk+-3.18.2/gtk/gtkcssprovider.c:
> * ## Default files
> *
> * An application can cause GTK+ to parse a specific CSS style sheet by
> * calling gtk_css_provider_load_from_file() and adding the provider with
> * gtk_style_context_add_provider() or 
> gtk_style_context_add_provider_for_screen().
> * In addition, certain files will be read when GTK+ is initialized. First,
> * the file `$XDG_CONFIG_HOME/gtk-3.0/gtk.css`
> * is loaded if it exists. Then, GTK+ tries to load
> * `$HOME/.themes/theme-name/gtk-3.0/gtk.css`,
> * falling back to
> * `datadir/share/themes/theme-name/gtk-3.0/gtk.css`,
> * where theme-name is the name of the current theme
> * (see the #GtkSettings:gtk-theme-name setting) and datadir
> * is the prefix configured when GTK+ was compiled, unless overridden by the
> * `GTK_DATA_PREFIX` environment variable.

With GTK+3 the logic is much the same, though there's some documentation shown
above in the file, whereas I haven't found any kind of specification in GTK+2.

Interestingly enough, the code we've discussed above only deals with loading
themes. GTK+ doesn't even expose an API to help you find themes! If it did we'd
be dealing with the issue of finding themes, not having them just not work. So
how are themes found? Let's look at the routines Xfce, LXDE and GNOME follow:

ibxfce4util-4.12.1/libxfce4util/xfce-resource.c:
>   /*
>    * Data dirs
>    */
>   dirs = _res_getenv ("XDG_DATA_DIRS", DEFAULT_XDG_DATA_DIRS);
>   _res_split_and_append (dirs, XFCE_RESOURCE_DATA);
>   _res_split_and_append (DATADIR, XFCE_RESOURCE_DATA);

ibxfce4util-4.12.1/libxfce4util/xfce-resource.c:
>   /*
>    * Themes dirs
>    */
>   path = xfce_get_homefile (".themes", NULL);
>   _save[XFCE_RESOURCE_THEMES] = g_strdup (path);
>   _list[XFCE_RESOURCE_THEMES] = g_slist_prepend (_list[XFCE_RESOURCE_THEMES], 
> path);
>
>   for (l = _list[XFCE_RESOURCE_DATA]; l != NULL; l = l->next)
>     {
>       path = g_build_filename ((const gchar *) l->data, "themes", NULL);
>       _list[XFCE_RESOURCE_THEMES] = g_slist_append 
> (_list[XFCE_RESOURCE_THEMES], path);
>     }

xfce4-settings-4.12.0/dialogs/appearance-settings/main.c:
>     /* Determine current theme */
>     active_theme_name = xfconf_channel_get_string (xsettings_channel, 
> "/Net/ThemeName", "Default");
>
>     /* Determine directories to look in for ui themes */
>     xfce_resource_push_path (XFCE_RESOURCE_THEMES, DATADIR G_DIR_SEPARATOR_S 
> "themes");
>     ui_theme_dirs = xfce_resource_dirs (XFCE_RESOURCE_THEMES);
>     xfce_resource_pop_path (XFCE_RESOURCE_THEMES);
>
>     /* Iterate over all base directories */
>     for (i = 0; ui_theme_dirs[i] != NULL; ++i)
>     {

Xfce uses a system of resource paths, and for its themes it turns to
XFCE_RESOURCE_THEMES for its paths. The only reason themes show up is because it
includes XDG_DATA_DIRS by default.

lxappearance-0.6.1/sr/widget-theme.c:
>     /* load user dir */
>     dir = g_build_filename(g_get_home_dir(), ".themes", NULL);
>     themes = load_themes_in_dir(dir, themes);
>     g_free(dir);
>
>     /* load system default */
>     dir = gtk_rc_get_theme_dir();
>     themes = load_themes_in_dir(dir, themes);
>     g_free(dir);

While lxappearance actually does what's most expected, it doesn't even find
themes in the Guix profile. Patching this to search XDG_DATA_DIRS would fix it.

gnome-tweak-tool-3.19.90/gtweak/tweaks/tweak_group_interface.py:
>     def _get_valid_themes(self):
>         """ Only shows themes that have variations for gtk+-3 and gtk+-2 """
>         dirs = ( os.path.join(gtweak.DATA_DIR, "themes"),
>                  os.path.join(GLib.get_user_data_dir(), "themes"),
>                  os.path.join(os.path.expanduser("~"), ".themes"))
>         valid = walk_directories(dirs, lambda d:
>                     os.path.exists(os.path.join(d, "gtk-2.0")) and \
>                         os.path.exists(os.path.join(d, "gtk-3.0")))
>         return valid

gnome-tweak-tool-3.19.90/gtweak/tweaks/tweak_group_interface.py:
>     LEGACY_THEME_DIR = os.path.join(GLib.get_home_dir(), ".themes")
>     THEME_DIR = os.path.join(GLib.get_user_data_dir(), "themes")

gnome-tweak-tool-3.19.90/gtweak/tweaks/tweak_group_interface.py:
>             #include both system, and user themes
>             #note: the default theme lives in 
> /system/data/dir/gnome-shell/theme
>             #      and not themes/, so add it manually later
>             dirs = [os.path.join(d, "themes") for d in 
> GLib.get_system_data_dirs()]
>             dirs += [ShellThemeTweak.THEME_DIR]

gnome-tweak-tool-3.19.90/gtweak/tweaks/tweak_group_interface.py:
>                 theme_members_path = "/".join(fragment)
>
>                 ok, updated = extract_zip_file(
>                                 z,
>                                 theme_members_path,
>                                 os.path.join(ShellThemeTweak.THEME_DIR, 
> theme_name, "gnome-shell"))
>
>                 if ok:
>                     if updated:
>                         self.notify_information(_("%s theme updated 
> successfully") % theme_name)
>                     else:
>                         self.notify_information(_("%s theme installed 
> successfully") % theme_name)

While at first gnome-tweak-tool looks promising in that it uses
get_user_data_dir and seems to think that ~/.themes is for legacy use (what?),
unfortunately get_user_data_dir isn't the same as get_system_data_dirs which
corresponds to XDG_DATA_DIRS, it instead corresponds to XDG_DATA_HOME. Which
defaults to $HOME/.local/share, though I'd image GNOME sets it to $HOME
otherwise GTK+ wouldn't find the themes it installs at all!

freeciv/client/gui-gtk-3.0/themes.c:
>   /* Freeciv-specific GTK3 themes directories */
>   strvec_iterate(data_dirs, dir_name) {
>     char buf[strlen(dir_name) + strlen("/themes/gui-gtk-3.0") + 1];
>
>     fc_snprintf(buf, sizeof(buf), "%s/themes/gui-gtk-3.0", dir_name);
>
>     directories[(*count)++] = fc_strdup(buf);
>   } strvec_iterate_end;
>
>   /* standard GTK+ themes directory (e.g. /usr/share/themes) */
>   standard_dir = gtk_rc_get_theme_dir();
>   directories[(*count)++] = fc_strdup(standard_dir);
>   g_free(standard_dir);
>
>   /* user GTK+ themes directory (~/.themes) */
>   home_dir = user_home_dir();
>   if (home_dir) {
>     char buf[strlen(home_dir) + 16];
>
>     fc_snprintf(buf, sizeof(buf), "%s/.themes/", home_dir);
>     directories[(*count)++] = fc_strdup(buf);
>   }

As a bonus, while technically not a theme switcher, Freeciv searches for themes
and will fail to find them in Guix too. This is bigger than just DEs.

I'm not aware what MATE or KDE do, but I assume it couldn't be much different to
the above examples. In the end, GTK+ dictates the behaviour.
So what do we do? I think there's a few possibilities:

A. Make a global GTK_DATA_DIR and link all themes to it. This means users can't
install their own themes either, so this is a bad option.

B. Build ~/.themes ourselves with symlinks while also letting users write to it
and hope that theme managers don't break if they try to overwrite themes, or
those that deal with XDG_DATA_DIRS finding duplicates. Bad option.

C. Check what NixOS does to fix this. They seem to have a patch for
gnome-tweak-tool which makes it use XDG_DATA_DIRS, but that's about it. It might
explain why my theming has been weird on NixOS. Same as D.

D. Patch everything to use XDG_DATA_DIRS and try to get upstream to do this.
This isn't a new thought either, freedesktop's Desktop Theme Specification
suggests this behaviour. It's a draft from 2013, but worth reviving I think.

Cheers,
Jookia.

diff --git a/gnu/packages/gtk.scm b/gnu/packages/gtk.scm
index 76b01ec..c3d3d12 100644
--- a/gnu/packages/gtk.scm
+++ b/gnu/packages/gtk.scm
@@ -528,7 +528,8 @@ is part of the GNOME accessibility project.")
             (sha256
              (base32
               "0mj6xn40py9r9lvzg633fal81xfwfm89d9mvz7jk4lmwk0g49imj"))
-            (patches (list (search-patch 
"gtk2-respect-GUIX_GTK2_PATH.patch")))))
+            (patches (list (search-patch "gtk2-respect-GUIX_GTK2_PATH.patch")
+                           (search-patch "gtk2-theme-paths.patch")))))
    (build-system gnu-build-system)
    (outputs '("out" "doc"))
    (propagated-inputs
@@ -589,7 +590,8 @@ application suites.")
             (sha256
              (base32
               "0lp1hn0qydxx03bianzzr0a4maqzsvylrkzr7c3p0050qihwbgjx"))
-            (patches (list (search-patch 
"gtk3-respect-GUIX_GTK3_PATH.patch")))))
+            (patches (list (search-patch "gtk3-respect-GUIX_GTK3_PATH.patch")
+                           (search-patch "gtk3-theme-paths.patch")))))
    (propagated-inputs
     `(("at-spi2-atk" ,at-spi2-atk)
       ("atk" ,atk)
diff --git a/gnu/packages/patches/gtk2-theme-paths.patch 
b/gnu/packages/patches/gtk2-theme-paths.patch
new file mode 100644
index 0000000..397cc97
--- /dev/null
+++ b/gnu/packages/patches/gtk2-theme-paths.patch
@@ -0,0 +1,35 @@
+diff -Naur gtk+-2.24.28.new/gtk/gtkrc.c gtk+-2.24.28/gtk/gtkrc.c
+--- gtk+-2.24.28.new/gtk/gtkrc.c       2016-03-13 10:31:14.413644362 +1100
++++ gtk+-2.24.28/gtk/gtkrc.c   2016-03-13 12:51:34.723398423 +1100
+@@ -808,6 +808,8 @@
+   gchar *path = NULL;
+   const gchar *home_dir;
+   gchar *subpath;
++  const gchar * const *xdg_data_dirs;
++  gint i;
+ 
+   if (type)
+     subpath = g_strconcat ("gtk-2.0-", type,
+@@ -830,6 +832,22 @@
+     }
+ 
+   if (!path)
++    {
++      xdg_data_dirs = g_get_system_data_dirs ();
++      for (i = 0; xdg_data_dirs[i]; i++)
++        {
++          path = g_build_filename (xdg_data_dirs[i], "themes", name, subpath, 
NULL);
++          if (g_file_test (path, G_FILE_TEST_EXISTS))
++            break;
++          else
++            {
++              g_free (path);
++              path = NULL;
++            }
++        }
++    }
++
++  if (!path)
+     {
+       gchar *theme_dir = gtk_rc_get_theme_dir ();
+       path = g_build_filename (theme_dir, name, subpath, NULL);
diff --git a/gnu/packages/patches/gtk3-theme-paths.patch 
b/gnu/packages/patches/gtk3-theme-paths.patch
new file mode 100644
index 0000000..422fee6
--- /dev/null
+++ b/gnu/packages/patches/gtk3-theme-paths.patch
@@ -0,0 +1,55 @@
+diff -Naur gtk+-3.18.2.new/gtk/gtkcssprovider.c 
gtk+-3.18.2/gtk/gtkcssprovider.c
+--- gtk+-3.18.2.new/gtk/gtkcssprovider.c       2016-03-13 10:36:40.543261551 
+1100
++++ gtk+-3.18.2/gtk/gtkcssprovider.c   2016-03-13 13:23:40.884949743 +1100
+@@ -3032,6 +3032,8 @@
+ {
+   gchar *path;
+   const gchar *var;
++  const gchar * const *xdg_data_dirs;
++  gint i;
+ 
+   /* First look in the user's config directory */
+   path = _gtk_css_find_theme_dir (g_get_user_data_dir (), "themes", name, 
variant);
+@@ -3043,6 +3045,15 @@
+   if (path)
+     return path;
+ 
++  /* Look in specified data directories */
++  xdg_data_dirs = g_get_system_data_dirs ();
++  for (i = 0; xdg_data_dirs[i]; i++)
++    {
++      path = _gtk_css_find_theme_dir (xdg_data_dirs[i], "themes", name, 
variant);
++      if (path)
++        return path;
++    }
++
+   /* Finally, try in the default theme directory */
+   var = g_getenv ("GTK_DATA_PREFIX");
+   if (!var)
+diff -Naur gtk+-3.18.2.new/gtk/inspector/visual.c 
gtk+-3.18.2/gtk/inspector/visual.c
+--- gtk+-3.18.2.new/gtk/inspector/visual.c     2016-03-13 10:36:40.639262027 
+1100
++++ gtk+-3.18.2/gtk/inspector/visual.c 2016-03-13 13:25:52.057600193 +1100
+@@ -228,6 +228,8 @@
+   GHashTable *t;
+   GHashTableIter iter;
+   gchar *theme, *path;
++  const gchar * const *xdg_data_dirs;
++  gint i;
+ 
+   t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+   /* Builtin themes */
+@@ -243,6 +245,14 @@
+   fill_gtk (path, t);
+   g_free (path);
+ 
++  xdg_data_dirs = g_get_system_data_dirs ();
++  for (i = 0; xdg_data_dirs[i]; i++)
++    {
++      path = g_build_filename (xdg_data_dirs[i], "themes", NULL);
++      fill_gtk (path, t);
++      g_free (path);
++    }
++
+   path = g_build_filename (g_get_home_dir (), ".themes", NULL);
+   fill_gtk (path, t);
+   g_free (path);
-- 
2.7.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]