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
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25
26#include "system/macros.h"
27#include "system/mem_alloc.h"
28// dt_gui_main_window() is used only under _WIN32 below; a Linux clang-tidy run analyses
29// the other branch and cannot see it. Removing this include breaks the Windows build.
30// The suppression is on the line itself: NOLINTNEXTLINE applies to the immediately
31// following line, so a multi-line reason above it suppresses a comment and nothing else.
32#include "gui/application.h" // NOLINT(misc-include-cleaner)
33#include "common/dbus.h"
34#include "control/progress.h"
35#include "control/control.h"
36
37#ifdef MAC_INTEGRATION
38#include <gtkosxapplication.h>
39#endif
40
41#ifdef _WIN32
42#include <gdk/gdkwin32.h>
43#ifndef ITaskbarList3_SetProgressValue
44 #define ITaskbarList3_SetProgressValue(This,hwnd,ullCompleted,ullTotal) (This)->lpVtbl->SetProgressValue(This,hwnd,ullCompleted,ullTotal)
45#endif
46#ifndef ITaskbarList3_SetProgressState
47 #define ITaskbarList3_SetProgressState(This,hwnd,tbpFlags) (This)->lpVtbl->SetProgressState(This,hwnd,tbpFlags)
48#endif
49#ifndef ITaskbarList3_HrInit
50 #define ITaskbarList3_HrInit(This) (This)->lpVtbl->HrInit(This)
51#endif
52#endif
53
54
55typedef struct _dt_progress_t
56{
57 double progress;
58 gchar *message;
60 dt_pthread_mutex_t mutex;
61 void *gui_data;
62
63 // cancel callback and its data
66
68
69static void global_progress_start(dt_control_t *control, dt_progress_t *progress)
70{
72
73#ifndef _WIN32
74 // this should work for unity as well as kde
75 // https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry
76 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
77 {
78 GError *error = NULL;
79 g_object_ref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
80
81 GVariantBuilder builder;
82 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
83 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
84 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(TRUE));
85 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
86
87 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
88 "com.canonical.Unity",
89 "/darktable",
90 "com.canonical.Unity.LauncherEntry",
91 "Update",
92 params,
93 &error);
94 if(error)
95 {
96 fprintf(stderr, "[progress_create] dbus error: %s\n", error->message);
97 g_error_free(error);
98 }
99 }
100
101#else // _WIN32
102
103 // we can't init this in dt_control_progress_init as it's run too early :/
104 if(!control->progress_system.taskbarlist)
105 {
106 void *taskbarlist;
107 if(CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void **)&taskbarlist) == S_OK)
108 if(ITaskbarList3_HrInit((ITaskbarList3 *)taskbarlist) == S_OK)
109 control->progress_system.taskbarlist = taskbarlist;
110 }
111
112 if(control->progress_system.taskbarlist)
113 {
114 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_gui_main_window()));
115 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NORMAL) != S_OK)
116 fprintf(stderr, "[progress_create] SetProgressState failed\n");
117 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
118 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
119 }
120
121#endif
122}
123
124static void global_progress_set(dt_control_t *control, dt_progress_t *progress, double value)
125{
127
128#ifndef _WIN32
129
130 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
131 {
132 GError *error = NULL;
133
134 GVariantBuilder builder;
135 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
136 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
137 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
138
139 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
140 "com.canonical.Unity",
141 "/darktable",
142 "com.canonical.Unity.LauncherEntry",
143 "Update",
144 params,
145 &error);
146 if(error)
147 {
148 fprintf(stderr, "[progress_set] dbus error: %s\n", error->message);
149 g_error_free(error);
150 }
151 }
152
153#else // _WIN32
154
155 if(control->progress_system.taskbarlist)
156 {
157 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_gui_main_window()));
158 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
159 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
160 }
161
162#endif
163}
164
165static void global_progress_end(dt_control_t *control, dt_progress_t *progress)
166{
168
169 // find the biggest progress value among the remaining progress bars
170 control->progress_system.global_progress = 0.0;
171 for(GList *iter = control->progress_system.list; iter; iter = g_list_next(iter))
172 {
173 // this is called after the current progress got removed from the list!
174 dt_progress_t *p = (dt_progress_t *)iter->data;
177 }
178
179#ifndef _WIN32
180
181 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
182 {
183 GError *error = NULL;
184
185 GVariantBuilder builder;
186 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
187 if(control->progress_system.n_progress_bar == 0)
188 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
189 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
190 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
191
192 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
193 "com.canonical.Unity",
194 "/darktable",
195 "com.canonical.Unity.LauncherEntry",
196 "Update",
197 params,
198 &error);
199 if(error)
200 {
201 fprintf(stderr, "[progress_destroy] dbus error: %s\n", error->message);
202 g_error_free(error);
203 }
204
205 g_object_unref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
207 }
208
209#else // _WIN32
210
211 if(control->progress_system.taskbarlist)
212 {
213 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_gui_main_window()));
214 if(control->progress_system.n_progress_bar == 0)
215 {
216 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NOPROGRESS) != S_OK)
217 fprintf(stderr, "[progress_create] SetProgressState failed\n");
218 }
219 else
220 {
222 control->progress_system.global_progress * 100, 100) != S_OK)
223 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
224 }
225 }
226
227#endif
228}
229
231{
232#ifndef _WIN32
233
234 if(dt_dbus_get_global() && dt_dbus_get_global()->dbus_connection)
235 {
236 GError *error = NULL;
237
238 GVariantBuilder builder;
239 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
240 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
241 GVariant *params = g_variant_new("(sa{sv})", "application://photos.ansel.Ansel.desktop", &builder);
242
243 g_dbus_connection_emit_signal(dt_dbus_get_global()->dbus_connection,
244 "com.canonical.Unity",
245 "/darktable",
246 "com.canonical.Unity.LauncherEntry",
247 "Update",
248 params,
249 &error);
250 if(error)
251 {
252 fprintf(stderr, "[progress_init] dbus error: %s\n", error->message);
253 g_error_free(error);
254 }
255
256 g_object_unref(G_OBJECT(dt_dbus_get_global()->dbus_connection));
258 }
259
260#else // _WIN32
261
262 // initializing control->progress_system.taskbarlist in here doesn't work,
263 // it seems to only succeed after dt_gui_gtk_init
264
265#endif // _WIN32
266}
267
268dt_progress_t *dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar,
269 const gchar *message)
270{
271 // create the object
272 dt_progress_t *progress = (dt_progress_t *)calloc(1, sizeof(dt_progress_t));
273 dt_pthread_mutex_init(&(progress->mutex), NULL);
274
275 // fill it with values
276 progress->message = g_strdup(message);
277 progress->has_progress_bar = has_progress_bar;
278
280
281 // add it to the global list
282 control->progress_system.list = g_list_append(control->progress_system.list, progress);
283 control->progress_system.list_length++;
284 if(has_progress_bar) global_progress_start(control, progress);
285
286 // tell the gui
287 if(!IS_NULL_PTR(control->progress_system.proxy.module))
288 progress->gui_data = control->progress_system.proxy.added(control->progress_system.proxy.module,
289 has_progress_bar, message);
290
292
293 return progress;
294}
295
297{
299
300 // tell the gui
301 if(!IS_NULL_PTR(control->progress_system.proxy.module))
302 control->progress_system.proxy.destroyed(control->progress_system.proxy.module, progress->gui_data);
303
304 // remove the object from the global list
305 control->progress_system.list = g_list_remove(control->progress_system.list, progress);
306 control->progress_system.list_length--;
307 if(progress->has_progress_bar) global_progress_end(control, progress);
308
310
311 // free the object
313 dt_free(progress->message);
314 dt_free(progress);
315}
316
318 dt_progress_cancel_callback_t cancel, void *data)
319{
320 // set the value
321 dt_pthread_mutex_lock(&progress->mutex);
322 progress->cancel = cancel;
323 progress->cancel_data = data;
324 dt_pthread_mutex_unlock(&progress->mutex);
325
326 // tell the gui
328 if(!IS_NULL_PTR(control->progress_system.proxy.module))
329 control->progress_system.proxy.cancellable(control->progress_system.proxy.module, progress->gui_data,
330 progress);
332}
333
334static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
335{
337}
338
343
345{
346 dt_pthread_mutex_lock(&progress->mutex);
347 if(IS_NULL_PTR(progress->cancel))
348 {
349 dt_pthread_mutex_unlock(&progress->mutex);
350 return;
351 }
352
353 // call the cancel callback
354 progress->cancel(progress, progress->cancel_data);
355
356 dt_pthread_mutex_unlock(&progress->mutex);
357
358 // the gui doesn't need to know I guess, it wouldn't to anything with that bit of information
359}
360
362{
363 // set the value
364 value = CLAMP(value, 0.0, 1.0);
365 dt_pthread_mutex_lock(&progress->mutex);
366 progress->progress = value;
367 dt_pthread_mutex_unlock(&progress->mutex);
368
369 // tell the gui
371 if(!IS_NULL_PTR(control->progress_system.proxy.module))
372 control->progress_system.proxy.updated(control->progress_system.proxy.module, progress->gui_data, value);
373
374 if(progress->has_progress_bar) global_progress_set(control, progress, value);
375
377}
378
380{
381 dt_pthread_mutex_lock(&progress->mutex);
382 double res = progress->progress;
383 dt_pthread_mutex_unlock(&progress->mutex);
384 return res;
385}
386
388{
389 dt_pthread_mutex_lock(&progress->mutex);
390 const gchar *res = progress->message;
391 dt_pthread_mutex_unlock(&progress->mutex);
392 return res;
393}
394
395void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
396{
397 dt_pthread_mutex_lock(&progress->mutex);
398 dt_free(progress->message);
399 progress->message = g_strdup(message);
400 dt_pthread_mutex_unlock(&progress->mutex);
401
402 // tell the gui
404 if(!IS_NULL_PTR(control->progress_system.proxy.module))
405 control->progress_system.proxy.message_updated(control->progress_system.proxy.module, progress->gui_data,
406 message);
408}
409
411{
412 dt_pthread_mutex_lock(&progress->mutex);
413 progress->gui_data = data;
414 dt_pthread_mutex_unlock(&progress->mutex);
415}
416
418{
419 dt_pthread_mutex_lock(&progress->mutex);
420 void *res = progress->gui_data;
421 dt_pthread_mutex_unlock(&progress->mutex);
422 return res;
423}
424
426{
427 dt_pthread_mutex_lock(&progress->mutex);
428 gboolean res = progress->has_progress_bar;
429 dt_pthread_mutex_unlock(&progress->mutex);
430 return res;
431}
432
434{
435 dt_pthread_mutex_lock(&progress->mutex);
436 gboolean res = !IS_NULL_PTR(progress->cancel);
437 dt_pthread_mutex_unlock(&progress->mutex);
438 return res;
439}
440
441// clang-format off
442// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
443// vim: shiftwidth=2 expandtab tabstop=2 cindent
444// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
445// 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_dbus_t * dt_dbus_get_global(void)
Definition darktable.c:641
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:182
#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:361
static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
Definition progress.c:334
void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
Definition progress.c:395
double dt_control_progress_get_progress(dt_progress_t *progress)
Definition progress.c:379
gboolean dt_control_progress_cancellable(dt_progress_t *progress)
Definition progress.c:433
const gchar * dt_control_progress_get_message(dt_progress_t *progress)
Definition progress.c:387
static void global_progress_set(dt_control_t *control, dt_progress_t *progress, double value)
Definition progress.c:124
static void global_progress_start(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:69
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:317
dt_progress_t * dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar, const gchar *message)
Definition progress.c:268
void dt_control_progress_init(struct dt_control_t *control)
Definition progress.c:230
#define ITaskbarList3_SetProgressValue(This, hwnd, ullCompleted, ullTotal)
Definition progress.c:44
void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:296
#define ITaskbarList3_SetProgressState(This, hwnd, tbpFlags)
Definition progress.c:47
void dt_control_progress_set_gui_data(dt_progress_t *progress, void *data)
Definition progress.c:410
void dt_control_progress_cancel(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:344
void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
Definition progress.c:339
#define ITaskbarList3_HrInit(This)
Definition progress.c:50
static void global_progress_end(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:165
gboolean dt_control_progress_has_progress_bar(dt_progress_t *progress)
Definition progress.c:425
void * dt_control_progress_get_gui_data(dt_progress_t *progress)
Definition progress.c:417
void(* dt_progress_cancel_callback_t)(dt_progress_t *progress, void *data)
Definition progress.h:32
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
gchar * message
Definition progress.c:58
void * cancel_data
Definition progress.c:65
dt_progress_cancel_callback_t cancel
Definition progress.c:64
void * gui_data
Definition progress.c:61
dt_pthread_mutex_t mutex
Definition progress.c:60
double progress
Definition progress.c:57
gboolean has_progress_bar
Definition progress.c:59
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