1 /*
2 This file is part of darktable,
3 Copyright (C) 2014-2020 darktable developers.
4
5 darktable 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 darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/dbus.h"
20 #include "control/progress.h"
21 #include "control/control.h"
22
23 #ifdef HAVE_UNITY
24 #include <unity/unity/unity.h>
25 #endif
26 #ifdef MAC_INTEGRATION
27 #include <gtkosxapplication.h>
28 #endif
29
30 #ifdef _WIN32
31 #include <gdk/gdkwin32.h>
32 #ifndef ITaskbarList3_SetProgressValue
33 #define ITaskbarList3_SetProgressValue(This,hwnd,ullCompleted,ullTotal) (This)->lpVtbl->SetProgressValue(This,hwnd,ullCompleted,ullTotal)
34 #endif
35 #ifndef ITaskbarList3_SetProgressState
36 #define ITaskbarList3_SetProgressState(This,hwnd,tbpFlags) (This)->lpVtbl->SetProgressState(This,hwnd,tbpFlags)
37 #endif
38 #ifndef ITaskbarList3_HrInit
39 #define ITaskbarList3_HrInit(This) (This)->lpVtbl->HrInit(This)
40 #endif
41 #endif
42
43
44 typedef struct _dt_progress_t
45 {
46 double progress;
47 gchar *message;
48 gboolean has_progress_bar;
49 dt_pthread_mutex_t mutex;
50 void *gui_data;
51
52 // cancel callback and its data
53 dt_progress_cancel_callback_t cancel;
54 void *cancel_data;
55
56 #ifdef HAVE_UNITY
57 UnityLauncherEntry *darktable_launcher;
58 #endif
59
60 } _dt_progress_t;
61
global_progress_start(dt_control_t * control,dt_progress_t * progress)62 static void global_progress_start(dt_control_t *control, dt_progress_t *progress)
63 {
64 control->progress_system.n_progress_bar++;
65
66 #ifndef _WIN32
67
68 #ifdef HAVE_UNITY
69
70 progress->darktable_launcher = unity_launcher_entry_get_for_desktop_id("darktable.desktop");
71 unity_launcher_entry_set_progress(progress->darktable_launcher, 0.0);
72 unity_launcher_entry_set_progress_visible(progress->darktable_launcher, TRUE);
73
74 #else
75
76 // this should work for unity as well as kde
77 // https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry
78 if(darktable.dbus && darktable.dbus->dbus_connection)
79 {
80 GError *error = NULL;
81 g_object_ref(G_OBJECT(darktable.dbus->dbus_connection));
82
83 GVariantBuilder builder;
84 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
85 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
86 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(TRUE));
87 GVariant *params = g_variant_new("(sa{sv})", "application://darktable.desktop", &builder);
88
89 g_dbus_connection_emit_signal(darktable.dbus->dbus_connection,
90 "com.canonical.Unity",
91 "/darktable",
92 "com.canonical.Unity.LauncherEntry",
93 "Update",
94 params,
95 &error);
96 if(error)
97 {
98 fprintf(stderr, "[progress_create] dbus error: %s\n", error->message);
99 g_error_free(error);
100 }
101 }
102
103 #endif // HAVE_UNITY
104
105 #else // _WIN32
106
107 // we can't init this in dt_control_progress_init as it's run too early :/
108 if(!control->progress_system.taskbarlist)
109 {
110 void *taskbarlist;
111 if(CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (void **)&taskbarlist) == S_OK)
112 if(ITaskbarList3_HrInit((ITaskbarList3 *)taskbarlist) == S_OK)
113 control->progress_system.taskbarlist = taskbarlist;
114 }
115
116 if(control->progress_system.taskbarlist)
117 {
118 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_ui_main_window(darktable.gui->ui)));
119 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NORMAL) != S_OK)
120 fprintf(stderr, "[progress_create] SetProgressState failed\n");
121 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
122 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
123 }
124
125 #endif
126 }
127
global_progress_set(dt_control_t * control,dt_progress_t * progress,double value)128 static void global_progress_set(dt_control_t *control, dt_progress_t *progress, double value)
129 {
130 control->progress_system.global_progress = MAX(control->progress_system.global_progress, value);
131
132 #ifndef _WIN32
133
134 #ifdef HAVE_UNITY
135
136 unity_launcher_entry_set_progress(progress->darktable_launcher, value);
137
138 #else
139
140 if(darktable.dbus && darktable.dbus->dbus_connection)
141 {
142 GError *error = NULL;
143
144 GVariantBuilder builder;
145 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
146 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
147 GVariant *params = g_variant_new("(sa{sv})", "application://darktable.desktop", &builder);
148
149 g_dbus_connection_emit_signal(darktable.dbus->dbus_connection,
150 "com.canonical.Unity",
151 "/darktable",
152 "com.canonical.Unity.LauncherEntry",
153 "Update",
154 params,
155 &error);
156 if(error)
157 {
158 fprintf(stderr, "[progress_set] dbus error: %s\n", error->message);
159 g_error_free(error);
160 }
161 }
162
163 #endif // HAVE_UNITY
164
165 #else // _WIN32
166
167 if(control->progress_system.taskbarlist)
168 {
169 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_ui_main_window(darktable.gui->ui)));
170 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd, control->progress_system.global_progress * 100, 100) != S_OK)
171 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
172 }
173
174 #endif
175 }
176
global_progress_end(dt_control_t * control,dt_progress_t * progress)177 static void global_progress_end(dt_control_t *control, dt_progress_t *progress)
178 {
179 control->progress_system.n_progress_bar--;
180
181 // find the biggest progress value among the remaining progress bars
182 control->progress_system.global_progress = 0.0;
183 for(GList *iter = control->progress_system.list; iter; iter = g_list_next(iter))
184 {
185 // this is called after the current progress got removed from the list!
186 dt_progress_t *p = (dt_progress_t *)iter->data;
187 const double value = dt_control_progress_get_progress(p);
188 control->progress_system.global_progress = MAX(control->progress_system.global_progress, value);
189 }
190
191 #ifndef _WIN32
192
193 #ifdef HAVE_UNITY
194
195 unity_launcher_entry_set_progress(progress->darktable_launcher, 1.0);
196 unity_launcher_entry_set_progress_visible(progress->darktable_launcher, FALSE);
197
198 #else
199
200 if(darktable.dbus && darktable.dbus->dbus_connection)
201 {
202 GError *error = NULL;
203
204 GVariantBuilder builder;
205 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
206 if(control->progress_system.n_progress_bar == 0)
207 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
208 g_variant_builder_add(&builder, "{sv}", "progress", g_variant_new_double(control->progress_system.global_progress));
209 GVariant *params = g_variant_new("(sa{sv})", "application://darktable.desktop", &builder);
210
211 g_dbus_connection_emit_signal(darktable.dbus->dbus_connection,
212 "com.canonical.Unity",
213 "/darktable",
214 "com.canonical.Unity.LauncherEntry",
215 "Update",
216 params,
217 &error);
218 if(error)
219 {
220 fprintf(stderr, "[progress_destroy] dbus error: %s\n", error->message);
221 g_error_free(error);
222 }
223
224 g_object_unref(G_OBJECT(darktable.dbus->dbus_connection));
225 }
226
227 #endif // HAVE_UNITY
228
229 #else // _WIN32
230
231 if(control->progress_system.taskbarlist)
232 {
233 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_ui_main_window(darktable.gui->ui)));
234 if(control->progress_system.n_progress_bar == 0)
235 {
236 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NOPROGRESS) != S_OK)
237 fprintf(stderr, "[progress_create] SetProgressState failed\n");
238 }
239 else
240 {
241 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd,
242 control->progress_system.global_progress * 100, 100) != S_OK)
243 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
244 }
245 }
246
247 #endif
248 }
249
dt_control_progress_init(struct dt_control_t * control)250 void dt_control_progress_init(struct dt_control_t *control)
251 {
252 #ifndef _WIN32
253
254 #ifdef HAVE_UNITY
255
256 UnityLauncherEntry *darktable_launcher = unity_launcher_entry_get_for_desktop_id("darktable.desktop");
257 unity_launcher_entry_set_progress_visible(darktable_launcher, FALSE);
258
259 #else
260
261 if(darktable.dbus->dbus_connection)
262 {
263 GError *error = NULL;
264
265 GVariantBuilder builder;
266 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
267 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
268 GVariant *params = g_variant_new("(sa{sv})", "application://darktable.desktop", &builder);
269
270 g_dbus_connection_emit_signal(darktable.dbus->dbus_connection,
271 "com.canonical.Unity",
272 "/darktable",
273 "com.canonical.Unity.LauncherEntry",
274 "Update",
275 params,
276 &error);
277 if(error)
278 {
279 fprintf(stderr, "[progress_init] dbus error: %s\n", error->message);
280 g_error_free(error);
281 }
282
283 g_object_unref(G_OBJECT(darktable.dbus->dbus_connection));
284 }
285
286 #endif // HAVE_UNITY
287
288 #else // _WIN32
289
290 // initializing control->progress_system.taskbarlist in here doesn't work,
291 // it seems to only succeed after dt_gui_gtk_init
292
293 #endif // _WIN32
294 }
295
dt_control_progress_create(dt_control_t * control,gboolean has_progress_bar,const gchar * message)296 dt_progress_t *dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar,
297 const gchar *message)
298 {
299 // create the object
300 dt_progress_t *progress = (dt_progress_t *)calloc(1, sizeof(dt_progress_t));
301 dt_pthread_mutex_init(&(progress->mutex), NULL);
302
303 // fill it with values
304 progress->message = g_strdup(message);
305 progress->has_progress_bar = has_progress_bar;
306
307 dt_pthread_mutex_lock(&control->progress_system.mutex);
308
309 // add it to the global list
310 control->progress_system.list = g_list_append(control->progress_system.list, progress);
311 control->progress_system.list_length++;
312 if(has_progress_bar) global_progress_start(control, progress);
313
314 // tell the gui
315 if(control->progress_system.proxy.module != NULL)
316 progress->gui_data = control->progress_system.proxy.added(control->progress_system.proxy.module,
317 has_progress_bar, message);
318
319 dt_pthread_mutex_unlock(&control->progress_system.mutex);
320
321 return progress;
322 }
323
dt_control_progress_destroy(dt_control_t * control,dt_progress_t * progress)324 void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
325 {
326 dt_pthread_mutex_lock(&control->progress_system.mutex);
327
328 // tell the gui
329 if(control->progress_system.proxy.module != NULL)
330 control->progress_system.proxy.destroyed(control->progress_system.proxy.module, progress->gui_data);
331
332 // remove the object from the global list
333 control->progress_system.list = g_list_remove(control->progress_system.list, progress);
334 control->progress_system.list_length--;
335 if(progress->has_progress_bar) global_progress_end(control, progress);
336
337 dt_pthread_mutex_unlock(&control->progress_system.mutex);
338
339 // free the object
340 dt_pthread_mutex_destroy(&progress->mutex);
341 g_free(progress->message);
342 free(progress);
343 }
344
dt_control_progress_make_cancellable(struct dt_control_t * control,dt_progress_t * progress,dt_progress_cancel_callback_t cancel,void * data)345 void dt_control_progress_make_cancellable(struct dt_control_t *control, dt_progress_t *progress,
346 dt_progress_cancel_callback_t cancel, void *data)
347 {
348 // set the value
349 dt_pthread_mutex_lock(&progress->mutex);
350 progress->cancel = cancel;
351 progress->cancel_data = data;
352 dt_pthread_mutex_unlock(&progress->mutex);
353
354 // tell the gui
355 dt_pthread_mutex_lock(&control->progress_system.mutex);
356 if(control->progress_system.proxy.module != NULL)
357 control->progress_system.proxy.cancellable(control->progress_system.proxy.module, progress->gui_data,
358 progress);
359 dt_pthread_mutex_unlock(&control->progress_system.mutex);
360 }
361
dt_control_progress_cancel_callback(dt_progress_t * progress,void * data)362 static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
363 {
364 dt_control_job_cancel((dt_job_t *)data);
365 }
366
dt_control_progress_attach_job(dt_control_t * control,dt_progress_t * progress,dt_job_t * job)367 void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
368 {
369 dt_control_progress_make_cancellable(control, progress, &dt_control_progress_cancel_callback, job);
370 }
371
dt_control_progress_cancel(dt_control_t * control,dt_progress_t * progress)372 void dt_control_progress_cancel(dt_control_t *control, dt_progress_t *progress)
373 {
374 dt_pthread_mutex_lock(&progress->mutex);
375 if(progress->cancel == NULL)
376 {
377 dt_pthread_mutex_unlock(&progress->mutex);
378 return;
379 }
380
381 // call the cancel callback
382 progress->cancel(progress, progress->cancel_data);
383
384 dt_pthread_mutex_unlock(&progress->mutex);
385
386 // the gui doesn't need to know I guess, it wouldn't to anything with that bit of information
387 }
388
dt_control_progress_set_progress(dt_control_t * control,dt_progress_t * progress,double value)389 void dt_control_progress_set_progress(dt_control_t *control, dt_progress_t *progress, double value)
390 {
391 // set the value
392 value = CLAMP(value, 0.0, 1.0);
393 dt_pthread_mutex_lock(&progress->mutex);
394 progress->progress = value;
395 dt_pthread_mutex_unlock(&progress->mutex);
396
397 // tell the gui
398 dt_pthread_mutex_lock(&control->progress_system.mutex);
399 if(control->progress_system.proxy.module != NULL)
400 control->progress_system.proxy.updated(control->progress_system.proxy.module, progress->gui_data, value);
401
402 if(progress->has_progress_bar) global_progress_set(control, progress, value);
403
404 dt_pthread_mutex_unlock(&control->progress_system.mutex);
405 }
406
dt_control_progress_get_progress(dt_progress_t * progress)407 double dt_control_progress_get_progress(dt_progress_t *progress)
408 {
409 dt_pthread_mutex_lock(&progress->mutex);
410 double res = progress->progress;
411 dt_pthread_mutex_unlock(&progress->mutex);
412 return res;
413 }
414
dt_control_progress_get_message(dt_progress_t * progress)415 const gchar *dt_control_progress_get_message(dt_progress_t *progress)
416 {
417 dt_pthread_mutex_lock(&progress->mutex);
418 const gchar *res = progress->message;
419 dt_pthread_mutex_unlock(&progress->mutex);
420 return res;
421 }
422
dt_control_progress_set_message(dt_control_t * control,dt_progress_t * progress,const char * message)423 void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
424 {
425 dt_pthread_mutex_lock(&progress->mutex);
426 g_free(progress->message);
427 progress->message = g_strdup(message);
428 dt_pthread_mutex_unlock(&progress->mutex);
429
430 // tell the gui
431 dt_pthread_mutex_lock(&control->progress_system.mutex);
432 if(control->progress_system.proxy.module != NULL)
433 control->progress_system.proxy.message_updated(control->progress_system.proxy.module, progress->gui_data,
434 message);
435 dt_pthread_mutex_unlock(&control->progress_system.mutex);
436 }
437
dt_control_progress_set_gui_data(dt_progress_t * progress,void * data)438 void dt_control_progress_set_gui_data(dt_progress_t *progress, void *data)
439 {
440 dt_pthread_mutex_lock(&progress->mutex);
441 progress->gui_data = data;
442 dt_pthread_mutex_unlock(&progress->mutex);
443 }
444
dt_control_progress_get_gui_data(dt_progress_t * progress)445 void *dt_control_progress_get_gui_data(dt_progress_t *progress)
446 {
447 dt_pthread_mutex_lock(&progress->mutex);
448 void *res = progress->gui_data;
449 dt_pthread_mutex_unlock(&progress->mutex);
450 return res;
451 }
452
dt_control_progress_has_progress_bar(dt_progress_t * progress)453 gboolean dt_control_progress_has_progress_bar(dt_progress_t *progress)
454 {
455 dt_pthread_mutex_lock(&progress->mutex);
456 gboolean res = progress->has_progress_bar;
457 dt_pthread_mutex_unlock(&progress->mutex);
458 return res;
459 }
460
dt_control_progress_cancellable(dt_progress_t * progress)461 gboolean dt_control_progress_cancellable(dt_progress_t *progress)
462 {
463 dt_pthread_mutex_lock(&progress->mutex);
464 gboolean res = progress->cancel != NULL;
465 dt_pthread_mutex_unlock(&progress->mutex);
466 return res;
467 }
468
469 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
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