Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
darkroom.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2014, 2018 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010 Stuart Henderson.
6 Copyright (C) 2010-2018, 2020 Tobias Ellinghaus.
7 Copyright (C) 2011-2012 Antony Dovgal.
8 Copyright (C) 2011 Edouard Gomez.
9 Copyright (C) 2011, 2013-2015 Jérémy Rosen.
10 Copyright (C) 2011 Karl Mikaelsson.
11 Copyright (C) 2011 Omari Stephens.
12 Copyright (C) 2011 Robert Bieber.
13 Copyright (C) 2011 Steven Carter.
14 Copyright (C) 2012-2016, 2019-2022 Aldric Renaudin.
15 Copyright (C) 2012 Christian Tellefsen.
16 Copyright (C) 2012 Frédéric Grollier.
17 Copyright (C) 2012-2013 José Carlos García Sogo.
18 Copyright (C) 2012-2022 Pascal Obry.
19 Copyright (C) 2012 Richard Wonka.
20 Copyright (C) 2012-2014 Ulrich Pegelow.
21 Copyright (C) 2013 Dennis Gnad.
22 Copyright (C) 2013 Pascal de Bruijn.
23 Copyright (C) 2013-2017, 2020 Roman Lebedev.
24 Copyright (C) 2014, 2017, 2020-2021 Dan Torop.
25 Copyright (C) 2014, 2017, 2019 parafin.
26 Copyright (C) 2014 Pedro Côrte-Real.
27 Copyright (C) 2014 Stéphane Gimenez.
28 Copyright (C) 2015 Guillaume Subiron.
29 Copyright (C) 2016 Asma.
30 Copyright (C) 2016 itinerarium.
31 Copyright (C) 2017-2019 Edgardo Hoszowski.
32 Copyright (C) 2017 Matthieu Moy.
33 Copyright (C) 2017-2019 Peter Budai.
34 Copyright (C) 2017 Žilvinas Žaltiena.
35 Copyright (C) 2018 Hans Rosenfeld.
36 Copyright (C) 2018 rawfiner.
37 Copyright (C) 2018 Rikard Öxler.
38 Copyright (C) 2019 Alexander Blinne.
39 Copyright (C) 2019 Alexis Mousset.
40 Copyright (C) 2019-2020, 2022-2026 Aurélien PIERRE.
41 Copyright (C) 2019, 2021 Bill Ferguson.
42 Copyright (C) 2019-2022 Diederik Ter Rahe.
43 Copyright (C) 2019-2022 Hanno Schwalm.
44 Copyright (C) 2019-2020 Heiko Bauke.
45 Copyright (C) 2019 jakubfi.
46 Copyright (C) 2019, 2021 luzpaz.
47 Copyright (C) 2019-2020 Philippe Weyland.
48 Copyright (C) 2020-2022 Chris Elston.
49 Copyright (C) 2020 GrahamByrnes.
50 Copyright (C) 2020-2021 Hubert Kowalski.
51 Copyright (C) 2020-2021 Marco.
52 Copyright (C) 2020-2021 Mark-64.
53 Copyright (C) 2020 Miloš Komarčević.
54 Copyright (C) 2020 Reinout Nonhebel.
55 Copyright (C) 2020 U-DESKTOP-HQME86J\marco.
56 Copyright (C) 2021 darkelectron.
57 Copyright (C) 2021 lhietal.
58 Copyright (C) 2021-2022 Nicolas Auffray.
59 Copyright (C) 2021-2022 Ralf Brown.
60 Copyright (C) 2021-2022 Sakari Kapanen.
61 Copyright (C) 2021 Victor Forsiuk.
62 Copyright (C) 2022 Martin Bařinka.
63 Copyright (C) 2023 Alynx Zhou.
64 Copyright (C) 2023 Luca Zulberti.
65 Copyright (C) 2023 Maurizio Paglia.
66 Copyright (C) 2023 Ricky Moon.
67 Copyright (C) 2025-2026 Guillaume Stutin.
68
69 darktable is free software: you can redistribute it and/or modify
70 it under the terms of the GNU General Public License as published by
71 the Free Software Foundation, either version 3 of the License, or
72 (at your option) any later version.
73
74 darktable is distributed in the hope that it will be useful,
75 but WITHOUT ANY WARRANTY; without even the implied warranty of
76 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77 GNU General Public License for more details.
78
79 You should have received a copy of the GNU General Public License
80 along with darktable. If not, see <http://www.gnu.org/licenses/>.
81*/
84#include "bauhaus/bauhaus.h"
85#include "common/collection.h"
86#include "common/colorspaces.h"
87#include "common/darktable.h"
88#include "gui/gdkkeys.h"
89#include "common/debug.h"
91#include "common/history.h"
92#include "common/image_cache.h"
93#include "common/imageio.h"
94#include "common/iop-autoset.h"
96#include "common/mipmap_cache.h"
97#include "common/selection.h"
98#include "common/tags.h"
99#include "common/undo.h"
100#include "control/conf.h"
101#include "control/control.h"
102#include "control/jobs.h"
103#include "develop/blend.h"
105#include "develop/develop.h"
106#include "develop/imageop.h"
107#include "develop/masks.h"
108#include "dtgtk/button.h"
109#include "dtgtk/thumbtable.h"
110
112#include "gui/draw.h"
113#include "gui/gtk.h"
114#include "gui/gui_throttle.h"
115#include "gui/guides.h"
116#include "gui/presets.h"
117#include "libs/colorpicker.h"
118#include "libs/lib.h"
119#include "views/view.h"
120#include "views/view_api.h"
121#ifdef GDK_WINDOWING_QUARTZ
122#include "osx/osx.h"
123#endif
124
125#include <gdk/gdkkeysyms.h>
126#include <glib.h>
127#include <math.h>
128#include <stdlib.h>
129#include <string.h>
130#include <unistd.h>
131#include <sys/types.h>
132#include <sys/stat.h>
133#include <fcntl.h>
134
135#ifndef G_SOURCE_FUNC // Defined for glib >= 2.58
136#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
137#endif
138
139DT_MODULE(1)
140
141typedef struct coords_t
142{
143 double roi_coord[2]; // coordinates in ROI space
144 double roi_delta[2]; // delta in ROI space
145 double full_delta[2]; // delta in image space
146 double full[2]; // coordinates in image space
148
149#define DARKROOM_EDGE_PAN_INTERVAL_MS 64
150#define DARKROOM_EDGE_PAN_MARGIN_PX DT_PIXEL_APPLY_DPI(100)
151#define DARKROOM_EDGE_PAN_SPEED_PX_PER_S 360.0f
152
154
155/* signal handler for filmstrip image switching */
156static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data);
157static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data);
158
159static void _dev_change_image(dt_view_t *self, const int32_t imgid);
161
162static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling);
163static void _release_expose_source_caches(void);
164static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y);
165
170
176static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data);
177static void _darkroom_autoset_button_set_running(const gboolean running);
178
179static void _darkroom_ioporder_quickbutton_clicked(GtkButton *button, gpointer user_data)
180{
181 dt_lib_module_t *module = dt_lib_get_module("ioporder");
182 if(module && module->show_popup)
183 module->show_popup(module);
184}
185
195static void _darkroom_autoset_button_set_running(const gboolean running)
196{
198 if(_darkroom_autoset_button_is_running == running) return;
199
201
202 gtk_widget_set_sensitive(_darkroom_autoset_button, !running);
203 gtk_widget_set_tooltip_text(_darkroom_autoset_button,
204 running ? _("Autoset is running on selected modules")
205 : _("Run autoset on selected modules\nRight click for options"));
206
207 if(running)
209 else
211}
212
213const char *name(const dt_view_t *self)
214{
215 return _("Darkroom");
216}
217
218
219void init(dt_view_t *self)
220{
221 self->data = malloc(sizeof(dt_develop_t));
222 dt_dev_init((dt_develop_t *)self->data, 1);
225}
226
227uint32_t view(const dt_view_t *self)
228{
229 return DT_VIEW_DARKROOM;
230}
231
232static void _reset_edge_pan()
233{
235 if(IS_NULL_PTR(gui)) return;
236 if(gui->pan_edge.timeout_source)
237 {
238 g_source_remove(gui->pan_edge.timeout_source);
239 }
240 memset(&gui->pan_edge, 0, sizeof(gui->pan_edge));
241}
242
244{
245 dt_develop_t *dev = (dt_develop_t *)self->data;
246
247 // Cancel any pending edge pan timeout and reset the state
249
254 {
256 {
258 "darkroom-cleanup-autoset-manager");
261 }
263 {
266 }
267 g_list_free(_autoset_manager->iop_to_set);
269 _autoset_manager = NULL;
270 }
274
275 // unref the grid lines popover if needed
279 dt_dev_cleanup(dev);
280 dt_free(dev);
281}
282
283static cairo_status_t _write_snapshot_data(void *closure, const unsigned char *data, unsigned int length)
284{
285 const int fd = GPOINTER_TO_INT(closure);
286 ssize_t res = write(fd, data, length);
287 if(res != length)
288 return CAIRO_STATUS_WRITE_ERROR;
289 return CAIRO_STATUS_SUCCESS;
290}
291
296
297static gboolean _darkroom_is_only_selected_sample(gboolean is_primary_sample, dt_colorpicker_sample_t *selected_sample, gboolean display_samples)
298{
299 return !is_primary_sample && selected_sample && !display_samples;
300}
301
303 float point[2])
304{
305 point[0] = sample->point[0];
306 point[1] = sample->point[1];
308}
309
311 float box[4])
312{
313 memcpy(box, sample->box, sizeof(float) * 4);
315}
316
329static void _darkroom_pickers_draw(dt_view_t *self, cairo_t *cri,
330 int32_t width, int32_t height, int32_t pozx, int32_t pozy,
331 GSList *samples, gboolean is_primary_sample)
332{
333 if(IS_NULL_PTR(samples)) return;
334
335 dt_develop_t *dev = (dt_develop_t *)self->data;
336
337 cairo_save(cri);
338 // The colorpicker samples bounding rectangle should only be displayed inside the visible image
339
340 const double wd = dev->roi.preview_width;
341 const double ht = dev->roi.preview_height;
342 const double scale = dt_dev_get_fit_scale(dev);
343 const double lw = 1.0 / scale;
344 const double dashes[1] = { lw * 4.0 };
345
346 dt_dev_rescale_roi(dev, cri, width, height);
347
348 // makes point sample crosshair gap look nicer
349 cairo_set_line_cap(cri, CAIRO_LINE_CAP_SQUARE);
350
352 const gboolean only_selected_sample
353 = _darkroom_is_only_selected_sample(is_primary_sample, selected_sample,
355
356 for( ; samples; samples = g_slist_next(samples))
357 {
358 dt_colorpicker_sample_t *sample = samples->data;
359 if(only_selected_sample && (sample != selected_sample))
360 continue;
361
362 // The picker is at the resolution of the preview pixelpipe. This
363 // is width/2 of a preview-pipe pixel in (scaled) user space
364 // coordinates. Use half pixel width so rounding to nearest device
365 // pixel doesn't make uneven centering.
366 double half_px = 0.5;
367 const double min_half_px_device = 4.0;
368 // FIXME: instead of going to all this effort to show how error-prone a preview pipe sample can be, just produce a better point sample
369 gboolean show_preview_pixel_scale = TRUE;
370
371 // overlays are aligned with pixels for a clean look
372 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
373 {
374 float image_box[4] = { 0.0f };
376 double x = image_box[0] * wd, y = image_box[1] * ht,
377 w = image_box[2] * wd, h = image_box[3] * ht;
378 cairo_user_to_device(cri, &x, &y);
379 cairo_user_to_device(cri, &w, &h);
380 x=round(x+0.5)-0.5;
381 y=round(y+0.5)-0.5;
382 w=round(w+0.5)-0.5;
383 h=round(h+0.5)-0.5;
384 cairo_device_to_user(cri, &x, &y);
385 cairo_device_to_user(cri, &w, &h);
386 cairo_rectangle(cri, x, y, w - x, h - y);
387 if(is_primary_sample)
388 {
389 // handles
390 const double hw = 5. / scale;
391 cairo_rectangle(cri, x - hw, y - hw, 2. * hw, 2. * hw);
392 cairo_rectangle(cri, x - hw, h - hw, 2. * hw, 2. * hw);
393 cairo_rectangle(cri, w - hw, y - hw, 2. * hw, 2. * hw);
394 cairo_rectangle(cri, w - hw, h - hw, 2. * hw, 2. * hw);
395 }
396 }
397 else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
398 {
399 // FIXME: to be really accurate, the colorpicker should render precisely over the nearest pixelpipe pixel, but this gets particularly tricky to do with iop pickers with transformations after them in the pipeline
400 float image_point[2] = { 0.0f };
401 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
402 double x = image_point[0] * wd;
403 double y = image_point[1] * ht;
404 cairo_user_to_device(cri, &x, &y);
405 x=round(x+0.5)-0.5;
406 y=round(y+0.5)-0.5;
407 // render picker center a reasonable size in device pixels
408 half_px = round(half_px * scale);
409 if(half_px < min_half_px_device)
410 {
411 half_px = min_half_px_device;
412 show_preview_pixel_scale = FALSE;
413 }
414 // crosshair radius
415 double crosshair = (is_primary_sample ? 4. : 5.) * half_px;
416 if(sample == selected_sample) crosshair *= 2;
417 cairo_device_to_user(cri, &x, &y);
418 cairo_device_to_user_distance(cri, &crosshair, &half_px);
419
420 // "handles"
421 if(is_primary_sample)
422 cairo_arc(cri, x, y, crosshair, 0., 2. * M_PI);
423 // crosshair
424 cairo_move_to(cri, x - crosshair, y);
425 cairo_line_to(cri, x + crosshair, y);
426 cairo_move_to(cri, x, y - crosshair);
427 cairo_line_to(cri, x, y + crosshair);
428 }
429
430 // default is to draw 1 (logical) pixel light lines with 1
431 // (logical) pixel dark outline for legibility
432 const double line_scale = (sample == selected_sample ? 2.0 : 1.0);
433 cairo_set_line_width(cri, lw * 3.0 * line_scale);
434 cairo_set_source_rgba(cri, 0.0, 0.0, 0.0, 0.4);
435 cairo_stroke_preserve(cri);
436
437 const gboolean draw_dashed = !is_primary_sample
438 && sample != selected_sample
439 && sample->size == DT_LIB_COLORPICKER_SIZE_BOX;
440 cairo_set_line_width(cri, lw * line_scale);
441 cairo_set_dash(cri, dashes, draw_dashed, 0.0);
442
443 cairo_set_source_rgba(cri, 1.0, 1.0, 1.0, 0.8);
444 cairo_stroke(cri);
445
446 // draw the actual color sampled
447 // FIXME: if an area sample is selected, when selected should fill it with colorpicker color?
448 // NOTE: The sample may be based on outdated data, but still
449 // display as it will update eventually. If we only drew on valid
450 // data, swatches on point live samples would flicker when the
451 // primary sample was drawn, and the primary sample swatch would
452 // flicker when an iop is adjusted.
454 {
455 float image_point[2] = { 0.0f };
456 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
457 if(sample == selected_sample)
458 cairo_arc(cri, image_point[0] * wd, image_point[1] * ht, half_px * 2., 0., 2. * M_PI);
459 else if(show_preview_pixel_scale)
460 cairo_rectangle(cri, image_point[0] * wd - half_px, image_point[1] * ht - half_px, half_px * 2., half_px * 2.);
461 else
462 cairo_arc(cri, image_point[0] * wd, image_point[1] * ht, half_px, 0., 2. * M_PI);
463
464 set_color(cri, sample->swatch);
465 cairo_fill(cri);
466 }
467 }
468
469 cairo_restore(cri);
470}
471
472void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
473{
474 dt_aligned_pixel_t Lab = { L, a, b, 1.f };
475 dt_aligned_pixel_t XYZ = { 0.f, 0.f, 0.f, 1.f };
478}
479
480static void _render_iso12646(cairo_t *cr, double width, double height, int border)
481{
482 // draw the white frame around picture
483 cairo_rectangle(cr, -border * .5f, -border * .5f, width + border, height + border);
484 cairo_set_source_rgb(cr, 1., 1., 1.);
485 cairo_fill(cr);
486}
487
488/* Debug-only darkroom expose mode:
489 * - no persistent surface locks,
490 * - no fallback cache,
491 * - no preview substitution,
492 * - only main backbuffer if currently available.
493 *
494 * Set to 1 for baseline debugging when troubleshooting display regressions. */
495#ifndef DARKROOM_EXPOSE_DUMB_DEBUG
496#define DARKROOM_EXPOSE_DUMB_DEBUG 0
497#endif
498
499#if DARKROOM_EXPOSE_DUMB_DEBUG
500static dt_dev_pixelpipe_cache_wait_t _darkroom_main_debug_wait = { 0 };
501
502static void _darkroom_debug_restart_cache_wait(gpointer user_data)
503{
504 dt_develop_t *dev = (dt_develop_t *)user_data;
505 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
507}
508
509static gboolean _render_main_direct_debug(cairo_t *cr, dt_develop_t *dev, const int width, const int height,
510 const int border, const dt_aligned_pixel_t bg_color)
511{
512 if(IS_NULL_PTR(cr) || IS_NULL_PTR(dev) || IS_NULL_PTR(dev->pipe)) return FALSE;
513
514 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
515 cairo_paint(cr);
516
517 if(!dt_dev_pixelpipe_is_backbufer_valid(dev->pipe, dev)) return FALSE;
518 const uint64_t hash = dt_dev_backbuf_get_hash(&dev->pipe->backbuf);
519 if(hash == (uint64_t)-1) return FALSE;
520
521 dt_pixel_cache_entry_t *entry = NULL;
522 void *data = NULL;
523 dt_dev_pixelpipe_cache_wait_set_owner(&_darkroom_main_debug_wait, "darkroom-debug-main", dev);
524 if(!dt_dev_pixelpipe_cache_peek_gui(dev->pipe, NULL, &data, &entry,
525 &_darkroom_main_debug_wait,
526 _darkroom_debug_restart_cache_wait, dev))
527 return FALSE;
528
530
531 const int bw = (int)dev->pipe->backbuf.width;
532 const int bh = (int)dev->pipe->backbuf.height;
533 if(bw <= 0 || bh <= 0)
534 {
536 return FALSE;
537 }
538
539 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, bw);
540 const size_t required_size = (size_t)stride * (size_t)bh;
541 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
542 if(entry_size < required_size || dt_pixel_cache_entry_get_data(entry) != data)
543 {
545 return FALSE;
546 }
547
548 cairo_surface_t *surface = cairo_image_surface_create_for_data((unsigned char *)data, CAIRO_FORMAT_RGB24,
549 bw, bh, stride);
550 if(IS_NULL_PTR(surface) || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
551 {
552 if(surface) cairo_surface_destroy(surface);
554 return FALSE;
555 }
556
557 float image_box[4] = { 0.0f };
559 const int wd = image_box[2];
560 const int ht = image_box[3];
561 cairo_translate(cr, image_box[0], image_box[1]);
562 if(dev->iso_12646.enabled) _render_iso12646(cr, wd, ht, border);
563 cairo_surface_set_device_scale(surface, darktable.gui->ppd, darktable.gui->ppd);
564 cairo_rectangle(cr, 0, 0, wd, ht);
565 cairo_set_source_surface(cr, surface, 0, 0);
566 cairo_fill(cr);
567
568 cairo_surface_destroy(surface);
570 return TRUE;
571}
572#endif
573
583
588static cairo_surface_t *_darkroom_preview_fallback_surface = NULL;
594
596{
597 if(IS_NULL_PTR(locked)) return;
598
599 if(locked->surface)
600 {
601 cairo_surface_destroy(locked->surface);
602 locked->surface = NULL;
603 }
604
605 /* These cairo views only mirror whatever cacheline the pipeline currently exposes as backbuffer.
606 * They never own the backbuffer keepalive ref themselves: `pixelpipe_hb.c` swaps that ownership
607 * when publishing a new backbuffer. Releasing the surface must therefore only drop the GUI view. */
608 locked->entry = NULL;
609 locked->data = NULL;
610 locked->hash = (uint64_t)-1;
611 locked->width = 0;
612 locked->height = 0;
613}
614
629
630static void _darkroom_restart_cache_wait(gpointer user_data)
631{
632 dt_develop_t *dev = (dt_develop_t *)user_data;
633 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
635}
636
638{
643#if DARKROOM_EXPOSE_DUMB_DEBUG
644 dt_dev_pixelpipe_cache_wait_cleanup(&_darkroom_main_debug_wait, "darkroom-release-debug");
645#endif
647}
648
650 const gboolean keep_previous_on_fail, const gboolean lock_read)
651{
652 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || IS_NULL_PTR(locked)) return FALSE;
653 (void)lock_read;
655 const char *wait_owner = "darkroom-unknown";
656 if(pipe == dev->pipe)
657 {
658 wait = &_darkroom_main_wait;
659 wait_owner = "darkroom-main";
660 }
661 else if(pipe == dev->preview_pipe)
662 {
664 wait_owner = "darkroom-preview";
665 }
666 if(!IS_NULL_PTR(wait))
667 dt_dev_pixelpipe_cache_wait_set_owner(wait, wait_owner, dev);
668
670 if(hash == (uint64_t)-1) return keep_previous_on_fail && (!IS_NULL_PTR(locked->surface));
671
672 /* Fast-path reuse is only valid if the cacheline identity/data pointer are
673 * still the same behind the hash key. This avoids reusing stale cairo views
674 * when an entry is recreated or replaced under the same key. */
675 dt_pixel_cache_entry_t *live_entry = NULL;
676 void *live_data = NULL;
677 if(!IS_NULL_PTR(locked->surface) && locked->hash == hash
678 && dt_dev_pixelpipe_cache_peek_gui(pipe, NULL, &live_data, &live_entry, wait,
680 && live_entry == locked->entry && live_data == locked->data)
681 {
682 locked->width = pipe->backbuf.width;
683 locked->height = pipe->backbuf.height;
684 return TRUE;
685 }
686
687 struct dt_pixel_cache_entry_t *entry = NULL;
688 /* GUI surfaces only borrow the currently published backbuffer. They rely on the backbuffer keepalive ref
689 * owned by `pixelpipe_hb.c`, so they must not take or drop their own cache refs here. */
690 void *data = NULL;
691 if(!dt_dev_pixelpipe_cache_peek_gui(pipe, NULL, &data, &entry, wait,
693 data = NULL;
694 if(IS_NULL_PTR(data))
695 {
696 /* Keep previous frame only while waiting for a *different* target hash.
697 * If requested hash equals the currently locked one but cache lookup fails,
698 * the cached line was likely flushed/invalidated: drop stale lock so the
699 * line can be recreated and displayed again. */
700 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
702 return FALSE;
703 }
704
705 const int width = pipe->backbuf.width;
706 const int height = pipe->backbuf.height;
707 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
708 const size_t required_size = (size_t)stride * (size_t)height;
709 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
710 if(width <= 0 || height <= 0 || entry_size < required_size || dt_pixel_cache_entry_get_data(entry) != data)
711 {
712 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
714 return FALSE;
715 }
716
717 if(!IS_NULL_PTR(locked->surface) && locked->data == data && locked->width == width && locked->height == height)
718 {
719 locked->hash = hash;
720 locked->entry = entry;
721 locked->data = data;
722 return TRUE;
723 }
724
725 cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_RGB24, width, height, stride);
726 if(IS_NULL_PTR(surface) || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
727 {
728 if(!IS_NULL_PTR(surface)) cairo_surface_destroy(surface);
729 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface)) return TRUE;
731 return FALSE;
732 }
733
735 locked->hash = hash;
736 locked->width = width;
737 locked->height = height;
738 locked->data = data;
739 locked->entry = entry;
740 locked->surface = surface;
741 return TRUE;
742}
743
744static gboolean _render_main_locked_surface(cairo_t *cr, dt_develop_t *dev, darkroom_locked_surface_t *locked,
745 const int width, const int height, const int border,
746 const dt_aligned_pixel_t bg_color)
747{
748 if(IS_NULL_PTR(cr) || IS_NULL_PTR(dev) || IS_NULL_PTR(locked) || IS_NULL_PTR(locked->surface)) return FALSE;
749 if(IS_NULL_PTR(locked->entry) || locked->hash == (uint64_t)-1) return FALSE;
750
751 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
752 cairo_paint(cr);
753
754 int wd = locked->width;
755 int ht = locked->height;
756 if(wd <= 0 || ht <= 0) return FALSE;
757
758 wd /= darktable.gui->ppd;
759 ht /= darktable.gui->ppd;
760 cairo_translate(cr, .5f * (width - wd), .5f * (height - ht));
761
762 if(dev->iso_12646.enabled) _render_iso12646(cr, wd, ht, border);
763
765 cairo_surface_set_device_scale(locked->surface, darktable.gui->ppd, darktable.gui->ppd);
766 cairo_rectangle(cr, 0, 0, wd, ht);
767 cairo_set_source_surface(cr, locked->surface, 0, 0);
768 cairo_fill(cr);
770
771 return TRUE;
772}
773
774static gboolean _build_preview_fallback_surface(dt_develop_t *dev, const int width, const int height, const int border,
775 const dt_aligned_pixel_t bg_color, const uint64_t zoom_hash)
776{
779 if(width <= 0 || height <= 0) return FALSE;
780
784 {
790 }
791
792 cairo_t *cr = cairo_create(_darkroom_preview_fallback_surface);
793 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
794
795 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
796 cairo_paint(cr);
797
798 const int wd = _darkroom_preview_locked.width;
799 const int ht = _darkroom_preview_locked.height;
800 const float ppd = darktable.gui->ppd;
801 const float preview_wd = wd / ppd;
802 const float preview_ht = ht / ppd;
803 const float preview_scale = dev->roi.scaling;
804 float image_box[4] = { 0.0f };
806
807 if(dev->iso_12646.enabled)
808 {
809
810
811 if(image_box[2] > 0 && image_box[3] > 0)
812 {
813 cairo_save(cr);
814 cairo_translate(cr, image_box[0], image_box[1]);
815 _render_iso12646(cr, image_box[2], image_box[3], border);
816 cairo_restore(cr);
817 }
818 }
819
821 cairo_surface_set_device_scale(_darkroom_preview_locked.surface, ppd, ppd);
822
823 // The preview surface already embeds the fit-to-window scale. To emulate the
824 // main pipe while it catches up, we only apply the additional darkroom zoom
825 // and pan around the preview image center in GUI logical coordinates.
826 const float roi_width = fminf(width, preview_wd * preview_scale);
827 const float roi_height = fminf(height, preview_ht * preview_scale);
828 const float rec_x = fmaxf(border, (width - roi_width) * 0.5f);
829 const float rec_y = fmaxf(border, (height - roi_height) * 0.5f);
830 const float rec_w = fminf(width - 2 * border, roi_width);
831 const float rec_h = fminf(height - 2 * border, roi_height);
832 cairo_rectangle(cr, rec_x, rec_y, rec_w, rec_h);
833 cairo_clip(cr);
834
835 const float tx = 0.5f * width - dev->roi.x * preview_wd * preview_scale;
836 const float ty = 0.5f * height - dev->roi.y * preview_ht * preview_scale;
837 cairo_translate(cr, tx, ty);
838 cairo_scale(cr, preview_scale, preview_scale);
839 cairo_rectangle(cr, 0, 0, preview_wd, preview_ht);
840 cairo_set_source_surface(cr, _darkroom_preview_locked.surface, 0, 0);
841 cairo_fill(cr);
843 cairo_destroy(cr);
844
848 return TRUE;
849}
850
851static gboolean _render_preview_fallback_surface(cairo_t *cr)
852{
854 cairo_set_source_surface(cr, _darkroom_preview_fallback_surface, 0, 0);
855 cairo_paint(cr);
856 return TRUE;
857}
858
859static void _paint_all(cairo_t *cri, cairo_t *cr, cairo_surface_t *image_surface)
860{
861 cairo_destroy(cr);
862 if(IS_NULL_PTR(image_surface)) return;
863 cairo_set_source_surface(cri, image_surface, 0, 0);
864 cairo_paint(cri);
865}
866
868{
875 /* Main backbuf hash for which we already asked for a retry redraw after failing
876 * to lock/render it. Used to re-queue at most once per distinct main frame so a
877 * permanently-unlockable main backbuf (e.g. evicted under memory pressure) does
878 * not spin the center redraw at frame-clock rate while we show the preview
879 * fallback. Reset whenever a main frame is rendered successfully. */
882
883static inline gboolean _darkroom_preview_fallback_valid(const dt_develop_t *dev, const int width,
884 const int height, const uint64_t zoom_hash)
885{
886 return dev
892}
893
895 const uint64_t zoom_hash)
896{
897 return state
900 && state->main_zoom_hash == zoom_hash;
901}
902
904{
905 if(IS_NULL_PTR(state)) return;
906 state->image_surface_imgid = UNKNOWN_IMAGE;
907 state->image_surface_has_main = FALSE;
908 state->image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
909 state->main_zoom_hash = 0;
910 state->pending_main_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
911}
912
913static void _darkroom_prepare_image_surface(dt_develop_t *dev, const int width, const int height,
915{
916 if(IS_NULL_PTR(dev) || !state) return;
917
918 if(state->image_surface_imgid != dev->image_storage.id
919 && state->image_surface_imgid != UNKNOWN_IMAGE)
920 {
923 }
924
925 if(state->image_surface_width == width
926 && state->image_surface_height == height
927 && !IS_NULL_PTR(dev->image_surface))
928 return;
929
930 state->image_surface_width = width;
931 state->image_surface_height = height;
932 if(dev->image_surface) cairo_surface_destroy(dev->image_surface);
933 dev->image_surface = dt_cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
934 state->image_surface_imgid = UNKNOWN_IMAGE;
935 state->image_surface_has_main = FALSE;
936 state->image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
938}
939
941 dt_view_t *self,
942 cairo_t *cri,
943 int32_t width,
944 int32_t height,
945 int32_t pointerx,
946 int32_t pointery)
947{
948 dt_times_t start;
949 dt_get_times(&start);
950
951 cairo_save(cri);
952
953 dt_develop_t *dev = (dt_develop_t *)self->data;
954
955 const int32_t border = dev->roi.border_size;
956
957#if DARKROOM_EXPOSE_DUMB_DEBUG
958 dt_aligned_pixel_t bg_color_dbg;
959 if(dev->iso_12646.enabled)
960 _colormanage_ui_color(50., 0., 0., bg_color_dbg);
961 else
962 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color_dbg);
963 _render_main_direct_debug(cri, dev, width, height, border, bg_color_dbg);
964 cairo_restore(cri);
965 return;
966#endif
967
968 static darkroom_expose_state_t expose_state = {
970 .image_surface_height = 0,
971 .image_surface_imgid = UNKNOWN_IMAGE,
972 .image_surface_has_main = FALSE,
973 .image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID,
974 .main_zoom_hash = 0,
975 .pending_main_hash = DT_PIXELPIPE_CACHE_HASH_INVALID,
976 };
977 const uint64_t zoom_hash = dt_hash(5381, (char *)&dev->roi, sizeof(dev->roi));
978 const gboolean roi_changed = !_darkroom_locked_main_valid_for_zoom(&expose_state, zoom_hash);
979
980 _darkroom_prepare_image_surface(dev, width, height, &expose_state);
981
982 cairo_t *cr = cairo_create(dev->image_surface);
983 const int full_width = dev->roi.preview_width;
984 const int full_height = dev->roi.preview_height;
985 const uint64_t main_backbuf_hash = dt_dev_backbuf_get_hash(&dev->pipe->backbuf);
986 const uint64_t preview_backbuf_hash = dt_dev_backbuf_get_hash(&dev->preview_pipe->backbuf);
987 const gboolean main_has_backbuf = main_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID;
988 const gboolean preview_has_backbuf = preview_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID;
989 // Compare main against the last main surface, not against the last source painted into
990 // image_surface. At fit the latter may be the preview pipe, whose different hash would make an
991 // old main backbuffer look new and briefly restore the previous zoom size.
992 const gboolean main_backbuf_is_newer
993 = main_has_backbuf && main_backbuf_hash != _darkroom_main_locked.hash;
994 const gboolean main_ready_for_current_view
995 = main_has_backbuf && (!roi_changed || main_backbuf_is_newer || !_darkroom_main_locked.surface);
996 const gboolean preview_matches_full_image
997 = preview_has_backbuf && dt_dev_pipelines_share_preview_output(dev)
998 && full_width > 0 && full_height > 0
999 && dev->preview_pipe->backbuf.width == full_width
1000 && dev->preview_pipe->backbuf.height == full_height;
1001 const gboolean full_image_backbuf_ready = main_ready_for_current_view || preview_matches_full_image;
1002
1003 dt_aligned_pixel_t bg_color = { 0.0f };
1004 const char *draw_source = "background only";
1005 uint64_t draw_hash = (uint64_t)-1;
1006 gboolean drawn = FALSE;
1007 gboolean drawn_from_main = FALSE;
1008
1009 // user param is Lab lightness/brightness (which is they same for greys)
1010 if(dev->iso_12646.enabled)
1011 _colormanage_ui_color(50., 0., 0., bg_color);
1012 else
1013 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color);
1014
1015 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1016
1017 /* Selection policy, kept intentionally linear:
1018 * 1. Prefer the main pipe whenever it already has the backbuf for the
1019 * current darkroom view. We do not guess that from size. If ROI did not
1020 * change, the existing main frame stays authoritative. If ROI changed, a
1021 * new main backbuf hash means the updated main frame arrived.
1022 * 2. If preview produced the exact same full-image buffer size first
1023 * (zoom-to-fit / same ROI), treat it exactly like main.
1024 * 3. Only when zoom/pan changed and main has not caught up yet, use a scaled
1025 * preview fallback surface. This gives immediate ROI feedback without
1026 * discarding the last valid main image for pure history changes.
1027 * 4. If nothing newer is ready, keep showing the last valid composed frame to
1028 * avoid flashing the background. */
1029
1030 /* Once a full-image backbuf exists again, the ROI-scaled preview fallback is
1031 * obsolete. Keep main/preview selection simple and never let the fallback
1032 * outlive a valid full-image source. */
1033 if(full_image_backbuf_ready)
1035
1036 /* Rule 1: main wins whenever it is ready for the current view. During a
1037 * zoom/pan/widget-size transition, the previous main hash stays visible until
1038 * a different main hash is published; only then does main override preview
1039 * fallback again. */
1040 if(main_ready_for_current_view)
1041 {
1044 && _render_main_locked_surface(cr, dev, &_darkroom_main_locked, width, height, border, bg_color))
1045 {
1046 expose_state.main_zoom_hash = zoom_hash;
1047 expose_state.image_surface_imgid = dev->image_storage.id;
1048 expose_state.image_surface_has_main = TRUE;
1051 drawn = TRUE;
1052 drawn_from_main = TRUE;
1053 draw_source = "fresh main backbuf";
1054 draw_hash = _darkroom_main_locked.hash;
1055 }
1056 else if(main_backbuf_is_newer && expose_state.pending_main_hash != main_backbuf_hash)
1057 {
1058 /* The main backbuf hash advanced but its cacheline could not be locked this
1059 * frame. Ask for one retry, then remember the hash: if the same main frame
1060 * stays unlockable (e.g. evicted under memory pressure), we keep presenting
1061 * the preview fallback below instead of re-queueing a redraw every frame.
1062 * A genuinely new main frame (different hash) re-arms the retry, and the
1063 * pipeline itself re-queues a redraw when it publishes a fresh backbuf. */
1064 expose_state.pending_main_hash = main_backbuf_hash;
1066 }
1067 }
1068
1069 /* Rule 2: preview is directly equivalent to main only when both pipes target
1070 * the same ROI. An uncropped edit mode changes the module contract, not the
1071 * current zoom: once zoomed, preview must use the scaled fallback below. */
1072 if(!drawn && preview_matches_full_image)
1073 {
1076 && _render_main_locked_surface(cr, dev, &_darkroom_preview_locked, width, height, border, bg_color))
1077 {
1078 // This frame validates the composed image, not the locked main surface. Keep main_zoom_hash
1079 // unchanged so the old main backbuffer cannot be reused as if it had been rendered at fit.
1080 expose_state.image_surface_imgid = dev->image_storage.id;
1081 expose_state.image_surface_has_main = TRUE;
1083 drawn = TRUE;
1084 drawn_from_main = TRUE;
1085 draw_source = "fresh full-size preview backbuf";
1086 draw_hash = _darkroom_preview_locked.hash;
1087 }
1088 }
1089
1090 /* Rule 3: scaled preview fallback is only for ROI changes. If the user just
1091 * zoomed/panned and main has not recomputed yet, rebuild a ROI-scaled preview
1092 * placeholder so navigation remains responsive. */
1093 if(!drawn && roi_changed && !full_image_backbuf_ready && preview_has_backbuf)
1094 {
1096 && _build_preview_fallback_surface(dev, width, height, border, bg_color, zoom_hash)
1098 {
1099 expose_state.image_surface_imgid = dev->image_storage.id;
1100 expose_state.image_surface_has_main = FALSE;
1101 drawn = TRUE;
1102 drawn_from_main = FALSE;
1103 draw_source = "fresh preview fallback";
1105 }
1106 }
1107
1108 /* Reuse the cached ROI-scaled preview fallback if it is still geometrically
1109 * valid for this zoom/pan. This avoids rebuilding it every expose while main
1110 * is still catching up. */
1111 if(!drawn && roi_changed && !full_image_backbuf_ready
1112 && _darkroom_preview_fallback_valid(dev, width, height, zoom_hash)
1114 {
1115 expose_state.image_surface_imgid = dev->image_storage.id;
1116 expose_state.image_surface_has_main = FALSE;
1117 drawn = TRUE;
1118 drawn_from_main = FALSE;
1119 draw_source = "reused preview fallback";
1121 }
1122
1123 /* Rule 4: if no newer source is ready, keep the last valid main frame for
1124 * the same zoom/pan. History-only changes should not glitch to preview or
1125 * background while the new main backbuf is still being produced. */
1126 if(!drawn && _darkroom_locked_main_valid_for_zoom(&expose_state, zoom_hash)
1127 && _render_main_locked_surface(cr, dev, &_darkroom_main_locked, width, height, border, bg_color))
1128 {
1129 expose_state.image_surface_imgid = dev->image_storage.id;
1130 expose_state.image_surface_has_main = TRUE;
1131 drawn = TRUE;
1132 drawn_from_main = TRUE;
1133 draw_source = "reused last main backbuf";
1134 draw_hash = _darkroom_main_locked.hash;
1135 }
1136
1137 if(drawn)
1138 {
1139 /* Persist the last composed frame so later exposes can reuse it if the
1140 * next requested source is temporarily unavailable. */
1141 expose_state.image_surface_has_main = drawn_from_main;
1142 expose_state.image_surface_hash = draw_hash;
1143 _paint_all(cri, cr, dev->image_surface);
1144 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
1145 draw_source, draw_hash);
1146 cairo_restore(cri);
1147 }
1148 else if(dev->image_surface && expose_state.image_surface_imgid == dev->image_storage.id)
1149 {
1150 /* No fresh source this time: repaint the last composed image surface
1151 * rather than clearing to background and waiting for another expose. */
1152 draw_source = expose_state.image_surface_has_main ? "reused last main surface"
1153 : "reused last preview surface";
1154 draw_hash = expose_state.image_surface_hash;
1155 _paint_all(cri, cr, dev->image_surface);
1156 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
1157 draw_source, draw_hash);
1158 cairo_restore(cri);
1159 }
1160 else
1161 {
1162 /* Cold-start / no valid frame cached anywhere yet. */
1163 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
1164 cairo_paint(cr);
1165 _paint_all(cri, cr, dev->image_surface);
1166 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
1167 draw_source, draw_hash);
1168 cairo_restore(cri);
1169 }
1170
1171 /* check if we should create a snapshot of view */
1173 {
1174 /* reset the request */
1176
1177 /* validation of snapshot filename */
1179
1180 /* Store current image surface to snapshot file.
1181 FIXME: add checks so that we don't make snapshots of preview pipe image surface.
1182 */
1183 const int fd = g_open(darktable.develop->proxy.snapshot.filename, O_CREAT | O_WRONLY | O_BINARY, 0600);
1184 cairo_surface_write_to_png_stream(dev->image_surface, _write_snapshot_data, GINT_TO_POINTER(fd));
1185 close(fd);
1186 }
1187
1188 // Displaying sample areas if enabled
1193 {
1194 _darkroom_pickers_draw(self, cri, width, height, pointerx, pointery, darktable.develop->color_picker.samples, FALSE);
1195 }
1196
1197 // draw guide lines if needed
1198 if(!dev->gui_module || !(dev->gui_module->flags() & IOP_FLAGS_GUIDES_SPECIAL_DRAW))
1199 {
1200 const float wd = dev->roi.preview_width;
1201 const float ht = dev->roi.preview_height;
1202 const float scaling = dt_dev_get_overlay_scale(dev);
1203
1204 cairo_save(cri);
1205 // don't draw guides on image margins
1206 dt_dev_clip_roi(dev, cri, width, height);
1207 // place origin at top-left corner of image
1208 dt_dev_rescale_roi(dev, cri, width, height);
1209
1210 // draw guides with backbuffer dimensions, positioning and scaling handled by transformations
1211 dt_guides_draw(cri, 0, 0, wd, ht, scaling);
1212 cairo_restore(cri);
1213 }
1214
1215 const gboolean picker_active = dt_iop_color_picker_is_visible(dev);
1216
1217 // draw colorpicker for in focus module or execute module callback hook
1218 // FIXME: draw picker in gui_post_expose() hook in libs/colorpicker.c -- catch would be that live samples would appear over guides, softproof/gamut text overlay would be hidden by picker
1219 if(picker_active)
1220 {
1221 GSList samples = { .data = darktable.develop->color_picker.primary_sample, .next = NULL };
1222 _darkroom_pickers_draw(self, cri, width, height, pointerx, pointery, &samples, TRUE);
1223 }
1224 else
1225 {
1226 // display mask if we have a current module activated or if the masks manager module is expanded
1227 const gboolean display_masks = (dev->gui_module && dev->gui_module->enabled)
1229
1230 if(dt_masks_get_visible_form(dev) && display_masks)
1231 dt_masks_events_post_expose(dev->gui_module, cri, width, height, pointerx, pointery);
1232
1233 // module
1234 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->gui_post_expose)
1235 dev->gui_module->gui_post_expose(dev->gui_module, cri, width, height, pointerx, pointery);
1236 }
1237
1238 // indicate if we are in gamut check or softproof mode
1240 {
1241 gchar *label = darktable.color_profiles->mode == DT_PROFILE_GAMUTCHECK ? _("gamut check") : _("soft proof");
1242 cairo_set_source_rgba(cri, 0.5, 0.5, 0.5, 0.5);
1243 PangoLayout *layout;
1244 PangoRectangle ink;
1245 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
1246 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
1247 layout = pango_cairo_create_layout(cri);
1248 pango_font_description_set_absolute_size(desc, DT_PIXEL_APPLY_DPI(20) * PANGO_SCALE);
1249 pango_layout_set_font_description(layout, desc);
1250 pango_layout_set_text(layout, label, -1);
1251 pango_layout_get_pixel_extents(layout, &ink, NULL);
1252 cairo_move_to(cri, ink.height * 2, height - (ink.height * 3));
1253 pango_cairo_layout_path(cri, layout);
1254 cairo_set_source_rgb(cri, 0.7, 0.7, 0.7);
1255 cairo_fill_preserve(cri);
1256 cairo_set_line_width(cri, 0.7);
1257 cairo_set_source_rgb(cri, 0.3, 0.3, 0.3);
1258 cairo_stroke(cri);
1259 pango_font_description_free(desc);
1260 g_object_unref(layout);
1261 }
1262
1263 dt_show_times_f(&start, "[darkroom]", "redraw");
1264}
1265
1266void reset(dt_view_t *self)
1267{
1269}
1270
1271static void _darkroom_log_image_load_error(const int ret)
1272{
1273 switch(ret)
1274 {
1276 dt_control_log(_("Could not load the image source data."));
1277 break;
1279 dt_control_log(_("Could not read image information from the database."));
1280 break;
1281 default:
1282 dt_control_log(_("We could not load the image."));
1283 break;
1284 }
1285}
1286
1287static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data)
1288{
1289 dt_view_t *self = (dt_view_t *)user_data;
1290 dt_develop_t *dev = (dt_develop_t *)self->data;
1291 if(request_id == 0) return;
1292 if(darktable.view_manager->current_view != self) return;
1293
1294
1295 if(result)
1296 {
1297 _darkroom_log_image_load_error((int)result);
1298 return;
1299 }
1300
1301 darktable.develop->proxy.wb_coeffs[0] = 0.f;
1302
1303 // synch gui and flag pipe as dirty
1304 // this is done here and not in dt_read_history, as it would else be triggered before module->gui_init.
1305 // locks history mutex internally
1308
1311
1315
1316 // Clean & Init the starting point of undo/redo
1320
1321 /* signal that darktable.develop is initialized and ready to be used */
1323
1325
1326 // clear selection, we don't want selections in darkroom
1328
1329 // change active image for global actions (menu)
1332
1334
1336
1338
1340}
1341
1343{
1344 uint32_t num_selected = dt_selection_get_length(darktable.selection);
1345 int32_t imgid = dt_control_get_mouse_over_id();
1346 (void)self;
1347
1349
1350 if(imgid != UNKNOWN_IMAGE)
1351 {
1352 ; // Needed to open image from filmstrip
1353 }
1354 else if(num_selected > 1)
1355 {
1356 dt_control_log(_("The current selection contains more than one image, which is ambiguous.\n"
1357 "Select exactly one image to enter the darkroom."));
1358 return 1;
1359 }
1360 else if(num_selected == 0 && imgid == UNKNOWN_IMAGE)
1361 {
1362 dt_control_log(_("There is no image selected.\n"
1363 "Select exactly one image to enter the darkroom."));
1364 return 1;
1365 }
1366 else
1367 {
1368 // Needed to open image at startup
1370 }
1371
1373
1374 if(imgid < 0)
1375 {
1376 // fail :(
1377 dt_control_log(_("No image to open !"));
1378 return 1;
1379 }
1380
1382 return 0;
1383}
1384
1385static void _dev_change_image(dt_view_t *self, int32_t imgid)
1386{
1387 // Lazy trick to cleanup, reset, reinit, reload everything without
1388 // having to duplicate most of (but not all) the code in leave(),
1389 // try_enter() and enter() : simulate a roundtrip through lighttable.
1390 // This way, all images are loaded through the same path, handled at an higher level.
1391 // It's more robust, although slightly slower than re-initing only what is needed.
1395}
1396
1397static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data)
1398{
1399 if(imgid > UNKNOWN_IMAGE)
1400 {
1401 // switch images in darkroom mode:
1402 _dev_change_image(user_data, imgid);
1403 }
1404}
1405
1408static gboolean _toolbar_show_popup(gpointer user_data)
1409{
1410 GtkPopover *popover = GTK_POPOVER(user_data);
1411
1412 GtkWidget *button = gtk_popover_get_relative_to(popover);
1413 GdkRectangle button_rect = { 0 };
1414 GtkWidget *anchor = dt_gui_get_popup_relative_widget(button, &button_rect);
1415 GdkDevice *pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default()));
1416
1417 int x, y;
1418 GdkWindow *pointer_window = gdk_device_get_window_at_position(pointer, &x, &y);
1419 gpointer pointer_widget = NULL;
1420 if(pointer_window)
1421 gdk_window_get_user_data(pointer_window, &pointer_widget);
1422
1423 gtk_popover_set_relative_to(popover, anchor ? anchor : button);
1424
1425 GdkRectangle rect = { button_rect.x + button_rect.width / 2, button_rect.y, 1, 1 };
1426
1427 if(pointer_widget == anchor)
1428 {
1429 rect.x = x;
1430 rect.y = y;
1431 }
1432 else if(pointer_widget && anchor && pointer_widget != anchor)
1433 {
1434 gtk_widget_translate_coordinates(pointer_widget, anchor, x, y, &rect.x, &rect.y);
1435 }
1436
1437 gtk_popover_set_pointing_to(popover, &rect);
1438
1439 // for the guides popover, it need to be updated before we show it
1440 if(darktable.view_manager && GTK_WIDGET(popover) == darktable.view_manager->guides_popover)
1442 else if(GTK_WIDGET(popover) == _darkroom_autoset_popover)
1444
1445 gtk_widget_show_all(GTK_WIDGET(popover));
1446
1447 // cancel glib timeout if invoked by long button press
1448 return FALSE;
1449}
1450
1457static gboolean _darkroom_toolbox_button_activate_accel(GtkAccelGroup *accel_group, GObject *accelerable,
1458 guint keyval, GdkModifierType modifier,
1459 gpointer data)
1460{
1461 GtkWidget *button = GTK_WIDGET(data);
1462 if(IS_NULL_PTR(button) || !gtk_widget_is_visible(button) || !gtk_widget_is_sensitive(button)) return FALSE;
1463
1464 gtk_button_clicked(GTK_BUTTON(button));
1465 return TRUE;
1466}
1467
1468static gboolean _darkroom_toolbox_button_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable,
1469 guint keyval, GdkModifierType modifier,
1470 gpointer data)
1471{
1472 GtkWidget *button = GTK_WIDGET(data);
1473 if(IS_NULL_PTR(button) || !gtk_widget_is_visible(button) || !gtk_widget_is_sensitive(button)) return FALSE;
1474
1475 GtkWidget *popover = g_object_get_data(G_OBJECT(button), "dt-darkroom-toolbox-popover");
1476 if(IS_NULL_PTR(popover) || !gtk_widget_is_sensitive(popover)) return FALSE;
1477
1478 gtk_widget_grab_focus(button);
1479 gtk_popover_set_relative_to(GTK_POPOVER(popover), button);
1480 _toolbar_show_popup(popover);
1481 return TRUE;
1482}
1483
1485{
1486 if(d->iso_12646.enabled)
1487 {
1488 // For ISO 12646, we want portraits and landscapes to cover roughly the same surface
1489 // no matter the size of the widget. Meaning we force them to fit a square
1490 // of length matching the smaller widget dimension. The goal is to leave
1491 // a consistent perceptual impression between pictures, independent from orientation.
1492 const int main_dim = MIN(d->roi.orig_width, d->roi.orig_height);
1493 d->roi.border_size = 0.125 * main_dim;
1494 }
1495 else
1496 {
1497 d->roi.border_size = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size"));
1498 }
1499
1500 dt_dev_configure(d, d->roi.orig_width - 2 * d->roi.border_size, d->roi.orig_height - 2 * d->roi.border_size);
1501}
1502
1503/* colour assessment */
1504static void _iso_12646_quickbutton_clicked(GtkWidget *w, gpointer user_data)
1505{
1506 dt_develop_t *d = (dt_develop_t *)user_data;
1507 if(!d->gui_attached) return;
1508
1509 d->iso_12646.enabled = !d->iso_12646.enabled;
1511
1512 // This is already called in _get_final_size_... but it's not enough
1514}
1515
1516/* overlay color */
1517static void _guides_quickbutton_clicked(GtkWidget *widget, gpointer user_data)
1518{
1519 dt_guides_button_toggled(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1521}
1522
1523static void _guides_view_changed(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self)
1524{
1526}
1527
1535/* display */
1536static void _display_quickbutton_clicked(GtkWidget *w, gpointer user_data)
1537{
1538}
1539
1540static void display_brightness_callback(GtkWidget *slider, gpointer user_data)
1541{
1542 dt_conf_set_int("display/brightness", (int)(dt_bauhaus_slider_get(slider)));
1545}
1546
1547static void display_borders_callback(GtkWidget *slider, gpointer user_data)
1548{
1549 dt_develop_t *d = (dt_develop_t *)user_data;
1550 dt_conf_set_int("plugins/darkroom/ui/border_size", (int)dt_bauhaus_slider_get(slider));
1554}
1555
1562static void display_mask_checker_1_callback(GtkColorButton *widget, gpointer user_data)
1563{
1564 dt_develop_t *d = (dt_develop_t *)user_data;
1565 GdkRGBA color;
1566 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &color);
1567 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/red", color.red);
1568 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/green", color.green);
1569 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/blue", color.blue);
1570 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1572}
1573
1574static void display_mask_checker_2_callback(GtkColorButton *widget, gpointer user_data)
1575{
1576 dt_develop_t *d = (dt_develop_t *)user_data;
1577 GdkRGBA color;
1578 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &color);
1579 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/red", color.red);
1580 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/green", color.green);
1581 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/blue", color.blue);
1582 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1584}
1585
1586static void display_mask_checker_size_callback(GtkWidget *slider, gpointer user_data)
1587{
1588 dt_develop_t *d = (dt_develop_t *)user_data;
1589 dt_conf_set_int("plugins/darkroom/colorbalancergb/checker/size", (int)dt_bauhaus_slider_get(slider));
1590 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1592}
1593
1594static void display_mask_black_and_white_callback(GtkToggleButton *toggle, gpointer user_data)
1595{
1596 dt_develop_t *d = (dt_develop_t *)user_data;
1597 dt_conf_set_bool("plugins/darkroom/colorbalancergb/mask_preview/greyscaled",
1598 gtk_toggle_button_get_active(toggle));
1599 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1601}
1602
1603static void _darkroom_change_rendering_size(GtkWidget *combobox, gpointer user_data)
1604{
1605 dt_develop_t *d = (dt_develop_t *)user_data;
1606 dt_conf_set_int("darkroom/render_size", dt_bauhaus_combobox_get(combobox));
1608}
1609
1610/* overexposed */
1611static void _overexposed_quickbutton_clicked(GtkWidget *w, gpointer user_data)
1612{
1613 dt_develop_t *d = (dt_develop_t *)user_data;
1614 d->overexposed.enabled = !d->overexposed.enabled;
1616}
1617
1618static void colorscheme_callback(GtkWidget *combo, gpointer user_data)
1619{
1620 dt_develop_t *d = (dt_develop_t *)user_data;
1622 if(d->overexposed.enabled == FALSE)
1623 gtk_button_clicked(GTK_BUTTON(d->overexposed.button));
1624
1626}
1627
1628static void lower_callback(GtkWidget *slider, gpointer user_data)
1629{
1630 dt_develop_t *d = (dt_develop_t *)user_data;
1632 if(d->overexposed.enabled == FALSE)
1633 gtk_button_clicked(GTK_BUTTON(d->overexposed.button));
1634
1636}
1637
1638static void upper_callback(GtkWidget *slider, gpointer user_data)
1639{
1640 dt_develop_t *d = (dt_develop_t *)user_data;
1642 if(d->overexposed.enabled == FALSE)
1643 gtk_button_clicked(GTK_BUTTON(d->overexposed.button));
1644
1646}
1647
1648static void mode_callback(GtkWidget *slider, gpointer user_data)
1649{
1650 dt_develop_t *d = (dt_develop_t *)user_data;
1652 if(d->overexposed.enabled == FALSE)
1653 gtk_button_clicked(GTK_BUTTON(d->overexposed.button));
1654
1656}
1657
1658/* rawoverexposed */
1659static void _rawoverexposed_quickbutton_clicked(GtkWidget *w, gpointer user_data)
1660{
1661 dt_develop_t *d = (dt_develop_t *)user_data;
1662 d->rawoverexposed.enabled = !d->rawoverexposed.enabled;
1664}
1665
1666static void rawoverexposed_mode_callback(GtkWidget *combo, gpointer user_data)
1667{
1668 dt_develop_t *d = (dt_develop_t *)user_data;
1670 if(d->rawoverexposed.enabled == FALSE)
1671 gtk_button_clicked(GTK_BUTTON(d->rawoverexposed.button));
1672
1674}
1675
1676static void rawoverexposed_colorscheme_callback(GtkWidget *combo, gpointer user_data)
1677{
1678 dt_develop_t *d = (dt_develop_t *)user_data;
1680 if(d->rawoverexposed.enabled == FALSE)
1681 gtk_button_clicked(GTK_BUTTON(d->rawoverexposed.button));
1682
1684}
1685
1686static void rawoverexposed_threshold_callback(GtkWidget *slider, gpointer user_data)
1687{
1688 dt_develop_t *d = (dt_develop_t *)user_data;
1690 if(d->rawoverexposed.enabled == FALSE)
1691 gtk_button_clicked(GTK_BUTTON(d->rawoverexposed.button));
1692
1694}
1695
1696/* softproof */
1708
1709/* gamut */
1722
1723/* set the gui state for both softproof and gamut checking */
1725{
1726 g_signal_handlers_block_by_func(d->profile.softproof_button, _softproof_quickbutton_clicked, d);
1727 g_signal_handlers_block_by_func(d->profile.gamut_button, _gamut_quickbutton_clicked, d);
1728
1729 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->profile.softproof_button), darktable.color_profiles->mode == DT_PROFILE_SOFTPROOF);
1730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->profile.gamut_button), darktable.color_profiles->mode == DT_PROFILE_GAMUTCHECK);
1731
1732 g_signal_handlers_unblock_by_func(d->profile.softproof_button, _softproof_quickbutton_clicked, d);
1733 g_signal_handlers_unblock_by_func(d->profile.gamut_button, _gamut_quickbutton_clicked, d);
1734}
1735
1736
1737static void softproof_profile_callback(GtkWidget *combo, gpointer user_data)
1738{
1739 dt_develop_t *d = (dt_develop_t *)user_data;
1740 gboolean profile_changed = FALSE;
1741 const int pos = dt_bauhaus_combobox_get(combo);
1742 for(GList *profiles = darktable.color_profiles->profiles; profiles; profiles = g_list_next(profiles))
1743 {
1745 if(pp->out_pos == pos)
1746 {
1750
1751 {
1756 }
1757 goto end;
1758 }
1759 }
1760
1761 // profile not found, fall back to sRGB. shouldn't happen
1762 fprintf(stderr, "can't find softproof profile `%s', using sRGB instead\n", dt_bauhaus_combobox_get_text(combo));
1766
1767end:
1768 if(profile_changed)
1769 {
1772 }
1773}
1774
1777#if 0
1778
1779static void _overlay_cycle_callback(dt_action_t *action)
1780{
1781 const int currentval = dt_conf_get_int("darkroom/ui/overlay_color");
1782 const int nextval = (currentval + 1) % 6; // colors can go from 0 to 5
1783 dt_conf_set_int("darkroom/ui/overlay_color", nextval);
1786}
1787
1788static void _toggle_mask_visibility_callback(dt_action_t *action)
1789{
1790 if(darktable.gui->reset) return;
1791
1792 dt_develop_t *dev = dt_action_view(action)->data;
1793 dt_iop_module_t *mod = dev->gui_module;
1794
1795 //retouch and spot removal module use masks differently and have different buttons associated
1796 //keep the shortcuts independent
1797 if(mod && strcmp(mod->so->op, "spots") != 0 && strcmp(mod->so->op, "retouch") != 0)
1798 {
1800
1801 ++darktable.gui->reset;
1802
1804
1806 if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
1807 {
1808 if(bd->masks_shown == DT_MASKS_EDIT_OFF)
1809 bd->masks_shown = DT_MASKS_EDIT_FULL;
1810 else
1811 bd->masks_shown = DT_MASKS_EDIT_OFF;
1812
1813 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), bd->masks_shown != DT_MASKS_EDIT_OFF);
1814 dt_masks_set_edit_mode(mod, bd->masks_shown);
1815
1816 // set all add shape buttons to inactive
1817 for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
1818 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE);
1819 }
1820
1821 --darktable.gui->reset;
1822 }
1823}
1824
1825
1826#endif
1827
1828static gboolean _quickbutton_press_release(GtkWidget *button, GdkEventButton *event, GtkWidget *popover)
1829{
1830 static guint start_time = 0;
1831
1832 int delay = 0;
1833 g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL);
1834
1835 if((event->type == GDK_BUTTON_PRESS && event->button == 3) ||
1836 (event->type == GDK_BUTTON_RELEASE && event->time - start_time > delay))
1837 {
1838 gtk_popover_set_relative_to(GTK_POPOVER(popover), button);
1839 g_object_set(G_OBJECT(popover), "transitions-enabled", FALSE, NULL);
1840
1841 _toolbar_show_popup(popover);
1842 return TRUE;
1843 }
1844 else
1845 {
1846 start_time = event->time;
1847 return FALSE;
1848 }
1849}
1850
1852{
1853 g_signal_connect(w, "button-press-event", G_CALLBACK(_quickbutton_press_release), p);
1854 g_signal_connect(w, "button-release-event", G_CALLBACK(_quickbutton_press_release), p);
1855}
1856
1857gboolean _focus_main_image(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1858 GdkModifierType modifier, gpointer data)
1859{
1860 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui));
1861 return TRUE;
1862}
1863
1864gboolean _switch_to_next_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1865 GdkModifierType modifier, gpointer data)
1866{
1867 dt_view_t *view = (dt_view_t *)data;
1868 dt_develop_t *dev = (dt_develop_t *)view->data;
1869 int32_t current_img = dev->image_storage.id;
1870 GList *current_collection = dt_collection_get_all(darktable.collection, -1);
1871 GList *current_item = g_list_find(current_collection, GINT_TO_POINTER(current_img));
1872
1873 if(current_item && current_item->next)
1874 {
1875 int32_t next_img = GPOINTER_TO_INT(current_item->next->data);
1876 g_list_free(current_collection);
1877 current_collection = NULL;
1878 _dev_change_image(data, next_img);
1879 }
1880 else
1881 {
1882 g_list_free(current_collection);
1883 current_collection = NULL;
1884 }
1885
1886 return TRUE;
1887}
1888
1889gboolean _switch_to_prev_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1890 GdkModifierType modifier, gpointer data)
1891{
1892 dt_view_t *view = (dt_view_t *)data;
1893 dt_develop_t *dev = (dt_develop_t *)view->data;
1894 int32_t current_img = dev->image_storage.id;
1895 GList *current_collection = dt_collection_get_all(darktable.collection, -1);
1896 GList *current_item = g_list_find(current_collection, GINT_TO_POINTER(current_img));
1897
1898 if(current_item && current_item->prev)
1899 {
1900 int32_t prev_img = GPOINTER_TO_INT(current_item->prev->data);
1901 g_list_free(current_collection);
1902 current_collection = NULL;
1903 _dev_change_image(data, prev_img);
1904 }
1905 else
1906 {
1907 g_list_free(current_collection);
1908 current_collection = NULL;
1909 }
1910
1911 return TRUE;
1912}
1913
1914static void _preview_pipe_finished(gpointer instance, gpointer user_data)
1915{
1916 // Get the mip size that is at most as big as our pipeline backbuf
1918 const gboolean autoset_running_before
1920 const int32_t imgid = darktable.develop->image_storage.id;
1922
1923 // Check if the cache is ready for that mipmap size.
1926 gboolean cache_ready = !IS_NULL_PTR(tmp.buf);
1928
1929 if(pipe->autoset)
1930 {
1933 }
1934
1935 const gboolean autoset_running_after
1937
1938 // While autoset iterates over modules, avoid spawning thumbnail refresh jobs on each preview completion.
1939 // We refresh once the autoset run is finished and the preview cache reached a stable state.
1940 if(cache_ready && !autoset_running_after)
1941 {
1942 const gboolean autoset_just_finished = autoset_running_before && !autoset_running_after;
1943 if(!autoset_running_before || autoset_just_finished)
1944 {
1947 }
1948 }
1949}
1950
1951/*
1952static gboolean _darkroom_toolbox_button_activate_accel(GtkAccelGroup *accel_group, GObject *accelerable,
1953 guint keyval, GdkModifierType modifier,
1954 gpointer data)
1955{
1956 GtkWidget *button = GTK_WIDGET(data);
1957 if(IS_NULL_PTR(button) || !gtk_widget_is_visible(button) || !gtk_widget_is_sensitive(button)) return FALSE;
1958
1959 gtk_button_clicked(GTK_BUTTON(button));
1960 return TRUE;
1961}
1962*/
1963
1964static void _darkroom_autoset_quickbutton_clicked(GtkButton *button, gpointer user_data)
1965{
1968 fprintf(stdout, "lauching autoset\n");
1969}
1970
1971static gchar *_darkroom_autoset_label(const dt_iop_module_t *module)
1972{
1973 gchar *clean_name = dt_history_item_get_name(module);
1974 gchar *label = g_strdup_printf("%s (%i)", clean_name, module->multi_priority);
1975 dt_free(clean_name);
1976 return label;
1977}
1978
1979static void _darkroom_autoset_module_toggled(GtkToggleButton *toggle, gpointer user_data)
1980{
1981 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
1982 dt_iop_autoset_module_set_enabled(module, gtk_toggle_button_get_active(toggle));
1983}
1984
1986{
1988
1989 GList *children = gtk_container_get_children(GTK_CONTAINER(_darkroom_autoset_list));
1990 for(GList *child = children; child; child = g_list_next(child))
1991 gtk_widget_destroy(GTK_WIDGET(child->data));
1992 g_list_free(children);
1993
1994 GtkWidget *title = gtk_label_new(_("Module instances to autoset"));
1995 gtk_box_pack_start(GTK_BOX(_darkroom_autoset_list), title, FALSE, FALSE, 0);
1996 dt_gui_add_class(title, "dt_section_label");
1997
1998 for(GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
1999 {
2000 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
2001 if(IS_NULL_PTR(module->autoset)) continue;
2002
2003 gchar *label = _darkroom_autoset_label(module);
2004 GtkWidget *toggle = gtk_check_button_new_with_label(label);
2005 g_free(label);
2006
2007 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), dt_iop_autoset_module_is_enabled(module));
2008 gtk_box_pack_start(GTK_BOX(_darkroom_autoset_list), toggle, FALSE, FALSE, 0);
2009 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(_darkroom_autoset_module_toggled), module);
2010 }
2011
2012 gtk_widget_show_all(_darkroom_autoset_list);
2013}
2014
2015static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data)
2016{
2018}
2019
2021{
2022 dt_develop_t *dev = (dt_develop_t *)self->data;
2023
2025 G_CALLBACK(_preview_pipe_finished), self);
2026
2027 dt_accels_new_darkroom_action(_switch_to_next_picture, self, N_("Darkroom/Actions"),
2028 N_("Switch to the next picture"), GDK_KEY_Right, GDK_MOD1_MASK, _("Triggers the action"));
2029 dt_accels_new_darkroom_action(_switch_to_prev_picture, self, N_("Darkroom/Actions"),
2030 N_("Switch to the previous picture"), GDK_KEY_Left, GDK_MOD1_MASK, _("Triggers the action"));
2031
2032 gchar *path = dt_accels_build_path(_("Darkroom/Actions"), _("Give focus to the main image"));
2034 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Return, 0);
2035 dt_free(path);
2036
2037 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up"));
2039 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, 0);
2040 dt_free(path);
2041
2042 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up (coarse step)"));
2044 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, GDK_SHIFT_MASK);
2045 dt_free(path);
2046
2047 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up (fine step)"));
2049 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, GDK_CONTROL_MASK);
2050 dt_free(path);
2051
2052 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down"));
2054 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, 0);
2055 dt_free(path);
2056
2057 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down (coarse step)"));
2059 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, GDK_SHIFT_MASK);
2060 dt_free(path);
2061
2062 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down (fine step)"));
2064 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, GDK_CONTROL_MASK);
2065 dt_free(path);
2066
2067 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left"));
2069 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, 0);
2070 dt_free(path);
2071
2072 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left (coarse step)"));
2074 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, GDK_SHIFT_MASK);
2075 dt_free(path);
2076
2077 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left (fine step)"));
2079 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, GDK_CONTROL_MASK);
2080 dt_free(path);
2081
2082 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right"));
2084 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, 0);
2085 dt_free(path);
2086
2087 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right (coarse step)"));
2089 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, GDK_SHIFT_MASK);
2090 dt_free(path);
2091
2092 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right (fine step)"));
2094 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, GDK_CONTROL_MASK);
2095 dt_free(path);
2096
2097 path = dt_accels_build_path(_("Darkroom/Main image"), _("Zoom in"));
2099 path, dt_ui_center(darktable.gui->ui), GDK_KEY_plus, GDK_CONTROL_MASK);
2100 dt_free(path);
2101
2102 path = dt_accels_build_path(_("Darkroom/Main image"), _("Zoom out"));
2104 path, dt_ui_center(darktable.gui->ui), GDK_KEY_minus, GDK_CONTROL_MASK);
2105 dt_free(path);
2106 /*
2107 * Add view specific tool buttons
2108 */
2109
2110 /* Enable ISO 12646-compliant colour assessment conditions */
2112 gtk_widget_set_tooltip_text(dev->iso_12646.button,
2113 _("toggle ISO 12646 color assessment conditions"));
2114 g_signal_connect(G_OBJECT(dev->iso_12646.button), "clicked", G_CALLBACK(_iso_12646_quickbutton_clicked), dev);
2117 N_("Darkroom/Toolbox"),
2118 N_("Toggle ISO 12646 color assessment conditions"), 0, 0,
2119 _("Triggers the action"));
2120
2121 /* display background options */
2122 {
2125 gtk_widget_set_tooltip_text(dev->display.button, _("Picture display options"));
2126 g_signal_connect(G_OBJECT(dev->display.button), "clicked",
2127 G_CALLBACK(_display_quickbutton_clicked), dev);
2128
2129 // and the popup window
2130 dev->display.floating_window = gtk_popover_new(dev->display.button);
2132 g_object_set_data(G_OBJECT(dev->display.button), "dt-darkroom-toolbox-popover",
2135 N_("Darkroom/Toolbox"),
2136 N_("Focus picture display options"), 0, 0,
2137 _("Shows the options popover"));
2138
2139 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2140 gtk_widget_set_margin_bottom(vbox, DT_PIXEL_APPLY_DPI(DT_GUI_BOX_SPACING));
2141 gtk_container_add(GTK_CONTAINER(dev->display.floating_window), vbox);
2142
2144 GtkWidget *brightness = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0, 100, 5, 50, 0);
2145 dt_bauhaus_slider_set(brightness, (int)dt_conf_get_int("display/brightness"));
2146 dt_bauhaus_widget_set_label(brightness, N_("Background brightness"));
2147 dt_bauhaus_slider_set_format(brightness, "%");
2148 g_signal_connect(G_OBJECT(brightness), "value-changed", G_CALLBACK(display_brightness_callback), dev);
2149 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(brightness), TRUE, TRUE, 0);
2150
2151 GtkWidget *borders = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0, 250, 5, 10, 0);
2152 dt_bauhaus_slider_set(borders, dt_conf_get_int("plugins/darkroom/ui/border_size"));
2153 dt_bauhaus_widget_set_label(borders, N_("Picture margins"));
2154 dt_bauhaus_slider_set_format(borders, "px");
2155 g_signal_connect(G_OBJECT(borders), "value-changed", G_CALLBACK(display_borders_callback), dev);
2156 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(borders), TRUE, TRUE, 0);
2157
2158 GtkWidget *rendering;
2160 N_("Rendering size"),
2161 _("Choose at what size the main preview is rendered.\n"
2162 "Full resolution renders the pipeline at the raw original resolution.\n"
2163 "It is pixel-perfect, especially regarding denoising and deblurring, but very slow.\n"
2164 "Scaled renders at screen resolution and is the best trade-off.\n"
2165 "Pixel-level accuracy is guaranteed only when zoomed-in at 100%."),
2166 dt_conf_get_int("darkroom/render_size"),
2168 N_("full resolution (slow)"),
2169 N_("scaled (default)")
2170 );
2171 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(rendering), TRUE, TRUE, 0);
2172
2173 gtk_box_pack_start(GTK_BOX(vbox), dt_ui_section_label_new(_("Mask preview settings")), FALSE, FALSE, 0);
2174
2175 GtkWidget *checker_1_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
2176 gtk_box_pack_start(GTK_BOX(checker_1_row), dt_ui_label_new(_("Checkerboard color 1")), TRUE, TRUE, 0);
2177 GtkWidget *checker_1 = gtk_color_button_new();
2178 GdkRGBA checker_color = {
2179 .red = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/red"),
2180 .green = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/green"),
2181 .blue = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/blue"),
2182 .alpha = 1.0
2183 };
2184 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(checker_1), &checker_color);
2185 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(checker_1), FALSE);
2186 gtk_color_button_set_title(GTK_COLOR_BUTTON(checker_1),
2187 _("Select color of the checkerboard from a swatch"));
2188 g_signal_connect(G_OBJECT(checker_1), "color-set", G_CALLBACK(display_mask_checker_1_callback), dev);
2189 gtk_box_pack_start(GTK_BOX(checker_1_row), checker_1, FALSE, FALSE, 0);
2190 gtk_box_pack_start(GTK_BOX(vbox), checker_1_row, FALSE, FALSE, 0);
2191
2192 GtkWidget *checker_2_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
2193 gtk_box_pack_start(GTK_BOX(checker_2_row), dt_ui_label_new(_("Checkerboard color 2")), TRUE, TRUE, 0);
2194 GtkWidget *checker_2 = gtk_color_button_new();
2195 checker_color.red = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/red");
2196 checker_color.green = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/green");
2197 checker_color.blue = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/blue");
2198 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(checker_2), &checker_color);
2199 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(checker_2), FALSE);
2200 gtk_color_button_set_title(GTK_COLOR_BUTTON(checker_2),
2201 _("Select color of the checkerboard from a swatch"));
2202 g_signal_connect(G_OBJECT(checker_2), "color-set", G_CALLBACK(display_mask_checker_2_callback), dev);
2203 gtk_box_pack_start(GTK_BOX(checker_2_row), checker_2, FALSE, FALSE, 0);
2204 gtk_box_pack_start(GTK_BOX(vbox), checker_2_row, FALSE, FALSE, 0);
2205
2206 GtkWidget *checker_size
2208 dt_bauhaus_slider_set(checker_size,
2209 dt_conf_get_int("plugins/darkroom/colorbalancergb/checker/size"));
2210 dt_bauhaus_slider_set_format(checker_size, " px");
2211 dt_bauhaus_widget_set_label(checker_size, _("Checkerboard size"));
2212 g_signal_connect(G_OBJECT(checker_size), "value-changed",
2213 G_CALLBACK(display_mask_checker_size_callback), dev);
2214 gtk_box_pack_start(GTK_BOX(vbox), checker_size, TRUE, TRUE, 0);
2215
2216 GtkWidget *black_and_white = gtk_check_button_new_with_label(_("Show a greyscaled mask image"));
2217 gtk_toggle_button_set_active(
2218 GTK_TOGGLE_BUTTON(black_and_white),
2219 dt_conf_get_bool("plugins/darkroom/colorbalancergb/mask_preview/greyscaled"));
2220 g_signal_connect(G_OBJECT(black_and_white), "toggled",
2221 G_CALLBACK(display_mask_black_and_white_callback), dev);
2222 gtk_box_pack_start(GTK_BOX(vbox), black_and_white, FALSE, FALSE, 0);
2223 }
2224
2226 gtk_widget_set_tooltip_text(_darkroom_ioporder_button, _("show the pipeline node graph"));
2227 g_signal_connect(G_OBJECT(_darkroom_ioporder_button), "clicked",
2231 N_("Darkroom/Toolbox"),
2232 N_("Show the pipeline node graph"), 0, 0,
2233 _("Triggers the action"));
2234
2235 GtkWidget *colorscheme, *mode;
2236
2237 /* create rawoverexposed popup tool */
2238 {
2239 // the button
2241 gtk_widget_set_tooltip_text(dev->rawoverexposed.button,
2242 _("toggle raw over exposed indication\nright click for options"));
2243 g_signal_connect(G_OBJECT(dev->rawoverexposed.button), "clicked",
2244 G_CALLBACK(_rawoverexposed_quickbutton_clicked), dev);
2247
2248 // and the popup window
2249 dev->rawoverexposed.floating_window = gtk_popover_new(dev->rawoverexposed.button);
2251 g_object_set_data(G_OBJECT(dev->rawoverexposed.button), "dt-darkroom-toolbox-popover",
2254 N_("Darkroom/Toolbox"),
2255 N_("Toggle raw over exposed indication"), 0, 0,
2256 _("Triggers the action"));
2258 N_("Darkroom/Toolbox"),
2259 N_("Focus raw over exposed indication options"), 0, 0,
2260 _("Shows the options popover"));
2261
2262 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2263 gtk_container_add(GTK_CONTAINER(dev->rawoverexposed.floating_window), vbox);
2264
2266 /* mode of operation */
2267 DT_BAUHAUS_COMBOBOX_NEW_FULL(darktable.bauhaus, mode, NULL, N_("mode"),
2268 _("select how to mark the clipped pixels"),
2270 N_("mark with CFA color"), N_("mark with solid color"), N_("false color"));
2271 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(mode), TRUE, TRUE, 0);
2272
2273 /* color scheme */
2274 // FIXME can't use DT_BAUHAUS_COMBOBOX_NEW_FULL because of (unnecessary?) translation context
2276 dt_bauhaus_widget_set_label(colorscheme, N_("color scheme"));
2277 dt_bauhaus_combobox_add(colorscheme, C_("solidcolor", "red"));
2278 dt_bauhaus_combobox_add(colorscheme, C_("solidcolor", "green"));
2279 dt_bauhaus_combobox_add(colorscheme, C_("solidcolor", "blue"));
2280 dt_bauhaus_combobox_add(colorscheme, C_("solidcolor", "black"));
2282 gtk_widget_set_tooltip_text(
2283 colorscheme,
2284 _("select the solid color to indicate over exposure.\nwill only be used if mode = mark with solid color"));
2285 g_signal_connect(G_OBJECT(colorscheme), "value-changed", G_CALLBACK(rawoverexposed_colorscheme_callback), dev);
2286 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(colorscheme), TRUE, TRUE, 0);
2287
2288 /* threshold */
2291 dt_bauhaus_widget_set_label(threshold, N_("clipping threshold"));
2292 gtk_widget_set_tooltip_text(
2293 threshold, _("threshold of what shall be considered overexposed\n1.0 - white level\n0.0 - black level"));
2294 g_signal_connect(G_OBJECT(threshold), "value-changed", G_CALLBACK(rawoverexposed_threshold_callback), dev);
2295 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(threshold), TRUE, TRUE, 0);
2296
2297 gtk_widget_show_all(vbox);
2298 }
2299
2300 /* create overexposed popup tool */
2301 {
2302 // the button
2304 gtk_widget_set_tooltip_text(dev->overexposed.button,
2305 _("toggle clipping indication\nright click for options"));
2306 g_signal_connect(G_OBJECT(dev->overexposed.button), "clicked",
2307 G_CALLBACK(_overexposed_quickbutton_clicked), dev);
2310
2311 // and the popup window
2312 dev->overexposed.floating_window = gtk_popover_new(dev->overexposed.button);
2314 g_object_set_data(G_OBJECT(dev->overexposed.button), "dt-darkroom-toolbox-popover",
2317 N_("Darkroom/Toolbox"),
2318 N_("Toggle clipping indication"), 0, 0,
2319 _("Triggers the action"));
2321 N_("Darkroom/Toolbox"),
2322 N_("Focus clipping indication options"), 0, 0,
2323 _("Shows the options popover"));
2324
2325 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2326 gtk_container_add(GTK_CONTAINER(dev->overexposed.floating_window), vbox);
2327
2329 /* preview mode */
2330 DT_BAUHAUS_COMBOBOX_NEW_FULL(darktable.bauhaus, mode, NULL, N_("clipping preview mode"),
2331 _("select the metric you want to preview\nfull gamut is the combination of all other modes"),
2332 dev->overexposed.mode, mode_callback, dev,
2333 N_("full gamut"), N_("any RGB channel"), N_("luminance only"), N_("saturation only"));
2334 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(mode), TRUE, TRUE, 0);
2335
2336 /* color scheme */
2337 DT_BAUHAUS_COMBOBOX_NEW_FULL(darktable.bauhaus, colorscheme, NULL, N_("color scheme"),
2338 _("select colors to indicate clipping"),
2340 N_("black & white"), N_("red & blue"), N_("purple & green"));
2341 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(colorscheme), TRUE, TRUE, 0);
2342
2343 /* lower */
2344 GtkWidget *lower = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), -32., -4., 1., -12.69, 2);
2346 dt_bauhaus_slider_set_format(lower, _(" EV"));
2347 dt_bauhaus_widget_set_label(lower, N_("lower threshold"));
2348 gtk_widget_set_tooltip_text(lower, _("clipping threshold for the black point,\n"
2349 "in EV, relatively to white (0 EV).\n"
2350 "8 bits sRGB clips blacks at -12.69 EV,\n"
2351 "8 bits Adobe RGB clips blacks at -19.79 EV,\n"
2352 "16 bits sRGB clips blacks at -20.69 EV,\n"
2353 "typical fine-art mat prints produce black at -5.30 EV,\n"
2354 "typical color glossy prints produce black at -8.00 EV,\n"
2355 "typical B&W glossy prints produce black at -9.00 EV."
2356 ));
2357 g_signal_connect(G_OBJECT(lower), "value-changed", G_CALLBACK(lower_callback), dev);
2358 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(lower), TRUE, TRUE, 0);
2359
2360 /* upper */
2361 GtkWidget *upper = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0.0, 100.0, 0.1, 99.99, 2);
2363 dt_bauhaus_slider_set_format(upper, "%");
2364 dt_bauhaus_widget_set_label(upper, N_("upper threshold"));
2365 /* xgettext:no-c-format */
2366 gtk_widget_set_tooltip_text(upper, _("clipping threshold for the white point.\n"
2367 "100% is peak medium luminance."));
2368 g_signal_connect(G_OBJECT(upper), "value-changed", G_CALLBACK(upper_callback), dev);
2369 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(upper), TRUE, TRUE, 0);
2370
2371 gtk_widget_show_all(vbox);
2372 }
2373
2374 /* create profile popup tool & buttons (softproof + gamut) */
2375 {
2376 // the softproof button
2378 gtk_widget_set_tooltip_text(dev->profile.softproof_button,
2379 _("toggle softproofing\nright click for profile options"));
2380 g_signal_connect(G_OBJECT(dev->profile.softproof_button), "clicked",
2381 G_CALLBACK(_softproof_quickbutton_clicked), dev);
2384
2385 // the gamut check button
2387 gtk_widget_set_tooltip_text(dev->profile.gamut_button,
2388 _("toggle gamut checking\nright click for profile options"));
2389 g_signal_connect(G_OBJECT(dev->profile.gamut_button), "clicked",
2390 G_CALLBACK(_gamut_quickbutton_clicked), dev);
2393
2394 // and the popup window, which is shared between the two profile buttons
2395 dev->profile.floating_window = gtk_popover_new(NULL);
2398 g_object_set_data(G_OBJECT(dev->profile.softproof_button), "dt-darkroom-toolbox-popover",
2400 g_object_set_data(G_OBJECT(dev->profile.gamut_button), "dt-darkroom-toolbox-popover",
2403 dev->profile.softproof_button, N_("Darkroom/Toolbox"),
2404 N_("Toggle softproofing"), 0, 0,
2405 _("Triggers the action"));
2407 dev->profile.softproof_button, N_("Darkroom/Toolbox"),
2408 N_("Focus softproof options"), 0, 0,
2409 _("Shows the options popover"));
2411 dev->profile.gamut_button, N_("Darkroom/Toolbox"),
2412 N_("Toggle gamut checking"), 0, 0,
2413 _("Triggers the action"));
2415 dev->profile.gamut_button, N_("Darkroom/Toolbox"),
2416 N_("Focus gamut checking options"), 0, 0,
2417 _("Shows the options popover"));
2418
2419 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2420 gtk_container_add(GTK_CONTAINER(dev->profile.floating_window), vbox);
2421
2423 char datadir[PATH_MAX] = { 0 };
2424 char confdir[PATH_MAX] = { 0 };
2425 dt_loc_get_user_config_dir(confdir, sizeof(confdir));
2426 dt_loc_get_datadir(datadir, sizeof(datadir));
2427
2429 dt_bauhaus_widget_set_label(softproof_profile, N_("softproof profile"));
2430 dt_bauhaus_combobox_set_entries_ellipsis(softproof_profile, PANGO_ELLIPSIZE_MIDDLE);
2431 gtk_box_pack_start(GTK_BOX(vbox), softproof_profile, TRUE, TRUE, 0);
2432
2433 for(const GList *l = darktable.color_profiles->profiles; l; l = g_list_next(l))
2434 {
2436 // the system display profile is only suitable for display purposes
2437 if(prof->out_pos > -1)
2438 {
2439 dt_bauhaus_combobox_add(softproof_profile, prof->name);
2441 && (prof->type != DT_COLORSPACE_FILE
2443 dt_bauhaus_combobox_set(softproof_profile, prof->out_pos);
2444 }
2445 }
2446
2447 char *system_profile_dir = g_build_filename(datadir, "color", "out", NULL);
2448 char *user_profile_dir = g_build_filename(confdir, "color", "out", NULL);
2449 char *tooltip = g_strdup_printf(_("softproof ICC profiles in %s or %s"), user_profile_dir, system_profile_dir);
2450 gtk_widget_set_tooltip_text(softproof_profile, tooltip);
2452 dt_free(system_profile_dir);
2453 dt_free(user_profile_dir);
2454
2455 g_signal_connect(G_OBJECT(softproof_profile), "value-changed", G_CALLBACK(softproof_profile_callback), dev);
2456
2458
2459 gtk_widget_show_all(vbox);
2460 }
2461
2462 /* create grid changer popup tool */
2463 {
2464 // the button
2466 gtk_widget_set_tooltip_text(darktable.view_manager->guides_toggle,
2467 _("toggle guide lines\nright click for guides options"));
2469 g_object_ref(darktable.view_manager->guides_popover);
2470 g_signal_connect(G_OBJECT(darktable.view_manager->guides_toggle), "clicked",
2471 G_CALLBACK(_guides_quickbutton_clicked), dev);
2473 g_object_set_data(G_OBJECT(darktable.view_manager->guides_toggle), "dt-darkroom-toolbox-popover",
2478 darktable.view_manager->guides_toggle, N_("Darkroom/Toolbox"),
2479 N_("Toggle guide lines"), 0, 0,
2480 _("Triggers the action"));
2482 darktable.view_manager->guides_toggle, N_("Darkroom/Toolbox"),
2483 N_("Focus guide lines options"), 0, 0,
2484 _("Shows the options popover"));
2485 // we want to update button state each time the view change
2487 G_CALLBACK(_guides_view_changed), dev);
2488 }
2489
2490 // Auto-set feature
2491 {
2493
2495 gtk_widget_set_tooltip_text(_darkroom_autoset_button, _("Run autoset on selected modules\nRight click for options"));
2496 g_signal_connect(G_OBJECT(_darkroom_autoset_button), "clicked",
2497 G_CALLBACK(_darkroom_autoset_quickbutton_clicked), dev);
2498 /* Ensure autoset button is placed first in the toolbox. */
2499 g_object_set_data(G_OBJECT(_darkroom_autoset_button), "dt-toolbox-priority", GINT_TO_POINTER(1));
2501
2504 g_object_set_data(G_OBJECT(_darkroom_autoset_button), "dt-darkroom-toolbox-popover",
2506 /*
2507 dt_accels_new_darkroom_action(_darkroom_toolbox_button_activate_accel, _darkroom_autoset_button,
2508 N_("Darkroom/Toolbox"),
2509 N_("Show the pipeline node graph"), 0, 0,
2510 _("Triggers the action"));
2511 */
2512 _darkroom_autoset_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2513 gtk_container_add(GTK_CONTAINER(_darkroom_autoset_popover), _darkroom_autoset_list);
2516
2518 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
2520 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
2522 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
2523 }
2524
2527 dev->roi.border_size = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size"));
2528}
2529
2531{
2532 dt_accels_t *accels = darktable.gui->accels;
2533 if(!darktable.gui->has_scroll_focus || accels->active_key.accel_key == 0) return FALSE;
2534
2535 // When declaring shortcuts, bauhaus widgets write their accel path into a private data field
2536 gchar *accel_path = g_object_get_data(G_OBJECT(darktable.gui->has_scroll_focus), "accel-path");
2537
2538 // Find if the registered accel keys matches currently pressed keys
2539 GtkAccelKey key = { 0 };
2540 return gtk_accel_map_lookup_entry(accel_path, &key)
2541 && key.accel_key == accels->active_key.accel_key
2542 && key.accel_mods == accels->active_key.accel_mods;
2543}
2544
2545
2546// If a bauhaus widget has the scroll focus from a keyboard shortcut,
2547// and the combination of keys attached to its accel path
2548// is still pressed, then we redirect any scroll event in the window to this widget.
2549// Warning: if mouse is over the central widget, central widget takes precedence over scrolling.
2550gboolean _scroll_on_focus(GdkEventScroll event, void *data)
2551{
2553 {
2554 // Pass-through the scrolling event to the scrolling handler of the widget
2555 gboolean ret;
2556 g_signal_emit_by_name(G_OBJECT(darktable.gui->has_scroll_focus), "scroll-event", &event, &ret);
2557 return ret;
2558 }
2559
2560 return FALSE;
2561}
2562
2563
2564void enter(dt_view_t *self)
2565{
2566 // Flush all background jobs (thumbnails generation) to spare resources for interactivity
2568
2569 dt_print(DT_DEBUG_CONTROL, "[run_job+] 11 %f in darkroom mode\n", dt_get_wtime());
2570 dt_develop_t *dev = (dt_develop_t *)self->data;
2571 dev->exit = 0;
2573
2574 // We need to init forms before we init module blending GUI
2575 dt_masks_gui_init(dev);
2576 dev->gui_module = NULL;
2577
2578 if(IS_NULL_PTR(dev->iop))
2579 dev->iop = dt_dev_load_modules(dev);
2580
2581 // Add IOP modules to the plugin list
2582 char option[1024];
2583 const char *active_plugin = dt_conf_get_string_const("plugins/darkroom/active");
2584 for(const GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
2585 {
2586 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2587
2588 /* initialize gui if iop have one defined */
2589 if(!dt_iop_is_hidden(module))
2590 {
2591 dt_iop_gui_init(module);
2593
2594 if(module->multi_priority == 0)
2595 {
2596 snprintf(option, sizeof(option), "plugins/darkroom/%s/expanded", module->op);
2597 module->expanded = dt_conf_get_bool(option);
2599
2600 if(active_plugin && !strcmp(module->op, active_plugin))
2602 }
2603 }
2604 }
2605
2606 // just make sure at this stage we have only history info into the undo, all automatic
2607 // tagging should be ignored.
2609
2611
2612 // Reset focus to center view
2614
2615 // Attach shortcuts to new widgets
2619
2620 // Attach bauhaus default signal callback to IOP
2622
2623 // This gets the first selected ID to scroll where relevant, so
2624 // runs it before clearing the selection
2626 gtk_widget_show(dt_ui_center(darktable.gui->ui));
2628
2629 /* connect signal for filmstrip image activate */
2632
2633 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); // ensure the center view has focus for keybindings to work
2634
2635 const int32_t imgid = _darkroom_pending_imgid;
2640 int ret = dt_dev_load_image(darktable.develop, imgid);
2641 _darkroom_image_loaded_callback(NULL, imgid, ret, self);
2642}
2643
2644void leave(dt_view_t *self)
2645{
2646 dt_develop_t *dev = (dt_develop_t *)self->data;
2649 "darkroom-leave-autoset");
2654
2656 if(dev->image_surface) cairo_surface_destroy(dev->image_surface);
2657 dev->image_surface = NULL;
2658
2659 // Send all pipeline shutdown signals first
2660 dev->exit = 1;
2664 dev->pipelines_started = FALSE;
2665 dt_dev_pixelpipe_cache_wait_dump_pending("darkroom-leave-before-cleanup");
2666
2667 /* Stop module-owned background threads that may still be mutating the pipe, dev or history (e.g.
2668 * drawlayer's asynchronous paint/commit worker) BEFORE we tear down pipeline nodes and history
2669 * below. Otherwise an in-flight commit resync can run a pipeline synch against pieces this function
2670 * is about to free, faulting in whatever module commits next. */
2671 for(GList *m = dev->iop; m; m = g_list_next(m))
2672 {
2673 dt_iop_module_t *mod = (dt_iop_module_t *)m->data;
2674 if(mod && mod->quiesce) mod->quiesce(mod);
2675 }
2676
2678
2679 // While we wait for possible pipelines to finish,
2680 // do the GUI cleaning.
2681
2682 // store last active plugin:
2684 dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op);
2685 else
2686 dt_conf_set_string("plugins/darkroom/active", "");
2687
2688 // Hide the popover floating windows
2689 gtk_widget_hide(dev->overexposed.floating_window);
2690 gtk_widget_hide(dev->rawoverexposed.floating_window);
2691 gtk_widget_hide(dev->profile.floating_window);
2692
2693 // Detach the default callback for bauhaus widgets
2695
2696 /* disconnect from filmstrip image activate */
2698 (gpointer)self);
2699
2701
2704
2705 // Detach shortcuts
2707
2708 // Restore the previous selection
2711
2713 gtk_widget_hide(dt_ui_center(darktable.gui->ui));
2714
2715 // Pipeline nodes reference modules from dev->iop
2716 // we need to destroy objects referencing modules
2717 // before destroying the actual modules being referenced.
2723
2730
2737
2738 /* Device-side cache payloads are only an acceleration layer. Once darkroom
2739 * leaves, drop the cl_mem objects these pipes produced -- but only on the
2740 * device(s) they themselves last ran on, so we never touch cache entries
2741 * another, still-running pipe (e.g. a background thumbnail export) holds on
2742 * its own OpenCL device. */
2744 if(dev->preview_pipe->last_devid != dev->pipe->last_devid)
2746
2750
2751 // Not sure why using g_list_free_full() here shits the bed
2752 while(dev->iop)
2753 {
2754 dt_iop_module_t *module = (dt_iop_module_t *)(dev->iop->data);
2755 if(!dt_iop_is_hidden(module)) dt_iop_gui_cleanup_module(module);
2756 dt_iop_cleanup_module(module);
2757 dt_free(module);
2758 dev->iop = g_list_delete_link(dev->iop, dev->iop);
2759 }
2760 while(dev->alliop)
2761 {
2763 dt_free(dev->alliop->data);
2764 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
2765 }
2766 dev->iop = dev->alliop = NULL;
2767
2768 // cleanup visible masks
2769 if(dev->form_gui)
2770 {
2771 dev->gui_module = NULL; // modules have already been g_free()
2773 }
2774
2775 // clear masks
2777 g_list_free_full(dev->forms, (void (*)(void *))dt_masks_free_form);
2778 dev->forms = NULL;
2779 g_list_free_full(dev->allforms, (void (*)(void *))dt_masks_free_form);
2780 dev->allforms = NULL;
2782
2783 // Fetch the new thumbnail if needed. Ensure it runs after we save history.
2786
2787 // Release the cache entries for histogram buffers
2790
2793
2796
2797 /* GUI backbuffers were already released when each pipeline was quiesced above. Keep the view-side teardown
2798 * free of extra unref paths so pipeline ownership stays centralized. */
2799
2800
2801 dt_print(DT_DEBUG_CONTROL, "[run_job-] 11 %f in darkroom mode\n", dt_get_wtime());
2802}
2803
2804// Leave central view
2806{
2807 // if we are not hovering over a thumbnail in the filmstrip -> show metadata of opened image.
2808 dt_develop_t *dev = (dt_develop_t *)self->data;
2810 dt_gui_gtk_t *gui = darktable.gui;
2811 gui->mouse.is_dragging = FALSE;
2813
2814 if(gui->pan_edge.timeout_source
2816 && !IS_NULL_PTR(ctl) && ctl->button_down && ctl->button_down_which == 1)
2817 {
2818 gui->pan_edge.velocity[0] = 0.0f;
2819 gui->pan_edge.velocity[1] = 0.0f;
2820 gui->pan_edge.last_time_us = 0;
2821 if(gui->pan_edge.timeout_source)
2822 {
2823 g_source_remove(gui->pan_edge.timeout_source);
2824 gui->pan_edge.timeout_source = 0;
2825 }
2826 gui->pan_edge.view = NULL;
2828 }
2829 else
2831
2832 // masks
2833 gboolean handled = FALSE;
2835 handled = TRUE;
2836 // module
2837 else if(dev->gui_module && dev->gui_module->mouse_leave
2838 && dev->gui_module->mouse_leave(dev->gui_module))
2839 handled = TRUE;
2840
2841 if(handled)
2843
2844 // reset any changes the selected plugin might have made.
2846 dt_control_change_cursor(GDK_LEFT_PTR);
2847}
2848
2849static gboolean _is_in_frame(const int width, const int height, const int x, const int y)
2850{
2851 return !((x < -DT_PIXEL_APPLY_DPI(2)) ||
2852 (x > (width + DT_PIXEL_APPLY_DPI(4))) ||
2853 (y < -DT_PIXEL_APPLY_DPI(2)) ||
2854 (y > (height + DT_PIXEL_APPLY_DPI(4))));
2855}
2856
2857/* This helper function tests for a position to be within the displayed area
2858 of an image. To avoid "border cases" we accept values to be slightly out of area too.
2859*/
2860static gboolean mouse_in_imagearea(dt_view_t *self, double x, double y)
2861{
2862 dt_develop_t *dev = (dt_develop_t *)self->data;
2863 float image_box[4] = { 0.0f };
2864 dt_dev_get_image_box_in_widget(dev, self->width, self->height, image_box);
2865
2866 return _is_in_frame(image_box[2], image_box[3], round(x - image_box[0]), round(y - image_box[1]));
2867}
2868
2869static gboolean mouse_in_actionarea(dt_view_t *self, double x, double y)
2870{
2871 return _is_in_frame(self->width, self->height, round(x), round(y));
2872}
2873
2874static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y)
2875{
2876 if(mouse_in_imagearea(self, x, y))
2878 else if(mouse_in_actionarea(self, x, y))
2880 else
2882}
2883
2885{
2886 dt_develop_t *dev = (dt_develop_t *)self->data;
2888}
2889
2890static void _delayed_history_commit(gpointer data)
2891{
2892 dt_develop_t *dev = (dt_develop_t *)data;
2893
2894 // Figure out if an history item needs to be added
2895 // aka drawn masks have changed somehow. This is more expensive
2896 // but more reliable than handling individually all editing operations
2897 // in all callbacks in all possible mask types.
2901
2902 if(dev->forms_changed)
2904}
2905
2915
2916static float _darkroom_edge_pan_velocity(const double position, const double size, const float margin)
2917{
2918 const double near_edge = position < margin ? margin - position
2919 : position > size - margin ? position - (size - margin)
2920 : 0.0;
2921 if(near_edge <= 0.0) return 0.0f;
2922
2923 const float edge_distance = CLAMPF(near_edge / margin, 0.0f, 1.0f);
2924 const float velocity = 0.10f + 0.90f * edge_distance * edge_distance;
2925 return position < margin ? -velocity : velocity;
2926}
2927
2929{
2930 dt_gui_gtk_t *gui = darktable.gui;
2931 if(IS_NULL_PTR(gui) || IS_NULL_PTR(dev)) return FALSE;
2932
2933 dt_masks_form_gui_t *form_gui = dev->form_gui;
2934 const gboolean creating_shape_mode = !IS_NULL_PTR(form_gui) && form_gui->creation;
2935
2936 return gui->mouse.is_dragging || creating_shape_mode;
2937}
2938
2947 const double pointer_x,
2948 const double pointer_y,
2949 const int width,
2950 const int height,
2952{
2953 dt_gui_gtk_t *gui = darktable.gui;
2955 if(IS_NULL_PTR(gui) || IS_NULL_PTR(ctl) || IS_NULL_PTR(self)) return;
2956
2957 dt_develop_t *dev = (dt_develop_t *)self->data;
2958 if(IS_NULL_PTR(dev)) return;
2959
2960 // recheck the global eligibility conditions
2962
2963 if(!gui->pan_edge.enabled
2965 || dev->roi.scaling <= 1.0f)
2966 return;
2967
2968 float image_box[4] = { 0.0f };
2970 const double image_x = pointer_x - image_box[0];
2971 const double image_y = pointer_y - image_box[1];
2972
2973 edge->dev = dev;
2974 edge->drag = TRUE;
2975 edge->inside_image = mouse_in_imagearea(self, pointer_x, pointer_y);
2976 const gboolean inside_action_area = mouse_in_actionarea(self, pointer_x, pointer_y)
2977 && image_box[2] > 0.0f && image_box[3] > 0.0f;
2978 edge->margin = inside_action_area
2979 ? CLAMPF(MIN(image_box[2], image_box[3]) / 3, 1, DARKROOM_EDGE_PAN_MARGIN_PX)
2980 : 0.0f;
2981 edge->in_margin = inside_action_area
2982 && edge->margin > 0.0f
2983 && (image_x < edge->margin
2984 || image_x > (double)image_box[2] - edge->margin
2985 || image_y < edge->margin
2986 || image_y > (double)image_box[3] - edge->margin);
2987
2988 if(!edge->in_margin) return;
2989
2990 edge->velocity[0] = _darkroom_edge_pan_velocity(image_x, image_box[2], edge->margin);
2991 edge->velocity[1] = _darkroom_edge_pan_velocity(image_y, image_box[3], edge->margin);
2992}
2993
3002 const double pointer_x,
3003 const double pointer_y,
3004 const int width,
3005 const int height)
3006{
3007 dt_gui_gtk_t *gui = darktable.gui;
3009 darkroom_edge_pan_test_t edge = { 0 };
3010 _darkroom_edge_pan_update_state(self, pointer_x, pointer_y, width, height, &edge);
3011
3012 if(IS_NULL_PTR(gui))
3013 return FALSE;
3014
3015 // reset and exit conditions
3016 if(!gui->pan_edge.enabled || IS_NULL_PTR(self) || IS_NULL_PTR(ctl) || !edge.drag)
3017 {
3018 gui->pan_edge.timeout_source = 0;
3020 return FALSE;
3021 }
3022
3023 if(!edge.in_margin)
3024 {
3025 // Leaving the edge band stops automatic ROI motion and immediately hands
3026 // control back to the regular drag path.
3027 gui->pan_edge.timeout_source = 0;
3028 gui->pan_edge.view = self;
3029 gui->pan_edge.velocity[0] = 0.0f;
3030 gui->pan_edge.velocity[1] = 0.0f;
3031 gui->pan_edge.last_time_us = 0;
3033 ctl->button_x = pointer_x;
3034 ctl->button_y = pointer_y;
3035 return FALSE;
3036 }
3037
3038 if(gui->pan_edge.velocity[0] == 0.0f && gui->pan_edge.velocity[1] == 0.0f)
3039 {
3040 // A mouse event may have stopped edge-pan before this tick runs.
3041 // End the timeout without releasing the stored drag reference position.
3042 gui->pan_edge.timeout_source = 0;
3043 gui->pan_edge.last_time_us = 0;
3044 ctl->button_x = pointer_x;
3045 ctl->button_y = pointer_y;
3046 return FALSE;
3047 }
3048
3049 const gint64 now_us = g_get_monotonic_time();
3050 const float elapsed_s = CLAMPF((now_us - gui->pan_edge.last_time_us) / 1000000.0f, 0.001f, 0.100f);
3051 gui->pan_edge.last_time_us = now_us;
3052
3053 float delta[2] = { gui->pan_edge.velocity[0] * DARKROOM_EDGE_PAN_SPEED_PX_PER_S * elapsed_s,
3055 };
3056 dt_develop_t *dev = edge.dev;
3058
3059 float roi[2] = { dev->roi.x + delta[0] / (float)dev->roi.processed_width,
3060 dev->roi.y + delta[1] / (float)dev->roi.processed_height
3061 };
3062 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
3063
3064 ctl->button_x = pointer_x;
3065 ctl->button_y = pointer_y;
3066
3067 if(dev->roi.x != roi[0] || dev->roi.y != roi[1])
3068 {
3069 dev->roi.x = roi[0];
3070 dev->roi.y = roi[1];
3071 // Updating ctl->button_x/y changes the cursor position, which is the same as a mouse move event.
3072 mouse_moved(self, pointer_x, pointer_y, 1.0, 0);
3073 //dt_control_queue_redraw_center();
3075 }
3076
3077 return TRUE;
3078}
3079
3086static gboolean _darkroom_edge_pan_tick(gpointer user_data)
3087{
3088 dt_gui_gtk_t *gui = darktable.gui;
3089 if(IS_NULL_PTR(gui))
3090 return FALSE;
3091
3092
3093 // Read the live pointer position instead of ctl->button_x/y: the latter only
3094 // stores the last mouse event, while the timeout must stop even if no event follows.
3095 dt_view_t *self = gui->pan_edge.view;
3097 GdkWindow *window = IS_NULL_PTR(center) ? NULL : gtk_widget_get_window(center);
3098 GdkDisplay *display = IS_NULL_PTR(window) ? NULL : gdk_window_get_display(window);
3099 GdkSeat *seat = IS_NULL_PTR(display) ? NULL : gdk_display_get_default_seat(display);
3100 GdkDevice *pointer = IS_NULL_PTR(seat) ? NULL : gdk_seat_get_pointer(seat);
3101 int pointer_x = 0;
3102 int pointer_y = 0;
3103 GtkAllocation allocation = { 0 };
3104
3105 if(IS_NULL_PTR(window) || IS_NULL_PTR(pointer))
3106 {
3107 gui->pan_edge.timeout_source = 0;
3109 return FALSE;
3110 }
3111
3112 gdk_window_get_device_position(window, pointer, &pointer_x, &pointer_y, NULL);
3113 gtk_widget_get_allocation(center, &allocation);
3114
3115 return _darkroom_edge_pan_apply(self, pointer_x, pointer_y, allocation.width, allocation.height);
3116}
3117
3118void mouse_moved(dt_view_t *self, double x, double y, double pressure, int which)
3119{
3120 dt_develop_t *dev = (dt_develop_t *)self->data;
3122 dt_gui_gtk_t *gui = darktable.gui;
3123
3124 const gboolean picker_active = dt_iop_color_picker_is_visible(dev);
3125
3126 // change cursor appearance by default
3128 gboolean handled = FALSE;
3129
3130 if(picker_active && ctl->button_down && ctl->button_down_which == 1)
3131 {
3132 // module requested a color box
3133 if(mouse_in_imagearea(self, x, y))
3134 {
3136 gboolean sample_changed = FALSE;
3137 float mouse_point[2] = { (float)x, (float)y };
3138 dt_dev_coordinates_widget_to_image_norm(dev, mouse_point, 1);
3139 const float delta[2] = {
3140 1.0f / dev->roi.processed_width,
3141 1.0f / dev->roi.processed_height
3142 };
3143
3144 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
3145 {
3146 float anchor[2] = { 0.0f };
3148 const float box[4] = {
3149 fmaxf(0.0, MIN(anchor[0], mouse_point[0]) - delta[0]),
3150 fmaxf(0.0, MIN(anchor[1], mouse_point[1]) - delta[1]),
3151 fminf(1.0, MAX(anchor[0], mouse_point[0]) + delta[0]),
3152 fminf(1.0, MAX(anchor[1], mouse_point[1]) + delta[1])
3153 };
3154 dt_boundingbox_t image_box = { sample->box[0], sample->box[1], sample->box[2], sample->box[3] };
3156
3157 for(int k = 0; k < 4; k++)
3158 sample_changed |= (image_box[k] != box[k]);
3159
3160 if(sample_changed)
3162 }
3163 else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
3164 {
3165 float image_point[2] = { 0.0f };
3166 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
3167 sample_changed = memcmp(image_point, mouse_point, sizeof(mouse_point)) != 0;
3168 if(sample_changed)
3170 }
3171 }
3172 handled = TRUE;
3173 }
3174 else if(picker_active)
3175 {
3176 // Keep module-specific hover overlays and live-edit cursors disabled while the picker owns the center view.
3177 handled = TRUE;
3178 }
3179
3180 // masks
3181 else if(dt_masks_get_visible_form(dev)
3182 && dt_masks_events_mouse_moved(dev->gui_module, x, y, pressure, which))
3183 {
3185 handled = TRUE;
3186 }
3187
3188 // module
3189 else if(dev->gui_module && dev->gui_module->mouse_moved
3190 &&dev->gui_module->mouse_moved(dev->gui_module, x, y, pressure, which))
3191 {
3192 handled = TRUE;
3193 }
3194
3195 if(handled && ctl->button_down && ctl->button_down_which == 1)
3196 gui->mouse.is_dragging = TRUE;
3197
3198 darkroom_edge_pan_test_t edge = { 0 };
3199 _darkroom_edge_pan_update_state(self, x, y, self->width, self->height, &edge);
3200
3201 if(!edge.drag && (gui->pan_edge.timeout_source || gui->pan_edge.block_normal_pan))
3202 {
3204 }
3205
3206 if(edge.drag && !edge.in_margin && gui->pan_edge.timeout_source)
3207 {
3208 /* The drag has already activated edge-pan. Leaving the edge band must stop
3209 timeout-driven ROI motion immediately and restore normal pan right away. */
3210 gui->pan_edge.view = self;
3211 gui->pan_edge.velocity[0] = 0.0f;
3212 gui->pan_edge.velocity[1] = 0.0f;
3213 gui->pan_edge.last_time_us = 0;
3215 if(gui->pan_edge.timeout_source)
3216 {
3217 g_source_remove(gui->pan_edge.timeout_source);
3218 gui->pan_edge.timeout_source = 0;
3219 }
3220 ctl->button_x = x;
3221 ctl->button_y = y;
3222 }
3223
3224 // While a left-button drag is in the edge band, the timeout owns ROI motion.
3225 // The current mouse position only updates the velocity that each tick applies.
3226 if(edge.in_margin)
3227 {
3228 gui->pan_edge.view = self;
3230 gui->pan_edge.velocity[0] = edge.velocity[0];
3231 gui->pan_edge.velocity[1] = edge.velocity[1];
3232 if(!gui->pan_edge.timeout_source)
3233 {
3234 gui->pan_edge.last_time_us = g_get_monotonic_time();
3237 }
3238 }
3239
3241
3243 && darktable.control->button_down_which == 1 && dev->roi.scaling > 1)
3244 {
3245 float delta[2] = { x - ctl->button_x, y - ctl->button_y };
3247
3248 float roi[2] = { dev->roi.x - (delta[0] / dev->roi.processed_width),
3249 dev->roi.y - (delta[1] / dev->roi.processed_height) };
3250 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
3251
3252 dev->roi.x = roi[0];
3253 dev->roi.y = roi[1];
3254 ctl->button_x = x;
3255 ctl->button_y = y;
3256
3258 return;
3259 }
3260
3261 if(handled)
3262 {
3264 return;
3265 }
3266
3267 // Edge-pan owns ROI motion only while the timeout is actively driving updates.
3269 {
3270 ctl->button_x = x;
3271 ctl->button_y = y;
3272 return;
3273 }
3274
3275 // panning with left mouse button
3277 {
3278 float delta[2] = { x - ctl->button_x, y - ctl->button_y };
3280
3281 // new roi position in full image scale
3282 float roi[2] = { dev->roi.x - (delta[0] / dev->roi.processed_width),
3283 dev->roi.y - (delta[1] / dev->roi.processed_height) };
3284 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
3285
3286 dev->roi.x = roi[0];
3287 dev->roi.y = roi[1];
3288
3289 // update clicked position
3290 ctl->button_x = x;
3291 ctl->button_y = y;
3292
3294 }
3295}
3296
3297
3298int button_released(dt_view_t *self, double x, double y, int which, uint32_t state)
3299{
3300 dt_develop_t *dev = (dt_develop_t *)self->data;
3301
3302 dt_print(DT_DEBUG_INPUT, "[darkroom] button released which: %d state: %d x: %.2f y: %.2f\n",
3303 which, state, x, y);
3304
3305 if(which == 1)
3306 {
3310 }
3311
3312 if(dt_iop_color_picker_is_visible(dev) && which == 1)
3313 {
3314 // only sample box picker at end, for speed
3316 dt_control_queue_cursor(GDK_LEFT_PTR);
3317
3319 return 1;
3320 }
3321 // masks
3324 {
3325 // Change on mask parameters and image output.
3327 dt_dev_pixelpipe_update_history_preview(dev); // Needed if mask selection changed
3329 return 1;
3330 }
3331
3332 // module
3333 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->button_released
3334 && dev->gui_module->button_released(dev->gui_module, x, y, which, state))
3335 {
3336 // Click in modules should handle history changes internally.
3337 return 1;
3338 }
3339
3340 return 0;
3341}
3342
3343
3344int button_pressed(dt_view_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
3345{
3347 dt_develop_t *dev = (dt_develop_t *)self->data;
3348
3349 dt_print(DT_DEBUG_INPUT, "[darkroom] button pressed which: %d type: %d x: %.2f y: %.2f pressure: %f\n",
3350 which, type, x, y, pressure);
3351
3352 // Grab focus on any click so we can interact from keyboard
3353 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui));
3354 if(which == 1)
3355 {
3358 }
3359
3361 {
3362 float point[2] = { (float)x, (float)y };
3364
3365 const float zoom_scale = dt_dev_get_fit_scale(dev);
3366 float handle[2] = { 6.0f, 6.0f };
3368 handle[0] /= dev->roi.processed_width;
3369 handle[1] /= dev->roi.processed_height;
3370
3371 if(which == 1)
3372 {
3373 if(mouse_in_imagearea(self, x, y))
3374 {
3375 // The default box will be a square with 1% of the image width
3376 const float delta_x = 0.01f;
3377 const float delta_y = delta_x * (float)dev->roi.processed_width / (float)dev->roi.processed_height;
3378
3379 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
3380 {
3381 /* Box drags need one anchor corner stored in sample->point so mouse motion can stretch
3382 the rectangle against that fixed corner. Keep that anchor local to the darkroom drag
3383 state, but let the picker API own the actual sampling geometry and update signaling. */
3384 float raw_point[2] = { point[0], point[1] };
3386 memcpy(sample->point, raw_point, sizeof(raw_point));
3387
3388 dt_boundingbox_t image_box = { sample->box[0], sample->box[1], sample->box[2], sample->box[3] };
3390
3391 // this is slightly more than as drawn, to give room for slop
3392 gboolean on_corner_prev_box = TRUE;
3393 float opposite[2] = { 0.0f };
3394
3395 if(fabsf(point[0] - image_box[0]) <= handle[0])
3396 opposite[0] = image_box[2];
3397 else if(fabsf(point[0] - image_box[2]) <= handle[0])
3398 opposite[0] = image_box[0];
3399 else
3400 on_corner_prev_box = FALSE;
3401
3402 if(fabsf(point[1] - image_box[1]) <= handle[1])
3403 opposite[1] = image_box[3];
3404 else if(fabsf(point[1] - image_box[3]) <= handle[1])
3405 opposite[1] = image_box[1];
3406 else
3407 on_corner_prev_box = FALSE;
3408
3409 if(on_corner_prev_box)
3410 {
3411 float raw_opposite[2] = { opposite[0], opposite[1] };
3412 dt_dev_coordinates_image_norm_to_raw_norm(dev, raw_opposite, 1);
3413 memcpy(sample->point, raw_opposite, sizeof(raw_opposite));
3414 }
3415 else
3416 {
3417 const dt_boundingbox_t box = {
3418 fmaxf(0.0, point[0] - delta_x),
3419 fmaxf(0.0, point[1] - delta_y),
3420 fminf(1.0, point[0] + delta_x),
3421 fminf(1.0, point[1] + delta_y)
3422 };
3424 }
3425 dt_control_queue_cursor(GDK_FLEUR);
3426 }
3427 else
3428 {
3429 /* Point pickers must not pre-write sample->point before going through the picker API,
3430 otherwise the setter sees no geometry change and skips the resample request. */
3432 }
3433 }
3435 return 1;
3436 }
3437
3438 if(which == 3)
3439 {
3440 // apply a live sample's area to the active picker?
3441 // FIXME: this is a naive implementation, nicer would be to cycle through overlapping samples then reset
3444 for(GSList *samples = darktable.develop->color_picker.samples; samples; samples = g_slist_next(samples))
3445 {
3446 dt_colorpicker_sample_t *live_sample = samples->data;
3447 if(live_sample->size == DT_LIB_COLORPICKER_SIZE_BOX && picker->kind != DT_COLOR_PICKER_POINT)
3448 {
3449 dt_boundingbox_t live_box = { live_sample->box[0], live_sample->box[1], live_sample->box[2], live_sample->box[3] };
3451 if(point[0] < live_box[0] || point[0] > live_box[2]
3452 || point[1] < live_box[1] || point[1] > live_box[3])
3453 continue;
3455 }
3456 else if(live_sample->size == DT_LIB_COLORPICKER_SIZE_POINT && picker->kind != DT_COLOR_PICKER_AREA)
3457 {
3458 // magic values derived from _darkroom_pickers_draw
3459 float slop[2] = {
3460 MAX(26.0f, roundf(3.0f * zoom_scale)),
3461 MAX(26.0f, roundf(3.0f * zoom_scale))
3462 };
3464 slop[0] /= dev->roi.processed_width;
3465 slop[1] /= dev->roi.processed_height;
3466 float live_point[2] = { live_sample->point[0], live_sample->point[1] };
3468 if(fabsf(point[0] - live_point[0]) > slop[0]
3469 || fabsf(point[1] - live_point[1]) > slop[1])
3470 continue;
3472 }
3473 else
3474 continue;
3475 return 1;
3476 }
3477
3478 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
3479 {
3480 // default is hardcoded this way
3481 // FIXME: color_pixer_proxy should have an dt_iop_color_picker_clear_area() function for this
3482 dt_boundingbox_t reset = { 0.01f, 0.01f, 0.99f, 0.99f };
3484 }
3485 return 1;
3486 }
3487 }
3488
3489 // masks
3491 && dt_masks_events_button_pressed(dev->gui_module, x, y, pressure, which, type, state))
3492 {
3493 if(which == 1)
3497 return 1;
3498 }
3499 // module
3500 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->button_pressed
3501 && dev->gui_module->button_pressed(dev->gui_module, x, y, pressure, which, type, state))
3502 {
3503 if(which == 1)
3505 return 1;
3506 }
3507
3508 if(which == 1 && dev->roi.scaling > 1.0f && mouse_in_imagearea(self, x, y))
3510
3511 if(which == 2)
3512 {
3513 // Incremental zoom-in on middle button click, from fit to 800%
3514 // by power of 2 increments (100%, 200%, 400%, 800%).
3515 float new_scale = 1.f;
3516 if(dev->roi.scaling < 1.f || dev->roi.scaling > 7.f / dev->roi.natural_scale)
3517 new_scale = 1.f; // zoom to fit
3518 else if(dev->roi.scaling * dev->roi.natural_scale < 1.f)
3519 new_scale = 1.f / dev->roi.natural_scale; // 100 %
3520 else
3521 new_scale = floorf(dev->roi.scaling * dev->roi.natural_scale) * 2.f / dev->roi.natural_scale;
3522
3523 const float point[2] = { x, y };
3524 return _change_scaling(dev, point, new_scale);
3525 }
3526
3527 return 0;
3528}
3529
3530static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling)
3531{
3532 const float old_scaling = dev->roi.scaling;
3533
3534 // Round scaling to 1.0 (fit) if close enough
3535 const float epsilon = fabsf(old_scaling - new_scaling);
3536 if(fabsf(new_scaling - 1.0f) < epsilon)
3537 dev->roi.scaling = 1.0f;
3538 else
3539 dev->roi.scaling = new_scaling;
3540
3542 {
3543 // Calculate zoom position offset to keep mouse position fixed during zoom
3544 float center[2] = { 0.0f };
3545 float mouse_offset[2] = { point[0], point[1] };
3546 dt_dev_get_widget_center(dev, center);
3547 mouse_offset[0] -= center[0];
3548 mouse_offset[1] -= center[1];
3549
3550
3551 // Keep the image point under the mouse fixed in widget coordinates while
3552 // the pipeline zoom stays DPI-invariant.
3553 const float old_zoom = dt_dev_get_widget_zoom_scale(dev, old_scaling);
3554 const float new_zoom = dt_dev_get_widget_zoom_scale(dev, dev->roi.scaling);
3555 if(old_zoom <= 1e-6f || new_zoom <= 1e-6f)
3556 {
3557 dev->roi.scaling = old_scaling;
3558 return 0;
3559 }
3560
3561 // Adjust the center to compensate for the scale change
3562 int proc_w = 0.f, proc_h = 0.f;
3563 dt_dev_get_processed_size(dev, &proc_w, &proc_h);
3564 dev->roi.x += mouse_offset[0] * (1.f / old_zoom - 1.f / new_zoom) / proc_w;
3565 dev->roi.y += mouse_offset[1] * (1.f / old_zoom - 1.f / new_zoom) / proc_h;
3566
3567 dt_dev_check_zoom_pos_bounds(dev, &dev->roi.x, &dev->roi.y, NULL, NULL);
3569 return 1;
3570 }
3571 else
3572 {
3573 // Invalid zoom level, keep previous value
3574 dev->roi.scaling = old_scaling;
3575 return 0;
3576 }
3577}
3578
3579static gboolean _center_view_free_zoom(dt_view_t *self, double x, double y, int up, int state, int flow)
3580{
3582
3583 // Commit the new scaling
3584 const float step = 1.02f;
3585 const float new_scaling = dev->roi.scaling * powf(step, (float)-flow);
3586 const float point[2] = { x, y };
3587 return _change_scaling(dev, point, new_scaling);
3588}
3589
3590
3591int scrolled(dt_view_t *self, double x, double y, int up, int state, int delta_y)
3592{
3594
3595 dt_develop_t *dev = (dt_develop_t *)self->data;
3596
3597 dt_print(DT_DEBUG_INPUT, "[darkroom] scrolled: up: %i x: %.2f y: %.2f state: %i flow: %i\n",
3598 up, x, y, state, delta_y);
3599
3600 // masks
3602 && dt_masks_events_mouse_scrolled(dev->gui_module, x, y, up, state, delta_y))
3603 {
3604 // Scroll on masks changes their size, therefore mask parameters and image output.
3606 return TRUE;
3607 }
3608
3609 // module
3610 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->scrolled && dev->gui_module->scrolled(dev->gui_module, x, y, up, state))
3611 {
3612 // Scroll in modules should handle history changes internally.
3613 return TRUE;
3614 }
3615
3616 // free zoom
3617 return _center_view_free_zoom(self, x, y, up, state, delta_y);
3618}
3619
3620static void _key_scroll(dt_develop_t *dev)
3621{
3622 dt_dev_check_zoom_pos_bounds(dev, &dev->roi.x, &dev->roi.y, NULL, NULL);
3625}
3626
3627
3628int key_pressed(dt_view_t *self, GdkEventKey *event)
3629{
3630 if(!gtk_window_is_active(GTK_WINDOW(darktable.gui->ui->main_window))) return FALSE;
3631
3632 dt_develop_t *dev = (dt_develop_t *)self->data;
3633
3635 {
3637 return 1;
3638 }
3639
3640 // module
3641 else if(!IS_NULL_PTR(dev->gui_module)
3642 && !IS_NULL_PTR(dev->gui_module->key_pressed)
3643 && dev->gui_module->key_pressed(dev->gui_module, event))
3644 {
3646 return 1;
3647 }
3648
3649 const gboolean shift = dt_modifier_is(event->state, GDK_SHIFT_MASK);
3650 const gboolean ctrl = dt_modifier_is(event->state, GDK_CONTROL_MASK);
3651 const gboolean ctrl_any = dt_modifiers_include(event->state, GDK_CONTROL_MASK);
3652 guint key = dt_keys_mainpad_alternatives(event->keyval);
3653
3654 if(ctrl_any)
3655 {
3656 const float zoom_step = 1.1f;
3657 float center[2] = { 0.0f };
3658 dt_dev_get_widget_center(dev, center);
3659
3660 switch(key)
3661 {
3662 case GDK_KEY_plus:
3663 return _change_scaling(dev, center, dev->roi.scaling * zoom_step);
3664 case GDK_KEY_minus:
3665 return _change_scaling(dev, center, dev->roi.scaling / zoom_step);
3666 }
3667 }
3668
3669 float multiplier = (shift) ? 4.f :
3670 (ctrl) ? 0.5f :
3671 1.f;
3672
3673 float delta[2] = { 10.f * multiplier, 10.f * multiplier };
3675
3676 switch(key)
3677 {
3678 case GDK_KEY_Up:
3679 {
3680 dev->roi.y -= delta[1] / (float)dev->roi.processed_height;
3681 _key_scroll(dev);
3682 return 1;
3683 }
3684 case GDK_KEY_Down:
3685 {
3686 dev->roi.y += delta[1] / (float)dev->roi.processed_height;
3687 _key_scroll(dev);
3688 return 1;
3689 }
3690 case GDK_KEY_Left:
3691 {
3692 dev->roi.x -= delta[0] / (float)dev->roi.processed_width;
3693 _key_scroll(dev);
3694 return 1;
3695 }
3696 case GDK_KEY_Right:
3697 {
3698 dev->roi.x += delta[0] / (float)dev->roi.processed_width;
3699 _key_scroll(dev);
3700 return 1;
3701 }
3702 case GDK_KEY_Escape:
3703 {
3704 dt_ctl_switch_mode_to("lighttable");
3705 return TRUE;
3706 }
3707 }
3708
3709 return 0;
3710}
3711
3712void configure(dt_view_t *self, int wd, int ht)
3713{
3714 dt_develop_t *dev = (dt_develop_t *)self->data;
3715
3716 // Configure event is called when initing the view AND upon window resizes events (through Gtk widget/window resize commands).
3717 // At init time, final window size may not be correct just yet.
3718 // It will be when we call dt_ui_restore_panels(), which will resize stuff properly,
3719 // but that will be only when entering the current view.
3720 // Until we run dt_dev_configure(), main preview pipe gets output size -1×-1 px
3721 // which aborts the pipe recompute early. As soon as we init
3722 // sizes with something "valid" with regard to the pipe, pipeline runs.
3723 // Problem is it will not be valid with regard to the window size and the output will be thrown out
3724 // until we get the final size.
3725 // TD;DR: until we get the final window size, which happens
3726 // only when entering the view, don't configure the main preview pipeline, which will disable useless recomputes.
3728 {
3729 // Reference dimensions before ISO 12646 mode
3730 dev->roi.orig_height = ht;
3731 dev->roi.orig_width = wd;
3733 }
3734}
3735
3736// clang-format off
3737// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3738// vim: shiftwidth=2 expandtab tabstop=2 cindent
3739// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3740// clang-format on
void dt_accels_connect_accels(dt_accels_t *accels)
Actually enable accelerators after having loaded user config.
void dt_accels_connect_active_group(dt_accels_t *accels, const gchar *group)
Connect the contextual active accels group to the window. Views can declare their own set of contextu...
void dt_accels_disconnect_active_group(dt_accels_t *accels)
Disconnect the contextual active accels group from the window.
void dt_accels_new_virtual_shortcut(dt_accels_t *accels, GtkAccelGroup *accel_group, const gchar *accel_path, GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
Add a new virtual shortcut. Virtual shortcuts are immutable, read-only and don't trigger any action....
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_attach_scroll_handler(dt_accels_t *accels, gboolean(*callback)(GdkEventScroll event, void *data), void *data)
Attach a new global scroll event callback. So far this is used in darkroom to redirect scroll events ...
void dt_accels_detach_scroll_handler(dt_accels_t *accels)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_add_int(dt_atomic_int *var, int incr)
int position()
#define m
Definition basecurve.c:278
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3483
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_combobox_set_entries_ellipsis(GtkWidget *widget, PangoEllipsizeMode ellipis)
Definition bauhaus.c:2064
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:2162
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1780
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
static void set_color(cairo_t *cr, GdkRGBA color)
Definition bauhaus.h:446
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:392
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static const float scaling
GList * dt_collection_get_all(const dt_collection_t *collection, int limit)
Definition collection.c:889
void dt_iop_color_picker_init(void)
void dt_iop_color_picker_cleanup(void)
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
gboolean dt_iop_color_picker_is_visible(const dt_develop_t *dev)
@ DT_COLOR_PICKER_AREA
@ DT_COLOR_PICKER_POINT
static void profile_changed(GtkWidget *widget, gpointer user_data)
Definition colorin.c:511
@ DT_LIB_COLORPICKER_SIZE_POINT
Definition colorpicker.h:37
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:38
@ DT_COLORSPACE_FILE
Definition colorspaces.h:83
@ DT_COLORSPACE_SRGB
Definition colorspaces.h:84
@ DT_COLORSPACES_PROFILE_TYPE_SOFTPROOF
Definition colorspaces.h:77
@ DT_PROFILE_GAMUTCHECK
@ DT_PROFILE_SOFTPROOF
@ DT_PROFILE_NORMAL
dt_Lab_to_XYZ(Lab, XYZ)
const float threshold
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
static dt_aligned_pixel_t RGB
const float delta
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_image_check_camera_missing_sample(const struct dt_image_t *img)
char * key
int type
char * name
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
void dt_ctl_switch_mode_to(const char *mode)
Definition control.c:657
int32_t dt_control_get_mouse_over_id()
Definition control.c:923
void dt_control_set_mouse_over_id(int32_t value)
Definition control.c:931
void dt_control_log(const char *msg,...)
Definition control.c:761
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
void dt_control_log_busy_leave()
Definition control.c:840
void dt_control_commit_cursor()
Definition control.c:334
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
void dt_control_set_keyboard_over_id(int32_t value)
Definition control.c:957
#define dt_control_change_cursor(cursor)
Definition control.h:116
#define dt_control_queue_cursor(cursor)
Definition control.h:135
#define dt_control_set_cursor_visible(visible)
Definition control.h:148
static gboolean _quickbutton_press_release(GtkWidget *button, GdkEventButton *event, GtkWidget *popover)
Definition darkroom.c:1828
void init(dt_view_t *self)
Definition darkroom.c:219
static void _overexposed_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1611
static dt_darkroom_layout_t _lib_darkroom_get_layout(dt_view_t *self)
Definition darkroom.c:292
static gboolean _darkroom_autoset_button_is_running
Definition darkroom.c:175
static gboolean mouse_in_actionarea(dt_view_t *self, double x, double y)
Definition darkroom.c:2869
static gboolean _darkroom_edge_pan_tick(gpointer user_data)
Move the darkroom ROI while a drag stays in the center-widget edge band.
Definition darkroom.c:3086
static void _darkroom_ioporder_quickbutton_clicked(GtkButton *button, gpointer user_data)
Definition darkroom.c:179
void leave(dt_view_t *self)
Definition darkroom.c:2644
static void _darkroom_sample_raw_point_to_image_norm(const dt_colorpicker_sample_t *const sample, float point[2])
Definition darkroom.c:302
static uint64_t _darkroom_preview_fallback_backbuf_hash
Definition darkroom.c:591
static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data)
Definition darkroom.c:1287
#define DARKROOM_EDGE_PAN_SPEED_PX_PER_S
Definition darkroom.c:151
static int _darkroom_preview_fallback_height
Definition darkroom.c:593
static dt_dev_pixelpipe_cache_wait_t _darkroom_preview_wait
Definition darkroom.c:587
static gboolean _is_scroll_captured_by_widget()
Definition darkroom.c:2530
void connect_button_press_release(GtkWidget *w, GtkWidget *p)
Definition darkroom.c:1851
int button_pressed(dt_view_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
Definition darkroom.c:3344
static void _release_expose_source_caches(void)
Definition darkroom.c:637
static void display_borders_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1547
static void rawoverexposed_threshold_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1686
static void display_brightness_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1540
static void _guides_quickbutton_clicked(GtkWidget *widget, gpointer user_data)
Definition darkroom.c:1517
gboolean _switch_to_prev_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1889
static gboolean _darkroom_toolbox_button_activate_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1457
void cleanup(dt_view_t *self)
Definition darkroom.c:243
static dt_iop_module_t * _darkroom_pending_focus_module
Definition darkroom.c:167
#define DARKROOM_EDGE_PAN_INTERVAL_MS
Definition darkroom.c:149
static cairo_status_t _write_snapshot_data(void *closure, const unsigned char *data, unsigned int length)
Definition darkroom.c:283
static void display_mask_checker_1_callback(GtkColorButton *widget, gpointer user_data)
Persist the global mask-preview appearance and resynchronize every node using it.
Definition darkroom.c:1562
static darkroom_locked_surface_t _darkroom_preview_locked
Definition darkroom.c:585
static gboolean _darkroom_is_only_selected_sample(gboolean is_primary_sample, dt_colorpicker_sample_t *selected_sample, gboolean display_samples)
Definition darkroom.c:297
static void display_mask_black_and_white_callback(GtkToggleButton *toggle, gpointer user_data)
Definition darkroom.c:1594
static gboolean mouse_in_imagearea(dt_view_t *self, double x, double y)
Definition darkroom.c:2860
static gboolean _is_in_frame(const int width, const int height, const int x, const int y)
Definition darkroom.c:2849
static darkroom_locked_surface_t _darkroom_main_locked
Definition darkroom.c:584
static int _darkroom_preview_fallback_width
Definition darkroom.c:592
void reset(dt_view_t *self)
Definition darkroom.c:1266
static gboolean _darkroom_locked_main_valid_for_zoom(const darkroom_expose_state_t *state, const uint64_t zoom_hash)
Definition darkroom.c:894
static void display_mask_checker_2_callback(GtkColorButton *widget, gpointer user_data)
Definition darkroom.c:1574
static gboolean _render_preview_fallback_surface(cairo_t *cr)
Definition darkroom.c:851
#define DARKROOM_EDGE_PAN_MARGIN_PX
Definition darkroom.c:150
static void _render_iso12646(cairo_t *cr, double width, double height, int border)
Definition darkroom.c:480
static void _dev_change_image(dt_view_t *self, const int32_t imgid)
Definition darkroom.c:1385
static GtkWidget * _darkroom_ioporder_button
Definition darkroom.c:168
static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y)
Definition darkroom.c:2874
static gboolean _darkroom_edge_pan_apply(dt_view_t *self, const double pointer_x, const double pointer_y, const int width, const int height)
Apply one edge-pan step when the current drag is still eligible.
Definition darkroom.c:3001
static gboolean _toolbar_show_popup(gpointer user_data)
Definition darkroom.c:1408
static void _preview_pipe_finished(gpointer instance, gpointer user_data)
Definition darkroom.c:1914
static void _darkroom_edge_pan_update_state(dt_view_t *self, const double pointer_x, const double pointer_y, const int width, const int height, darkroom_edge_pan_test_t *edge)
Test every condition that allows edge-pan for the current pointer.
Definition darkroom.c:2946
static gboolean _lock_pipe_surface(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, darkroom_locked_surface_t *locked, const gboolean keep_previous_on_fail, const gboolean lock_read)
Definition darkroom.c:649
static void upper_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1638
int scrolled(dt_view_t *self, double x, double y, int up, int state, int delta_y)
Definition darkroom.c:3591
static gboolean _build_preview_fallback_surface(dt_develop_t *dev, const int width, const int height, const int border, const dt_aligned_pixel_t bg_color, const uint64_t zoom_hash)
Definition darkroom.c:774
static void colorscheme_callback(GtkWidget *combo, gpointer user_data)
Definition darkroom.c:1618
static void _delayed_history_commit(gpointer data)
Definition darkroom.c:2890
int key_pressed(dt_view_t *self, GdkEventKey *event)
Definition darkroom.c:3628
static GtkWidget * _darkroom_autoset_popover
Definition darkroom.c:173
static uint64_t _darkroom_preview_fallback_zoom_hash
Definition darkroom.c:590
static void _key_scroll(dt_develop_t *dev)
Definition darkroom.c:3620
static void _release_preview_fallback_surface(void)
Definition darkroom.c:615
gboolean _focus_main_image(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1857
static gboolean _center_view_free_zoom(dt_view_t *self, double x, double y, int up, int state, int flow)
Definition darkroom.c:3579
void mouse_leave(dt_view_t *self)
Definition darkroom.c:2805
static void _darkroom_reset_expose_state(darkroom_expose_state_t *state)
Definition darkroom.c:903
static void _darkroom_restart_cache_wait(gpointer user_data)
Definition darkroom.c:630
void mouse_moved(dt_view_t *self, double x, double y, double pressure, int which)
Definition darkroom.c:3118
static gboolean _darkroom_toolbox_button_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1468
static void rawoverexposed_colorscheme_callback(GtkWidget *combo, gpointer user_data)
Definition darkroom.c:1676
void configure(dt_view_t *self, int wd, int ht)
Definition darkroom.c:3712
static void _softproof_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1697
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
static void lower_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1628
static void _darkroom_log_image_load_error(const int ret)
Definition darkroom.c:1271
int button_released(dt_view_t *self, double x, double y, int which, uint32_t state)
Definition darkroom.c:3298
static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data)
Definition darkroom.c:1397
static dt_autoset_manager_t * _autoset_manager
Definition darkroom.c:171
static void _reset_edge_pan()
Definition darkroom.c:232
static void _guides_view_changed(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self)
Definition darkroom.c:1523
void enter(dt_view_t *self)
Definition darkroom.c:2564
static void _darkroom_change_rendering_size(GtkWidget *combobox, gpointer user_data)
Definition darkroom.c:1603
static cairo_surface_t * _darkroom_preview_fallback_surface
Definition darkroom.c:588
static gboolean _render_main_locked_surface(cairo_t *cr, dt_develop_t *dev, darkroom_locked_surface_t *locked, const int width, const int height, const int border, const dt_aligned_pixel_t bg_color)
Definition darkroom.c:744
static void display_mask_checker_size_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1586
static float _darkroom_edge_pan_velocity(const double position, const double size, const float margin)
Definition darkroom.c:2916
static GtkWidget * _darkroom_autoset_list
Definition darkroom.c:174
static void _darkroom_autoset_popover_rebuild(dt_develop_t *dev)
Definition darkroom.c:1985
static void _darkroom_autoset_quickbutton_clicked(GtkButton *button, gpointer user_data)
Definition darkroom.c:1964
static void rawoverexposed_mode_callback(GtkWidget *combo, gpointer user_data)
Definition darkroom.c:1666
static void _gamut_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1710
static dt_dev_pixelpipe_cache_wait_t _darkroom_main_wait
Definition darkroom.c:586
static void _display_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1536
void expose(dt_view_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition darkroom.c:940
static void _rawoverexposed_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1659
static void _darkroom_sample_raw_box_to_image_norm(const dt_colorpicker_sample_t *const sample, float box[4])
Definition darkroom.c:310
static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling)
Definition darkroom.c:3530
gboolean _switch_to_next_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1864
static gchar * _darkroom_autoset_label(const dt_iop_module_t *module)
Definition darkroom.c:1971
static int32_t _darkroom_preview_fallback_imgid
Definition darkroom.c:589
static void _get_final_size_with_iso_12646(dt_develop_t *d)
Definition darkroom.c:1484
static gboolean _darkroom_edge_pan_enable_check(dt_develop_t *dev)
Definition darkroom.c:2928
static void _paint_all(cairo_t *cri, cairo_t *cr, cairo_surface_t *image_surface)
Definition darkroom.c:859
static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data)
Definition darkroom.c:2015
void gui_init(dt_view_t *self)
Definition darkroom.c:2020
static void _darkroom_autoset_button_set_running(const gboolean running)
Reflect autoset processing state on the darkroom quick button.
Definition darkroom.c:195
static void _update_softproof_gamut_checking(dt_develop_t *d)
Definition darkroom.c:1724
static GtkWidget * _darkroom_autoset_button
Definition darkroom.c:172
gboolean _scroll_on_focus(GdkEventScroll event, void *data)
Definition darkroom.c:2550
static void softproof_profile_callback(GtkWidget *combo, gpointer user_data)
Definition darkroom.c:1737
int try_enter(dt_view_t *self)
Definition darkroom.c:1342
void mouse_enter(dt_view_t *self)
Definition darkroom.c:2884
static int32_t _darkroom_pending_imgid
Definition darkroom.c:166
static void _darkroom_autoset_module_toggled(GtkToggleButton *toggle, gpointer user_data)
Definition darkroom.c:1979
static void _release_locked_surface(darkroom_locked_surface_t *locked)
Definition darkroom.c:595
static void mode_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1648
static gboolean _darkroom_center_pan_drag
Definition darkroom.c:169
static void _darkroom_prepare_image_surface(dt_develop_t *dev, const int width, const int height, darkroom_expose_state_t *state)
Definition darkroom.c:913
static void _iso_12646_quickbutton_clicked(GtkWidget *w, gpointer user_data)
Definition darkroom.c:1504
static void _darkroom_pickers_draw(dt_view_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pozx, int32_t pozy, GSList *samples, gboolean is_primary_sample)
Draw colorpicker samples overlays in darkroom view.
Definition darkroom.c:329
void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
Definition darkroom.c:472
static gboolean _darkroom_preview_fallback_valid(const dt_develop_t *dev, const int width, const int height, const uint64_t zoom_hash)
Definition darkroom.c:883
darktable_t darktable
Definition darktable.c:181
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1594
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
static gboolean dt_modifiers_include(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:901
#define UNKNOWN_IMAGE
Definition darktable.h:182
@ DT_DEBUG_INPUT
Definition darktable.h:729
@ DT_DEBUG_CONTROL
Definition darktable.h:716
@ DT_DEBUG_DEV
Definition darktable.h:717
#define O_BINARY
Definition darktable.h:77
float dt_boundingbox_t[4]
Definition darktable.h:709
#define DT_MODULE(MODVER)
Definition darktable.h:140
#define dt_free(ptr)
Definition darktable.h:456
static void dt_get_times(dt_times_t *t)
Definition darktable.h:921
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1043
static double dt_get_wtime(void)
Definition darktable.h:914
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:893
#define PATH_MAX
Definition darktable.h:1062
#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
void dt_dev_history_free_history(dt_develop_t *dev)
Free the whole history list attached to dev->history.
void dt_dev_pop_history_items(dt_develop_t *dev)
Thread-safe wrapper around dt_dev_pop_history_items_ext(), then update GUI.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_dev_pixelpipe_cache_wait_cleanup(dt_dev_pixelpipe_cache_wait_t *wait, const char *reason)
Cancel one pending GUI cache wait request and clear its runtime state.
gboolean dt_dev_pixelpipe_is_backbufer_valid(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_cache_wait_dump_pending(const char *reason)
Dump pending GUI cache wait requests for lifecycle debugging.
void dt_dev_pixelpipe_change_zoom_main(dt_develop_t *dev)
void dt_dev_pixelpipe_cache_wait_set_owner(dt_dev_pixelpipe_cache_wait_t *wait, const char *owner_tag, gpointer owner_object)
Attach debug ownership metadata to one cache wait request.
gboolean dt_dev_pixelpipe_cache_peek_gui(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, void **data, dt_pixel_cache_entry_t **cache_entry, dt_dev_pixelpipe_cache_wait_t *wait, dt_dev_pixelpipe_cache_ready_callback_t restart, gpointer restart_data)
#define dt_dev_pixelpipe_rebuild_all(dev)
#define dt_dev_pixelpipe_resync_history_main(dev)
#define dt_dev_pixelpipe_update_history_main(dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:979
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:309
void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
Get the displayed image rectangle in darkroom widget coordinates.
Definition develop.c:1730
void dt_dev_get_widget_center(const dt_develop_t *dev, float *point)
Get the center of the darkroom widget in logical coordinates.
Definition develop.c:1723
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:188
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1712
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:887
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1614
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1131
void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
Ensure that the current ROI position is within allowed bounds .
Definition develop.c:944
gboolean dt_dev_check_zoom_scale_bounds(dt_develop_t *dev)
Ensure that the current zoom level is within allowed bounds (for scrolling).
Definition develop.c:1844
void dt_dev_reset_roi(dt_develop_t *dev)
Definition develop.c:1751
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:98
void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp, const int64_t hash, const int64_t history_hash)
Definition develop.c:1886
float dt_dev_get_widget_zoom_scale(const dt_develop_t *dev, const float scaling)
Convert a darkroom scaling factor to GUI logical zoom.
Definition develop.c:1717
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1124
gboolean dt_dev_pipelines_share_preview_output(dt_develop_t *dev)
Tell whether the darkroom main and preview pipes currently target the same GUI output.
Definition develop.c:457
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1681
gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Clip the view to the ROI. WARNING: this must be done before any translation.
Definition develop.c:1781
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1625
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:1824
void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
Convert a widget-space distance to processed-image pixels.
Definition develop.c:986
float dt_dev_get_fit_scale(dt_develop_t *dev)
Get the scale factor that maps preview-buffer pixels to GUI coordinates.
Definition develop.c:1706
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1003
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1493
void dt_dev_start_all_pipelines(dt_develop_t *dev)
Definition develop.c:811
@ DT_DEV_IMAGE_STORAGE_DB_NOT_READ
Definition develop.h:526
@ DT_DEV_IMAGE_STORAGE_MIPMAP_NOT_FOUND
Definition develop.h:525
#define dt_dev_configure(dev, wd, ht)
Definition develop.h:601
void dtgtk_cairo_paint_bulb(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_overexposed(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_gamut_check(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_grid(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_flowchart(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Paint the flowchart icon using normalized 0..1 coordinates.
void dtgtk_cairo_paint_rawoverexposed(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_softproof(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_wand(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_display(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
#define dt_pthread_rwlock_wrlock
Definition dtpthread.h:394
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:113
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:143
void dt_gui_add_help_link(GtkWidget *widget, char *link)
Definition gtk.c:2022
GtkWidget * dt_gui_get_popup_relative_widget(GtkWidget *widget, GdkRectangle *rect)
Resolve the widget used as parent for nested popups on Wayland.
Definition gtk.c:2925
void dt_gui_refocus_center()
Definition gtk.c:3234
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#define dt_accels_new_darkroom_action(a, b, c, d, e, f, g)
Definition gtk.h:430
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:451
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
#define DT_GUI_MODULE(x)
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height, const float zoom_scale)
Definition guides.c:783
void dt_guides_update_popover_values()
Definition guides.c:847
void dt_guides_set_overlay_colors()
Definition guides.c:666
GtkWidget * dt_guides_popover(dt_view_t *self, GtkWidget *button)
Definition guides.c:703
void dt_guides_button_toggled(gboolean active)
Definition guides.c:776
void dt_guides_update_button_state()
Definition guides.c:766
const char * tooltip
Definition image.h:251
void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1998
void dt_iop_gui_set_expander(dt_iop_module_t *module)
Definition imageop.c:2731
void dt_iop_gui_init(dt_iop_module_t *module)
Definition imageop.c:1229
void dt_iop_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1561
void dt_iop_gui_update_expanded(dt_iop_module_t *module)
Definition imageop.c:2338
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2169
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1127
void dt_bauhaus_value_changed_default_callback(GtkWidget *widget)
Definition imageop.c:3226
@ IOP_FLAGS_GUIDES_SPECIAL_DRAW
Definition imageop.h:178
int dt_iop_autoset_advance(struct dt_develop_t *dev, dt_autoset_manager_t *manager)
Definition iop-autoset.c:97
gboolean dt_iop_autoset_module_is_enabled(const dt_iop_module_t *module)
Definition iop-autoset.c:45
void dt_iop_autoset_build_list(struct dt_develop_t *dev, dt_autoset_manager_t *manager)
Definition iop-autoset.c:64
void dt_iop_autoset_module_set_enabled(const dt_iop_module_t *module, const gboolean enabled)
Definition iop-autoset.c:55
static const float x
void dt_control_flush_jobs_queue(dt_control_t *control, dt_job_queue_t queue_id)
Definition jobs.c:384
@ DT_JOB_QUEUE_SYSTEM_FG
Definition jobs.h:54
void dt_lib_colorpicker_set_box_area(dt_lib_t *lib, const dt_boundingbox_t box)
Definition lib.c:1456
gboolean dt_lib_gui_get_expanded(dt_lib_module_t *module)
Definition lib.c:1119
void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
Definition lib.c:1478
dt_lib_module_t * dt_lib_get_module(const char *name)
Definition lib.c:1499
float *const restrict const size_t k
void dt_masks_events_post_expose(struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
int dt_masks_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event)
@ DT_MASKS_EDIT_OFF
Definition masks.h:201
@ DT_MASKS_EDIT_FULL
Definition masks.h:202
void dt_masks_free_form(dt_masks_form_t *form)
void dt_masks_gui_init(struct dt_develop_t *dev)
int dt_masks_events_button_released(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state)
int dt_masks_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which)
int dt_masks_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, uint32_t state, int delta_y)
@ DT_MASKS_GROUP
Definition masks.h:133
int dt_masks_events_mouse_leave(struct dt_iop_module_t *module)
int dt_masks_events_button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state)
int dt_masks_events_mouse_enter(struct dt_iop_module_t *module)
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
#define DEVELOP_MASKS_NB_SHAPES
Definition masks.h:927
void dt_masks_gui_cleanup(struct dt_develop_t *dev)
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
#define CLAMPF(a, mn, mx)
Definition math.h:89
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
dt_mipmap_size_t dt_mipmap_cache_get_fitting_size(const dt_mipmap_cache_t *cache, const int32_t width, const int32_t height, const uint32_t imgid)
#define dt_mipmap_cache_get(A, B, C, D, E, F)
@ DT_MIPMAP_TESTLOCK
#define dt_mipmap_cache_release(A, B)
dt_mipmap_size_t
float dt_aligned_pixel_t[4]
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
void dt_dev_pixelpipe_cache_unref_hash(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Find the entry matching hash, and decrease its ref_count if found.
void dt_dev_pixelpipe_cache_flush_clmem_for_pipe(dt_dev_pixelpipe_cache_t *cache, const int devid)
Like dt_dev_pixelpipe_cache_flush_clmem(), for callers that do not hold darktable....
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache entry.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void dt_dev_pixelpipe_cleanup_nodes(dt_dev_pixelpipe_t *pipe)
static void dt_dev_backbuf_set_hash(dt_backbuf_t *backbuf, const uint64_t hash)
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
#define lw
Definition retouch.c:1055
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:69
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:179
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:266
void dt_selection_select_single(dt_selection_t *selection, int32_t imgid)
Definition selection.c:289
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:368
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_DEVELOP_INITIALIZE
This signal is raised when darktable.develop is initialized.
Definition signal.h:169
@ DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE
Definition signal.h:95
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
@ DT_SIGNAL_DEVELOP_IMAGE_CHANGED
This signal is raised when image is changed in darkroom.
Definition signal.h:221
@ DT_SIGNAL_CONTROL_PROFILE_USER_CHANGED
This signal is raised when a profile is changed by the user 1 uint32_t : the profile type that has ch...
Definition signal.h:242
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:174
@ DT_SIGNAL_DARKROOM_UI_CHANGED
Signal that the darkroom GUI color changed.
Definition signal.h:224
@ DT_SIGNAL_DEVELOP_MODULE_REMOVE
This signal is raised when a module is removed from the history stack 1 module no returned value.
Definition signal.h:215
@ DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED
This signal is raised by viewmanager when a view has changed. 1 : dt_view_t * the old view 2 : dt_vie...
Definition signal.h:81
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
uint64_t image_surface_hash
Definition darkroom.c:873
gboolean image_surface_has_main
Definition darkroom.c:872
struct dt_pixel_cache_entry_t * entry
Definition darkroom.c:580
cairo_surface_t * surface
Definition darkroom.c:581
struct dt_undo_t * undo
Definition darktable.h:787
struct dt_lib_t * lib
Definition darktable.h:771
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:790
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_colorspaces_t * color_profiles
Definition darktable.h:788
struct dt_collection_t * collection
Definition darktable.h:781
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:776
struct dt_selection_t * selection
Definition darktable.h:782
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_view_manager_t * view_manager
Definition darktable.h:772
struct dt_control_t * control
Definition darktable.h:773
GtkAccelKey active_key
GtkAccelGroup * darkroom_accels
gboolean progress_cursor_active
Definition iop-autoset.h:24
void(* default_value_changed_callback)(GtkWidget *widget)
Definition bauhaus.h:288
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
dt_lib_colorpicker_size_t size
Definition colorpicker.h:63
dt_boundingbox_t box
Definition colorpicker.h:62
dt_colorspaces_color_profile_type_t type
dt_colorspaces_color_profile_type_t softproof_type
cmsHTRANSFORM transform_xyz_to_display
dt_colorspaces_color_mode_t mode
char softproof_filename[512]
int button_down_which
Definition control.h:216
double button_y
Definition control.h:217
double button_x
Definition control.h:217
int button_down
Definition control.h:216
dt_pthread_mutex_t busy_mutex
dt_backbuf_t backbuf
dt_atomic_int shutdown
int32_t gui_attached
Definition develop.h:162
cairo_surface_t * image_surface
Definition develop.h:499
struct dt_develop_t::@25 profile
dt_image_t image_storage
Definition develop.h:259
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
struct dt_develop_t::@23 display
int32_t orig_height
Definition develop.h:205
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:391
int32_t border_size
Definition develop.h:200
dt_backbuf_t display_histogram
Definition develop.h:337
GList * iop
Definition develop.h:279
gboolean request
Definition develop.h:419
dt_backbuf_t output_histogram
Definition develop.h:336
int32_t preview_height
Definition develop.h:213
struct dt_develop_t::@24 iso_12646
int32_t processed_width
Definition develop.h:233
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
GSList * samples
Definition develop.h:392
dt_clipping_preview_mode_t mode
Definition develop.h:455
struct dt_develop_t::@20::@27 snapshot
int32_t orig_width
Definition develop.h:205
gboolean forms_changed
Definition develop.h:326
GtkWidget * floating_window
Definition develop.h:449
float lower
Definition develop.h:453
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
gboolean pipelines_started
Definition develop.h:345
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
GList * alliop
Definition develop.h:281
dt_aligned_pixel_t wb_coeffs
Definition develop.h:442
struct dt_iop_module_t *struct dt_iop_color_picker_t * picker
Definition develop.h:383
dt_dev_overexposed_colorscheme_t colorscheme
Definition develop.h:452
GList * allforms
Definition develop.h:329
dt_backbuf_t raw_histogram
Definition develop.h:335
float scaling
Definition develop.h:193
struct dt_masks_form_gui_t * form_gui
Definition develop.h:327
struct dt_develop_t::@17 roi
GtkWidget * button
Definition develop.h:449
struct dt_colorpicker_sample_t * selected_sample
Definition develop.h:393
struct dt_develop_t::@20 proxy
int32_t processed_height
Definition develop.h:233
int32_t preview_width
Definition develop.h:213
GtkWidget * gamut_button
Definition develop.h:487
gboolean enabled
Definition develop.h:387
dt_pthread_rwlock_t masks_mutex
Definition develop.h:333
struct dt_develop_t::@22 rawoverexposed
const gchar * filename
Definition develop.h:420
float threshold
Definition develop.h:466
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
float upper
Definition develop.h:454
GtkWidget * softproof_button
Definition develop.h:487
gboolean display_samples
Definition develop.h:394
GList * forms
Definition develop.h:321
struct dt_develop_t::@21 overexposed
float natural_scale
Definition develop.h:224
gboolean block_normal_pan
Definition gtk.h:183
double ppd
Definition gtk.h:200
gboolean is_dragging
Definition gtk.h:213
int32_t reset
Definition gtk.h:172
dt_accels_t * accels
Definition gtk.h:194
struct dt_gui_gtk_t::@47 mouse
GtkWidget * has_scroll_focus
Definition gtk.h:228
dt_ui_t * ui
Definition gtk.h:164
gint64 last_time_us
Definition gtk.h:181
gboolean enabled
Definition gtk.h:182
guint timeout_source
Definition gtk.h:178
float velocity[2]
Definition gtk.h:180
struct dt_view_t * view
Definition gtk.h:179
struct dt_gui_gtk_t::@46 pan_edge
int32_t id
Definition image.h:319
dt_iop_module_t *dt_iop_color_picker_kind_t kind
GModule *dt_dev_operation_t op
Definition imageop.h:230
gpointer blend_data
Definition imageop.h:318
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:316
GModule *dt_dev_operation_t op
Definition imageop.h:256
gboolean enabled
Definition imageop.h:298
dt_iop_module_so_t * so
Definition imageop.h:359
gboolean creation
Definition masks.h:503
dt_masks_type_t type
Definition masks.h:378
GList * points
Definition masks.h:377
uint64_t hash
void * data
dt_thumbtable_t * thumbtable_lighttable
GtkWidget * main_window
dt_thumbtable_t * thumbtable_filmstrip
struct dt_view_manager_t::@67 proxy
dt_view_t * current_view
Definition view.h:201
GtkWidget * guides_popover
Definition view.h:220
dt_darkroom_layout_t(* get_layout)(struct dt_view_t *view)
Definition view.h:245
void(* set_default_cursor)(struct dt_view_t *view, double x, double y)
Definition view.h:246
struct dt_view_t * view
Definition view.h:244
GtkWidget * guides_toggle
Definition view.h:220
struct dt_view_manager_t::@67::@70 darkroom
uint32_t height
Definition view.h:159
GModule *void * data
Definition view.h:157
uint32_t width
Definition view.h:159
double x
double y
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
int dt_thumbtable_scroll_to_selection(dt_thumbtable_t *table)
Scroll to show selected content.
Definition thumbtable.c:572
void dt_thumbtable_update_parent(dt_thumbtable_t *table)
A widget to manage and display image thumbnails in Ansel's lighttable and filmstrip views.
#define dt_thumbtable_refresh_thumbnail(table, imgid, reinit)
Definition thumbtable.h:283
static void dt_thumbtable_show(dt_thumbtable_t *table)
Show the thumbnail table widget.
Definition thumbtable.h:380
static void dt_thumbtable_hide(dt_thumbtable_t *table)
Hide the thumbnail table widget.
Definition thumbtable.h:395
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
void dt_undo_clear(dt_undo_t *self, uint32_t filter)
Definition undo.c:337
@ DT_UNDO_TAGS
Definition undo.h:46
@ DT_UNDO_DEVELOP
Definition undo.h:52
char * dt_get_help_url(char *name)
void dt_view_manager_module_toolbox_add(dt_view_manager_t *vm, GtkWidget *tool, dt_view_type_flags_t views)
Definition view.c:1326
void dt_view_active_images_add(int32_t imgid, gboolean raise)
Definition view.c:1276
void dt_view_active_images_reset(gboolean raise)
Definition view.c:1267
int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
Definition view.c:235
void dt_view_image_info_update(int32_t imgid)
Definition view.c:1470
int32_t dt_view_active_images_get_first()
Definition view.c:1306
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:140
@ DT_VIEW_DARKROOM
Definition view.h:78
dt_darkroom_layout_t
Definition view.h:92
@ DT_DARKROOM_LAYOUT_EDITING
Definition view.h:94