Skip to content

gtk4-prep: event controllers - #21659

Merged
TurboGit merged 95 commits into
darktable-org:masterfrom
Arecsu:gtk4/event-controllers-all
Aug 4, 2026
Merged

gtk4-prep: event controllers#21659
TurboGit merged 95 commits into
darktable-org:masterfrom
Arecsu:gtk4/event-controllers-all

Conversation

@Arecsu

@Arecsu Arecsu commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Huge PR. Needs further testing to see if interactions, keyboard events, mouse, trackpad, scrolls, anything in the UI UX responds ok, across Linux, macOS, Windows.

I've been testing this, polishing code, fixing bugs across the commits. So far so good. But I'm pretty sure I might be missing something, somewhere, deep down a weird module used by a small fraction of people, or could be even obvious issues which I didn't notice.

Mostly mechanical changes, but so many of them had to be made for their particular context. Some code clean up here and there, helpers, macros, but mostly porting to be gtk4 compatible.

Given all changes across files are somewhat related to one another, I concluded that splitting this into many PRs will be more of a time and effort block than anything else. A bug that might happen to a particular module might have its solution (or bug) related to a similar code path somewhere else. So, that's my logic for having this PR in one go.

I need help in testing this!

Skipped in this PR

Focus events (focus-in-event/focus-out-event, ~30 callers): GtkEventControllerFocus doesn't exist in GTK 3.24 — it's a GTK4-only class. We can't convert these until we actually switch to GTK4, so they stay as g_signal_connect for now and will become a mechanical find-and-replace at migration time.

Dialog enter key (dt_handle_dialog_enter, ~6 callers): These bind Enter to "ok" in modal dialogs using g_signal_connect(dialog, "key-press-event", ...). On GTK4, GtkDialog handles this natively, so the workaround can simply be deleted — no conversion needed. It's deferred because it touches the same foundation layer in gtk.c that's being left alone until the switch.

Foundation infrastructure in gtk.c itself (~33 signals): These are the remaining event signals inside src/gui/gtk.c that cover low-level UI — the panel resize handles (12 signals), notebook tab reordering (3), scrollable containers, toast/log widgets, and most importantly the internals of dt_gui_connect_click() and dt_gui_connect_double_click() themselves. These can't be converted on GTK 3.24 because they involve window-level event propagation, scrolled-window interaction, and the gesture controller creation code that everything else depends on. They stay as-is until the actual GTK4 switch, when the whole helper layer can be replaced, I guess, with native GTK4 APIs.

Related: #20433

@Arecsu
Arecsu force-pushed the gtk4/event-controllers-all branch from b8a350c to 676138c Compare July 27, 2026 21:29
@Arecsu
Arecsu marked this pull request as ready for review July 27, 2026 21:39
@TurboGit

Copy link
Copy Markdown
Member

Soon I'll start using this PR daily for testing.

@TurboGit TurboGit added this to the 5.8 milestone Jul 30, 2026
@TurboGit TurboGit added the gtk4 label Jul 30, 2026
@TurboGit

Copy link
Copy Markdown
Member

@Arecsu

Found a bug, you cannot define a new shortcut using the keyboard icon top-right.

  • As soon as you click on the keyboard icon the pref window open
  • It should change the cursor and let you fly over a widget to select
  • And clicking on the widget it should open the pref window on the shortcut tab

@zisoft

zisoft commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Dragging the visible area in the navigation panel does not work:

Bildschirmfoto 2026-07-31 um 11 33 56
  • enter darkroom
  • set zoom to 100%
  • try to drag the visible area - no reaction

@TurboGit

Copy link
Copy Markdown
Member

Another one, selecting any algorithm on the retouch module crashes dt.

(gdb) bt
#0  0x00007fff39397a3e in rt_select_algorithm_callback
    (togglebutton=<optimized out>, e=0x1, self=0x55555928fab0)
    at /home/obry/dev/builds/c-darktable/x86_64-linux-gnu-ai/src/src/iop/retouch.c:2025
#1  0x00007ffff6c9e662 in ??? () at /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#2  0x00007ffff66b1cb1 in ??? ()
    at /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#3  0x00007ffff66c8a78 in ??? ()
    at /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#4  0x00007ffff66ce568 in g_signal_emit_valist ()
    at /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#5  0x00007ffff66ce623 in g_signal_emit ()
    at /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#6  0x00007ffff6dbed46 in ??? () at /usr/lib/x86_64-linux-gnu/libgtk-3.so.0

@Arecsu

Arecsu commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@zisoft @TurboGit guys, thank you so much for this feedback! If you can keep them coming, that would be incredible. I'm somewhat busy more than normal these days, so further testing on my side is a bit limited, but I'll find a spot of time to prioritize these fix you all found for sure. Thank you!

@Arecsu

Arecsu commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Fixes:

  • The keyboard icon (shortcuts button) in the top toolbar. Clicking it used to jump straight to the preferences window instead of entering shortcut-mapping mode. Both are fixed now: short click enters mapping mode, long-press and right-click each open exactly one window (at some point it opened two windows) and the long-press timing is tracked properly.

  • The shortcuts dialog in the keyboard shortcut window. Clicking a collapsible row in the actions list now behaves like it did before this PR. It got super buggy with my round of commits, but now it has been fixed. Also: on macOS you can now delete a selected shortcut with the delete (backspace) key, same as in the presets dialog. That never worked on macOS before, not even in the old code it seems.

  • Row clicks in a few treeviews (collect, import, one settings page) could land on the wrong row: always one row off, in a way that was easy to miss. That's fixed.

  • Keyboard handling, and this is the one to keep an eye on. While testing, I found that a lot of the keyboard-related changes in this PR were silently not receiving key events at all on GTK 3 — it only became visible when I tried to make the delete key in macOS as mentioned. There's a bridge for that now: the GTK4-style code is in place, and on GTK 3 it behaves exactly like the old code did. Since this sits underneath every keyboard-related change in the PR, it deserves a bit of extra attention when testing.

  • Dragging the visible area in the navigation panel. Fixed as well. This one was related to the keyboard button problem. In fact, there were many places with this problem, like the soft-proof, grid, all bottom bar buttons, and more :)

  • The retouch module should work as expected now

@Arecsu
Arecsu force-pushed the gtk4/event-controllers-all branch from 93b61f8 to d7d545d Compare August 1, 2026 02:35
@TurboGit

TurboGit commented Aug 4, 2026

Copy link
Copy Markdown
Member

@Arecsu : There is a conflict resolution needed.

Also, I would propose to merge this just after the conflict resolution is fixed to gain more field testing. On my side I have not discovered other issues. Ok?

Arecsu added 18 commits August 4, 2026 10:10
Restore the scroll event masks that were removed when cleaning up
gtk_widget_add_events() calls during the event controller conversion.
Both widgets still use the old g_signal_connect('scroll-event', ...)
for event forwarding (colorequal forwards to notebook/sliders,
tonequal controls propagation), which requires GDK_SCROLL_MASK in
the widget's event mask to function.
- Properly indent closing brace in tonecurve.c secondary-button handler
- Add missing newline before closing brace in colorzones.c
- Remove stray duplicate void gui_init line in map_locations.c
Update copyright end-year to 2026 in files touched by event controller
conversion that still had outdated ranges.
@Arecsu
Arecsu force-pushed the gtk4/event-controllers-all branch from d7d545d to 88e6de8 Compare August 4, 2026 13:35
@Arecsu

Arecsu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@TurboGit solved. I've also solved the last bug I could find in darkroom second window, where the touchpad two-pinch zoom gesture wouldn't work. I've matched the same interaction from darkroom main image area, which works great already.

@TurboGit TurboGit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's bite the bullet!

@TurboGit
TurboGit merged commit 98d894a into darktable-org:master Aug 4, 2026
5 checks passed
@Arecsu

Arecsu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

There's going to be some PRs that will need to adapt their code to event controllers for sure. In any case, feel free to report problems. At least from current master nothing seems to be going wrong

@Arecsu

Arecsu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

PRs still carrying old-style GTK event code need adaptation

Need conversion (carry old-style event code in converted files):

  • IOP Color EQ : Add interactive editing mode for color adjustments in image #21397 (colorequal, @Christian-Bouhon) — CONFLICTING. Branch head still wires the graph area with removed API; on rebase these four must become controllers:

    g_signal_connect(G_OBJECT(g->area), "button-press-event", ...);
    g_signal_connect(G_OBJECT(g->area), "button-release-event", ...);
    g_signal_connect(G_OBJECT(g->area), "motion-notify-event", ...);
    g_signal_connect(G_OBJECT(g->area), "scroll-event", ...);
    gtk_widget_add_events(g->area, GDK_BUTTON_PRESS_MASK | ... | GDK_SMOOTH_SCROLL_MASK);

    (The new mouse_moved()/mouse_leave()/scrolled() IOP hooks are fine — those are module vfuncs, not GTK signals.)

  • Additional button to remove the star rating of an image #21199 (switch_unrated, @deekayhd) — CONFLICTING, stale since Jul 4. Adds four old-style connects on a thumbnail widget in dtgtk/thumbnail.c (converted by gtk4-prep: event controllers #21659):

    g_signal_connect(G_OBJECT(thumb->w_unrate), "enter-notify-event", ...);
    g_signal_connect(G_OBJECT(thumb->w_unrate), "leave-notify-event", ...);
    g_signal_connect(G_OBJECT(thumb->w_unrate), "button-press-event", ...);
    g_signal_connect(G_OBJECT(thumb->w_unrate), "button-release-event", ...);

    Use GtkEventControllerMotion + dt_gui_connect_click().

  • Introduce generic theme variants. #21431 (theme variants, @TurboGit) — adds 5× gtk_widget_add_events(labelev, GDK_BUTTON_PRESS_MASK) in gui/preferences.c (removed API in GTK4; currently dead — no handlers connected on those new event boxes), and the branch carries the pre-existing double-click-to-reset-language g_signal_connect(labelev, "button-press-event", ...) which gtk4-prep: event controllers #21659 converted on master. Small cleanup: drop the add_events lines, port the double-click to a gesture.

Superseded


Might be rebase only (touch converted files, add no event code): #21661, #21512, #21648, #21620, #21107, #21255, #21590, #21534, #21319, #21252, #21368, #21715, #21024, #21033 — no old-style event code in their diffs; just conflict resolution.

Some things could explode. Like #21677 which found a bug related to this PR. #21718 should solve it in the meantime.


There might be more PRs in the way which I'm not accounting here. This is the analysis that I was able to do for the time being.

@kofa73

kofa73 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Ah sorry, I didn't have time before; I see it has been merged and there have been more fixes since. Does this help?

Panel Review — base=4a8a1cfd90 (gtk4/event-controllers-all)

Important (accepted, severity medium)

  • src/develop/imageop.c:3369 — Callbacks that handle the secondary mouse button are connected with dt_gui_connect_click(), which creates a primary-button-only gesture, so those right-click branches (module presets menu, curve-node deletion, map-location context menu, filter value popups) are unreachable.

    • Evidence:
      • src/gui/gtk.h:566dt_gui_connect_click_all is dt_gui_connect_click() plus gtk_gesture_single_set_button(…, 0); plain dt_gui_connect_click() leaves GtkGestureSingle's default button property (1), so the gesture rejects button 2/3 events before emitting pressed/released.
      • src/gui/gtk.c:4786dt_gui_connect_click creates GtkGestureMultiPress without changing its default button; the all-buttons variant is a separate macro.
      • src/develop/imageop.c:2768_iop_plugin_body_pressed still opens the presets popup for gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY, but is connected at imageop.c:3369 with dt_gui_connect_click(), replacing a button-press-event handler that received every button. Impact: right-click on a module body no longer opens the presets menu.
      • src/develop/imageop.c:2823_iop_plugin_header_released's GDK_BUTTON_SECONDARY branch (presets popup) is unreachable; connected at imageop.c:3363 with dt_gui_connect_click().
      • src/iop/tonecurve.c:1945 — secondary-button node-delete branch unreachable; connected at tonecurve.c:1301.
      • src/iop/rgbcurve.c:1400 — same dead branch; connected at rgbcurve.c:1529.
      • src/iop/basecurve.c:1937 — same dead branch; connected at basecurve.c:2130.
      • src/iop/colorzones.c:2186 — same dead branch; connected at colorzones.c:2706.
      • src/libs/map_locations.c:853_click_on_view handles the context menu only for GDK_BUTTON_SECONDARY; gui_init (map_locations.c:928) connects it with dt_gui_connect_click. Impact: the location context menu and its row actions do not open.
      • src/libs/filters/filename.c:375 and src/libs/filters/misc.c:465_filename_press/_misc_press have the same dead GDK_BUTTON_SECONDARY branch (the "right-click to get existing filenames/extensions" popup advertised in their tooltips), while src/libs/modulegroups.c:2992 shows the working pattern gtk_gesture_single_set_button(g, GDK_BUTTON_SECONDARY).
    • Flagged by: Claude, Codex · independent Round-0 support: 2 (raised separately for the imageop/curve sites and the map-locations site, then merged)
  • src/gui/gtk.c:4835 — On GTK3, dt_gui_connect_double_click() invokes the registered pressed callback twice with n_press=2 for one double-click: GtkGestureMultiPress handles the second GDK_BUTTON_PRESS and the bridge handles the following GDK_2BUTTON_PRESS.

    • Evidence:
      • src/gui/gtk.c:4808 / 4806 — the helper's premise ("GDK always delivers GDK_2BUTTON_PRESS for the second press of a double-click, never a second GDK_BUTTON_PRESS") contradicts GDK's documented sequence (gdkevents.h:721-732: GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE, GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE), so the gesture reaches n_press=2 on its own and the bridge adds a duplicate call rather than the only call.
      • src/gui/gtk.c:4835_dbl_clk_handler forwards GDK_2BUTTON_PRESS/GDK_3BUTTON_PRESS to the same pressed callback with n=2/3, in addition to the gesture's own n_press=2 emission.
      • src/dtgtk/thumbtable.c:2639_event_button_press_cb is connected both through dt_gui_connect_click_all (thumbtable.c:2630) and through the bridge, so the double-click branch at thumbtable.c:1463 runs dt_view_manager_switch("darkroom") twice; a same-view switch (src/views/view.c:232) still destroys and rebuilds every expandable lib plugin. Impact: duplicate view switch / panel rebuild, and duplicate deselect+select+ACTIVATE in filmstrip mode.
      • Confirmed against GTK 3.24.52 sources (gtkgesturemultipress.c:194, gdkevents.h:719): the second GDK_BUTTON_PRESS increments n_press, then the bridge forwards GDK_2BUTTON_PRESS to the same callback — two n_press=2 calls.
    • Flagged by: Claude · independent Round-0 support: 1 · claim sharpened in round 1 on the panel's converged wording
  • src/dtgtk/culling.c:936 — Several new event-controller callbacks obtain a GdkEvent copy with gtk_get_current_event() (transfer full) and never call gdk_event_free(), leaking one event per motion/scroll/click/keypress.

    • Evidence:
      • src/dtgtk/culling.c:936_event_motion_notify_cb allocates a copy for root coordinates on every pointer motion over the culling view and never frees it. Impact: continuous heap growth while the mouse moves in culling mode.
      • src/dtgtk/thumbtable.c:1560 — same unfreed copy on every motion event over the lighttable thumbtable.
      • src/dtgtk/culling.c:924_event_button_press_cb leaks a copy per button press (used only for gdk_event_get_root_coords).
      • src/views/darkroom.c:5024 / 5058_second_window_scrolled_callback returns on all paths without gdk_event_free(current); gtk_get_current_event() returns an owned event the caller must free. Impact: leaked on every scroll over the second darkroom window.
      • src/libs/tools/colorlabels.c:285 — the secondary-click branch takes a copy for root coordinates and never frees it.
      • src/gui/accelerators.c:2069_view_key_pressed_cb passes gtk_get_current_event() straight into dt_gui_search_start() and drops the reference, leaking one event per key press in the shortcuts dialog.
    • Flagged by: Claude · independent Round-0 support: 1 · the darkroom.c site was independently re-raised in round 2 and folded in
  • src/gui/color_picker_proxy.c:414 — The color-picker button lost its GClosureNotify, so dt_iop_color_picker_t is leaked and the global picker_proxy is no longer cleared when the button is destroyed.

    • Evidence:
      • src/gui/color_picker_proxy.c:414dt_gui_connect_click(button, _color_picker_clicked, NULL, color_picker) replaces g_signal_connect_data(…, color_picker, (GClosureNotify)_color_picker_destroy, 0) and has no destroy notify, while the sibling bauhaus-quad path at color_picker_proxy.c:430 still passes _color_picker_destroy. Impact: the g_malloc'd picker struct (color_picker_proxy.c:402) is leaked for every picker button created in a module GUI.
      • src/gui/color_picker_proxy.c:387_color_picker_destroy is documented as clearing darktable.lib->proxy.colorpicker.picker_proxy "to prevent use-after-free in dt_iop_color_picker_reset"; on the button path that cleanup no longer runs, so picker_proxy can keep pointing at a picker whose colorpick widget was destroyed and is dereferenced in _color_picker_reset (color_picker_proxy.c:108).
    • Flagged by: Claude · independent Round-0 support: 1
  • src/dtgtk/thumbtable.c:1605 — The thumbtable and culling button-release handlers lost their primary-button test while being connected with dt_gui_connect_click_all, so middle- and right-button releases now change the image selection.

    • Evidence:
      • src/dtgtk/thumbtable.c:1605_event_button_release_cb no longer checks GDK_BUTTON_PRIMARY (the pre-migration handler required it) and is connected with dt_gui_connect_click_all at thumbtable.c:2630, which sets the gesture button to 0 (any button). Impact: a right- or middle-button release over a thumbnail runs dt_selection_toggle/select_range/select_single.
      • src/dtgtk/culling.c:1038 — same dropped GDK_BUTTON_PRIMARY test, connected via dt_gui_connect_click_all (culling.c:1258), while the middle-button press path at culling.c:906 is the zoom toggle. Impact: middle-click zoom in culling also flips table->selection, changing which image later act-on operations apply to.
    • Flagged by: Claude · independent Round-0 support: 1

Minor (accepted, severity low)

  • src/bauhaus/bauhaus.c:547_popup_scroll_cb passes delta 0 to _slider_zoom_range() when no unit delta could be derived, which resets the popup slider's zoom range instead of doing nothing.

    • Evidence:
      • src/bauhaus/bauhaus.c:545 — the return value of dt_gui_get_scroll_unit_delta_fallback() is ignored and _slider_zoom_range(w, delta) is called unconditionally with delta still 0, whereas the replaced code only called it inside if(dt_gui_get_scroll_unit_delta(event, &delta)).
      • src/bauhaus/bauhaus.c:485_slider_zoom_range() with zoom == 0 toggles d->min/d->max between the soft and hard range (the middle-click "reset zoom range" action) and returns before the zoom arithmetic. Impact: a smooth scroll over an open slider popup can silently switch the slider between soft and hard range.
      • src/gui/gtk.c:625dt_gui_get_scroll_unit_deltas_fallback re-reads the raw event through dt_gui_get_scroll_unit_deltas, whose smooth-scroll accumulator is independent of the accumulator in _scroll_proxy_real (gtk.c:4970) that gates the callback, so returning FALSE with delta == 0 is a common case for touchpads.
    • Flagged by: Claude · independent Round-0 support: 1
  • src/develop/imageop.c:2493_gui_reset_clicked calls dt_dev_add_history_item twice for one module-reset click.

    • Evidence: src/develop/imageop.c:2491 — the new gesture callback contains dt_dev_add_history_item(module->dev, module, TRUE) twice in a row, while the retained original _gui_reset_callback (imageop.c:2450) has exactly one call. Impact: the undo record, pipeline TOP_CHANGED invalidation and history autosave path run twice per reset click.
    • Flagged by: Claude · independent Round-0 support: 1
  • src/gui/gtk.c:631 — The new smooth-scroll fallback re-runs the GTK3 unit accumulator after the DISCRETE proxy has already accumulated the same stream, dropping or delaying preset and combo scroll steps.

    • Evidence:
      • src/gui/gtk.c:4970 — the DISCRETE scroll proxy attenuates smooth deltas, accumulates them, and emits an integer step only after its accumulator reaches one.
      • src/gui/gtk.c:631 — the fallback ignores the callback's raw dx/dy when a current event exists and calls dt_gui_get_scroll_unit_deltas() again.
      • src/gui/gtk.c:522dt_gui_get_scroll_unit_deltas() has a separate static smooth-scroll accumulator.
      • src/bauhaus/bauhaus.c:1053 + :538, src/develop/imageop.c:3231 + :2547 — both DISCRETE consumers call the new fallback helper on each proxy callback.
      • src/gui/presets.c:1149_presets_scrolled ignores the fallback return value, so a dropped step calls dt_gui_presets_apply_adjacent_preset(module, 0), which binds LIMIT 0, matches no row and leaves extreme set. Impact: no preset is applied but a misleading toast preset '<name>' (last) is shown.
    • Flagged by: Codex · independent Round-0 support: 1

Contested (reviewers held positions — you decide)

  • src/gui/gtk.h:556ASSERT_FUNC_TYPE was changed from a type-checked conditional into an explicit cast, which silently disables all dt_gui_connect_* callback signature checks exactly while ~200 callbacks are retyped by hand. (severity medium; a lowering to low was proposed but did not converge)

    • For:
      • src/gui/gtk.h:556(void)((expected_type)(func)) is a function-pointer cast, always well-formed in C; compiling both macro forms with gcc shows the old form (void)(1 ? (func) : (expected_type)0) reports "pointer type mismatch in conditional expression" for a wrong callback while the new form emits no diagnostic.
      • src/gui/gtk.h:563 — all dt_gui_connect_* macros route their callback type checks through ASSERT_FUNC_TYPE, so a mismatched controller callback signature now compiles and only misbehaves at runtime through the untyped GCallback.
      • src/gui/preferences_ai.c:1595 — restoring the conditional macro through an overlay header and syntax-checking every file containing a dt_gui_connect_* call yields exactly four diagnostics, all in preferences_ai.c (1595, 1675, 1844, 1845), each only gpointer user_data against a typed dt_prefs_ai_data_t *data.
    • Against:
      • The parent macro at the immediate parent commit 19b892af4c^ was (void)(sizeof((func), (expected_type)0)) — a comma expression that is already type-insensitive — so the cast removes no active check at that point in history.
      • No callback in the tree has a wrong parameter count, return type or controller type, so the disabled check currently masks no runtime defect; the impact is a lost compile-time diagnostic only (hence the proposed low).
    • Disputed facts / how to check: which baseline the macro is compared against. Against master/4a8a1cfd90's src/gui/gtk.h, ASSERT_FUNC_TYPE is (void)(1 ? (func) : (expected_type)0) and the branch does remove a real diagnostic (264 call sites); against the branch's own immediate parent 19b892af4c^ it was already the type-insensitive sizeof form. Resolve with git show 4a8a1cfd90:src/gui/gtk.h | grep -A2 ASSERT_FUNC_TYPE and git show 19b892af4c^:src/gui/gtk.h | grep -A2 ASSERT_FUNC_TYPE. The panel did not converge on whether the severity is medium (a lost check across a 200-callback hand-migration) or low (no live mismatch today).
  • src/iop/tonecurve.c:1947 — All four curve editors dropped the early return that ended the secondary-button endpoint-reset branch, so an endpoint reset falls through into the node-removal code and decrements the persisted node count — currently masked because the click gesture accepts only the primary button. (severity low)

    • For:
      • src/iop/tonecurve.c:1947 — the return TRUE that terminated the g->selected == 0 || g->selected == nodes - 1 branch was dropped during the void conversion, so control continues into the shift-down loop and p->tonecurve_nodes[ch]--. Impact: right-clicking a curve endpoint would reset it and delete a node from the persisted params.
      • src/iop/rgbcurve.c:1402 — identical dropped early return before the loop that decrements p->curve_num_nodes[ch].
      • src/iop/basecurve.c:1939 — identical dropped early return; base 4a8a1cfd90 had return TRUE at basecurve.c:1945.
      • src/iop/colorzones.c:2188 — identical dropped early return; control now continues into _delete_node() after the endpoint reset.
    • Against: the code change is confirmed in all four files, but reachability is currently blocked: all four connect with dt_gui_connect_click(), whose GtkGestureMultiPress keeps GtkGestureSingle's default button=1, so no secondary press reaches these callbacks today.
    • Disputed facts / how to check: nobody disputes the missing return TRUE; the disagreement is whether a latent defect that is only masked by a second accepted defect (see the accepted dt_gui_connect_click issue above) counts as a defect now. Note the coupling: fixing the accepted primary-button issue makes this one live and will start silently deleting curve nodes from saved params. Fix both together.

Unresolved

None — every issue reached at least one peer-review pass with all three seats engaged.


Rejected (raised then dropped)

  • src/gui/gtk.c:4811 — "The converted accelerators, Bauhaus, collection, tagging, darkroom and reset-label handlers rely on n_press >= 2 but are not connected to the GTK3 double-click bridge, so those double-click actions are unreachable on GTK3."
    • Raised because: the in-tree comment at gtk.c:4804 states that GtkGestureMultiPress does not process GDK_2BUTTON_PRESS/GDK_3BUTTON_PRESS, and 7 call sites guard on n_press >= 2 with no bridge (accelerators.c:2320/3099, bauhaus.c:3961/4132, collect.c:724/4073, tagging.c:1513/3558 and 2716/3695, darkroom.c:5132/5547, resetlabel.c:39/71).
    • Dropped because: on GTK 3.24 GtkGestureMultiPress does its own press counting and emits pressed with n_press=2 for the second GDK_BUTTON_PRESS — verified at runtime by feeding a synthesized BUTTON_PRESS/RELEASE/BUTTON_PRESS sequence to a multipress gesture (produced n_press=1 then n_press=2; the GDK_2BUTTON_PRESS event was ignored). The base revision already relies on this at bauhaus.c:3889. The repository comment at gtk.c:4804 is itself inaccurate — which is exactly what makes the accepted double-fire issue above real.
  • src/libs/map_locations.c:858 — "Treeview gesture callbacks pass widget-allocation coordinates directly to bin-window hit testing, so row actions target the wrong row after scrolling."
    • Raised because: map_locations.c:858, masks.c:1236, tagging.c:1490 and tagging.c:2671 pass x/y straight to gtk_tree_view_get_path_at_pos() while all four trees sit inside dt_ui_resize_wrap().
    • Dropped because: GTK3 vertical scrolling of a GtkTreeView does not move bin_window (it uses gdk_window_scroll plus priv->dy); widget and bin-window coordinates differ only by effective header height (y) and hadjustment (x). All four trees hide their headers (map_locations.c:904, masks.c:2346, tagging.c:3514, tagging.c:3638), all four pass NULL for the column, and gtk_tree_view_get_path_at_pos derives the row from y alone. Measured on GTK 3.24 at vadjustment=500: widget (20,30) maps to bin (20,30) and both resolve to the same GtkTreePath.
  • src/gui/gtk.c:4959 — "The shared scroll-controller proxy discards GTK4 controller callbacks when no current GDK event is available, so the newly migrated scroll handlers never receive scroll input."
    • Raised because: _scroll_proxy_real returns early when gtk_get_current_event() is NULL, and src/libs/histogram.c:487 explicitly notes that this returns NULL inside GTK4 event-controller callbacks.
    • Dropped because: the tree builds only against GTK3 (src/CMakeLists.txt:312, find_package(GTK3 3.24.15 REQUIRED)). In GTK3 the scroll controller runs inside gtk_main_do_event's propagation, which keeps the event on the current-event stack, so gtk.c:4958 returns a non-NULL copy (freed at gtk.c:5001) and the early return is not taken — verified at runtime on GTK 3.24. Under GTK4 gtk_get_current_event() has a different signature and this code would not compile unchanged. Still worth a note for the eventual GTK4 switch, but it is not a defect in this tree.
  • src/develop/imageop.c:881 — "gtk_widget_destroy is removed in GTK4."
    • Dropped because: gtk_widget_destroy() is valid, non-deprecated GTK3 API; the build requires GTK3 3.24.15 (src/CMakeLists.txt:312), the cited line is unchanged from base (imageop.c:875), and 120 other call sites remain in src/. Syntax-checking with the project's own flags succeeds with zero errors.
  • src/bauhaus/bauhaus.c:1051 — "key-press-event is removed in GTK4."
    • Dropped because: key-press-event is a valid GTK3 GtkWidget signal; the base revision already connected the same signal on the same popup area (base bauhaus.c:1002, handler _popup_key_press) — only the handler name changed to _popup_key_handler, with a matching signature.
  • src/gui/accelerators.c:2938 — "key-press-event is removed in GTK4."
    • Dropped because: valid GTK3 API on a GTK3-only build; the same file already connects key-press-event on two treeviews at base (accelerators.c:2913, 3014), _shortcuts_view_realized() guards against duplicate connection via the accel-dialog-key-connected object data (accelerators.c:2930-2932), and _shortcuts_dialog_key_pressed (accelerators.c:2910) has the GTK3 signature.
  • src/views/lighttable.c:642 — "gtk_widget_show_all is removed in GTK4."
    • Dropped because: valid GTK3 API used at 166 sites in src/; the base revision connected it directly as the popover's button-press handler (base lighttable.c:1478-1479), so the gesture callback only relocates an existing call.

Note on the four rejections above: they all assert that a GTK3 symbol "is removed in GTK4". That is true of GTK4, but this branch still builds against GTK3 only (find_package(GTK3 3.24.15 REQUIRED)), and every cited call is either unchanged from base or a relocation of an existing one. They are correct observations about future GTK4 work, not defects introduced by this diff.

Merged

  • "The map-locations callback contains secondary-button handling but is connected with the primary-button-only helper" → folded into the accepted src/develop/imageop.c:3369 issue — identical mechanism (dt_gui_connect_click leaves GtkGestureSingle button=1, so GDK_BUTTON_SECONDARY branches never run); every call site is preserved as its own evidence point.
  • "_second_window_scrolled_callback leaks the GdkEvent returned by gtk_get_current_event()" (raised fresh in round 2) → folded into the accepted src/dtgtk/culling.c:936 leak issue — same mechanism, and darkroom.c:5024 was already one of its evidence points; the round-2 raise added the darkroom.c:5058 all-paths-return detail.

@Arecsu

Arecsu commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

This definitely helps but there have been many PRs and commits addressing some of the things here AFAIK. If you can run the same checks you've done against all gtk4-prep titled commits from master, that would be neat

@kofa73

kofa73 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Yes, running for 21742. I'll go to sleep afterwards, but can do the rest. Mention me on an issue at any time if you want a review; if the budget allows, I'll do it.

@Arecsu

Arecsu commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @kofa73. I'm currently running all these suggestions over my workflow, testing, debugging, cleaning up things. I'm doing this by merging all my PRs that are still open because some of them already address points in the write-ups

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants