Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
control.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2016 johannes hanika.
4 Copyright (C) 2010 Brian Teague.
5 Copyright (C) 2010, 2015 Bruce Guenter.
6 Copyright (C) 2010-2013 Henrik Andersson.
7 Copyright (C) 2010 Pascal de Bruijn.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2010-2019 Tobias Ellinghaus.
10 Copyright (C) 2010 Wyatt Olson.
11 Copyright (C) 2011 Antony Dovgal.
12 Copyright (C) 2011 Robert Bieber.
13 Copyright (C) 2011 Rostyslav Pidgornyi.
14 Copyright (C) 2011-2013, 2016 Ulrich Pegelow.
15 Copyright (C) 2012-2014, 2019-2020 Aldric Renaudin.
16 Copyright (C) 2012 James C. McPherson.
17 Copyright (C) 2012-2013 José Carlos García Sogo.
18 Copyright (C) 2012 Michal Babej.
19 Copyright (C) 2012 Michal Fabik.
20 Copyright (C) 2012-2015 parafin.
21 Copyright (C) 2012 Richard Wonka.
22 Copyright (C) 2012-2013 Simon Spannagel.
23 Copyright (C) 2013-2014 Jérémy Rosen.
24 Copyright (C) 2013, 2018-2022 Pascal Obry.
25 Copyright (C) 2013 Pierre Le Magourou.
26 Copyright (C) 2013-2016 Roman Lebedev.
27 Copyright (C) 2016 Asma.
28 Copyright (C) 2016-2017 Peter Budai.
29 Copyright (C) 2018 Andreas Schneider.
30 Copyright (C) 2018-2019 Edgardo Hoszowski.
31 Copyright (C) 2018 rawfiner.
32 Copyright (C) 2018 Rikard Öxler.
33 Copyright (C) 2019, 2022-2023, 2025-2026 Aurélien PIERRE.
34 Copyright (C) 2020 Chris Elston.
35 Copyright (C) 2020-2022 Diederik Ter Rahe.
36 Copyright (C) 2020 Hanno Schwalm.
37 Copyright (C) 2020 Hubert Kowalski.
38 Copyright (C) 2022 Martin Bařinka.
39 Copyright (C) 2024-2026 Guillaume Stutin.
40
41 darktable is free software: you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation, either version 3 of the License, or
44 (at your option) any later version.
45
46 darktable is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with darktable. If not, see <http://www.gnu.org/licenses/>.
53*/
54#ifdef HAVE_CONFIG_H
55#include "config.h"
56#endif
57#include "bauhaus/bauhaus.h"
58#include "common/colorspaces.h"
59#include "common/darktable.h"
60#include "common/debug.h"
61#include "common/image_cache.h"
62#include "common/imageio.h"
63#include "control/conf.h"
64#include "control/control.h"
65#include "develop/develop.h"
66#include "develop/imageop.h"
67
68#include "gui/draw.h"
69#include "gui/gtk.h"
70#include "views/view.h"
71
72#include <assert.h>
73#include <gdk/gdkkeysyms.h>
74#include <glib/gstdio.h>
75#include <lcms2.h>
76#include <math.h>
77#include <stdlib.h>
78#include <string.h>
79#include <strings.h>
80
82
84{
85 if(IS_NULL_PTR(input)) return;
86 _pointer_input = *input;
87}
88
90{
91 if(IS_NULL_PTR(input)) return;
92 *input = _pointer_input;
93}
94
96{
97 // same thread as init
98 s->gui_thread = pthread_self();
99 s->cursor.shape = GDK_LEFT_PTR;
100 s->cursor.current_shape = GDK_LEFT_PTR;
101 s->cursor.shape_str = NULL;
102 s->cursor.current_shape_str = NULL;
103 s->cursor.hide = FALSE;
104 // s->last_expose_time = dt_get_wtime();
105 s->log_pos = s->log_ack = 0;
106 s->log_busy = 0;
108 dt_pthread_mutex_init(&(s->log_mutex), NULL);
109
110 s->toast_pos = s->toast_ack = 0;
111 s->toast_busy = 0;
114
115 pthread_cond_init(&s->cond, NULL);
122
123 // start threads
125
126 s->button_down = 0;
127 s->button_down_which = 0;
128 s->mouse_over_id = -1;
129 s->keyboard_over_id = -1;
130 s->cursor.lock = FALSE;
131}
132
133// used for debugging, might remove later
135{
136 gchar *shape_str = NULL;
137 switch(cursor)
138 {
139 case GDK_X_CURSOR: shape_str = g_strdup("GDK_X_CURSOR"); break;
140 case GDK_ARROW: shape_str = g_strdup("GDK_ARROW"); break;
141 case GDK_BASED_ARROW_DOWN: shape_str = g_strdup("GDK_BASED_ARROW_DOWN"); break;
142 case GDK_BASED_ARROW_UP: shape_str = g_strdup("GDK_BASED_ARROW_UP"); break;
143 case GDK_BOAT: shape_str = g_strdup("GDK_BOAT"); break;
144 case GDK_BOGOSITY: shape_str = g_strdup("GDK_BOGOSITY"); break;
145 case GDK_BOTTOM_LEFT_CORNER: shape_str = g_strdup("GDK_BOTTOM_LEFT_CORNER"); break;
146 case GDK_BOTTOM_RIGHT_CORNER: shape_str = g_strdup("GDK_BOTTOM_RIGHT_CORNER"); break;
147 case GDK_BOTTOM_SIDE: shape_str = g_strdup("GDK_BOTTOM_SIDE"); break;
148 case GDK_BOTTOM_TEE: shape_str = g_strdup("GDK_BOTTOM_TEE"); break;
149 case GDK_BOX_SPIRAL: shape_str = g_strdup("GDK_BOX_SPIRAL"); break;
150 case GDK_CENTER_PTR: shape_str = g_strdup("GDK_CENTER_PTR"); break;
151 case GDK_CIRCLE: shape_str = g_strdup("GDK_CIRCLE"); break;
152 case GDK_CLOCK: shape_str = g_strdup("GDK_CLOCK"); break;
153 case GDK_COFFEE_MUG: shape_str = g_strdup("GDK_COFFEE_MUG"); break;
154 case GDK_CROSS: shape_str = g_strdup("GDK_CROSS"); break;
155 case GDK_CROSS_REVERSE: shape_str = g_strdup("GDK_CROSS_REVERSE"); break;
156 case GDK_CROSSHAIR: shape_str = g_strdup("GDK_CROSSHAIR"); break;
157 case GDK_DIAMOND_CROSS: shape_str = g_strdup("GDK_DIAMOND_CROSS"); break;
158 case GDK_DOT: shape_str = g_strdup("GDK_DOT"); break;
159 case GDK_DOTBOX: shape_str = g_strdup("GDK_DOTBOX"); break;
160 case GDK_DOUBLE_ARROW: shape_str = g_strdup("GDK_DOUBLE_ARROW"); break;
161 case GDK_DRAFT_LARGE: shape_str = g_strdup("GDK_DRAFT_LARGE"); break;
162 case GDK_DRAFT_SMALL: shape_str = g_strdup("GDK_DRAFT_SMALL"); break;
163 case GDK_DRAPED_BOX: shape_str = g_strdup("GDK_DRAPED_BOX"); break;
164 case GDK_EXCHANGE: shape_str = g_strdup("GDK_EXCHANGE"); break;
165 case GDK_FLEUR: shape_str = g_strdup("GDK_FLEUR"); break;
166 case GDK_GOBBLER: shape_str = g_strdup("GDK_GOBBLER"); break;
167 case GDK_GUMBY: shape_str = g_strdup("GDK_GUMBY"); break;
168 case GDK_HAND1: shape_str = g_strdup("GDK_HAND1"); break;
169 case GDK_HAND2: shape_str = g_strdup("GDK_HAND2"); break;
170 case GDK_HEART: shape_str = g_strdup("GDK_HEART"); break;
171 case GDK_ICON: shape_str = g_strdup("GDK_ICON"); break;
172 case GDK_IRON_CROSS: shape_str = g_strdup("GDK_IRON_CROSS"); break;
173 case GDK_LEFT_PTR: shape_str = g_strdup("GDK_LEFT_PTR"); break;
174 case GDK_LEFT_SIDE: shape_str = g_strdup("GDK_LEFT_SIDE"); break;
175 case GDK_LEFT_TEE: shape_str = g_strdup("GDK_LEFT_TEE"); break;
176 case GDK_LEFTBUTTON: shape_str = g_strdup("GDK_LEFTBUTTON"); break;
177 case GDK_LL_ANGLE: shape_str = g_strdup("GDK_LL_ANGLE"); break;
178 case GDK_LR_ANGLE: shape_str = g_strdup("GDK_LR_ANGLE"); break;
179 case GDK_MAN: shape_str = g_strdup("GDK_MAN"); break;
180 case GDK_MIDDLEBUTTON: shape_str = g_strdup("GDK_MIDDLEBUTTON"); break;
181 case GDK_MOUSE: shape_str = g_strdup("GDK_MOUSE"); break;
182 case GDK_PENCIL: shape_str = g_strdup("GDK_PENCIL"); break;
183 case GDK_PIRATE: shape_str = g_strdup("GDK_PIRATE"); break;
184 case GDK_PLUS: shape_str = g_strdup("GDK_PLUS"); break;
185 case GDK_QUESTION_ARROW: shape_str = g_strdup("GDK_QUESTION_ARROW"); break;
186 case GDK_RIGHT_PTR: shape_str = g_strdup("GDK_RIGHT_PTR"); break;
187 case GDK_RIGHT_SIDE: shape_str = g_strdup("GDK_RIGHT_SIDE"); break;
188 case GDK_RIGHT_TEE: shape_str = g_strdup("GDK_RIGHT_TEE"); break;
189 case GDK_RIGHTBUTTON: shape_str = g_strdup("GDK_RIGHTBUTTON"); break;
190 case GDK_RTL_LOGO: shape_str = g_strdup("GDK_RTL_LOGO"); break;
191 case GDK_SAILBOAT: shape_str = g_strdup("GDK_SAILBOAT"); break;
192 case GDK_SB_DOWN_ARROW: shape_str = g_strdup("GDK_SB_DOWN_ARROW"); break;
193 case GDK_SB_H_DOUBLE_ARROW: shape_str = g_strdup("GDK_SB_H_DOUBLE_ARROW"); break;
194 case GDK_SB_LEFT_ARROW: shape_str = g_strdup("GDK_SB_LEFT_ARROW"); break;
195 case GDK_SB_RIGHT_ARROW: shape_str = g_strdup("GDK_SB_RIGHT_ARROW"); break;
196 case GDK_SB_UP_ARROW: shape_str = g_strdup("GDK_SB_UP_ARROW"); break;
197 case GDK_SB_V_DOUBLE_ARROW: shape_str = g_strdup("GDK_SB_V_DOUBLE_ARROW"); break;
198 case GDK_SHUTTLE: shape_str = g_strdup("GDK_SHUTTLE"); break;
199 case GDK_SIZING: shape_str = g_strdup("GDK_SIZING"); break;
200 case GDK_SPIDER: shape_str = g_strdup("GDK_SPIDER"); break;
201 case GDK_SPRAYCAN: shape_str = g_strdup("GDK_SPRAYCAN"); break;
202 case GDK_STAR: shape_str = g_strdup("GDK_STAR"); break;
203 case GDK_TARGET: shape_str = g_strdup("GDK_TARGET"); break;
204 case GDK_TCROSS: shape_str = g_strdup("GDK_TCROSS"); break;
205 case GDK_TOP_LEFT_ARROW: shape_str = g_strdup("GDK_TOP_LEFT_ARROW"); break;
206 case GDK_TOP_LEFT_CORNER: shape_str = g_strdup("GDK_TOP_LEFT_CORNER"); break;
207 case GDK_TOP_RIGHT_CORNER: shape_str = g_strdup("GDK_TOP_RIGHT_CORNER"); break;
208 case GDK_TOP_SIDE: shape_str = g_strdup("GDK_TOP_SIDE"); break;
209 case GDK_TOP_TEE: shape_str = g_strdup("GDK_TOP_TEE"); break;
210 case GDK_TREK: shape_str = g_strdup("GDK_TREK"); break;
211 case GDK_UL_ANGLE: shape_str = g_strdup("GDK_UL_ANGLE"); break;
212 case GDK_UMBRELLA: shape_str = g_strdup("GDK_UMBRELLA"); break;
213 case GDK_UR_ANGLE: shape_str = g_strdup("GDK_UR_ANGLE"); break;
214 case GDK_WATCH: shape_str = g_strdup("GDK_WATCH"); break;
215 case GDK_XTERM: shape_str = g_strdup("GDK_XTERM"); break;
216 case GDK_LAST_CURSOR: shape_str = g_strdup("GDK_LAST_CURSOR"); break;
217 case GDK_BLANK_CURSOR: shape_str = g_strdup("GDK_BLANK_CURSOR"); break;
218 case GDK_CURSOR_IS_PIXMAP: shape_str = g_strdup("GDK_CURSOR_IS_PIXMAP"); break;
219 default: break;
220 }
221 return shape_str;
222}
223
228
233
234static void _control_set_cursor_on_widget(GtkWidget *widget, GdkCursor *cursor)
235{
236 if(IS_NULL_PTR(widget)) return;
237
238 GdkWindow *window = gtk_widget_get_window(widget);
239 if(!IS_NULL_PTR(window)) gdk_window_set_cursor(window, cursor);
240}
241
242static void _control_apply_cursor(GdkCursor *cursor)
243{
244 GtkWidget *main_window = dt_ui_main_window(darktable.gui->ui);
245 GtkWidget *center_base = dt_ui_center_base(darktable.gui->ui);
247
248 _control_set_cursor_on_widget(main_window, cursor);
249
250 if(gtk_widget_get_window(center_base) != gtk_widget_get_window(main_window))
251 _control_set_cursor_on_widget(center_base, cursor);
252
253 if(gtk_widget_get_window(center) != gtk_widget_get_window(center_base)
254 && gtk_widget_get_window(center) != gtk_widget_get_window(main_window))
255 _control_set_cursor_on_widget(center, cursor);
256}
257
258static void _control_store_current_cursor(const dt_cursor_t shape, const char *shape_str)
259{
260 gchar *current_shape_str = g_strdup(shape_str);
261
264 darktable.control->cursor.current_shape_str = current_shape_str;
265}
266
274void dt_control_change_cursor_by_name(const char *curs_str)
275{
276 if(IS_NULL_PTR(curs_str)) return;
277
279 {
280 const gboolean hide = darktable.control->cursor.hide;
281 const gboolean current_is_named_cursor =
283 && g_strcmp0(darktable.control->cursor.current_shape_str, curs_str) == 0;
284
285 // "progress" is a special cursor that should not overwrite the queued cursor.
286 if(g_strcmp0(curs_str, "progress") != 0)
288
289 if(hide)
290 {
292 && darktable.control->cursor.current_shape == GDK_BLANK_CURSOR)
293 return;
294 }
295 else if(current_is_named_cursor)
296 return;
297
298 // We choose the GTK constructor here because named cursors are pixmaps in
299 // GTK's cache, while the hidden cursor is one of the standard shapes.
300 const dt_cursor_t chosen_shape = hide ? GDK_BLANK_CURSOR : GDK_CURSOR_IS_PIXMAP;
301 GdkCursor *cursor_shape = hide
302 ? gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BLANK_CURSOR)
303 : gdk_cursor_new_from_name(gdk_display_get_default(), curs_str);
304
305 if(IS_NULL_PTR(cursor_shape)) return;
306 _control_apply_cursor(cursor_shape);
307 _control_store_current_cursor(chosen_shape, hide ? NULL : curs_str);
308
310 dt_print(DT_DEBUG_CONTROL, "Changing cursor to `%s`\n", hide ? "GDK_BLANK_CURSOR" : curs_str);
311
312 g_object_unref(cursor_shape);
313 }
314}
315
327{
328 if(IS_NULL_PTR(curs_str)) return;
330 GdkDisplay *display = gdk_display_get_default();
331 if(!IS_NULL_PTR(display)) gdk_display_flush(display);
332}
333
335{
336 //fprintf(stderr, "Committing cursor \n");
337 if(darktable.control->log_busy > 0) return;
338
341 else
343}
344
345void dt_control_change_cursor_EXT(dt_cursor_t cursor, const char *file, int line)
346{
347 const gboolean hide = darktable.control->cursor.hide;
348
349 // GDK_CURSOR_IS_PIXMAP is returned by GTK for named cursors and custom pixmaps.
350 // It is not a cursor shape that can be constructed with gdk_cursor_new_for_display().
351 const dt_cursor_t requested_shape = cursor == GDK_CURSOR_IS_PIXMAP ? GDK_LEFT_PTR : cursor;
352 const dt_cursor_t chosen_shape = hide ? GDK_BLANK_CURSOR : requested_shape;
353
354 // Keep the requested cursor queued even if the visible cursor stays blank.
355 dt_control_queue_cursor_EXT(requested_shape, file, line);
356
358 && darktable.control->cursor.current_shape == chosen_shape)
359 return;
360
362 {
363 GdkCursor *cursor_shape = gdk_cursor_new_for_display(gdk_display_get_default(), chosen_shape);
364 if(IS_NULL_PTR(cursor_shape)) return;
365 _control_apply_cursor(cursor_shape);
366 g_object_unref(cursor_shape);
367 _control_store_current_cursor(chosen_shape, NULL);
368
371 "Changing cursor to `%s`, requested from %s:%d\n",
372 hide ? "GDK_BLANK_CURSOR" : _get_cursor_name(requested_shape), file, line);
373 }
374}
375
376void dt_control_queue_cursor_EXT(dt_cursor_t cursor, const char *file, int line)
377{
378 const dt_cursor_t requested_shape = cursor == GDK_CURSOR_IS_PIXMAP ? GDK_LEFT_PTR : cursor;
379
380 if(darktable.control->cursor.shape == requested_shape
382 return;
383
385 dt_print(DT_DEBUG_CONTROL, "Queue cursor to `%s`, requested from %s:%d\n", _get_cursor_name(requested_shape), file, line);
386
389 darktable.control->cursor.shape = requested_shape;
390}
391
398void dt_control_queue_cursor_by_name(const char *curs_str)
399{
400 if(IS_NULL_PTR(curs_str)) return;
401 // "progress" is a special cursor that should not overwrite the queued cursor.
402 // Use dt_change_cursor_by_name() to set it directly without queuing.
403 if(g_strcmp0(curs_str, "progress") == 0)
404 return;
405
406 if(g_strcmp0(darktable.control->cursor.shape_str, curs_str) == 0) return;
407
408 GdkCursor *cursor = gdk_cursor_new_from_name(gdk_display_get_default(), curs_str);
409 if(IS_NULL_PTR(cursor)) return;
410 g_object_unref(cursor);
411
413 darktable.control->cursor.shape_str = g_strdup(curs_str);
414}
415
416void dt_control_set_cursor_visible_EXT(gboolean visible, const char *file, int line)
417{
419 dt_print(DT_DEBUG_CONTROL, "%s cursor, requested from %s:%d\n", visible ? "Show" : "Hide", file, line);
420 darktable.control->cursor.hide = !visible;
421}
422
427
432
434{
435 // FIXME: when shutdown, run_mutex is not inited anymore!
438 int running = s->running;
440 return running;
441}
442
444{
446 // thread safe quit, 1st pass:
452
453 if(gtk_main_level() > 0) gtk_main_quit();
454}
455
457{
460 s->running = 0;
463 pthread_cond_broadcast(&s->cond);
464
465 /* then wait for kick_on_workers_thread */
466 pthread_join(s->kick_on_workers_thread, NULL);
467
468 int k;
469 for(k = 0; k < s->num_threads; k++)
470 // pthread_kill(s->thread[k], 9);
471 pthread_join(s->thread[k], NULL);
472 for(k = 0; k < DT_CTL_WORKER_RESERVED; k++)
473 // pthread_kill(s->thread_res[k], 9);
474 pthread_join(s->thread_res[k], NULL);
475
476 // Workers are joined: nothing else touches the queues now. Dispose every still-queued job here,
477 // while the plug-in .so files that own their callbacks (state_changed_cb, params_destroy) are
478 // still loaded. dt_control_cleanup() -> dt_control_jobs_cleanup() runs much later, after
479 // dt_lib_cleanup() has dlclose()'d those modules, so cancelling a leftover job there jumps into
480 // an unmapped callback and crashes (e.g. a pending textnotes-load job -> SIGSEGV).
482}
483
485{
486 // vacuum TODO: optional?
487 // DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "PRAGMA incremental_vacuum(0)", NULL, NULL, NULL);
488 // DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "vacuum", NULL, NULL, NULL);
490 g_free(s->cursor.shape_str);
491 g_free(s->cursor.current_shape_str);
499}
500
501
502// ================================================================================
503// gui functions:
504// ================================================================================
505
506gboolean dt_control_configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
507{
508 // re-configure all components:
509 dt_view_manager_configure(darktable.view_manager, event->width, event->height);
510 return TRUE;
511}
512
513static GdkRGBA lookup_color(GtkStyleContext *context, const char *name)
514{
515 GdkRGBA color, fallback = {1.0, 0.0, 0.0, 1.0};
516 if(!gtk_style_context_lookup_color (context, name, &color))
517 color = fallback;
518 return color;
519}
520
521void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
522{
523 PangoRectangle ink;
524 PangoLayout *layout;
525 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
526 const float fontsize = DT_PIXEL_APPLY_DPI(14);
527 pango_font_description_set_absolute_size(desc, fontsize * PANGO_SCALE);
528 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
529 layout = pango_cairo_create_layout(cr);
530 pango_layout_set_font_description(layout, desc);
531 pango_layout_set_text(layout, darktable.main_message ? darktable.main_message : _("Working..."), -1);
532 pango_layout_get_pixel_extents(layout, &ink, NULL);
533 if(ink.width > width * 0.98)
534 {
535 pango_layout_set_text(layout, "...", -1);
536 pango_layout_get_pixel_extents(layout, &ink, NULL);
537 }
538 const float xc = width / 2.0, yc = height * 0.85 - DT_PIXEL_APPLY_DPI(30), wd = ink.width * .5f;
539 cairo_move_to(cr, xc - wd, yc + 1. / 3. * fontsize - fontsize);
540 pango_cairo_layout_path(cr, layout);
541 cairo_set_line_width(cr, 2.0);
543 cairo_stroke_preserve(cr);
545 cairo_fill(cr);
546 pango_font_description_free(desc);
547 g_object_unref(layout);
548}
549
550void *dt_control_expose(void *voidptr)
551{
552 int pointerx, pointery;
553 if(IS_NULL_PTR(darktable.gui->surface)) return NULL;
557 gdk_window_get_device_position(gtk_widget_get_window(widget),
558 gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget))),
559 &pointerx, &pointery, NULL);
560
561 // create a gtk-independent surface to draw on
562 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
563 cairo_t *cr = cairo_create(cst);
564
565 // TODO: control_expose: only redraw the part not overlapped by temporary control panel show!
566 //
569
570 GtkStyleContext *context = gtk_widget_get_style_context(widget);
571
572 // look up some colors once
573 GdkRGBA bg_color = lookup_color(context, "bg_color");
574
575 gdk_cairo_set_source_rgba(cr, &bg_color);
576 cairo_save(cr);
577 cairo_rectangle(cr, 0, 0, width, height);
578 cairo_paint(cr);
579 cairo_clip(cr);
580 cairo_new_path(cr);
581 // draw view
582 dt_view_manager_expose(darktable.view_manager, cr, width, height, pointerx, pointery);
583 cairo_restore(cr);
584
585 // draw busy indicator
587
588 if(darktable.control->log_busy > 0)
589 {
591 // force set the cursor to arrow with busy indicator
593 }
594 else // Apply cursor change
596
598
599 // Draw progress bar (guard during early startup)
601 {
602 const float progress_h = DT_PIXEL_APPLY_DPI(5);
603 cairo_rectangle(cr, 0, height - progress_h,
605 progress_h);
606 cairo_set_source_rgba(cr, 0., 0., 0., 0.33);
607 cairo_fill(cr);
608 }
609 cairo_destroy(cr);
610
611 cairo_t *cr_pixmap = cairo_create(darktable.gui->surface);
612 cairo_set_source_surface(cr_pixmap, cst, 0, 0);
613 cairo_paint(cr_pixmap);
614 cairo_destroy(cr_pixmap);
615
616 cairo_surface_destroy(cst);
617 return NULL;
618}
619
624
629
630void dt_control_mouse_moved(double x, double y, double pressure, int which)
631{
633}
634
635void dt_control_key_pressed(GdkEventKey *event)
636{
638}
639
640void dt_control_button_released(double x, double y, int which, uint32_t state)
641{
644
646}
647
649{
654 gtk_widget_set_tooltip_text(widget, "");
655}
656
657static gboolean _dt_ctl_switch_mode_to(gpointer user_data)
658{
659 const char *mode = (const char*)user_data;
662 return FALSE;
663}
664
665static gboolean _dt_ctl_switch_mode_to_by_view(gpointer user_data)
666{
667 const dt_view_t *view = (const dt_view_t*)user_data;
670 return FALSE;
671}
672
673void dt_ctl_switch_mode_to(const char *mode)
674{
676 if(current_view && !strcmp(mode, current_view->module_name))
677 {
678 // if we are not in lighttable, we switch back to that view
679 if(strcmp(current_view->module_name, "lighttable")) dt_ctl_switch_mode_to("lighttable");
680 return;
681 }
682
683 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to, (gpointer)mode);
684}
685
687{
689 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to_by_view, (gpointer)view);
690}
691
692void dt_ctl_reload_view(const char *mode)
693{
695 if(current_view && g_strcmp0(current_view->module_name, "lighttable"))
696 dt_ctl_switch_mode_to("lighttable");
697 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to, (gpointer)mode);
698}
699
710
721
722void dt_control_button_pressed(double x, double y, double pressure, int which, int type, uint32_t state)
723{
729 // adding pressure to this data structure is not needed right now. should the need ever arise: here is the
730 // place to do it :)
731 //const float wd = darktable.control->width;
732 const float ht = darktable.control->height;
733
734 // ack log message:
736 const float /*xc = wd/4.0-20,*/ yc = ht * 0.85 + 10;
738 if(which == 1 /*&& x > xc - 10 && x < xc + 10*/ && y > yc - 10 && y < yc + 10)
739 {
741 {
742 g_source_remove(darktable.control->log_message_timeout_id);
744 }
747 return;
748 }
750
751 // ack toast message:
754 if(which == 1 /*&& x > xc - 10 && x < xc + 10*/ && y > yc - 10 && y < yc + 10)
755 {
757 {
760 }
763 return;
764 }
766
768}
769
770static gboolean _redraw_center(gpointer user_data)
771{
774 return FALSE; // don't call this again
775}
776
777void dt_control_log(const char *msg, ...)
778{
780 va_list ap;
781 va_start(ap, msg);
782 char *escaped_msg = g_markup_vprintf_escaped(msg, ap);
783 const int msglen = strlen(escaped_msg);
785 dt_free(escaped_msg);
786 va_end(ap);
788 g_source_remove(darktable.control->log_message_timeout_id);
791
793 = g_timeout_add(DT_CTL_LOG_TIMEOUT + 1000 * (msglen / 40),
796 // redraw center later in gui thread:
797 g_idle_add(_redraw_center, 0);
798}
799
800static void _toast_log(const gboolean markup, const char *msg, va_list ap)
801{
803
804 // if we don't want markup, we escape <>&... so they are not interpreted later
805 if(markup)
807 else
808 {
809 char *escaped_msg = g_markup_vprintf_escaped(msg, ap);
811 dt_free(escaped_msg);
812 }
813
820 // redraw center later in gui thread:
821 g_idle_add(_redraw_center, 0);
822}
823
824void dt_toast_log(const char *msg, ...)
825{
826 va_list ap;
827 va_start(ap, msg);
828 _toast_log(FALSE, msg, ap);
829 va_end(ap);
830}
831
832void dt_toast_markup_log(const char *msg, ...)
833{
834 va_list ap;
835 va_start(ap, msg);
836 _toast_log(TRUE, msg, ap);
837 va_end(ap);
838}
839
847
855
863
871
876
881
886
891
896
901
902static int _widget_queue_draw(void *widget_data)
903{
904 dt_control_redraw_widget_t *const redraw_widget = (dt_control_redraw_widget_t *)widget_data;
905 GtkWidget *const widget = GTK_WIDGET(g_weak_ref_get(&redraw_widget->widget_ref));
906 if(!IS_NULL_PTR(widget))
907 {
908 gtk_widget_queue_draw(widget);
909 g_object_unref(widget);
910 }
911
912 return FALSE;
913}
914
915static void _widget_queue_draw_cleanup(void *widget_data)
916{
917 dt_control_redraw_widget_t *const redraw_widget = (dt_control_redraw_widget_t *)widget_data;
918 g_weak_ref_clear(&redraw_widget->widget_ref);
919 dt_free(redraw_widget);
920}
921
923{
925 {
926 dt_control_redraw_widget_t *const redraw_widget = calloc(1, sizeof(dt_control_redraw_widget_t));
927 if(IS_NULL_PTR(redraw_widget)) return;
928
929 g_weak_ref_init(&redraw_widget->widget_ref, G_OBJECT(widget));
930 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, _widget_queue_draw, redraw_widget, _widget_queue_draw_cleanup);
931 }
932}
933
934void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
935{
937}
938
946
948{
951 {
953
954 // If we reset mouse_over_id to -1, aka "none" signal,
955 // reset also the keyboard_over_id, in a "loose focus" way,
956 // to keep only the selection for common/act_on.h
960 }
961 else
963}
964
972
979
980// clang-format off
981// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
982// vim: shiftwidth=2 expandtab tabstop=2 cindent
983// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
984// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
int type
char * name
void dt_ctl_switch_mode_to(const char *mode)
Definition control.c:673
static int _widget_queue_draw(void *widget_data)
Definition control.c:902
void dt_control_set_cursor_visible_EXT(gboolean visible, const char *file, int line)
Set whether the cursor should be visible or not.
Definition control.c:416
void dt_control_key_pressed(GdkEventKey *event)
Definition control.c:635
static void _dt_ctl_switch_mode_prepare()
Definition control.c:648
void dt_control_init(dt_control_t *s)
Definition control.c:95
static void _control_set_cursor_on_widget(GtkWidget *widget, GdkCursor *cursor)
Definition control.c:234
int32_t dt_control_get_mouse_over_id()
Definition control.c:939
void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
Definition control.c:521
void dt_control_mouse_is_dragging(gboolean state)
Definition control.c:423
gchar * _get_cursor_name(dt_cursor_t cursor)
Definition control.c:134
void dt_control_button_pressed(double x, double y, double pressure, int which, int type, uint32_t state)
Definition control.c:722
void * dt_control_expose(void *voidptr)
Definition control.c:550
void dt_toast_log(const char *msg,...)
Definition control.c:824
void dt_ctl_switch_mode_to_by_view(const dt_view_t *view)
Definition control.c:686
void dt_control_queue_cursor_EXT(dt_cursor_t cursor, const char *file, int line)
Definition control.c:376
void dt_control_get_pointer_input(dt_control_pointer_input_t *input)
Definition control.c:89
void dt_control_set_mouse_over_id(int32_t value)
Definition control.c:947
static gboolean _redraw_center(gpointer user_data)
Definition control.c:770
void dt_control_forbid_change_cursor()
Definition control.c:224
void dt_toast_markup_log(const char *msg,...)
Definition control.c:832
void dt_control_log(const char *msg,...)
Definition control.c:777
static GdkRGBA lookup_color(GtkStyleContext *context, const char *name)
Definition control.c:513
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
static gboolean _dt_ctl_switch_mode_to_by_view(gpointer user_data)
Definition control.c:665
void dt_control_quit()
Definition control.c:443
void dt_control_mouse_leave()
Definition control.c:620
void dt_control_toast_busy_enter()
Definition control.c:848
void dt_control_shutdown(dt_control_t *s)
Definition control.c:456
void dt_control_log_busy_leave()
Definition control.c:856
void dt_control_mouse_is_painting(gboolean state)
Definition control.c:428
gboolean dt_control_configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
Definition control.c:506
void dt_control_change_cursor_by_name_and_flush(const char *curs_str)
Apply a named cursor immediatelly and flush display updates for immediate feedback.
Definition control.c:326
void dt_control_cleanup(dt_control_t *s)
Definition control.c:484
void dt_control_button_released(double x, double y, int which, uint32_t state)
Definition control.c:640
void dt_control_commit_cursor()
Definition control.c:334
void dt_control_set_pointer_input(const dt_control_pointer_input_t *input)
Definition control.c:83
static void _control_store_current_cursor(const dt_cursor_t shape, const char *shape_str)
Definition control.c:258
void dt_control_change_cursor_by_name(const char *curs_str)
Apply a GTK named cursor without changing the queued cursor.
Definition control.c:274
void dt_control_allow_change_cursor()
Definition control.c:229
int dt_control_running()
Definition control.c:433
static void _control_apply_cursor(GdkCursor *cursor)
Definition control.c:242
void dt_control_log_busy_enter()
Definition control.c:840
static dt_control_pointer_input_t _pointer_input
Definition control.c:81
void dt_control_mouse_moved(double x, double y, double pressure, int which)
Definition control.c:630
void dt_ctl_reload_view(const char *mode)
Definition control.c:692
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
Definition control.c:922
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:934
int32_t dt_control_get_keyboard_over_id()
Definition control.c:965
void dt_control_toast_redraw()
request redraw of the toast widget. This redraws the message label.
Definition control.c:892
void dt_control_mouse_enter()
Definition control.c:625
void dt_control_change_cursor_EXT(dt_cursor_t cursor, const char *file, int line)
Definition control.c:345
void dt_control_log_redraw()
request redraw of the log widget. This redraws the message label.
Definition control.c:887
static gboolean _dt_ctl_toast_message_timeout_callback(gpointer data)
Definition control.c:711
void dt_control_navigation_redraw()
request redraw of the navigation widget. This redraws the wiget of the navigation module.
Definition control.c:882
static void _widget_queue_draw_cleanup(void *widget_data)
Definition control.c:915
void dt_control_toast_busy_leave()
Definition control.c:864
void dt_control_queue_cursor_by_name(const char *curs_str)
Queue a GTK named cursor for the next cursor commit.
Definition control.c:398
static void _toast_log(const gboolean markup, const char *msg, va_list ap)
Definition control.c:800
static gboolean _dt_ctl_log_message_timeout_callback(gpointer data)
Definition control.c:700
static gboolean _dt_ctl_switch_mode_to(gpointer user_data)
Definition control.c:657
void dt_control_queue_redraw()
request redraw of the workspace. This redraws the whole workspace within a gdk critical section to pr...
Definition control.c:872
void dt_control_set_keyboard_over_id(int32_t value)
Definition control.c:973
#define DT_CTL_LOG_TIMEOUT
Definition control.h:205
#define DT_CTL_TOAST_SIZE
Definition control.h:206
#define DT_CTL_TOAST_MSG_SIZE
Definition control.h:207
#define dt_control_change_cursor(cursor)
Definition control.h:116
#define DT_CTL_LOG_SIZE
Definition control.h:203
#define DT_CTL_LOG_MSG_SIZE
Definition control.h:204
#define DT_CTL_TOAST_TIMEOUT
Definition control.h:208
GdkCursorType dt_cursor_t
Definition control.h:70
uint32_t view(const dt_view_t *self)
Definition darkroom.c:228
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
@ DT_DEBUG_VERBOSE
Definition darktable.h:765
@ DT_DEBUG_CONTROL
Definition darktable.h:738
#define dt_free(ptr)
Definition darktable.h:478
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
#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 darktable.h:293
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:369
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:389
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
void dt_gui_gtk_set_source_rgb(cairo_t *cr, dt_gui_color_t color)
Definition gtk.c:523
void dt_gui_gtk_quit()
Definition gtk.c:535
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
@ DT_GUI_COLOR_LOG_FG
Definition gtk.h:151
@ DT_GUI_COLOR_LOG_BG
Definition gtk.h:150
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
static int dt_cairo_image_surface_get_width(cairo_surface_t *surface)
Definition gtk.h:340
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
GtkWidget * dt_ui_center_base(dt_ui_t *ui)
static int dt_cairo_image_surface_get_height(cairo_surface_t *surface)
Definition gtk.h:344
static const float x
void dt_control_jobs_drain(dt_control_t *control)
Definition jobs.c:673
void dt_control_jobs_cleanup(dt_control_t *control)
Definition jobs.c:696
void dt_control_jobs_init(dt_control_t *control)
Definition jobs.c:640
#define DT_CTL_WORKER_RESERVED
Definition jobs.h:37
float *const restrict const size_t k
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_CONTROL_REDRAW_ALL
This signal is raised when dt_control_queue_redraw() is called. no param, no returned value.
Definition signal.h:69
@ DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE
This signal is raised when mouse hovers over image thumbs both on lighttable and in the filmstrip....
Definition signal.h:59
@ DT_SIGNAL_CONTROL_TOAST_REDRAW
This signal is raised when dt_control_toast_redraw() is called. no param, no returned value.
Definition signal.h:288
@ DT_SIGNAL_CONTROL_REDRAW_CENTER
This signal is raised when dt_control_queue_redraw_center() is called. no param, no returned value.
Definition signal.h:74
@ DT_SIGNAL_CONTROL_LOG_REDRAW
This signal is raised when dt_control_log_redraw() is called. no param, no returned value.
Definition signal.h:283
@ DT_SIGNAL_CONTROL_NAVIGATION_REDRAW
This signal is raised when dt_control_navigation_redraw() is called. no param, no returned value.
Definition signal.h:278
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
int32_t unmuted
Definition darktable.h:788
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_view_manager_t * view_manager
Definition darktable.h:800
char * main_message
Definition darktable.h:865
struct dt_control_t * control
Definition darktable.h:801
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
pthread_t * thread
Definition control.h:263
dt_pthread_mutex_t mutex
Definition control.h:280
dt_pthread_mutex_t run_mutex
Definition control.h:260
int32_t height
Definition control.h:218
struct dt_control_t::@12 cursor
int32_t num_threads
Definition control.h:262
int32_t running
Definition control.h:258
int button_down_which
Definition control.h:220
dt_cursor_t shape
Definition control.h:231
double button_y
Definition control.h:221
int button_type
Definition control.h:220
struct dt_control_t::@13 progress_system
int toast_ack
Definition control.h:247
char toast_message[10][300]
Definition control.h:248
gchar * shape_str
Definition control.h:233
gchar * current_shape_str
Definition control.h:234
int toast_busy
Definition control.h:250
pthread_t thread_res[DT_CTL_WORKER_RESERVED]
Definition control.h:272
dt_pthread_mutex_t log_mutex
Definition control.h:244
int32_t width
Definition control.h:218
pthread_cond_t cond
Definition control.h:261
guint log_message_timeout_id
Definition control.h:242
guint toast_message_timeout_id
Definition control.h:249
dt_pthread_mutex_t res_mutex
Definition control.h:269
dt_cursor_t current_shape
Definition control.h:232
int toast_pos
Definition control.h:247
pthread_t kick_on_workers_thread
Definition control.h:263
int32_t mouse_over_id
Definition control.h:223
dt_pthread_mutex_t queue_mutex
Definition control.h:260
dt_pthread_mutex_t toast_mutex
Definition control.h:251
int32_t keyboard_over_id
Definition control.h:224
char log_message[10][1000]
Definition control.h:241
double button_x
Definition control.h:221
dt_pthread_mutex_t cond_mutex
Definition control.h:260
dt_pthread_mutex_t global_mutex
Definition control.h:254
gboolean hide
Definition control.h:236
gboolean lock
Definition control.h:229
pthread_t gui_thread
Definition control.h:219
int button_down
Definition control.h:220
int completed
Definition develop.h:499
struct dt_develop_t::@26 progress
gboolean is_dragging
Definition gtk.h:218
int32_t center_tooltip
Definition gtk.h:180
cairo_surface_t * surface
Definition gtk.h:169
gboolean is_painting
Definition gtk.h:219
dt_ui_t * ui
Definition gtk.h:165
struct dt_gui_gtk_t::@49 mouse
char module_name[64]
Definition view.h:154
GtkWidget * window
void dt_view_manager_mouse_moved(dt_view_manager_t *vm, double x, double y, double pressure, int which)
Definition view.c:521
void dt_view_manager_mouse_enter(dt_view_manager_t *vm)
Definition view.c:515
void dt_view_manager_configure(dt_view_manager_t *vm, int width, int height)
Definition view.c:629
int dt_view_manager_button_released(dt_view_manager_t *vm, double x, double y, int which, uint32_t state)
Definition view.c:570
void dt_view_manager_expose(dt_view_manager_t *vm, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition view.c:446
int dt_view_manager_key_pressed(dt_view_manager_t *vm, GdkEventKey *event)
Definition view.c:543
int dt_view_manager_button_pressed(dt_view_manager_t *vm, double x, double y, double pressure, int which, int type, uint32_t state)
Definition view.c:597
int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
Definition view.c:236
int dt_view_manager_switch_by_view(dt_view_manager_t *vm, const dt_view_t *nv)
Definition view.c:258
void dt_view_manager_mouse_leave(dt_view_manager_t *vm)
Definition view.c:493
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:141
void dt_hinter_set_message(dt_ui_t *ui, const char *message)