How to invalidate (expose) a rectangle in gtk4?

As part of learning gtk4 I’m porting my interactive caliper tool https://github.com/dov/dovtk-lasso to to gtk4. One of the stumbling blocks was how to invalidate a rectangular region of a widget (drawing area) in gtk4? Is this concept now obsolete and gtk4 always redraw the entire widgets (like in OpenGL?). The old code looked like this:

 gdk_window_invalidate_rect(
   gtk_widget_get_window(drawing_area),  &rect, TRUE);

Thanks!

Yes, the concept is entirely obsolete by the rendering model of GTK4, which is based on a tree of rendering operations—as opposed to the immediate mode rendering of GTK2 and GTK3. You should only ever call gtk_widget_queue_draw() to cause a widget to be redrawn.

Ideally, all the elements that can be redrawn should also be widgets; if they are not widgets, you can redraw them all, or cache the render nodes that represent the content of your widget, and only recreate the nodes that intersect the region you care about.

I see why the handling of expose events was changed, but is there any particular reason why damage tracking was removed in GTK4? It seems like this would still be wanted, at least for cases where it’s known the scenegraph is re-using old nodes?

The damage tracking is all internal. The render node tree knows the extents of each operation, and the GskRenderer knows which branches can be discarded if they fall outside of the clip.

Sorry, I should illustrate what I mean, if you run a GTK3 app like gnome-control-center with WAYLAND_DEBUG, and you run your cursor over the ListBox, you can see the damage rects only applying to the individual list box rows:

$ WAYLAND_DEBUG=1 gnome-control-center 2>&1 | grep damage
...
[3132331.736]  -> wl_surface@43.damage(26, 251, 242, 90)
[3132785.261]  -> wl_surface@43.damage(26, 296, 242, 45)
[3132788.400]  -> wl_surface@43.damage(26, 342, 242, 45)

If you do the same thing with gtk4-demo, you will see the whole window get damaged each time:

$ WAYLAND_DEBUG=1 gtk4-demo 2>&1 | grep damage
...
[3258295.966]  -> wl_surface@29.damage(0, 0, 852, 652)
[3259000.964]  -> wl_surface@29.damage(0, 0, 852, 652)
[3259059.364]  -> wl_surface@29.damage(0, 0, 852, 652)

For an app that uses GtkDrawingArea or GTKGLArea and does its own surface caching (maybe into cairo surfaces or internal GL textures) there is similarly no way to send that info back to the window system, GDK always seems to consider the whole GdkSurface “updated.”

(Not sure if this is related to the thread or not, I can bring this discussion elsewhere if it isn’t)

I still don’t understand if and how I can still do in gtk4 what I did in gtk2 and gtk3 (by different methods) in dovtk_lasso. The library is used to create arbitrary rubber band overlays on top of any widget. E.g. a caliper to measure the distance. Or drawing a selection area on top of a widget. See image for an example:

In gtk3 I would do this by connecting by g_signal_connect_after(widget, "draw", ...) and then draw my overlay in the signal handle. For every change of the overlay my code would calculate a small set of damaged rectangles that would be requested to be exposed. The solution worked very well and flicker free for a full screen application.

I’m not sure what you mean that “all the elements should be widgets”? Should a caliper for measuring distance be a widget? How would that help with the minimal exposure?

Thanks!

You can’t really override another widget’s drawing in that way in GTK4. One way to do it in GTK4 would be to create a new widget that has one child and then draws the lasso over the child in its snapshot method. As far as I can see there is currently no way to say that your drawing is only restricted to a sub-region of the widget’s allocated area, but that shouldn’t cause flickering or slowdown – GTK itself will cache the child widget’s most recent rendering to a texture. At most it may cause some overdraw within the wayland/x11 compositor.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.