As one example, we dynamically update the “export capabilities” of an image based on changes in the GUI. For instance if you toggle the “Save as Animation” option in the GIF export, that information is passed as data to the defined run function to update how the image should be exported. We’ll expand on those uses later, like to export in different scales without altering the working image.
Weird behavior though. I spent an afternoon chasing a weird message about run_data missing. It turns out that if you call Gimp.ImageProcedure.new(...) without the run_data (so, only the first 4 args), the procedure execution is itself called without a data (so, only 5 args).
Yes, that what I ended up doing. But not using a parameter in a call ending up changing how another piece of code is called somewhat later is not really expected.
Just some background info: providing a way to add “user data” to a callback call is a common pattern. In particular in GLib/GObject/GTK/etc., this is done for every callback-setting API (see for instance signal-handler connecting). This is a way to let developers add custom data in case they need any during the callback call (additionally to other callback args provided by the underlying library, such as signal information in the signal-handling case, or procedure arguments in case of a libgimp plug-in procedure).
The alternative to such a “user-data” pattern would be things like storing data in global scope (which is usually frown upon in modern C programming, and in general even, especially with functional programming which taught developers that it’s better to always pass a full context through function’s args).
Now it’s true that it is very seldomly used/useful for plug-in procedures (unlike e.g. signal handling where this user-data is very often used). In fact, I don’t even think we have a single case in any of the core plug-ins where we use this user-data argument. Nevertheless better have it and not use it than miss it the day when we’ll need this arg.
Also it’s just better to always stay consistent: callbacks are registered with an optional user-data (and an optional destroy function is the user-data is not static). You don’t have to use it, but just know it exists.
Well, I have tested that you can pass a Python function here at registration, and call that function at execution time.
So you can for instance define several procedures that all use the same general implementation function, but where the “variant” (say, a filter on layers) is defined at registration time by passing the appropriate function in run_data.