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