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