Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
progress.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2014, 2016-2017 Tobias Ellinghaus.
4 Copyright (C) 2015 Roman Lebedev.
5 Copyright (C) 2016 parafin.
6 Copyright (C) 2019-2020 Pascal Obry.
7 Copyright (C) 2021 Ralf Brown.
8 Copyright (C) 2022-2023 Aurélien PIERRE.
9 Copyright (C) 2022 Martin Bařinka.
10 Copyright (C) 2025 Hubert Figuière.
11 Copyright (C) 2026 Guillaume Stutin.
12
13 darktable is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 darktable is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with darktable. If not, see <http://www.gnu.org/licenses/>.
25*/
26
27#include "system/macros.h"
28#include "system/mem_alloc.h"
29// dt_gui_get_global() and dt_gui_main_window() are used only under _WIN32 below; a Linux
30// clang-tidy run analyses the other branch and cannot see them. Removing this include breaks
31// the Windows build.
32// The suppression is on the line itself: NOLINTNEXTLINE applies to the immediately
33// following line, so a multi-line reason above it suppresses a comment and nothing else.
34#include "gui/application.h" // NOLINT(misc-include-cleaner)
35#include "common/dbus.h"
36#include "control/progress.h"
37#include "control/control.h"
38
39#ifdef MAC_INTEGRATION
40#include <gtkosxapplication.h>
41#endif
42
43#ifdef _WIN32
44#include <gdk/gdkwin32.h>
45#ifndef ITaskbarList3_SetProgressValue
46 #define ITaskbarList3_SetProgressValue(This,hwnd,ullCompleted,ullTotal) (This)->lpVtbl->SetProgressValue(This,hwnd,ullCompleted,ullTotal)
47#endif
48#ifndef ITaskbarList3_SetProgressState
49 #define ITaskbarList3_SetProgressState(This,hwnd,tbpFlags) (This)->lpVtbl->SetProgressState(This,hwnd,tbpFlags)
50#endif
51#ifndef ITaskbarList3_HrInit
52 #define ITaskbarList3_HrInit(This) (This)->lpVtbl->HrInit(This)
53#endif
54
55/* The taskbar button the progress is drawn on belongs to the main window. Without a GUI --
56 * ansel-cli, the unit tests -- there is no window and no button, and dt_gui_main_window()
57 * would dereference the missing GUI. Jobs create progress bars in either case. */
58static HWND _taskbar_window(void)
59{
60 if(IS_NULL_PTR(dt_gui_get_global())) return NULL;
61 GdkWindow *window = gtk_widget_get_window(dt_gui_main_window());
62 return IS_NULL_PTR(window) ? NULL : GDK_WINDOW_HWND(window);
63}
64#endif
65
66
67typedef struct _dt_progress_t
68{
69 double progress;
70 gchar *message;
72 dt_pthread_mutex_t mutex;
73 void *gui_data;
74
75 // cancel callback and its data
78
80
81static void global_progress_start(dt_control_t *control, dt_progress_t *progress)
82{
84
85#ifndef _WIN32
86 // this should work for unity as well as kde
87 // https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry
88 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
89 {
90 GError *error = NULL;
91 g_object_ref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
92
93 GVariantBuilder builder;
94 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
95 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
96 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(TRUE));
97 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
98
99 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
100 "com.canonical.Unity",
101 "/darktable",
102 "com.canonical.Unity.LauncherEntry",
103 "Update",
104 params,
105 &error);
106 if(error)
107 {
108 fprintf(stderr, "[progress_create] dbus error: %s\n", error->message);
109 g_error_free(error);
110 }
111 }
112
113#else // _WIN32
114
115 const HWND hwnd = _taskbar_window();
116 if(IS_NULL_PTR(hwnd)) return;
117
118 // we can't init this in dt_control_progress_init as it's run too early :/
119 if(!control->progress_system.taskbarlist)
120 {
121 void *taskbarlist;
122 if(CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void **)&taskbarlist) == S_OK)
123 if(ITaskbarList3_HrInit((ITaskbarList3 *)taskbarlist) == S_OK)
124 control->progress_system.taskbarlist = taskbarlist;
125 }
126
127 if(control->progress_system.taskbarlist)
128 {
129 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NORMAL) != S_OK)
130 fprintf(stderr, "[progress_create] SetProgressState failed\n");
131 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
132 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
133 }
134
135#endif
136}
137
138static void global_progress_set(dt_control_t *control, dt_progress_t *progress, double value)
139{
141
142#ifndef _WIN32
143
144 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
145 {
146 GError *error = NULL;
147
148 GVariantBuilder builder;
149 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
150 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
151 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
152
153 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
154 "com.canonical.Unity",
155 "/darktable",
156 "com.canonical.Unity.LauncherEntry",
157 "Update",
158 params,
159 &error);
160 if(error)
161 {
162 fprintf(stderr, "[progress_set] dbus error: %s\n", error->message);
163 g_error_free(error);
164 }
165 }
166
167#else // _WIN32
168
169 const HWND hwnd = _taskbar_window();
170 if(control->progress_system.taskbarlist && !IS_NULL_PTR(hwnd))
171 {
172 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
173 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
174 }
175
176#endif
177}
178
179static void global_progress_end(dt_control_t *control, dt_progress_t *progress)
180{
182
183 // find the biggest progress value among the remaining progress bars
184 control->progress_system.global_progress = 0.0;
185 for(GList *iter = control->progress_system.list; iter; iter = g_list_next(iter))
186 {
187 // this is called after the current progress got removed from the list!
188 dt_progress_t *p = (dt_progress_t *)iter->data;
191 }
192
193#ifndef _WIN32
194
195 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
196 {
197 GError *error = NULL;
198
199 GVariantBuilder builder;
200 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
201 if(control->progress_system.n_progress_bar == 0)
202 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
203 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
204 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
205
206 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
207 "com.canonical.Unity",
208 "/darktable",
209 "com.canonical.Unity.LauncherEntry",
210 "Update",
211 params,
212 &error);
213 if(error)
214 {
215 fprintf(stderr, "[progress_destroy] dbus error: %s\n", error->message);
216 g_error_free(error);
217 }
218
219 g_object_unref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
221 }
222
223#else // _WIN32
224
225 const HWND hwnd = _taskbar_window();
226 if(control->progress_system.taskbarlist && !IS_NULL_PTR(hwnd))
227 {
228 if(control->progress_system.n_progress_bar == 0)
229 {
230 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NOPROGRESS) != S_OK)
231 fprintf(stderr, "[progress_create] SetProgressState failed\n");
232 }
233 else
234 {
236 control->progress_system.global_progress * 100, 100) != S_OK)
237 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
238 }
239 }
240
241#endif
242}
243
245{
246#ifndef _WIN32
247
248 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
249 {
250 GError *error = NULL;
251
252 GVariantBuilder builder;
253 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
254 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
255 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
256
257 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
258 "com.canonical.Unity",
259 "/darktable",
260 "com.canonical.Unity.LauncherEntry",
261 "Update",
262 params,
263 &error);
264 if(error)
265 {
266 fprintf(stderr, "[progress_init] dbus error: %s\n", error->message);
267 g_error_free(error);
268 }
269
270 g_object_unref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
272 }
273
274#else // _WIN32
275
276 // initializing control->progress_system.taskbarlist in here doesn't work,
277 // it seems to only succeed after dt_gui_gtk_init
278
279#endif // _WIN32
280}
281
282dt_progress_t *dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar,
283 const gchar *message)
284{
285 // create the object
286 dt_progress_t *progress = (dt_progress_t *)calloc(1, sizeof(dt_progress_t));
287 dt_pthread_mutex_init(&(progress->mutex), NULL);
288
289 // fill it with values
290 progress->message = g_strdup(message);
291 progress->has_progress_bar = has_progress_bar;
292
294
295 // add it to the global list
296 control->progress_system.list = g_list_append(control->progress_system.list, progress);
297 control->progress_system.list_length++;
298 if(has_progress_bar) global_progress_start(control, progress);
299
300 // tell the gui
301 if(!IS_NULL_PTR(control->progress_system.proxy.module))
302 progress->gui_data = control->progress_system.proxy.added(control->progress_system.proxy.module,
303 has_progress_bar, message);
304
306
307 return progress;
308}
309
311{
313
314 // tell the gui
315 if(!IS_NULL_PTR(control->progress_system.proxy.module))
316 control->progress_system.proxy.destroyed(control->progress_system.proxy.module, progress->gui_data);
317
318 // remove the object from the global list
319 control->progress_system.list = g_list_remove(control->progress_system.list, progress);
320 control->progress_system.list_length--;
321 if(progress->has_progress_bar) global_progress_end(control, progress);
322
324
325 // free the object
327 dt_free(progress->message);
328 dt_free(progress);
329}
330
332 dt_progress_cancel_callback_t cancel, void *data)
333{
334 // set the value
335 dt_pthread_mutex_lock(&progress->mutex);
336 progress->cancel = cancel;
337 progress->cancel_data = data;
338 dt_pthread_mutex_unlock(&progress->mutex);
339
340 // tell the gui
342 if(!IS_NULL_PTR(control->progress_system.proxy.module))
343 control->progress_system.proxy.cancellable(control->progress_system.proxy.module, progress->gui_data,
344 progress);
346}
347
348static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
349{
351}
352
357
359{
360 dt_pthread_mutex_lock(&progress->mutex);
361 if(IS_NULL_PTR(progress->cancel))
362 {
363 dt_pthread_mutex_unlock(&progress->mutex);
364 return;
365 }
366
367 // call the cancel callback
368 progress->cancel(progress, progress->cancel_data);
369
370 dt_pthread_mutex_unlock(&progress->mutex);
371
372 // the gui doesn't need to know I guess, it wouldn't to anything with that bit of information
373}
374
376{
377 // set the value
378 value = CLAMP(value, 0.0, 1.0);
379 dt_pthread_mutex_lock(&progress->mutex);
380 progress->progress = value;
381 dt_pthread_mutex_unlock(&progress->mutex);
382
383 // tell the gui
385 if(!IS_NULL_PTR(control->progress_system.proxy.module))
386 control->progress_system.proxy.updated(control->progress_system.proxy.module, progress->gui_data, value);
387
388 if(progress->has_progress_bar) global_progress_set(control, progress, value);
389
391}
392
394{
395 dt_pthread_mutex_lock(&progress->mutex);
396 double res = progress->progress;
397 dt_pthread_mutex_unlock(&progress->mutex);
398 return res;
399}
400
402{
403 dt_pthread_mutex_lock(&progress->mutex);
404 const gchar *res = progress->message;
405 dt_pthread_mutex_unlock(&progress->mutex);
406 return res;
407}
408
409void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
410{
411 dt_pthread_mutex_lock(&progress->mutex);
412 dt_free(progress->message);
413 progress->message = g_strdup(message);
414 dt_pthread_mutex_unlock(&progress->mutex);
415
416 // tell the gui
418 if(!IS_NULL_PTR(control->progress_system.proxy.module))
419 control->progress_system.proxy.message_updated(control->progress_system.proxy.module, progress->gui_data,
420 message);
422}
423
425{
427 for(GList *iter = control->progress_system.list; iter; iter = g_list_next(iter))
428 {
429 dt_progress_t *progress = (dt_progress_t *)iter->data;
430 dt_pthread_mutex_lock(&progress->mutex);
431 callback(progress->message, data);
432 dt_pthread_mutex_unlock(&progress->mutex);
433 }
435}
436
438{
439 dt_pthread_mutex_lock(&progress->mutex);
440 progress->gui_data = data;
441 dt_pthread_mutex_unlock(&progress->mutex);
442}
443
445{
446 dt_pthread_mutex_lock(&progress->mutex);
447 void *res = progress->gui_data;
448 dt_pthread_mutex_unlock(&progress->mutex);
449 return res;
450}
451
453{
454 dt_pthread_mutex_lock(&progress->mutex);
455 gboolean res = progress->has_progress_bar;
456 dt_pthread_mutex_unlock(&progress->mutex);
457 return res;
458}
459
461{
462 dt_pthread_mutex_lock(&progress->mutex);
463 gboolean res = !IS_NULL_PTR(progress->cancel);
464 dt_pthread_mutex_unlock(&progress->mutex);
465 return res;
466}
467
468// clang-format off
469// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
470// vim: shiftwidth=2 expandtab tabstop=2 cindent
471// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
472// clang-format on
GtkWidget * dt_gui_main_window(void)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
struct dt_dbus_t * dt_dbus_get_global(void)
Definition darktable.c:644
GtkWidget * window
const int res
Definition dtpthread.h:351
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
void dt_control_job_cancel(_dt_job_t *job)
Definition jobs.c:183
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
#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
void dt_control_progress_set_progress(dt_control_t *control, dt_progress_t *progress, double value)
Definition progress.c:375
static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
Definition progress.c:348
static HWND _taskbar_window(void)
Definition progress.c:58
void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
Definition progress.c:409
double dt_control_progress_get_progress(dt_progress_t *progress)
Definition progress.c:393
gboolean dt_control_progress_cancellable(dt_progress_t *progress)
Definition progress.c:460
const gchar * dt_control_progress_get_message(dt_progress_t *progress)
Definition progress.c:401
static void global_progress_set(dt_control_t *control, dt_progress_t *progress, double value)
Definition progress.c:138
void dt_control_progress_foreach(dt_control_t *control, dt_progress_foreach_callback_t callback, void *data)
Definition progress.c:424
static void global_progress_start(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:81
void dt_control_progress_make_cancellable(struct dt_control_t *control, dt_progress_t *progress, dt_progress_cancel_callback_t cancel, void *data)
Definition progress.c:331
dt_progress_t * dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar, const gchar *message)
Definition progress.c:282
void dt_control_progress_init(struct dt_control_t *control)
Definition progress.c:244
#define ITaskbarList3_SetProgressValue(This, hwnd, ullCompleted, ullTotal)
Definition progress.c:46
void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:310
#define ITaskbarList3_SetProgressState(This, hwnd, tbpFlags)
Definition progress.c:49
void dt_control_progress_set_gui_data(dt_progress_t *progress, void *data)
Definition progress.c:437
void dt_control_progress_cancel(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:358
void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
Definition progress.c:353
#define ITaskbarList3_HrInit(This)
Definition progress.c:52
static void global_progress_end(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:179
gboolean dt_control_progress_has_progress_bar(dt_progress_t *progress)
Definition progress.c:452
void * dt_control_progress_get_gui_data(dt_progress_t *progress)
Definition progress.c:444
void(* dt_progress_foreach_callback_t)(const gchar *message, void *data)
Definition progress.h:67
void(* dt_progress_cancel_callback_t)(dt_progress_t *progress, void *data)
Definition progress.h:33
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
gchar * message
Definition progress.c:70
void * cancel_data
Definition progress.c:77
dt_progress_cancel_callback_t cancel
Definition progress.c:76
void * gui_data
Definition progress.c:73
dt_pthread_mutex_t mutex
Definition progress.c:72
double progress
Definition progress.c:69
gboolean has_progress_bar
Definition progress.c:71
dt_pthread_mutex_t mutex
Definition control.h:268
void(* destroyed)(dt_lib_module_t *self, struct dt_lib_backgroundjob_element_t *instance)
Definition control.h:279
GList * list
Definition control.h:264
void(* message_updated)(dt_lib_module_t *self, struct dt_lib_backgroundjob_element_t *instance, const char *message)
Definition control.h:283
void(* updated)(dt_lib_module_t *self, struct dt_lib_backgroundjob_element_t *instance, double value)
Definition control.h:282
dt_lib_module_t *void *(* added)(dt_lib_module_t *self, gboolean has_progress_bar, const gchar *message)
Definition control.h:278
size_t list_length
Definition control.h:265
double global_progress
Definition control.h:267
ITaskbarList3 * taskbarlist
Definition control.h:271
void(* cancellable)(dt_lib_module_t *self, struct dt_lib_backgroundjob_element_t *instance, dt_progress_t *progress)
Definition control.h:280
struct dt_control_t::@8 progress_system
size_t n_progress_bar
Definition control.h:266
struct dt_control_t::@8::@10 proxy
GDBusConnection * dbus_connection
Definition dbus.h:39
#define MAX(a, b)
Definition thinplate.c:29