Re: dialog response signal class closure




On Jan 4, 2008, at 3:57 PM, Kevin Ryde wrote:

The program below is an attempt to subclass Gtk2::Dialog and use a class
closure to get a new default response signal handler.  But it gets
response values as numbers like -5 for the button or -4 for a
delete-event, instead of the 'ok' and 'delete-event' strings which come
from an ordinary $dialog->signal_connect.  Is that meant to be so?  If
yes are there constants for GTK_RESPONSE_OK etc lurking around to test
the values against?

Wow, that's quite a can of worms you've opened.


No, that is not intended. And no, the constants don't exist. Gtk+'s ABI stability guarantee means that you should be safe if you define your own constants to wrap these negative integer values, but you'll likely have to have version checks in your module if and when we fix this in the bindings.



The Gory Details:


GtkDialog's "response" signal has a signature of

   response (GtkDialog, integer)

This is actually defined upstream by gtk+:

        http://library.gnome.org/devel/gtk/unstable/GtkDialog.html#GtkDialog-response

(note where it says "gint arg1").

The response id is a simple integer; values of GtkResponseType are typically used here. The idea is that these special negative values are the typical response codes, but you can pass any response code you want simply by using a positive integer that won't be confused for one of the standard ones.


In the bindings, decided that treating the response ids like enumerations would be a good thing, and use a special conversion function for GtkResponseType that passes on any number that does not match one of the enumeration values. However, since the type system says that the "response" signal's argument is a plain integer, our only way to get this custom handling on that parameter is for Gtk2/xs/ GtkDialog.xs to install a custom marshaler for the "response" signal.

The problem you are seeing results from the fact that user callbacks (e.g. $dialog->signal_connect (...)) go through the function gperl_signal_connect(), which checks with the registry of custom marshalers, while using a class closure through Glib::Type::register_object() (which is the actual entry point behind Glib::Object::Subclass) does not. In fact, the class closures are attached with the default marshaler in all cases. The default marshaler looks at the types of all the GValues that come in and maps those to perl; and since the "response" signal's argument type is G_TYPE_INT, the response value goes straight to int, with no special handling. Lose.

So, i cooked up a quick fix (attached; against CVS HEAD but should work with pretty much any version) that makes Glib::Type::register_object() look at the custom marshaler registry when connecting the class_closure. This causes your code's do_response() to get the string 'ok' instead of the integer -5 --- but all is not well. The line

    return $self->signal_chain_from_overridden ($resp);

causes the warning

Argument "ok" isn't numeric in subroutine entry at response- marshaler.pl line 17.

because Glib::Object::signal_chain_from_overridden() asks the GLib type system for the parameter types of the current signal so that it can convert the arguments. And since the type system says "response"'s argument is G_TYPE_INT, we try to convert 'ok' to int. Fail.


Curse, sputter, swear.


Now, one other caveat is that you're returning the value from chaining up in a signal that returns no value and doesn't have an implementation in the parent. So, you don't actually need to chain up from "response".

But, that only affects this particular signal, and is not a general solution --- other signals will suffer the same problems on signal_chain_from_overridden(). There are currently four custom marshalers in Gtk2... and, looking quickly over them, it looks like calling signal_chain_from_overridden() in these may be problematic:

- Gtk2::Editable uses a custom marshaler for "insert-text" to allow you to change the text. In doing so, it changes the number of parameters, meaning you can't just pass the parameters you received to the parent.

- GtkMenuItem's "toggle-size-request" signal gets a gpointer for its argument, so Gtk2::MenuItem overrides this marshaler to dereference the gint* and write to it. Again, calling signal_chain_from_overridden() is broken here. The parent class simply sets the value to zero.

- GtkTreeModel's "rows-reordered" signal gets a gpointer argument, which is an array of integers. Gtk2::TreeModel overrides this marshaler to pass perl an array, and coerce the returned array back into the gpointer argument. Once again, signal_chain_from_overridden() won't know what to do with the argument and will likely crash. This is a GInterface signal, and i'm not sure what is supposed to happen if you do signal_chain_from_overridden().

- We've already discussed GtkDialog's "response" signal.


So, it's apparent that overriding the marshaler causes problems for signal_chain_from_overridden(). I'm not really sure how we missed that before. A proper fix for this likely involves having a custom "chain marshaler" to bookend with the custom signal marshaler, and setting up signal_chain_from_overridden() to look for these. It's far too late to cook up that patch tonight.


In the meantime, for your dialog subclass, either:

- do the somewhat dirty signal_connect-to-self-in-INIT_INSTANCE so that the custom marshaler fires, OR

- make a simple set of constant mappings for the negative integer enumeration values and pretend nothing is wrong. If you do this, your code will probably wind up looking like this:

       sub do_response {
           my ($self, $response) = @_;
           if ($Gtk2::VERSION < the version that fixes this bug) {
               $response = 'ok' if $response == -5;
               ...
           }
           if ($response eq 'ok') {
               ...
           }
       }

unless you don't mind simply requiring bindings new enough not to have the problem.

  OR

- define a special set of positive values as custom return codes for your module's dialog, and use those constants in both your class closure and in external handlers connected to the response signal. This one is kinda icky.


Attachment: class-closure-marshaler.patch
Description: Binary data



--
elysse:  You dance better than some.
me:  "Some" what?
elysse:  Some asparagus.




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]