gui: fix flaky cursor indicators on GTK3 backends - #21950
Conversation
|
First test:
Second test:
Third test
|
|
@kofa73 I need your skynet agent to dig further on this 🙏, it's gets difficult for me as I'm at the point where I don't have ways to reproduce some of them on my end. Good report @wpferguson by the way, it definitely helps |
|
@Arecsu I'll be able to provide feedback later this afternoon, currently over the limit. |
|
Sorry, I was on the master branch when I ran them. I think they picked up the right branch automatically, but I'll re-run the analysis just to be sure. Watch this space. |
|
Nice! I was just about to dig into it, just finished reading the message. Thanks for the heads up |
|
The workflow is a strict code review, it does not support investigations. Right now, I'm manually coordinating Claude and Gemini. :-) I can surely have them generate patches and send to you via email, if that's OK. |
|
Yes of course, patches is what I'm looking for more than anything, thanks! And of course the write up is useful in the same level already |
|
Updated based on this branch. It appears master has changes that will be relevant (@Christian-Bouhon , see issue 0: your changes bypassing cursor helpers). @Arecsu @wpferguson — here is what the three test failures come down to, plus one thing Short version: tests 1 and 2 are real regressions from this PR and share a single root Two different bases are referenced below, because the branch has drifted:
0. Rebase hazard: tone equalizer and colour equalizerThis is the one I would look at first, because it is invisible from the branch.
// master, src/iop/toneequal.c, switch_cursors()
GtkWidget *widget = dt_ui_main_window(darktable.gui->ui);
...
else if(g->cursor_valid)
{
dt_control_change_cursor("none"); // -> centre canvas after this PR
...
}
else if(!g->cursor_valid)
{
GdkCursor *const cursor =
gdk_cursor_new_from_name(gdk_display_get_default(), "default");
gdk_window_set_cursor(gtk_widget_get_window(widget), cursor); // -> toplevelOn master those raw writes are at On the branch as it stands this is harmless — Worth noting independently of this PR: those six raw writes are a step backwards from the 1. Root cause shared by tests 1 and 2: two cursor targets that never agreeThe PR retargets static GtkWidget *_cursor_target(void)
{
if(!darktable.gui || !darktable.gui->ui) return NULL;
GtkWidget *widget = dt_ui_center(darktable.gui->ui);
return widget ? widget : dt_ui_main_window(darktable.gui->ui);
}
Meanwhile Test 1 — mapping mode leaves "circle with slash" stuck
So There is an extra twist that explains why it looked completely unshakeable rather than Note the help button next to it ( Test 2 — histogram no longer shows the hand cursor
Trap in the obvious fix: 2. Test 3 is not a regression — it is deliberate behaviour from 2023
That "center 10%" is exact, and it falls straight out of float h_border = border / wd;
float v_border = border / ht;
if(!(g->clip_x || g->clip_y || g->clip_w != 1.0f || g->clip_h != 1.0f))
h_border = v_border = 0.45;When the crop box still covers the whole image — the state right after opening crop on an
The earlier steps in the same test fit too: right side → This PR does not touch 3. The original #21846 crop freeze — narrowed, still unprovenSeparate from all of the above, and still the thing the issue was actually filed about:
The only guard that can produce "indicators dead, crop still works" is the second half of if(!g->preview_ready || self->dev->preview_pipe->loading) return 0;
So the working hypothesis is: This is code reading, not runtime evidence. One Incidentally, the 4. Three more things worth checking before mergea. A cursor requested for the canvas can end up permanently on the toplevel. b. The busy / wait cursor is probably now invisible where the user is looking. A rule that would fix both: clearing a widget-local cursor means c. What the PR gets rightThe per-instance The direction is also right for GTK4 — per-widget cursors are GTK4's model. What seems to be
Also worth deciding explicitly which of the current |
|
Nice, thanks, and thanks for the email! Cursors work good on my end (macos) except window resize one as mentioned, and metadata grab handlers dont remain active while resizing. But wont stay stuck either, its a minor thing I guess. Couldnt solve that one, seems to be a profound gtk3 thing under macos as well... Needs further testing on other platforms |
Related to #21846
The cursor indicators in darkroom were flaky on some GTK3 backends. Several controls were changing one shared cursor, while widget-local cursors could also pin or clear the wrong window. The resize wrappers additionally kept their hover and drag state in shared globals.
This keeps the cursor close to the widget that owns the interaction:
NULL, so busy, help, and shortcut-mapping cursors can still inherit correctly.The changes stay within the existing GTK3/GTK4 compatibility boundary and do not add platform-specific cursor workarounds.
I manually tested shortcut mapping, the histogram grab cursor, panel handles, metadata handles, range selectors, the timeline, crop cursor zones, busy/help cursor inheritance, and both equalizer cursor paths. These are now behaving consistently on the tested systems.
Two GTK3/macOS limitations are intentionally left outside this PR. Native window-frame resizing is handled by AppKit/GTK outside the normal widget hierarchy and can still leave a frame cursor stuck. Also, while actively dragging a metadata separator, GTK3 can still show the text-entry cursor when the pointer crosses an entry child window. The handle itself and the resize operation work; the child-window cursor issue was left alone rather than shipping recursive cursor overrides or a seat-grab workaround.