Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
signal.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011-2012 Henrik Andersson.
4 Copyright (C) 2011-2012 johannes hanika.
5 Copyright (C) 2012 Ivan Tarozzi.
6 Copyright (C) 2012 José Carlos García Sogo.
7 Copyright (C) 2012-2015 Jérémy Rosen.
8 Copyright (C) 2012 Richard Wonka.
9 Copyright (C) 2012-2016 Tobias Ellinghaus.
10 Copyright (C) 2014, 2016 Roman Lebedev.
11 Copyright (C) 2014 Ronny Kahl.
12 Copyright (C) 2015 Edouard Gomez.
13 Copyright (C) 2016, 2019-2021 Pascal Obry.
14 Copyright (C) 2018-2019 Edgardo Hoszowski.
15 Copyright (C) 2018 Rikard Öxler.
16 Copyright (C) 2019-2022 Aldric Renaudin.
17 Copyright (C) 2019 Ulrich Pegelow.
18 Copyright (C) 2020 Chris Elston.
19 Copyright (C) 2020 Hanno Schwalm.
20 Copyright (C) 2020 Hubert Kowalski.
21 Copyright (C) 2020-2021 Philippe Weyland.
22 Copyright (C) 2021 Ralf Brown.
23 Copyright (C) 2022-2023, 2025 Aurélien PIERRE.
24 Copyright (C) 2022 Martin Bařinka.
25
26 darktable is free software: you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation, either version 3 of the License, or
29 (at your option) any later version.
30
31 darktable is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with darktable. If not, see <http://www.gnu.org/licenses/>.
38*/
39#include "common/logging.h"
40#include "system/macros.h"
41#include "system/mem_alloc.h"
42#include "control/signal.h"
43#include "control/control.h"
44#include <glib.h>
45#include <string.h>
46
47#ifdef DT_HAVE_SIGNAL_TRACE
48#include <execinfo.h>
49#endif
50
51typedef struct dt_control_signal_t
52{
53 /* the sinks for the signals */
54 GObject *sink;
56
57/*
58 GSignalFlags signal_flags,
59 ...);
60 */
62{
63 const char *name;
64 GSignalAccumulator accumulator;
65 gpointer accu_data;
67 GSignalCMarshaller c_marshaller;
68 guint n_params;
70 GCallback destructor;
71 gboolean synchronous;
73
74
75static GType uint_arg[] = { G_TYPE_UINT };
76static GType uint64_2arg[] = { G_TYPE_UINT64, G_TYPE_UINT64 };
77static GType int_arg[] = { G_TYPE_INT };
78static GType uint_2arg[] = { G_TYPE_UINT, G_TYPE_UINT };
79static GType pointer_arg[] = { G_TYPE_POINTER };
80static GType pointer_2arg[] = { G_TYPE_POINTER, G_TYPE_POINTER };
81static GType collection_args[] = { G_TYPE_UINT, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_UINT };
82static GType image_export_arg[]
83 = { G_TYPE_UINT, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER };
85= { G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_POINTER };
86static GType geotag_arg[] = { G_TYPE_POINTER, G_TYPE_UINT };
87static GType file_crawling_arg[] = { G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT };
88static GType mask_change_arg[] = { G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_UINT };
89
90// callback for the destructor of DT_SIGNAL_COLLECTION_CHANGED
91static void _collection_changed_destroy_callback(gpointer instance, int query_change, int changed_property,
92 gpointer imgs, const int next, gpointer user_data)
93{
94 if(imgs)
95 {
96 g_list_free(imgs);
97 imgs = NULL;
98 }
99}
100
101// callback for the destructor of DT_SIGNAL_IMAGE_INFO_CHANGED
102static void _image_info_changed_destroy_callback(gpointer instance, gpointer imgs, gpointer user_data)
103{
104 if(!IS_NULL_PTR(imgs))
105 {
106 g_list_free(imgs);
107 imgs = NULL;
108 }
109}
110
111// callback for the destructor of DT_SIGNAL_PRESETS_CHANGED
112static void _presets_changed_destroy_callback(gpointer instance, gpointer module, gpointer user_data)
113{
114 dt_free(module);
115}
116
117// callback for the destructor of DT_SIGNAL_GEOTAG_CHANGED
118static void _image_geotag_destroy_callback(gpointer instance, gpointer imgs, const int locid, gpointer user_data)
119{
120 if(!IS_NULL_PTR(imgs))
121 {
122 g_list_free(imgs);
123 imgs = NULL;
124 }
125}
126
128 /* Global signals */
129 { "dt-global-mouse-over-image-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
130 FALSE }, // DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE
131 { "dt-global-active-images-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
132 FALSE }, // DT_SIGNAL_ACTIVE_IMAGES_CHANGE
133
134 { "dt-control-redraw-all", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
135 FALSE }, // DT_SIGNAL_CONTROL_REDRAW_ALL
136 { "dt-control-redraw-center", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
137 FALSE }, // DT_SIGNAL_CONTROL_REDRAW_CENTER
138
139 { "dt-viewmanager-view-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, pointer_2arg, NULL,
140 FALSE }, // DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED
141 { "dt-viewmanager-view-cannot-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, pointer_2arg,
142 NULL, FALSE }, // DT_SIGNAL_VIEWMANAGER_VIEW_CANNOT_CHANGE
143 { "dt-viewmanager-thumbtable-activate", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__UINT, 1, uint_arg,
144 NULL, FALSE }, // DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE
145 { "dt-viewmanager-filmstrip-activate", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__UINT, 1, uint_arg,
146 NULL, FALSE }, // DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE
147 { "dt-viewmanager-filmstrip-drag-begin", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__UINT, 1, uint_arg,
148 NULL, FALSE }, // DT_SIGNAL_VIEWMANAGER_FILMSTRIP_DRAG_BEGIN
149
150 { "dt-collection-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 4, collection_args,
151 G_CALLBACK(_collection_changed_destroy_callback), FALSE }, // DT_SIGNAL_COLLECTION_CHANGED
152 { "dt-selection-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
153 FALSE }, // DT_SIGNAL_SELECTION_CHANGED
154 { "dt-tag-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
155 FALSE }, // DT_SIGNAL_TAG_CHANGED
156 { "dt-geotag-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, geotag_arg,
157 G_CALLBACK(_image_geotag_destroy_callback), FALSE }, // DT_SIGNAL_GEOTAG_CHANGED
158 { "dt-metadata-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__UINT, 1, uint_arg, NULL,
159 FALSE }, // DT_SIGNAL_METADATA_CHANGED
160 { "dt-image-info-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg,
161 G_CALLBACK(_image_info_changed_destroy_callback), FALSE }, // DT_SIGNAL_IMAGE_INFO_CHANGED
162 { "dt-style-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
163 FALSE }, // DT_SIGNAL_STYLE_CHANGED
164 { "dt-images-order-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg, NULL,
165 FALSE }, // DT_SIGNAL_IMAGES_ORDER_CHANGE
166 { "dt-filmrolls-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
167 FALSE }, // DT_SIGNAL_FILMROLLS_CHANGED
168 { "dt-filmrolls-removed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
169 FALSE }, // DT_SIGNAL_FILMROLLS_REMOVED
170 { "dt-presets-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg,
171 G_CALLBACK(_presets_changed_destroy_callback), FALSE }, // DT_SIGNAL_PRESETS_CHANGED
172
173 /* Develop related signals */
174 { "dt-develop-initialized", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
175 FALSE }, // DT_SIGNAL_DEVELOP_INITIALIZED
176 { "dt-develop-preview-pipe-finished", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
177 FALSE }, // DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
178 { "dt-develop-ui-pipe-finished", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
179 FALSE }, // DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED
180 { "dt-cacheline-ready", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, uint64_2arg, NULL,
181 FALSE }, // DT_SIGNAL_CACHELINE_READY (hash, producer_node_key)
182 { "dt-develop-modulegroups-set", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__POINTER, 1, pointer_arg, NULL,
183 FALSE }, // DT_SIGNAL_DEVELOP_MODULEGROUPS_SET
184 { "dt-develop-history-will-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 3,
185 history_will_change_arg, NULL, FALSE }, // DT_SIGNAL_HISTORY_WILL_CHANGE
186 { "dt-develop-history-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
187 FALSE }, // DT_SIGNAL_HISTORY_CHANGE
188 { "dt-history-resync", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
189 FALSE }, // DT_SIGNAL_HISTORY_RESYNC
190 { "dt-develop-module-remove", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg, NULL,
191 TRUE }, // DT_SIGNAL_MODULE_REMOVE
192 { "dt-develop-module-moved", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
193 FALSE }, // DT_SIGNAL_DEVELOP_MODULE_MOVED
194 { "dt-develop-image-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
195 FALSE }, // DT_SIGNAL_DEVELOP_IMAGE_CHANGED
196 { "dt-darkroom-ui-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
197 FALSE}, // DT_SIGNAL_DARKROOM_UI_CHANGED
198 { "dt-image-loaded", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, uint_2arg,
199 NULL, FALSE }, // DT_SIGNAL_IMAGE_LOADED
200 { "dt-control-profile-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
201 FALSE }, // DT_SIGNAL_CONTROL_PROFILE_CHANGED
202 { "dt-control-profile-user-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__UINT, 1, uint_arg, NULL,
203 FALSE }, // DT_SIGNAL_CONTROL_PROFILE_USER_CHANGED
204 { "dt-image-import", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__INT, 1, int_arg, NULL,
205 FALSE }, // DT_SIGNAL_IMAGE_IMPORT
206 { "dt-image-export-tmpfile", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 6, image_export_arg, NULL,
207 TRUE }, // DT_SIGNAL_IMAGE_EXPORT_TMPFILE
208 { "dt-imageio-storage-change", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
209 FALSE }, // DT_SIGNAL_IMAGEIO_STORAGE_CHANGE
210
211
212 { "dt-preferences-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
213 FALSE }, // DT_SIGNAL_PREFERENCES_CHANGE
214
215
216 { "dt-control-navigation-redraw", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
217 FALSE }, // DT_SIGNAL_CONTROL_NAVIGATION_REDRAW
218
219 { "dt-control-log-redraw", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
220 FALSE }, // DT_SIGNAL_CONTROL_LOG_REDRAW
221
222 { "dt-control-toast-redraw", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
223 FALSE }, // DT_SIGNAL_CONTROL_TOAST_REDRAW
224
225 { "dt-control-pickerdata-ready", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
226 TRUE }, // DT_SIGNAL_CONTROL_PICKERDATA_REAEDY
227
228 { "dt-control-notebook-tab-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg, NULL,
229 FALSE }, // DT_SIGNAL_CONTROL_NOTEBOOK_TAB_CHANGED (owner)
230
231 { "dt-metadata-update", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
232 FALSE }, // DT_SIGNAL_METADATA_UPDATE
233
234 { "dt-location-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg, NULL,
235 TRUE }, // DT_SIGNAL_LOCATION_CHANGED
236
237 { "dt-mask-selection-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 2, pointer_2arg, NULL,
238 TRUE }, // DT_SIGNAL_MASK_SELECTION_CHANGED
239 { "dt-mask-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 3, mask_change_arg, NULL,
240 FALSE }, //DT_SIGNAL_MASK_CHANGED
241 { "dt-mask-shape-buttons-deactivate", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 1, pointer_arg, NULL,
242 FALSE }, // DT_SIGNAL_MASK_SHAPE_BUTTONS_DEACTIVATE
243 { "dt-mask-shape-buttons-sync", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
244 FALSE }, // DT_SIGNAL_MASK_SHAPE_BUTTONS_SYNC
245 { "dt-develop-masks-gui-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
246 FALSE }, // DT_SIGNAL_DEVELOP_MASKS_GUI_CHANGED
247
248 { "dt-control-filelist-update", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_generic, 4, file_crawling_arg, NULL,
249 TRUE }, // DT_SIGNAL_FILELIST_UPDATED: synchronous so crawler-owned pointers stay valid until handlers return.
250 // 4th param (scan_errors) added for the import dialog's recursive-scan error count; this
251 // signal is exclusively raised/consumed by common/import.c.
252
253 { "dt-folder-survey-changed", NULL, NULL, G_TYPE_NONE, g_cclosure_marshal_VOID__VOID, 0, NULL, NULL,
254 FALSE }, // DT_SIGNAL_FOLDER_SURVEY_CHANGED
255};
256
257static GType _signal_type;
258
260{
261 dt_control_signal_t *ctlsig = g_malloc0(sizeof(dt_control_signal_t));
262
263 /* setup dummy gobject typeinfo */
264 GTypeQuery query;
265 GTypeInfo type_info = { 0, (GBaseInitFunc)NULL, (GBaseFinalizeFunc)NULL, (GClassInitFunc)NULL,
266 (GClassFinalizeFunc)NULL, NULL, 0, 0, (GInstanceInitFunc)NULL };
267
268 g_type_query(G_TYPE_OBJECT, &query);
269 type_info.class_size = query.class_size;
270 type_info.instance_size = query.instance_size;
271 _signal_type = g_type_register_static(G_TYPE_OBJECT, "DarktableSignals", &type_info, 0);
272
273 /* create our pretty empty gobject */
274 ctlsig->sink = g_object_new(_signal_type, NULL);
275
276 /* create the signals */
277 for(int k = 0; k < DT_SIGNAL_COUNT; k++)
278 {
279 g_signal_newv(_signal_description[k].name, _signal_type, G_SIGNAL_RUN_LAST, 0,
280 _signal_description[k].accumulator, _signal_description[k].accu_data,
281 _signal_description[k].c_marshaller, _signal_description[k].return_type,
282 _signal_description[k].n_params, _signal_description[k].param_types);
283 if(_signal_description[k].destructor)
284 {
285 g_signal_connect_after(G_OBJECT(ctlsig->sink), _signal_description[k].name,
287 }
288 }
289 return ctlsig;
290}
291
293{
294 if(IS_NULL_PTR(ctlsig)) return;
295 if(!IS_NULL_PTR(ctlsig->sink)) g_object_unref(ctlsig->sink);
296 dt_free(ctlsig);
297}
298
305
306static gboolean _signal_emit(gpointer user_data)
307{
308 _signal_param_t *params = (_signal_param_t *)user_data;
309 g_signal_emitv(params->instance_and_params, params->signal_id, 0, NULL);
310 return G_SOURCE_REMOVE;
311}
312
313static void _signal_param_cleanup(gpointer user_data)
314{
315 _signal_param_t *params = (_signal_param_t *)user_data;
316 if(IS_NULL_PTR(params)) return;
317 // We iterate over the whole emitted argument vector (instance at index 0 + signal params)
318 // and only unset slots that currently hold a valid GValue type.
319 for(int i = 0; i <= params->n_params; i++)
320 {
321 GValue *value = &params->instance_and_params[i];
322 const GType value_type = G_VALUE_TYPE(value);
323 if(value_type == G_TYPE_INVALID) continue;
324 if(!g_type_check_is_value_type(value_type)) continue;
325 g_value_unset(value);
326 }
327 dt_free(params->instance_and_params);
328 dt_free(params);
329}
330
331typedef struct async_com_data
332{
333 GCond end_cond;
334 GMutex end_mutex;
335 gpointer user_data;
337
338gboolean _async_com_callback(gpointer data)
339{
340 async_com_data *communication = (async_com_data*)data;
341 g_mutex_lock(&communication->end_mutex);
342 _signal_emit(communication->user_data);
343 _signal_param_cleanup(communication->user_data);
344
345 g_cond_signal(&communication->end_cond);
346 g_mutex_unlock(&communication->end_mutex);
347 return FALSE;
348}
349
350static void _print_trace (const char* op)
351{
352#ifdef DT_HAVE_SIGNAL_TRACE
354 {
355 void *array[10];
356 size_t size;
357 char **strings;
358 size_t i;
359
360 size = backtrace (array, 10);
361 strings = backtrace_symbols (array, size);
362
363 for (i = 0; i < size; i++)
364 dt_print(DT_DEBUG_SIGNAL, "[signal-trace-%s]: %s\n", op, strings[i]);
365
366 dt_free(strings);
367 }
368#endif
369}
370
372{
373 if(IS_NULL_PTR(ctlsig)) return; // no signal system in this process (GUI-less)
374 // ignore all signals on shutdown
375 if(!dt_control_running()) return;
376
377 dt_signal_description *signal_description = &_signal_description[signal];
378
379 _signal_param_t *params = (_signal_param_t *)malloc(sizeof(_signal_param_t));
380 if(IS_NULL_PTR(params)) return;
381
382 GValue *instance_and_params = calloc(1 + signal_description->n_params, sizeof(GValue));
383 if(IS_NULL_PTR(instance_and_params))
384 {
385 dt_free(params);
386 return;
387 }
388
390 {
391 dt_print(DT_DEBUG_SIGNAL, "[signal] raised: %s\n", signal_description->name);
392 _print_trace("raise");
393 }
394
395 // 0th element has to be the instance to call
396 g_value_init(instance_and_params, _signal_type);
397 g_value_set_object(instance_and_params, ctlsig->sink);
398
399 // the rest of instance_and_params will be the params for the callback
400 va_list extra_args;
401 va_start(extra_args, signal);
402
403 for(int i = 1; i <= signal_description->n_params; i++)
404 {
405 GType type = signal_description->param_types[i-1];
406 g_value_init(&instance_and_params[i], type);
407 switch(type)
408 {
409 case G_TYPE_UINT:
410 g_value_set_uint(&instance_and_params[i], va_arg(extra_args, guint));
411 break;
412 case G_TYPE_UINT64:
413 g_value_set_uint64(&instance_and_params[i], va_arg(extra_args, guint64));
414 break;
415 case G_TYPE_STRING:
416 g_value_set_string(&instance_and_params[i], va_arg(extra_args, const char *));
417 break;
418 case G_TYPE_INT:
419 g_value_set_int(&instance_and_params[i], va_arg(extra_args, int));
420 break;
421 case G_TYPE_POINTER:
422 g_value_set_pointer(&instance_and_params[i], va_arg(extra_args, void *));
423 break;
424 default:
425 fprintf(stderr, "error: unsupported parameter type `%s' for signal `%s'\n",
426 g_type_name(type), signal_description->name);
427 va_end(extra_args);
428 for(int j = 0; j <= i; j++) g_value_unset(&instance_and_params[j]);
429 dt_free(instance_and_params);
430 dt_free(params);
431 return;
432 }
433 }
434
435 va_end(extra_args);
436
437 params->instance_and_params = instance_and_params;
438 params->signal_id = g_signal_lookup(_signal_description[signal].name, _signal_type);
439 params->n_params = signal_description->n_params;
440
441 if(!signal_description->synchronous)
442 {
443 g_main_context_invoke_full(NULL, G_PRIORITY_DEFAULT, _signal_emit, params, _signal_param_cleanup);
444 }
445 else
446 {
447 if(pthread_equal(dt_control_get_global()->gui_thread, pthread_self()))
448 {
449 _signal_emit(params);
450 _signal_param_cleanup(params);
451 }
452 else
453 {
454 async_com_data communication;
455 g_mutex_init(&communication.end_mutex);
456 g_cond_init(&communication.end_cond);
457 g_mutex_lock(&communication.end_mutex);
458 communication.user_data = params;
459 g_main_context_invoke(NULL,_async_com_callback,&communication);
460 g_cond_wait(&communication.end_cond,&communication.end_mutex);
461 g_mutex_unlock(&communication.end_mutex);
462 g_mutex_clear(&communication.end_mutex);
463 }
464 }
465}
466
467void dt_control_signal_connect(const dt_control_signal_t *ctlsig, dt_signal_t signal, GCallback cb,
468 gpointer user_data)
469{
470 if(IS_NULL_PTR(ctlsig)) return;
472 {
473 dt_print(DT_DEBUG_SIGNAL, "[signal] connected: %s\n", _signal_description[signal].name);
474 _print_trace("connect");
475 }
476 g_signal_connect(G_OBJECT(ctlsig->sink), _signal_description[signal].name, G_CALLBACK(cb), user_data);
477}
478
479void dt_control_signal_disconnect(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
480{
481 if(IS_NULL_PTR(ctlsig)) return;
483 {
484 dt_print(DT_DEBUG_SIGNAL, "[signal] disconnected\n");
485 _print_trace("disconnect");
486 }
487 g_signal_handlers_disconnect_matched(G_OBJECT(ctlsig->sink), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0,
488 0, NULL, cb, user_data);
489}
490
491void dt_control_signal_disconnect_all(const struct dt_control_signal_t *ctlsig, gpointer user_data)
492{
493 if(IS_NULL_PTR(ctlsig) || IS_NULL_PTR(user_data)) return;
495 {
496 dt_print(DT_DEBUG_SIGNAL, "[signal] disconnected all\n");
497 _print_trace("disconnect_all");
498 }
499 // G_SIGNAL_MATCH_DATA only: matches on user_data alone, across every signal id and callback
500 // function ever connected on this sink for that data -- the blanket variant of the (func, data)
501 // matched disconnect above.
502 g_signal_handlers_disconnect_matched(G_OBJECT(ctlsig->sink), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
503 user_data);
504}
505
506void dt_control_signal_block_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
507{
508 if(IS_NULL_PTR(ctlsig)) return;
509 g_signal_handlers_block_by_func(G_OBJECT(ctlsig->sink), cb, user_data);
510}
511
512void dt_control_signal_unblock_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
513{
514 if(IS_NULL_PTR(ctlsig)) return;
515 g_signal_handlers_unblock_by_func(G_OBJECT(ctlsig->sink), cb, user_data);
516}
517
518// clang-format off
519// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
520// vim: shiftwidth=2 expandtab tabstop=2 cindent
521// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
522// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int dt_control_running()
Definition control.c:442
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:651
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_SIGNAL
Definition logging.h:70
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#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
size_t size
Definition mipmap_cache.c:3
const char * name
Definition pdf.h:90
static GType _signal_type
Definition signal.c:257
static GType file_crawling_arg[]
Definition signal.c:87
static GType int_arg[]
Definition signal.c:77
static dt_signal_description _signal_description[DT_SIGNAL_COUNT]
Definition signal.c:127
void dt_control_signal_raise(const dt_control_signal_t *ctlsig, dt_signal_t signal,...)
Definition signal.c:371
static void _image_info_changed_destroy_callback(gpointer instance, gpointer imgs, gpointer user_data)
Definition signal.c:102
static GType uint64_2arg[]
Definition signal.c:76
static void _collection_changed_destroy_callback(gpointer instance, int query_change, int changed_property, gpointer imgs, const int next, gpointer user_data)
Definition signal.c:91
static gboolean _signal_emit(gpointer user_data)
Definition signal.c:306
static GType pointer_arg[]
Definition signal.c:79
static GType pointer_2arg[]
Definition signal.c:80
void dt_control_signal_unblock_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
Definition signal.c:512
static void _signal_param_cleanup(gpointer user_data)
Definition signal.c:313
void dt_control_signal_cleanup(dt_control_signal_t *ctlsig)
Definition signal.c:292
static GType image_export_arg[]
Definition signal.c:83
static GType collection_args[]
Definition signal.c:81
static void _print_trace(const char *op)
Definition signal.c:350
dt_control_signal_t * dt_control_signal_init()
Definition signal.c:259
static GType mask_change_arg[]
Definition signal.c:88
static void _image_geotag_destroy_callback(gpointer instance, gpointer imgs, const int locid, gpointer user_data)
Definition signal.c:118
void dt_control_signal_disconnect_all(const struct dt_control_signal_t *ctlsig, gpointer user_data)
Definition signal.c:491
gboolean _async_com_callback(gpointer data)
Definition signal.c:338
static void _presets_changed_destroy_callback(gpointer instance, gpointer module, gpointer user_data)
Definition signal.c:112
void dt_control_signal_disconnect(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
Definition signal.c:479
static GType history_will_change_arg[]
Definition signal.c:85
static GType uint_arg[]
Definition signal.c:75
static GType uint_2arg[]
Definition signal.c:78
static GType geotag_arg[]
Definition signal.c:86
void dt_control_signal_block_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
Definition signal.c:506
void dt_control_signal_connect(const dt_control_signal_t *ctlsig, dt_signal_t signal, GCallback cb, gpointer user_data)
Definition signal.c:467
@ DT_DEBUG_SIGNAL_ACT_DISCONNECT
Definition signal.h:348
@ DT_DEBUG_SIGNAL_ACT_CONNECT
Definition signal.h:347
@ DT_DEBUG_SIGNAL_ACT_RAISE
Definition signal.h:346
@ DT_DEBUG_SIGNAL_ACT_PRINT_TRACE
Definition signal.h:349
int32_t dt_get_signal_debug_acts(void)
Definition darktable.c:2090
gboolean dt_get_signal_debug(const int signal)
Definition darktable.c:2095
dt_signal_t
enum of signals to listen for in darktable.
Definition signal.h:57
@ DT_SIGNAL_COUNT
Definition signal.h:340
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
guint signal_id
Definition signal.c:302
guint n_params
Definition signal.c:303
GValue * instance_and_params
Definition signal.c:301
GCond end_cond
Definition signal.c:333
GMutex end_mutex
Definition signal.c:334
gpointer user_data
Definition signal.c:335
GObject * sink
Definition signal.c:54
const char * name
Definition signal.c:63
gpointer accu_data
Definition signal.c:65
GSignalCMarshaller c_marshaller
Definition signal.c:67
GType * param_types
Definition signal.c:69
GSignalAccumulator accumulator
Definition signal.c:64
GCallback destructor
Definition signal.c:70
gboolean synchronous
Definition signal.c:71