Broken annotation for GimpRunBrushCallback
@nielsdg I think you can help me on this one.
I tested one of our function with a callback: gimp_brush_select_new()
The annotations:
/**
* gimp_brush_select_new:
* @title: Title of the brush selection dialog.
* @brush_name: The name of the brush to set as the first selected.
* @opacity: The initial opacity of the brush.
* @spacing: The initial spacing of the brush (if < 0 then use brush default spacing).
* @paint_mode: The initial paint mode.
* @callback: (scope notified): The callback function to call each time a settings change.
* @data: (closure callback): the run_data given to @callback.
* @data_destroy: (destroy data): the destroy function for @data.
*
* Invokes a brush selection dialog then run @callback with the selected
* brush, various settings and user's @data.
*
* Returns: (transfer none): the name of a temporary PDB procedure. The
* string belongs to the brush selection dialog and will be
* freed automatically when the dialog is closed.
**/
In particular, I believe the (scope notified)
is important to tell the binding that this callback can be reused several time and has a life expectancy longer than the function call.
Unfortunately while the following code works fine in C:
static void
brush_cb (const gchar *brush_name,
gdouble opacity,
gint spacing,
GimpLayerMode paint_mode,
gint width,
gint height,
const guchar *mask_data,
gboolean dialog_closing,
gpointer user_data)
{
printf ("Selected '%s': %f %d %d\n",
brush_name, opacity, spacing, paint_mode);
}
/* … later in some running code: */
{
gimp_brush_select_new ("test", "Untitled", 50.0, 10,
GIMP_LAYER_MODE_NORMAL, brush_cb, NULL, NULL);
}
I.e. it opens the brush select dialog, and every time I change a settings, the callback is called and print on stdout.
… the following same code in Python (you may just test it in the Python console but it does the same thing as proper plug-in code) doesn't work though:
def cb(n, o, s, m, w, h, d, c, u):
print("Selected: {} with {} {} {}".format(n, o, s, p))
Gimp.brush_select_new('test', 'Untitled', 50.0, 10, Gimp.LayerMode.NORMAL, cb, None)
The relevant part of the crash trace:
4 <signal handler called>
No symbol table info available.
#5 0x00007f3409f524e4 in _pygi_marshal_to_py_array.lto_priv.0 () from /usr/lib64/python3.9/site-packages/gi/_gi.cpython-39-x86_64-linux-gnu.so
No symbol table info available.
#6 0x00007f3409f4801e in _pygi_closure_handle () from /usr/lib64/python3.9/site-packages/gi/_gi.cpython-39-x86_64-linux-gnu.so
No symbol table info available.
#7 0x00007f3417e328cf in ffi_closure_unix64_inner () from /lib64/libffi.so.6
No symbol table info available.
#8 0x00007f3417e32c78 in ffi_closure_unix64 () from /lib64/libffi.so.6
No symbol table info available.
#9 0x00007f3407cdfd5d in gimp_temp_brush_idle (data=0x564a6a8ccf00) at ../../../../../../../dev/src/gimp/libgimp/gimpbrushselect.c:242
No locals.
As you can see from the trace, it crashes precisely when the plug-in tries to run the data->callback()
.
Are we doing anything wrong for this (or all API) callbacks? I have not tested other functions with callback right now, but I think to remember once I had tested such function with callback and managed to get it working with the proper (scope)
annotation, but now it doesn't seem to do anything. Do we have to use GClosure
everywhere? I mean, docs doesn't seem to say simple C callbacks don't work, only that it's better to avoid with complicated cases (several callbacks, etc.):
Callbacks are hard to support for introspection bindings because of their complex life-cycle. Try to avoid having more than one callback in the same function, and consider using GClosure when you need more.
Is there any way to fix this other than GClosure? What's your opinion/advice/fix?