Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
splash.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4 Copyright (C) 2026 Guillaume Stutin.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "gui/splash.h"
21
22#include "system/macros.h"
23#include "system/mem_alloc.h"
24#include "common/paths.h"
27
28#include <glib/gi18n.h>
29#include <gtk/gtk.h>
30#include <math.h>
31#include <pango/pangocairo.h>
32#include <stdlib.h>
33#include "config.h"
55
56typedef struct dt_splash_slide_t
57{
58 gchar *path;
59 gchar *author;
61
62static dt_splash_t *splash = NULL;
63
64static gboolean _splash_env_is_truthy(const char *value)
65{
66 if(IS_NULL_PTR(value)) return FALSE;
67 if(value[0] == '\0') return TRUE;
68 if(g_ascii_strcasecmp(value, "0") == 0) return FALSE;
69 if(g_ascii_strcasecmp(value, "false") == 0) return FALSE;
70 if(g_ascii_strcasecmp(value, "no") == 0) return FALSE;
71 if(g_ascii_strcasecmp(value, "off") == 0) return FALSE;
72 return TRUE;
73}
74
75static gboolean _splash_is_disabled(void)
76{
77 return _splash_env_is_truthy(g_getenv("ANSEL_NO_SPLASH"))
78 || _splash_env_is_truthy(g_getenv("ANSEL_DISABLE_SPLASH"))
79 || _splash_env_is_truthy(g_getenv("DARKTABLE_NO_SPLASH"))
80 || _splash_env_is_truthy(g_getenv("DARKTABLE_DISABLE_SPLASH"));
81}
82
84{
85 if(IS_NULL_PTR(splash) || IS_NULL_PTR(splash->window) || IS_NULL_PTR(parent)) return;
86 gtk_window_set_transient_for(GTK_WINDOW(splash->window), GTK_WINDOW(parent));
87 gtk_window_set_keep_above(GTK_WINDOW(splash->window), TRUE);
88}
89
90static void _splash_force_show(void)
91{
93
94 gtk_widget_show_all(splash->window);
95 gtk_window_present(GTK_WINDOW(splash->window));
96 gtk_widget_show_now(splash->window);
97 gtk_widget_queue_draw(splash->drawing);
98 gtk_widget_queue_draw(splash->window);
99
100 GdkDisplay *display = gdk_display_get_default();
101 if(display) gdk_display_flush(display);
102
103 splash->shown = TRUE;
104}
105
107{
108 if(IS_NULL_PTR(splash)) return;
110 {
111 g_object_unref(splash->slide_pixbuf);
112 splash->slide_pixbuf = NULL;
113 }
118}
119
120static void _splash_add_css(const char *data)
121{
122 if(IS_NULL_PTR(splash) || IS_NULL_PTR(splash->css)) return;
123 gtk_css_provider_load_from_data(splash->css, data, -1, NULL);
124}
125
126static gchar *_splash_build_data_path(const char *subpath)
127{
128 char datadir[DT_PATH_MAX] = { 0 };
129 dt_loc_get_datadir(datadir, sizeof(datadir));
130 return g_build_filename(datadir, "pixmaps", "splash", subpath, NULL);
131}
132
133static gchar *_splash_build_author_list(guint max_names)
134{
136 return g_strdup(_("Contributors"));
137
138 GString *buf = g_string_new(NULL);
139 guint used = 0;
140 g_string_append(buf, "© ");
141
142 for(guint i = 0; i < splash->authors->len && used < max_names; i++)
143 {
144 const gchar *name = (const gchar *)splash->authors->pdata[i];
145 if(IS_NULL_PTR(name) || !name[0]) continue;
146 used++;
147
148 g_string_append(buf, name);
149
150 if(used < max_names)
151 g_string_append(buf, ", ");
152 }
153
154 g_string_append(buf, "… and all contributors.");
155
156 return g_string_free(buf, FALSE);
157}
158
159static GtkWidget *_splash_shadow_label_new(const gchar *text, const gchar *name, const gchar *shadow_name,
160 GtkWidget **out_main)
161{
162 GtkWidget *fixed = gtk_fixed_new();
163 GtkWidget *shadow1 = gtk_label_new(text);
164 GtkWidget *shadow2 = gtk_label_new(text);
165 GtkWidget *label = gtk_label_new(text);
166
167 if(shadow_name) gtk_widget_set_name(shadow1, shadow_name);
168 if(shadow_name) gtk_widget_set_name(shadow2, shadow_name);
169 if(name) gtk_widget_set_name(label, name);
170
171 PangoAttrList *shadow_attrs = pango_attr_list_new();
172 PangoAttribute *fg = pango_attr_foreground_new(0, 0, 0);
173 PangoAttribute *alpha = pango_attr_foreground_alpha_new((guint16)(0.75 * 65535));
174 pango_attr_list_insert(shadow_attrs, fg);
175 pango_attr_list_insert(shadow_attrs, alpha);
176 gtk_label_set_attributes(GTK_LABEL(shadow1), shadow_attrs);
177 gtk_label_set_attributes(GTK_LABEL(shadow2), shadow_attrs);
178 pango_attr_list_unref(shadow_attrs);
179
180 PangoAttrList *main_attrs = pango_attr_list_new();
181 PangoAttribute *main_fg = pango_attr_foreground_new(65535, 65535, 65535);
182 PangoAttribute *main_alpha = pango_attr_foreground_alpha_new(65535);
183 pango_attr_list_insert(main_attrs, main_fg);
184 pango_attr_list_insert(main_attrs, main_alpha);
185 gtk_label_set_attributes(GTK_LABEL(label), main_attrs);
186 pango_attr_list_unref(main_attrs);
187
188 gtk_fixed_put(GTK_FIXED(fixed), shadow1, 1, 1);
189 gtk_fixed_put(GTK_FIXED(fixed), shadow2, 2, 2);
190 gtk_fixed_put(GTK_FIXED(fixed), label, 0, 0);
191
192 GList *shadows = NULL;
193 shadows = g_list_append(shadows, shadow1);
194 shadows = g_list_append(shadows, shadow2);
195 g_object_set_data_full(G_OBJECT(label), "splash-shadow-labels", shadows, (GDestroyNotify)g_list_free);
196
197 if(out_main) *out_main = label;
198
199 return fixed;
200}
201
202static void _splash_shadow_label_set_text(GtkWidget *label, const gchar *text)
203{
204 if(IS_NULL_PTR(label)) return;
205 gtk_label_set_text(GTK_LABEL(label), text);
206 GList *shadows = (GList *)g_object_get_data(G_OBJECT(label), "splash-shadow-labels");
207 for(GList *iter = shadows; iter; iter = g_list_next(iter))
208 gtk_label_set_text(GTK_LABEL(iter->data), text);
209}
210
211static dt_splash_slide_t *_splash_slide_new(const gchar *path, const gchar *author)
212{
213 dt_splash_slide_t *slide = g_malloc0(sizeof(dt_splash_slide_t));
214 slide->path = g_strdup(path);
215 slide->author = author ? g_strdup(author) : NULL;
216 return slide;
217}
218
219static void _splash_slide_free(gpointer data)
220{
221 dt_splash_slide_t *slide = (dt_splash_slide_t *)data;
222 if(IS_NULL_PTR(slide)) return;
223 dt_free(slide->path);
224 dt_free(slide->author);
225 dt_free(slide);
226}
227
228static gboolean _splash_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
229{
230 if(IS_NULL_PTR(splash) || !splash->slides || splash->slides->len == 0) return FALSE;
231
232 GtkAllocation alloc;
233 gtk_widget_get_allocation(widget, &alloc);
234 const int width = alloc.width;
235 const int height = alloc.height;
236
238 if(!slide || !slide->path) return FALSE;
239
240 int scale_factor = gtk_widget_get_scale_factor(widget);
241 if(scale_factor < 1) scale_factor = 1;
242 const int dev_width = width * scale_factor;
243 const int dev_height = height * scale_factor;
244
245 const int slide_index = splash->current_slide % splash->slides->len;
246 const gboolean cache_ok = splash->slide_pixbuf
247 && splash->slide_cache_index == slide_index
248 && splash->slide_cache_width == dev_width
249 && splash->slide_cache_height == dev_height
250 && splash->slide_cache_scale == scale_factor;
251
252 if(!cache_ok)
253 {
255
256 GError *error = NULL;
257 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(slide->path, &error);
258 if(IS_NULL_PTR(pixbuf))
259 {
260 if(error) g_error_free(error);
261 return FALSE;
262 }
263
264 const int img_w = gdk_pixbuf_get_width(pixbuf);
265 const int img_h = gdk_pixbuf_get_height(pixbuf);
266 const double scale = fmax((double)dev_width / img_w, (double)dev_height / img_h);
267 const int scaled_w = (int)ceil(img_w * scale);
268 const int scaled_h = (int)ceil(img_h * scale);
269
270 splash->slide_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scaled_w, scaled_h, GDK_INTERP_HYPER);
271 g_object_unref(pixbuf);
272 splash->slide_cache_index = slide_index;
273 splash->slide_cache_width = dev_width;
274 splash->slide_cache_height = dev_height;
275 splash->slide_cache_scale = scale_factor;
276 }
277
278 const int scaled_w = gdk_pixbuf_get_width(splash->slide_pixbuf);
279 const int scaled_h = gdk_pixbuf_get_height(splash->slide_pixbuf);
280 const int offset_x = (dev_width - scaled_w) / 2;
281 const int offset_y = (dev_height - scaled_h) / 2;
282
283 cairo_save(cr);
284 cairo_scale(cr, 1.0 / scale_factor, 1.0 / scale_factor);
285 cairo_rectangle(cr, 0, 0, dev_width, dev_height);
286 cairo_clip(cr);
287
288 gdk_cairo_set_source_pixbuf(cr, splash->slide_pixbuf, offset_x, offset_y);
289 cairo_paint(cr);
290
291 cairo_restore(cr);
292
293 if(slide->author && slide->author[0])
294 {
295 gchar *label = g_strdup_printf(_("© %s"), slide->author);
296 PangoLayout *layout = gtk_widget_create_pango_layout(widget, label);
297 PangoFontDescription *desc = pango_font_description_from_string("14px Roboto");
298 pango_layout_set_font_description(layout, desc);
299 pango_font_description_free(desc);
300 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
301 pango_layout_set_width(layout, (width - 32) * PANGO_SCALE);
302
303 int text_w = 0, text_h = 0;
304 pango_layout_get_pixel_size(layout, &text_w, &text_h);
305
306 const int pad = 6;
307 const int margin = 0;
308 int box_w = text_w + pad * 2;
309 int box_h = text_h + pad * 2;
310 int x = width - box_w - margin;
311 int y = margin;
312
313 cairo_save(cr);
314 cairo_rectangle(cr, x, y, box_w, box_h);
315 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.55);
316 cairo_fill(cr);
317
318 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.85);
319 cairo_move_to(cr, x + pad, y + pad);
320 pango_cairo_show_layout(cr, layout);
321 cairo_restore(cr);
322
323 g_object_unref(layout);
324 dt_free(label);
325 }
326
327 return FALSE;
328}
329
330static gboolean _splash_slide_advance(gpointer user_data)
331{
332 if(IS_NULL_PTR(splash) || !splash->drawing) return G_SOURCE_REMOVE;
333 if(!splash->slides || splash->slides->len == 0) return G_SOURCE_CONTINUE;
336 gtk_widget_queue_draw(splash->drawing);
337 return G_SOURCE_CONTINUE;
338}
339
340static void _splash_load_authors(void)
341{
342 char datadir[DT_PATH_MAX] = { 0 };
343 dt_loc_get_datadir(datadir, sizeof(datadir));
344 gchar *path = g_build_filename(datadir, "AUTHORS", NULL);
345 gchar *content = NULL;
346 gsize len = 0;
347
348 if(g_file_get_contents(path, &content, &len, NULL) && content)
349 {
350 gchar **lines = g_strsplit(content, "\n", -1);
351 for(gint i = 0; lines[i]; i++)
352 {
353 gchar *trim = g_strstrip(lines[i]);
354 if(trim[0] == '\0') continue;
355 if(trim[0] == '*') continue;
356 // AUTHORS lists one person in every section they belong to (developer and translator, say),
357 // while the splash flattens the sections into a single line: keep each name once.
358 if(g_ptr_array_find_with_equal_func(splash->authors, trim, g_str_equal, NULL)) continue;
359 g_ptr_array_add(splash->authors, g_strdup(trim));
360 }
361 g_strfreev(lines);
362 dt_free(content);
363 }
364 dt_free(path);
365
366 if(splash->authors->len == 0)
367 {
368 g_ptr_array_add(splash->authors, g_strdup(_("Darktable & Ansel contributors")));
369 }
370}
371
372static void _splash_load_slides(void)
373{
374 const gchar *default_author = _("Boilerplate image");
375 gchar *list_path = _splash_build_data_path("slides.txt");
376 gchar *content = NULL;
377 gsize len = 0;
378 if(g_file_get_contents(list_path, &content, &len, NULL) && content)
379 {
380 gchar **lines = g_strsplit(content, "\n", -1);
381 for(gint i = 0; lines[i]; i++)
382 {
383 gchar *line = g_strstrip(lines[i]);
384 if(line[0] == '\0' || line[0] == '#') continue;
385
386 gchar **parts = g_strsplit(line, "|", 2);
387 const gchar *name = parts[0] ? g_strstrip(parts[0]) : NULL;
388 const gchar *author = (parts[1] && parts[1][0]) ? g_strstrip(parts[1]) : default_author;
389 if(name && name[0])
390 {
391 gchar *path = NULL;
392 if(g_path_is_absolute(name))
393 path = g_strdup(name);
394 else
396
397 if(g_file_test(path, G_FILE_TEST_EXISTS))
398 g_ptr_array_add(splash->slides, _splash_slide_new(path, author));
399 dt_free(path);
400 }
401 g_strfreev(parts);
402 }
403 g_strfreev(lines);
404 dt_free(content);
405 }
406 dt_free(list_path);
407}
408
409static void _splash_update_message(const gchar *message)
410{
414 gtk_widget_queue_draw(splash->label_message);
415 gtk_widget_queue_draw(splash->drawing);
416 for(int i = 0; i < 2; i++)
417 gtk_main_iteration_do(FALSE);
418}
419
420static gchar *_splash_capitalize_name(const char *name)
421{
422 if(IS_NULL_PTR(name) || !name[0]) return g_strdup("");
423 gchar *out = g_strdup(name);
424 out[0] = g_ascii_toupper(out[0]);
425 return out;
426}
427
428static gboolean _splash_logo_set_from_path(GtkWidget *logo, const char *path, int target_size, int scale_factor)
429{
430 if(IS_NULL_PTR(logo) || IS_NULL_PTR(path)) return FALSE;
431 if(scale_factor < 1) scale_factor = 1;
432
433 const int target_px = target_size * scale_factor;
434 GError *error = NULL;
435 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, target_px, target_px, TRUE, &error);
436 if(IS_NULL_PTR(pixbuf))
437 {
438 if(error) g_error_free(error);
439 return FALSE;
440 }
441
442 cairo_surface_t *surface = gdk_cairo_surface_create_from_pixbuf(pixbuf, scale_factor, NULL);
443 g_object_unref(pixbuf);
444 if(IS_NULL_PTR(surface)) return FALSE;
445
446 gtk_image_set_from_surface(GTK_IMAGE(logo), surface);
447 cairo_surface_destroy(surface);
448 return TRUE;
449}
450
451static GtkWidget *_splash_create_logo(int target_size, int scale_factor, gchar **out_path)
452{
453 char datadir[DT_PATH_MAX] = { 0 };
454 char sharedir[DT_PATH_MAX] = { 0 };
455 dt_loc_get_datadir(datadir, sizeof(datadir));
456 dt_loc_get_sharedir(sharedir, sizeof(sharedir));
457
458 // Best effort to find a logo
459 GtkWidget *image = gtk_image_new();
460 GPtrArray *paths = g_ptr_array_new_with_free_func(g_free);
461 g_ptr_array_add(paths, g_build_filename(datadir, "pixmaps", "scalable", "ansel.svg", NULL));
462 g_ptr_array_add(paths, g_build_filename(sharedir, "icons", "hicolor", "scalable", "apps", "ansel.svg", NULL));
463 const char *sizes[] = { "256x256", "128x128", "64x64", NULL };
464 for(guint i = 0; sizes[i]; i++)
465 {
466 g_ptr_array_add(paths, g_build_filename(datadir, "pixmaps", sizes[i], "ansel.png", NULL));
467 g_ptr_array_add(paths, g_build_filename(sharedir, "icons", "hicolor", sizes[i], "apps", "ansel.png", NULL));
468 }
469
470 gboolean loaded = FALSE;
471 for(guint i = 0; i < paths->len && !loaded; i++)
472 {
473 const gchar *path = (const gchar *)paths->pdata[i];
474 if(path && g_file_test(path, G_FILE_TEST_EXISTS))
475 {
476 if(_splash_logo_set_from_path(image, path, target_size, scale_factor))
477 {
478 if(out_path) *out_path = g_strdup(path);
479 loaded = TRUE;
480 }
481 }
482 }
483
484 if(!loaded)
485 {
486 gtk_image_set_from_icon_name(GTK_IMAGE(image), "ansel", GTK_ICON_SIZE_DIALOG);
487 gtk_image_set_pixel_size(GTK_IMAGE(image), target_size);
488 }
489
490 g_ptr_array_free(paths, TRUE);
491 return image;
492}
493
495{
497
498 GtkWidget *scale_widget = splash->window ? splash->window : splash->logo;
499 int scale_factor = gtk_widget_get_scale_factor(scale_widget);
500 if(scale_factor < 1) scale_factor = 1;
501 if(scale_factor == splash->logo_scale_factor) return;
502
503 const int target_size = 128;
504 if(_splash_logo_set_from_path(splash->logo, splash->logo_path, target_size, scale_factor))
505 splash->logo_scale_factor = scale_factor;
506}
507
508static void _splash_scale_factor_changed(GObject *object, GParamSpec *pspec, gpointer user_data)
509{
510 (void)object;
511 (void)pspec;
512 (void)user_data;
514}
515
517{
518 if(splash) return;
519 if(_splash_is_disabled()) return;
520
521 splash = calloc(1, sizeof(dt_splash_t));
522 splash->authors = g_ptr_array_new_with_free_func(g_free);
523 splash->slides = g_ptr_array_new_with_free_func(_splash_slide_free);
525 splash->shown = FALSE;
526
529
530 splash->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
531 gtk_window_set_decorated(GTK_WINDOW(splash->window), FALSE);
532 gtk_window_set_resizable(GTK_WINDOW(splash->window), FALSE);
533 gtk_window_set_position(GTK_WINDOW(splash->window), GTK_WIN_POS_CENTER);
534 gtk_window_set_type_hint(GTK_WINDOW(splash->window), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
535 gtk_window_set_keep_above(GTK_WINDOW(splash->window), TRUE);
536 gtk_window_set_default_size(GTK_WINDOW(splash->window), 960, 600);
537 gtk_widget_set_app_paintable(splash->window, TRUE);
538 gtk_widget_set_name(splash->window, "ansel-splash");
539 g_signal_connect(G_OBJECT(splash->window), "notify::scale-factor",
540 G_CALLBACK(_splash_scale_factor_changed), NULL);
541
542 GtkWidget *overlay = gtk_overlay_new();
543 gtk_container_add(GTK_CONTAINER(splash->window), overlay);
544
545 splash->drawing = gtk_drawing_area_new();
546 gtk_widget_set_hexpand(splash->drawing, TRUE);
547 gtk_widget_set_vexpand(splash->drawing, TRUE);
548 gtk_widget_set_name(splash->drawing, "splash-background");
549 gtk_container_add(GTK_CONTAINER(overlay), splash->drawing);
550 g_signal_connect(G_OBJECT(splash->drawing), "draw", G_CALLBACK(_splash_draw), NULL);
551
552 GtkWidget *info_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
553 gtk_widget_set_name(info_box, "splash-info");
554 gtk_widget_set_halign(info_box, GTK_ALIGN_START);
555 gtk_widget_set_valign(info_box, GTK_ALIGN_END);
556 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), info_box);
557
558 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
559 gtk_widget_set_name(header, "splash-header");
560 gtk_box_pack_start(GTK_BOX(info_box), header, FALSE, FALSE, 0);
561
563 splash->logo_path = NULL;
565 if(splash->logo)
566 {
567 gtk_widget_set_name(splash->logo, "splash-logo");
568 gtk_widget_set_size_request(splash->logo, 128, 128);
569 gtk_widget_set_halign(splash->logo, GTK_ALIGN_START);
570 gtk_widget_set_valign(splash->logo, GTK_ALIGN_START);
571 gtk_box_pack_start(GTK_BOX(header), splash->logo, FALSE, FALSE, 0);
572 }
573
574 GtkWidget *title_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
575 gtk_widget_set_name(title_box, "splash-title-box");
576 gtk_box_pack_start(GTK_BOX(header), title_box, FALSE, FALSE, 0);
577
578 gchar *app_name = _splash_capitalize_name(PACKAGE_NAME);
579 GtkWidget *title_fixed = _splash_shadow_label_new(app_name, "splash-title", "splash-title-shadow", NULL);
580 dt_free(app_name);
581 gtk_box_pack_start(GTK_BOX(title_box), title_fixed, FALSE, FALSE, 0);
582
583 GtkWidget *version_fixed = _splash_shadow_label_new(darktable_package_string, "splash-version",
584 "splash-version-shadow", NULL);
585 gtk_box_pack_start(GTK_BOX(title_box), version_fixed, FALSE, FALSE, 0);
586
587 gchar *authors_line = _splash_build_author_list(5);
588 GtkWidget *authors_fixed = _splash_shadow_label_new(authors_line, "splash-authors", "splash-authors-shadow", NULL);
589 dt_free(authors_line);
590 gtk_box_pack_start(GTK_BOX(title_box), authors_fixed, FALSE, FALSE, 0);
591
592 GtkWidget *ticker_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
593 gtk_widget_set_name(ticker_box, "splash-ticker");
594 gtk_widget_set_halign(ticker_box, GTK_ALIGN_FILL);
595 gtk_widget_set_valign(ticker_box, GTK_ALIGN_END);
596 gtk_widget_set_hexpand(ticker_box, TRUE);
597 gtk_widget_set_size_request(ticker_box, -1, 28);
598 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), ticker_box);
599
600 GtkWidget *message_fixed = _splash_shadow_label_new(_("Starting..."), "splash-message",
601 "splash-message-shadow",
603 gtk_box_pack_start(GTK_BOX(ticker_box), message_fixed, FALSE, FALSE, 0);
604
605 splash->css = gtk_css_provider_new();
607 "#ansel-splash {"
608 " background-color: #777777;"
609 "}"
610 "#splash-info {"
611 " background-color: transparent;"
612 " background-image: none;"
613 " box-shadow: none;"
614 " margin: 12px 0;"
615 " padding: 12px 0;"
616 " -GtkBox-spacing: 12px;"
617 "}"
618 "#splash-header {"
619 " -GtkBox-spacing: 12px;"
620 " margin: 12px 0;"
621 " padding: 12px 0;"
622 "}"
623 "#splash-title-box {"
624 " -GtkBox-spacing: 12px;"
625 " padding-top: 18px;"
626 "}"
627 "#splash-logo {"
628 " padding: 0;"
629 " margin: 0;"
630 "}"
631 "#splash-title {"
632 " color: #f2f2f2;"
633 " font: 700 40px \"Roboto\";"
634 "}"
635 "#splash-title-shadow {"
636 " color: rgba(0,0,0,0.75);"
637 " font: 700 40px \"Roboto\";"
638 "}"
639 "#splash-version {"
640 " color: rgb(255,255,255);"
641 " font: 16px \"Roboto\";"
642 "}"
643 "#splash-version-shadow {"
644 " color: rgba(0,0,0,0.75);"
645 " font: 16px \"Roboto\";"
646 "}"
647 "#splash-message {"
648 " color: rgba(255,255,255,0.9);"
649 " font: 16px \"Roboto\";"
650 "}"
651 "#splash-message-shadow {"
652 " color: rgba(0,0,0,0.75);"
653 " font: 16px \"Roboto\";"
654 "}"
655 "#splash-ticker {"
656 " background: transparent;"
657 " padding: 24px;"
658 " background-image: none;"
659 " box-shadow: none;"
660 " margin: 0;"
661 "}"
662 "#splash-authors {"
663 " color: rgba(255,255,255,0.92);"
664 " font: 16px \"Roboto\";"
665 " margin: 6px 0;"
666 "}"
667 "#splash-authors-shadow {"
668 " color: rgba(0,0,0,0.75);"
669 " font: 16px \"Roboto\";"
670 " margin: 6px 0;"
671 "}"
672 );
673
674 GdkScreen *screen = gdk_screen_get_default();
675 if(screen)
676 gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(splash->css),
677 GTK_STYLE_PROVIDER_PRIORITY_USER + 2);
678
681
682 splash->slide_timeout_id = g_timeout_add(4000, _splash_slide_advance, NULL);
683}
684
685void dt_gui_splash_update(const char *message)
686{
687 if(IS_NULL_PTR(splash) || IS_NULL_PTR(message)) return;
688 _splash_update_message(message);
689}
690
691void dt_gui_splash_updatef(const char *format, ...)
692{
693 if(IS_NULL_PTR(splash) || IS_NULL_PTR(format)) return;
694 va_list ap;
695 va_start(ap, format);
696 gchar *buf = g_strdup_vprintf(format, ap);
697 va_end(ap);
699 dt_free(buf);
700}
701
703{
704 if(IS_NULL_PTR(splash)) return;
705
706 if(splash->slide_timeout_id) g_source_remove(splash->slide_timeout_id);
707
708 gtk_widget_destroy(splash->window);
709 GdkScreen *screen = gdk_screen_get_default();
710 if(screen)
711 gtk_style_context_remove_provider_for_screen(screen, GTK_STYLE_PROVIDER(splash->css));
712 g_object_unref(splash->css);
713
714 g_ptr_array_free(splash->authors, TRUE);
715 g_ptr_array_free(splash->slides, TRUE);
718
720}
721
722// clang-format off
723// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
724// vim: shiftwidth=2 expandtab tabstop=2 cindent
725// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
726// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const float x
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
#define PACKAGE_NAME
const char darktable_package_string[]
void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
void dt_loc_get_datadir(char *datadir, size_t bufsize)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
static void _splash_load_slides(void)
Definition splash.c:372
static gboolean _splash_slide_advance(gpointer user_data)
Definition splash.c:330
static GtkWidget * _splash_create_logo(int target_size, int scale_factor, gchar **out_path)
Definition splash.c:451
static void _splash_clear_slide_cache(void)
Definition splash.c:106
static gchar * _splash_build_author_list(guint max_names)
Definition splash.c:133
void dt_gui_splash_init(void)
Definition splash.c:516
static dt_splash_slide_t * _splash_slide_new(const gchar *path, const gchar *author)
Definition splash.c:211
static void _splash_load_authors(void)
Definition splash.c:340
static gboolean _splash_is_disabled(void)
Definition splash.c:75
static gchar * _splash_build_data_path(const char *subpath)
Definition splash.c:126
static void _splash_slide_free(gpointer data)
Definition splash.c:219
void dt_gui_splash_updatef(const char *format,...)
Definition splash.c:691
static void _splash_add_css(const char *data)
Definition splash.c:120
static GtkWidget * _splash_shadow_label_new(const gchar *text, const gchar *name, const gchar *shadow_name, GtkWidget **out_main)
Definition splash.c:159
static gboolean _splash_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
Definition splash.c:228
static gboolean _splash_env_is_truthy(const char *value)
Definition splash.c:64
static void _splash_force_show(void)
Definition splash.c:90
void dt_gui_splash_update(const char *message)
Definition splash.c:685
static gchar * _splash_capitalize_name(const char *name)
Definition splash.c:420
void dt_gui_splash_set_transient_for(GtkWidget *parent)
Definition splash.c:83
static void _splash_update_message(const gchar *message)
Definition splash.c:409
static void _splash_scale_factor_changed(GObject *object, GParamSpec *pspec, gpointer user_data)
Definition splash.c:508
void dt_gui_splash_close(void)
Definition splash.c:702
static dt_splash_t * splash
Definition splash.c:62
static gboolean _splash_logo_set_from_path(GtkWidget *logo, const char *path, int target_size, int scale_factor)
Definition splash.c:428
static void _splash_update_logo_for_scale(void)
Definition splash.c:494
static void _splash_shadow_label_set_text(GtkWidget *label, const gchar *text)
Definition splash.c:202
gchar * path
Definition splash.c:58
gchar * author
Definition splash.c:59
GtkWidget * logo
Definition splash.c:39
int logo_scale_factor
Definition splash.c:41
GdkPixbuf * slide_pixbuf
Definition splash.c:42
GtkWidget * drawing
Definition splash.c:37
gchar * logo_path
Definition splash.c:40
GtkWidget * window
Definition splash.c:36
int slide_cache_height
Definition splash.c:44
GPtrArray * slides
Definition splash.c:51
gint current_slide
Definition splash.c:52
int slide_cache_scale
Definition splash.c:45
gboolean shown
Definition splash.c:47
GPtrArray * authors
Definition splash.c:50
int slide_cache_index
Definition splash.c:46
guint slide_timeout_id
Definition splash.c:53
GtkCssProvider * css
Definition splash.c:48
GtkWidget * label_message
Definition splash.c:38
int slide_cache_width
Definition splash.c:43
#define DT_GUI_BOX_SPACING