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