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
424{
425 // FIXME: when shutdown, run_mutex is not inited anymore!
428 int running = s->running;
430 return running;
431}
432
434{
436 // thread safe quit, 1st pass:
442
443 if(gtk_main_level() > 0) gtk_main_quit();
444}
445
447{
450 s->running = 0;
453 pthread_cond_broadcast(&s->cond);
454
455 /* then wait for kick_on_workers_thread */
456 pthread_join(s->kick_on_workers_thread, NULL);
457
458 int k;
459 for(k = 0; k < s->num_threads; k++)
460 // pthread_kill(s->thread[k], 9);
461 pthread_join(s->thread[k], NULL);
462 for(k = 0; k < DT_CTL_WORKER_RESERVED; k++)
463 // pthread_kill(s->thread_res[k], 9);
464 pthread_join(s->thread_res[k], NULL);
465
466}
467
469{
470 // vacuum TODO: optional?
471 // DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "PRAGMA incremental_vacuum(0)", NULL, NULL, NULL);
472 // DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "vacuum", NULL, NULL, NULL);
474 g_free(s->cursor.shape_str);
475 g_free(s->cursor.current_shape_str);
483}
484
485
486// ================================================================================
487// gui functions:
488// ================================================================================
489
490gboolean dt_control_configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
491{
492 // re-configure all components:
493 dt_view_manager_configure(darktable.view_manager, event->width, event->height);
494 return TRUE;
495}
496
497static GdkRGBA lookup_color(GtkStyleContext *context, const char *name)
498{
499 GdkRGBA color, fallback = {1.0, 0.0, 0.0, 1.0};
500 if(!gtk_style_context_lookup_color (context, name, &color))
501 color = fallback;
502 return color;
503}
504
505void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
506{
507 PangoRectangle ink;
508 PangoLayout *layout;
509 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
510 const float fontsize = DT_PIXEL_APPLY_DPI(14);
511 pango_font_description_set_absolute_size(desc, fontsize * PANGO_SCALE);
512 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
513 layout = pango_cairo_create_layout(cr);
514 pango_layout_set_font_description(layout, desc);
515 pango_layout_set_text(layout, darktable.main_message ? darktable.main_message : _("Working..."), -1);
516 pango_layout_get_pixel_extents(layout, &ink, NULL);
517 if(ink.width > width * 0.98)
518 {
519 pango_layout_set_text(layout, "...", -1);
520 pango_layout_get_pixel_extents(layout, &ink, NULL);
521 }
522 const float xc = width / 2.0, yc = height * 0.85 - DT_PIXEL_APPLY_DPI(30), wd = ink.width * .5f;
523 cairo_move_to(cr, xc - wd, yc + 1. / 3. * fontsize - fontsize);
524 pango_cairo_layout_path(cr, layout);
525 cairo_set_line_width(cr, 2.0);
527 cairo_stroke_preserve(cr);
529 cairo_fill(cr);
530 pango_font_description_free(desc);
531 g_object_unref(layout);
532}
533
534void *dt_control_expose(void *voidptr)
535{
536 int pointerx, pointery;
537 if(IS_NULL_PTR(darktable.gui->surface)) return NULL;
541 gdk_window_get_device_position(gtk_widget_get_window(widget),
542 gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget))),
543 &pointerx, &pointery, NULL);
544
545 // create a gtk-independent surface to draw on
546 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
547 cairo_t *cr = cairo_create(cst);
548
549 // TODO: control_expose: only redraw the part not overlapped by temporary control panel show!
550 //
553
554 GtkStyleContext *context = gtk_widget_get_style_context(widget);
555
556 // look up some colors once
557 GdkRGBA bg_color = lookup_color(context, "bg_color");
558
559 gdk_cairo_set_source_rgba(cr, &bg_color);
560 cairo_save(cr);
561 cairo_rectangle(cr, 0, 0, width, height);
562 cairo_paint(cr);
563 cairo_clip(cr);
564 cairo_new_path(cr);
565 // draw view
566 dt_view_manager_expose(darktable.view_manager, cr, width, height, pointerx, pointery);
567 cairo_restore(cr);
568
569 // draw busy indicator
571
572 if(darktable.control->log_busy > 0)
573 {
575 // force set the cursor to arrow with busy indicator
577 }
578 else // Apply cursor change
580
582
583 // Draw progress bar (guard during early startup)
585 {
586 const float progress_h = DT_PIXEL_APPLY_DPI(5);
587 cairo_rectangle(cr, 0, height - progress_h,
589 progress_h);
590 cairo_set_source_rgba(cr, 0., 0., 0., 0.33);
591 cairo_fill(cr);
592 }
593 cairo_destroy(cr);
594
595 cairo_t *cr_pixmap = cairo_create(darktable.gui->surface);
596 cairo_set_source_surface(cr_pixmap, cst, 0, 0);
597 cairo_paint(cr_pixmap);
598 cairo_destroy(cr_pixmap);
599
600 cairo_surface_destroy(cst);
601 return NULL;
602}
603
608
613
614void dt_control_mouse_moved(double x, double y, double pressure, int which)
615{
617}
618
619void dt_control_key_pressed(GdkEventKey *event)
620{
622}
623
624void dt_control_button_released(double x, double y, int which, uint32_t state)
625{
628
630}
631
633{
638 gtk_widget_set_tooltip_text(widget, "");
639}
640
641static gboolean _dt_ctl_switch_mode_to(gpointer user_data)
642{
643 const char *mode = (const char*)user_data;
646 return FALSE;
647}
648
649static gboolean _dt_ctl_switch_mode_to_by_view(gpointer user_data)
650{
651 const dt_view_t *view = (const dt_view_t*)user_data;
654 return FALSE;
655}
656
657void dt_ctl_switch_mode_to(const char *mode)
658{
660 if(current_view && !strcmp(mode, current_view->module_name))
661 {
662 // if we are not in lighttable, we switch back to that view
663 if(strcmp(current_view->module_name, "lighttable")) dt_ctl_switch_mode_to("lighttable");
664 return;
665 }
666
667 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to, (gpointer)mode);
668}
669
671{
673 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to_by_view, (gpointer)view);
674}
675
676void dt_ctl_reload_view(const char *mode)
677{
679 if(current_view && g_strcmp0(current_view->module_name, "lighttable"))
680 dt_ctl_switch_mode_to("lighttable");
681 g_main_context_invoke(NULL, _dt_ctl_switch_mode_to, (gpointer)mode);
682}
683
694
705
706void dt_control_button_pressed(double x, double y, double pressure, int which, int type, uint32_t state)
707{
713 // adding pressure to this data structure is not needed right now. should the need ever arise: here is the
714 // place to do it :)
715 //const float wd = darktable.control->width;
716 const float ht = darktable.control->height;
717
718 // ack log message:
720 const float /*xc = wd/4.0-20,*/ yc = ht * 0.85 + 10;
722 if(which == 1 /*&& x > xc - 10 && x < xc + 10*/ && y > yc - 10 && y < yc + 10)
723 {
725 {
726 g_source_remove(darktable.control->log_message_timeout_id);
728 }
731 return;
732 }
734
735 // ack toast message:
738 if(which == 1 /*&& x > xc - 10 && x < xc + 10*/ && y > yc - 10 && y < yc + 10)
739 {
741 {
744 }
747 return;
748 }
750
752}
753
754static gboolean _redraw_center(gpointer user_data)
755{
758 return FALSE; // don't call this again
759}
760
761void dt_control_log(const char *msg, ...)
762{
764 va_list ap;
765 va_start(ap, msg);
766 char *escaped_msg = g_markup_vprintf_escaped(msg, ap);
767 const int msglen = strlen(escaped_msg);
769 dt_free(escaped_msg);
770 va_end(ap);
772 g_source_remove(darktable.control->log_message_timeout_id);
775
777 = g_timeout_add(DT_CTL_LOG_TIMEOUT + 1000 * (msglen / 40),
780 // redraw center later in gui thread:
781 g_idle_add(_redraw_center, 0);
782}
783
784static void _toast_log(const gboolean markup, const char *msg, va_list ap)
785{
787
788 // if we don't want markup, we escape <>&... so they are not interpreted later
789 if(markup)
791 else
792 {
793 char *escaped_msg = g_markup_vprintf_escaped(msg, ap);
795 dt_free(escaped_msg);
796 }
797
804 // redraw center later in gui thread:
805 g_idle_add(_redraw_center, 0);
806}
807
808void dt_toast_log(const char *msg, ...)
809{
810 va_list ap;
811 va_start(ap, msg);
812 _toast_log(FALSE, msg, ap);
813 va_end(ap);
814}
815
816void dt_toast_markup_log(const char *msg, ...)
817{
818 va_list ap;
819 va_start(ap, msg);
820 _toast_log(TRUE, msg, ap);
821 va_end(ap);
822}
823
831
839
847
855
860
865
870
875
880
885
886static int _widget_queue_draw(void *widget_data)
887{
888 dt_control_redraw_widget_t *const redraw_widget = (dt_control_redraw_widget_t *)widget_data;
889 GtkWidget *const widget = GTK_WIDGET(g_weak_ref_get(&redraw_widget->widget_ref));
890 if(!IS_NULL_PTR(widget))
891 {
892 gtk_widget_queue_draw(widget);
893 g_object_unref(widget);
894 }
895
896 return FALSE;
897}
898
899static void _widget_queue_draw_cleanup(void *widget_data)
900{
901 dt_control_redraw_widget_t *const redraw_widget = (dt_control_redraw_widget_t *)widget_data;
902 g_weak_ref_clear(&redraw_widget->widget_ref);
903 dt_free(redraw_widget);
904}
905
907{
909 {
910 dt_control_redraw_widget_t *const redraw_widget = calloc(1, sizeof(dt_control_redraw_widget_t));
911 if(IS_NULL_PTR(redraw_widget)) return;
912
913 g_weak_ref_init(&redraw_widget->widget_ref, G_OBJECT(widget));
914 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, _widget_queue_draw, redraw_widget, _widget_queue_draw_cleanup);
915 }
916}
917
918void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
919{
921}
922
930
932{
935 {
937
938 // If we reset mouse_over_id to -1, aka "none" signal,
939 // reset also the keyboard_over_id, in a "loose focus" way,
940 // to keep only the selection for common/act_on.h
944 }
945 else
947}
948
956
963
964// clang-format off
965// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
966// vim: shiftwidth=2 expandtab tabstop=2 cindent
967// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
968// 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:657
static int _widget_queue_draw(void *widget_data)
Definition control.c:886
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:619
static void _dt_ctl_switch_mode_prepare()
Definition control.c:632
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:923
void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
Definition control.c:505
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:706
void * dt_control_expose(void *voidptr)
Definition control.c:534
void dt_toast_log(const char *msg,...)
Definition control.c:808
void dt_ctl_switch_mode_to_by_view(const dt_view_t *view)
Definition control.c:670
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:931
static gboolean _redraw_center(gpointer user_data)
Definition control.c:754
void dt_control_forbid_change_cursor()
Definition control.c:224
void dt_toast_markup_log(const char *msg,...)
Definition control.c:816
void dt_control_log(const char *msg,...)
Definition control.c:761
static GdkRGBA lookup_color(GtkStyleContext *context, const char *name)
Definition control.c:497
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:861
static gboolean _dt_ctl_switch_mode_to_by_view(gpointer user_data)
Definition control.c:649
void dt_control_quit()
Definition control.c:433
void dt_control_mouse_leave()
Definition control.c:604
void dt_control_toast_busy_enter()
Definition control.c:832
void dt_control_shutdown(dt_control_t *s)
Definition control.c:446
void dt_control_log_busy_leave()
Definition control.c:840
gboolean dt_control_configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
Definition control.c:490
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:468
void dt_control_button_released(double x, double y, int which, uint32_t state)
Definition control.c:624
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:423
static void _control_apply_cursor(GdkCursor *cursor)
Definition control.c:242
void dt_control_log_busy_enter()
Definition control.c:824
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:614
void dt_ctl_reload_view(const char *mode)
Definition control.c:676
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:906
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:918
int32_t dt_control_get_keyboard_over_id()
Definition control.c:949
void dt_control_toast_redraw()
request redraw of the toast widget. This redraws the message label.
Definition control.c:876
void dt_control_mouse_enter()
Definition control.c:609
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:871
static gboolean _dt_ctl_toast_message_timeout_callback(gpointer data)
Definition control.c:695
void dt_control_navigation_redraw()
request redraw of the navigation widget. This redraws the wiget of the navigation module.
Definition control.c:866
static void _widget_queue_draw_cleanup(void *widget_data)
Definition control.c:899
void dt_control_toast_busy_leave()
Definition control.c:848
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:784
static gboolean _dt_ctl_log_message_timeout_callback(gpointer data)
Definition control.c:684
static gboolean _dt_ctl_switch_mode_to(gpointer user_data)
Definition control.c:641
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:856
void dt_control_set_keyboard_over_id(int32_t value)
Definition control.c:957
#define DT_CTL_LOG_TIMEOUT
Definition control.h:201
#define DT_CTL_TOAST_SIZE
Definition control.h:202
#define DT_CTL_TOAST_MSG_SIZE
Definition control.h:203
#define dt_control_change_cursor(cursor)
Definition control.h:116
#define DT_CTL_LOG_SIZE
Definition control.h:199
#define DT_CTL_LOG_MSG_SIZE
Definition control.h:200
#define DT_CTL_TOAST_TIMEOUT
Definition control.h:204
GdkCursorType dt_cursor_t
Definition control.h:70
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_VERBOSE
Definition darktable.h:743
@ DT_DEBUG_CONTROL
Definition darktable.h:716
#define dt_free(ptr)
Definition darktable.h:456
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
#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:281
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
void dt_gui_gtk_set_source_rgb(cairo_t *cr, dt_gui_color_t color)
Definition gtk.c:442
void dt_gui_gtk_quit()
Definition gtk.c:454
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:316
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:334
#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:338
static const float x
void dt_control_jobs_cleanup(dt_control_t *control)
Definition jobs.c:673
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:347
@ 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:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
int32_t unmuted
Definition darktable.h:760
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_view_manager_t * view_manager
Definition darktable.h:772
char * main_message
Definition darktable.h:837
struct dt_control_t * control
Definition darktable.h:773
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
pthread_t * thread
Definition control.h:259
dt_pthread_mutex_t mutex
Definition control.h:276
dt_pthread_mutex_t run_mutex
Definition control.h:256
int32_t height
Definition control.h:214
struct dt_control_t::@12 cursor
int32_t num_threads
Definition control.h:258
int32_t running
Definition control.h:254
int button_down_which
Definition control.h:216
dt_cursor_t shape
Definition control.h:227
double button_y
Definition control.h:217
int button_type
Definition control.h:216
struct dt_control_t::@13 progress_system
int toast_ack
Definition control.h:243
char toast_message[10][300]
Definition control.h:244
gchar * shape_str
Definition control.h:229
gchar * current_shape_str
Definition control.h:230
int toast_busy
Definition control.h:246
pthread_t thread_res[DT_CTL_WORKER_RESERVED]
Definition control.h:268
dt_pthread_mutex_t log_mutex
Definition control.h:240
int32_t width
Definition control.h:214
pthread_cond_t cond
Definition control.h:257
guint log_message_timeout_id
Definition control.h:238
guint toast_message_timeout_id
Definition control.h:245
dt_pthread_mutex_t res_mutex
Definition control.h:265
dt_cursor_t current_shape
Definition control.h:228
int toast_pos
Definition control.h:243
pthread_t kick_on_workers_thread
Definition control.h:259
int32_t mouse_over_id
Definition control.h:219
dt_pthread_mutex_t queue_mutex
Definition control.h:256
dt_pthread_mutex_t toast_mutex
Definition control.h:247
int32_t keyboard_over_id
Definition control.h:220
char log_message[10][1000]
Definition control.h:237
double button_x
Definition control.h:217
dt_pthread_mutex_t cond_mutex
Definition control.h:256
dt_pthread_mutex_t global_mutex
Definition control.h:250
gboolean hide
Definition control.h:232
gboolean lock
Definition control.h:225
pthread_t gui_thread
Definition control.h:215
int button_down
Definition control.h:216
int completed
Definition develop.h:493
struct dt_develop_t::@26 progress
int32_t center_tooltip
Definition gtk.h:175
cairo_surface_t * surface
Definition gtk.h:168
dt_ui_t * ui
Definition gtk.h:164
char module_name[64]
Definition view.h:153
void dt_view_manager_mouse_moved(dt_view_manager_t *vm, double x, double y, double pressure, int which)
Definition view.c:520
void dt_view_manager_mouse_enter(dt_view_manager_t *vm)
Definition view.c:514
void dt_view_manager_configure(dt_view_manager_t *vm, int width, int height)
Definition view.c:628
int dt_view_manager_button_released(dt_view_manager_t *vm, double x, double y, int which, uint32_t state)
Definition view.c:569
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:445
int dt_view_manager_key_pressed(dt_view_manager_t *vm, GdkEventKey *event)
Definition view.c:542
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:596
int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
Definition view.c:235
int dt_view_manager_switch_by_view(dt_view_manager_t *vm, const dt_view_t *nv)
Definition view.c:257
void dt_view_manager_mouse_leave(dt_view_manager_t *vm)
Definition view.c:492
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:140
void dt_hinter_set_message(dt_ui_t *ui, const char *message)