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 darktable.dbus->dbus_connection = NULL;
226 }
227
228 #endif // HAVE_UNITY
229
230 #else // _WIN32
231
232 if(control->progress_system.taskbarlist)
233 {
234 HWND hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(dt_ui_main_window(darktable.gui->ui)));
235 if(control->progress_system.n_progress_bar == 0)
236 {
237 if(ITaskbarList3_SetProgressState(control->progress_system.taskbarlist, hwnd, TBPF_NOPROGRESS) != S_OK)
238 fprintf(stderr, "[progress_create] SetProgressState failed\n");
239 }
240 else
241 {
242 if(ITaskbarList3_SetProgressValue(control->progress_system.taskbarlist, hwnd,
243 control->progress_system.global_progress * 100, 100) != S_OK)
244 fprintf(stderr, "[progress_create] SetProgressValue failed\n");
245 }
246 }
247
248 #endif
249 }
250
dt_control_progress_init(struct dt_control_t * control)251 void dt_control_progress_init(struct dt_control_t *control)
252 {
253 #ifndef _WIN32
254
255 #ifdef HAVE_UNITY
256
257 UnityLauncherEntry *darktable_launcher = unity_launcher_entry_get_for_desktop_id("darktable.desktop");
258 unity_launcher_entry_set_progress_visible(darktable_launcher, FALSE);
259
260 #else
261
262 if(darktable.dbus->dbus_connection)
263 {
264 GError *error = NULL;
265
266 GVariantBuilder builder;
267 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
268 g_variant_builder_add(&builder, "{sv}", "progress-visible", g_variant_new_boolean(FALSE));
269 GVariant *params = g_variant_new("(sa{sv})", "application://darktable.desktop", &builder);
270
271 g_dbus_connection_emit_signal(darktable.dbus->dbus_connection,
272 "com.canonical.Unity",
273 "/darktable",
274 "com.canonical.Unity.LauncherEntry",
275 "Update",
276 params,
277 &error);
278 if(error)
279 {
280 fprintf(stderr, "[progress_init] dbus error: %s\n", error->message);
281 g_error_free(error);
282 }
283
284 g_object_unref(G_OBJECT(darktable.dbus->dbus_connection));
285 darktable.dbus->dbus_connection = NULL;
286 }
287
288 #endif // HAVE_UNITY
289
290 #else // _WIN32
291
292 // initializing control->progress_system.taskbarlist in here doesn't work,
293 // it seems to only succeed after dt_gui_gtk_init
294
295 #endif // _WIN32
296 }
297
dt_control_progress_create(dt_control_t * control,gboolean has_progress_bar,const gchar * message)298 dt_progress_t *dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar,
299 const gchar *message)
300 {
301 // create the object
302 dt_progress_t *progress = (dt_progress_t *)calloc(1, sizeof(dt_progress_t));
303 dt_pthread_mutex_init(&(progress->mutex), NULL);
304
305 // fill it with values
306 progress->message = g_strdup(message);
307 progress->has_progress_bar = has_progress_bar;
308
309 dt_pthread_mutex_lock(&control->progress_system.mutex);
310
311 // add it to the global list
312 control->progress_system.list = g_list_append(control->progress_system.list, progress);
313 control->progress_system.list_length++;
314 if(has_progress_bar) global_progress_start(control, progress);
315
316 // tell the gui
317 if(control->progress_system.proxy.module != NULL)
318 progress->gui_data = control->progress_system.proxy.added(control->progress_system.proxy.module,
319 has_progress_bar, message);
320
321 dt_pthread_mutex_unlock(&control->progress_system.mutex);
322
323 return progress;
324 }
325
dt_control_progress_destroy(dt_control_t * control,dt_progress_t * progress)326 void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
327 {
328 dt_pthread_mutex_lock(&control->progress_system.mutex);
329
330 // tell the gui
331 if(control->progress_system.proxy.module != NULL)
332 control->progress_system.proxy.destroyed(control->progress_system.proxy.module, progress->gui_data);
333
334 // remove the object from the global list
335 control->progress_system.list = g_list_remove(control->progress_system.list, progress);
336 control->progress_system.list_length--;
337 if(progress->has_progress_bar) global_progress_end(control, progress);
338
339 dt_pthread_mutex_unlock(&control->progress_system.mutex);
340
341 // free the object
342 dt_pthread_mutex_destroy(&progress->mutex);
343 g_free(progress->message);
344 free(progress);
345 }
346
dt_control_progress_make_cancellable(struct dt_control_t * control,dt_progress_t * progress,dt_progress_cancel_callback_t cancel,void * data)347 void dt_control_progress_make_cancellable(struct dt_control_t *control, dt_progress_t *progress,
348 dt_progress_cancel_callback_t cancel, void *data)
349 {
350 // set the value
351 dt_pthread_mutex_lock(&progress->mutex);
352 progress->cancel = cancel;
353 progress->cancel_data = data;
354 dt_pthread_mutex_unlock(&progress->mutex);
355
356 // tell the gui
357 dt_pthread_mutex_lock(&control->progress_system.mutex);
358 if(control->progress_system.proxy.module != NULL)
359 control->progress_system.proxy.cancellable(control->progress_system.proxy.module, progress->gui_data,
360 progress);
361 dt_pthread_mutex_unlock(&control->progress_system.mutex);
362 }
363
dt_control_progress_cancel_callback(dt_progress_t * progress,void * data)364 static void dt_control_progress_cancel_callback(dt_progress_t *progress, void *data)
365 {
366 dt_control_job_cancel((dt_job_t *)data);
367 }
368
dt_control_progress_attach_job(dt_control_t * control,dt_progress_t * progress,dt_job_t * job)369 void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
370 {
371 dt_control_progress_make_cancellable(control, progress, &dt_control_progress_cancel_callback, job);
372 }
373
dt_control_progress_cancel(dt_control_t * control,dt_progress_t * progress)374 void dt_control_progress_cancel(dt_control_t *control, dt_progress_t *progress)
375 {
376 dt_pthread_mutex_lock(&progress->mutex);
377 if(progress->cancel == NULL)
378 {
379 dt_pthread_mutex_unlock(&progress->mutex);
380 return;
381 }
382
383 // call the cancel callback
384 progress->cancel(progress, progress->cancel_data);
385
386 dt_pthread_mutex_unlock(&progress->mutex);
387
388 // the gui doesn't need to know I guess, it wouldn't to anything with that bit of information
389 }
390
dt_control_progress_set_progress(dt_control_t * control,dt_progress_t * progress,double value)391 void dt_control_progress_set_progress(dt_control_t *control, dt_progress_t *progress, double value)
392 {
393 // set the value
394 value = CLAMP(value, 0.0, 1.0);
395 dt_pthread_mutex_lock(&progress->mutex);
396 progress->progress = value;
397 dt_pthread_mutex_unlock(&progress->mutex);
398
399 // tell the gui
400 dt_pthread_mutex_lock(&control->progress_system.mutex);
401 if(control->progress_system.proxy.module != NULL)
402 control->progress_system.proxy.updated(control->progress_system.proxy.module, progress->gui_data, value);
403
404 if(progress->has_progress_bar) global_progress_set(control, progress, value);
405
406 dt_pthread_mutex_unlock(&control->progress_system.mutex);
407 }
408
dt_control_progress_get_progress(dt_progress_t * progress)409 double dt_control_progress_get_progress(dt_progress_t *progress)
410 {
411 dt_pthread_mutex_lock(&progress->mutex);
412 double res = progress->progress;
413 dt_pthread_mutex_unlock(&progress->mutex);
414 return res;
415 }
416
dt_control_progress_get_message(dt_progress_t * progress)417 const gchar *dt_control_progress_get_message(dt_progress_t *progress)
418 {
419 dt_pthread_mutex_lock(&progress->mutex);
420 const gchar *res = progress->message;
421 dt_pthread_mutex_unlock(&progress->mutex);
422 return res;
423 }
424
dt_control_progress_set_message(dt_control_t * control,dt_progress_t * progress,const char * message)425 void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
426 {
427 dt_pthread_mutex_lock(&progress->mutex);
428 g_free(progress->message);
429 progress->message = g_strdup(message);
430 dt_pthread_mutex_unlock(&progress->mutex);
431
432 // tell the gui
433 dt_pthread_mutex_lock(&control->progress_system.mutex);
434 if(control->progress_system.proxy.module != NULL)
435 control->progress_system.proxy.message_updated(control->progress_system.proxy.module, progress->gui_data,
436 message);
437 dt_pthread_mutex_unlock(&control->progress_system.mutex);
438 }
439
dt_control_progress_set_gui_data(dt_progress_t * progress,void * data)440 void dt_control_progress_set_gui_data(dt_progress_t *progress, void *data)
441 {
442 dt_pthread_mutex_lock(&progress->mutex);
443 progress->gui_data = data;
444 dt_pthread_mutex_unlock(&progress->mutex);
445 }
446
dt_control_progress_get_gui_data(dt_progress_t * progress)447 void *dt_control_progress_get_gui_data(dt_progress_t *progress)
448 {
449 dt_pthread_mutex_lock(&progress->mutex);
450 void *res = progress->gui_data;
451 dt_pthread_mutex_unlock(&progress->mutex);
452 return res;
453 }
454
dt_control_progress_has_progress_bar(dt_progress_t * progress)455 gboolean dt_control_progress_has_progress_bar(dt_progress_t *progress)
456 {
457 dt_pthread_mutex_lock(&progress->mutex);
458 gboolean res = progress->has_progress_bar;
459 dt_pthread_mutex_unlock(&progress->mutex);
460 return res;
461 }
462
dt_control_progress_cancellable(dt_progress_t * progress)463 gboolean dt_control_progress_cancellable(dt_progress_t *progress)
464 {
465 dt_pthread_mutex_lock(&progress->mutex);
466 gboolean res = progress->cancel != NULL;
467 dt_pthread_mutex_unlock(&progress->mutex);
468 return res;
469 }
470
471 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
472 // vim: shiftwidth=2 expandtab tabstop=2 cindent
473 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
474