conformal / gotk3

Go bindings for GTK3
ISC License
470 stars 81 forks source link

Callback parameters (and mapping plain-old-data structs to Go) #14

Open raichu opened 11 years ago

raichu commented 11 years ago

I'm a bit confused about how to obtain the parameters in a signal handler. As an example (source):

void frame_callback(GtkWindow *window, 
      GdkEvent *event, gpointer data)
{
   int x, y;
   char buf[10];
   x = event->configure.x;
   y = event->configure.y;
   sprintf(buf, "%d, %d", x, y);
   gtk_window_set_title(window, buf);
}

...

 g_signal_connect(G_OBJECT(window), "configure-event",
        G_CALLBACK(frame_callback), NULL);

How can I access the event->configure struct? According to the signal example, the only parameter I can pass to the event handler function in Go is ctx *glib.CallbackContext. Does ctx.Arg(0) correspond to the C-pointer event above?

If that is the case (about which I'm not sure), the type of ctx.Arg(0) is CallbackArg but I need to add a function like


func (c CallbackArg) Configure() *EventConfigure {
    return wrapEventConfigure(event->configure)
}

and EventConfigure struct?

Similar to the issue with GdkRectangle, if we simply define

type EventConfigure {
    C.GdkEventConfigure
}

it's fields won't be accessible to user code. Maybe it's time to establish a convention for this?

raichu commented 11 years ago

Whoops. I can't do that apparently, because CallbackArg is in glib package whereas Event and related stuff are in gdk, and gdk uses glib already!

raichu commented 11 years ago

I just push some initial draft in 087681c It can be used as

    w.Connect("configure-event", func(ctx *glib.CallbackContext) {
        ev := gdk.ToEvent(ctx.Arg(0))
        w := ev.Configure().Width
        ...
    })

EventConfigure is not compatible with the GtkEventConfigure (since C.int and enum are int32)

jrick commented 11 years ago

Yes, you've discovered one of the bigger issues with gotk3, and one that I want to fix when I get some more time. What I am thinking of doing is changing the callback func signature to actually expect the Go types for the same C types you would see, and then use type assertion or the reflect package and cast or use the wrap* functions to create the Go types we need for the C parameters.

Only issue I can see with this though is that it's not really type safe (just like C). What happens if your callback forces a *GtkWidget to be created, but what was actually passed was a gint? So, I'm still open on suggestions on how to best handle this.

edit: just to be clear, I'm not completely against type unsafeness as long as it's documented clearly. (glib.Object).Emit() is already type unsafe, for the same reasons as I explained above. Would love to find a good way to do this while still enforcing type safety, but with the limited information we can glean from a void, such is life.

raichu commented 11 years ago

Instead of having manual casting, maybe it's better to extend gdk.Event type. One can verify the cast to an extent by checking the Type field of the GdkEvent (087681c is baby steps towards that direction). But of course, the signature is a problem: glib package cannot have gdk.Event as a parameter since gdk uses glib.

raichu commented 11 years ago

What do you think about this?

I also checked his fork and there are too many things to merge. Branches are diverging, so hopefully you'll start doing some merging :)

raichu commented 11 years ago

Ping? :)

jrick commented 11 years ago

I took a look at his branch and he has a lot of good work done. I also checked his blog, where he explained the design desicion he took and how he went out about implementing it, and there were some other comments from him saying that he does think his work is ready for inclusion back into our branch. I welcome the change (and using GClosure looks better than the plan I had to solve this), but at the moment it doesn't seem ready to add.

I'll try to get in contact with him and see what the status of this feature is, and ask about inclusion into Conformal's repo.

weberc2 commented 10 years ago

I'm not sure what you guys came up with, but I solved the problem this way:

// EventKey is a representation of GDK's GdkEventKey.
type EventKey struct {
       *Event
}

// Native returns a pointer to the underlying GdkEventKey.
func (v *EventKey) Native() uintptr {
       return uintptr(unsafe.Pointer(v.native()))
}

func (v *EventKey) native() *C.GdkEventKey {
       return (*C.GdkEventKey)(unsafe.Pointer(v.Event.native()))
}

func (v *EventKey) KeyVal() uint {
       c := v.native().keyval
       return uint(c)
}

The fields aren't user accessible, but we can expose them as methods. Also, by wrapping the EventKey around Event, you don't have to worry about Event.free() being called twice (maybe that's a non-issue? I'm too tired to think through the implications at the moment).