1 /*
2  * Copyright (C) 2018-2020 Purism SPC
3  *
4  * SPDX-License-Identifier: LGPL-2.1+
5  */
6 #include "config.h"
7 #include "hdy-main-private.h"
8 #include <gio/gio.h>
9 #include <glib/gi18n-lib.h>
10 #include <gtk/gtk.h>
11 
12 static gint hdy_initialized = FALSE;
13 
14 /**
15  * SECTION:hdy-main
16  * @short_description: Library initialization.
17  * @Title: hdy-main
18  *
19  * Before using the Handy library you should initialize it by calling the
20  * hdy_init() function.
21  * This makes sure translations, types, themes, and icons for the Handy library
22  * are set up properly.
23  */
24 
25 /* The style provider priority to use for libhandy widgets custom styling. It is
26  * higher than themes and settings, allowing to override theme defaults, but
27  * lower than applications and user provided styles, so application developers
28  * can nonetheless apply custom styling on top of it. */
29 #define HDY_STYLE_PROVIDER_PRIORITY_OVERRIDE (GTK_STYLE_PROVIDER_PRIORITY_SETTINGS + 1)
30 
31 #define HDY_THEMES_PATH "/sm/puri/handy/themes/"
32 
33 static inline gboolean
hdy_resource_exists(const gchar * resource_path)34 hdy_resource_exists (const gchar *resource_path)
35 {
36   return g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, NULL, NULL);
37 }
38 
39 static gchar *
hdy_themes_get_theme_name(gboolean * prefer_dark_theme)40 hdy_themes_get_theme_name (gboolean *prefer_dark_theme)
41 {
42   gchar *theme_name = NULL;
43   gchar *p;
44 
45   g_assert (prefer_dark_theme);
46 
47   theme_name = g_strdup (g_getenv ("GTK_THEME"));
48 
49   if (theme_name == NULL) {
50     g_object_get (gtk_settings_get_default (),
51                   "gtk-theme-name", &theme_name,
52                   "gtk-application-prefer-dark-theme", prefer_dark_theme,
53                   NULL);
54 
55     return theme_name;
56   }
57 
58   /* Theme variants are specified with the syntax
59    * "<theme>:<variant>" e.g. "Adwaita:dark" */
60   if (NULL != (p = strrchr (theme_name, ':'))) {
61     *p = '\0';
62     p++;
63     *prefer_dark_theme = g_strcmp0 (p, "dark") == 0;
64   }
65 
66   return theme_name;
67 }
68 
69 static void
hdy_themes_update(GtkCssProvider * css_provider)70 hdy_themes_update (GtkCssProvider *css_provider)
71 {
72   g_autofree gchar *theme_name = NULL;
73   g_autofree gchar *resource_path = NULL;
74   gboolean prefer_dark_theme = FALSE;
75 
76   g_assert (GTK_IS_CSS_PROVIDER (css_provider));
77 
78   theme_name = hdy_themes_get_theme_name (&prefer_dark_theme);
79 
80   /* First check with full path to theme+variant */
81   resource_path = g_strdup_printf (HDY_THEMES_PATH"%s%s.css",
82                                    theme_name, prefer_dark_theme ? "-dark" : "");
83 
84   if (!hdy_resource_exists (resource_path)) {
85     /* Now try without the theme variant */
86     g_free (resource_path);
87     resource_path = g_strdup_printf (HDY_THEMES_PATH"%s.css", theme_name);
88 
89     if (!hdy_resource_exists (resource_path)) {
90       /* Now fallback to shared styling */
91       g_free (resource_path);
92       resource_path = g_strdup (HDY_THEMES_PATH"shared.css");
93 
94       g_assert (hdy_resource_exists (resource_path));
95     }
96   }
97 
98   gtk_css_provider_load_from_resource (css_provider, resource_path);
99 }
100 
101 static void
load_fallback_style(void)102 load_fallback_style (void)
103 {
104   g_autoptr (GtkCssProvider) css_provider = NULL;
105 
106   css_provider = gtk_css_provider_new ();
107   gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
108                                              GTK_STYLE_PROVIDER (css_provider),
109                                              GTK_STYLE_PROVIDER_PRIORITY_FALLBACK);
110 
111   gtk_css_provider_load_from_resource (css_provider, HDY_THEMES_PATH"fallback.css");
112 }
113 
114 /**
115  * hdy_style_init:
116  *
117  * Initializes the style classes. This must be called once GTK has been
118  * initialized.
119  *
120  * Since: 1.0
121  */
122 static void
hdy_style_init(void)123 hdy_style_init (void)
124 {
125   static gsize guard = 0;
126   g_autoptr (GtkCssProvider) css_provider = NULL;
127   GtkSettings *settings;
128 
129   if (!g_once_init_enter (&guard))
130     return;
131 
132   css_provider = gtk_css_provider_new ();
133   gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
134                                              GTK_STYLE_PROVIDER (css_provider),
135                                              HDY_STYLE_PROVIDER_PRIORITY_OVERRIDE);
136 
137   settings = gtk_settings_get_default ();
138   g_signal_connect_swapped (settings,
139                            "notify::gtk-theme-name",
140                            G_CALLBACK (hdy_themes_update),
141                            css_provider);
142   g_signal_connect_swapped (settings,
143                            "notify::gtk-application-prefer-dark-theme",
144                            G_CALLBACK (hdy_themes_update),
145                            css_provider);
146 
147   hdy_themes_update (css_provider);
148 
149   load_fallback_style ();
150 
151   g_once_init_leave (&guard, 1);
152 }
153 
154 /**
155  * hdy_icons_init:
156  *
157  * Initializes the embedded icons. This must be called once GTK has been
158  * initialized.
159  *
160  * Since: 1.0
161  */
162 static void
hdy_icons_init(void)163 hdy_icons_init (void)
164 {
165   static gsize guard = 0;
166 
167   if (!g_once_init_enter (&guard))
168     return;
169 
170   gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
171                                     "/sm/puri/handy/icons");
172 
173   g_once_init_leave (&guard, 1);
174 }
175 
176 /**
177  * hdy_init:
178  *
179  * Call this function just after initializing GTK, if you are using
180  * #GtkApplication it means it must be called when the #GApplication::startup
181  * signal is emitted. If libhandy has already been initialized, the function
182  * will simply return.
183  *
184  * This makes sure translations, types, themes, and icons for the Handy library
185  * are set up properly.
186  */
187 void
hdy_init(void)188 hdy_init (void)
189 {
190   if (hdy_initialized)
191     return;
192 
193   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
194   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
195   hdy_init_public_types ();
196 
197   hdy_style_init ();
198   hdy_icons_init ();
199 
200   hdy_initialized = TRUE;
201 }
202