0008 - Callbacks and Events

In this blog entry, we’ll be looking at two flavours of callback and the implications of using each one.

GtkD signals fall into two broad categories:

  • those that expect association with void callbacks, and
  • those that expect callbacks to return a value, usually a Boolean.

So, what’s the difference?

void Callbacks

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

The most noticeable feature of a void callback is that it isn’t processed as a GDK Event. if you have a look at the definition of a void-return callback, you’ll see one argument of type Button.

	void callbackName(Button b)
	{
	      ...
	}

No mention of an Event, no return value. Clearly, this is a quick-n-dirty approach meant to avoid extra baggage and detail. Hook the button up to the signal, do a straight jump into the callback function, do your business and get out with no data returned to the caller.

And when we hook up the signal, it too is straightforward and simple:

	addOnClicked(&callbackName);

bool Callbacks

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

These are made more complex by the fact that they are processed as GDK Events. Events can originate from anything controlled by the user via keyboard, mouse, or any other input device. Events can be about the motion of the mouse pointer, the pressing of a key (or combination of keys) or the visibility of a UI entity. Events can be chained together by hooking up multiple signals to the same widget (usually a Button) – something we’ll talk more about in a later blog entry.

In a nutshell, hooking up a bool signal gives us an event structure from which we can pull pertinent information while inside the callback function.

Why a Boolean Return Value is of… Value

Another feature of GDK signals is that they can be chained together. Hook up any number of signals, or two or more instances of the same signal, and you’ve got a chain of signals. If you want, this signal chain can be interrupted by changing the return value of one of the callbacks.

So, let’s say you’ve got three signals hooked up:

	addOnButtonRelease(&callbackOne);
	addOnButtonRelease(&callbackTwo);
	addOnButtonRelease(&callbackThree);

And another control (probably a Button) that switches callbackTwo()’s return value between true and false. With a return value of true, callbackTwo() is the last one called. But when it’s switched to false, callbackThree() also gets called.

When you hook up multiple signals, they’re processed in the same order as they’re hooked up.

So here’s the deal:

  • return(true): stop processing callbacks and return to the main loop, and
  • return(false): more callbacks to come, keep going.

Why These Differences Matter

As I said, a void callback is for quick-n-dirty handling of user input. When you need to get fancy, deal with the details of the event that triggered the callback or deal with an interruptible callback chain, you need to use one of the Boolean-oriented signals.

To the Code…

Comparing today’s example files, you won’t see much difference until you get to the MyButton class. (For now, ignore the fact that I’ve set up the TestRigWindow’s onDestroy signal in two quite different ways, which in the end give us exactly the same functionality.) No, what we’re interested in is the signal hook-ups in the MyButton and MyArgsButton classes.

void-ness

Here, have a look:

	addOnClicked(delegate void(_) { buttonAction(args); });

The call to addOnClicked() may seem like overkill, but here’s how it breaks down:

  • Because we want to pass the command line arguments all the way into the MyButton object, we have to define this callback as a delegate. If we didn’t, we’d get a compiler error.
  • void(_) defines the callback’s return value and that underscore basically says: the widget calling this callback is the widget we’re talking about. This may seem unnecessary, but when you consider that ‘delegate’ is about forcing a callback to remain within the scope of the caller, it makes more sense. It’s a way of making sure the scope of caller/callback are in sync.
  • The curly braces surround what is, in effect, statements in an inline function. Because the callback function itself is here reduced to just another statement in an ersatz function, we can get away with passing those arguments right in there.

And there you have it. If we weren’t passing in the command line arguments, you wouldn’t need such a complex callback definition. Instead, you could use what we’ve used before, a simple:

	addOnClicked(&callbackFunction);

bool-ness

Looking at the bool callback example, the only thing about the signal hook-up that’s different is what appears directly after the delegate keyword:

	addOnButtonRelease(delegate bool(Event e, Widget w){ buttonAction(args); return(false); } );

Again, the first bit is the return value we’re expecting from the callback function followed by the invisible arguments being passed into the callback function.

Invisible arguments?

Yeah, I didn’t mention it before, but if you look at either example, the arguments immediately following delegate void or delegate bool aren’t mentioned in the actual callback functions. It seems that because they’re self-referential, they don’t need to be mentioned, not the Button, not the Widget, and not the Event. The only argument we mention in the callback function is the string array containing the command line arguments.

Conclusion

Further down the road, we’ll revisit callbacks. There’s lots more to talk about: signal chains, interrupting signal chains… yeah, all kinds of stuff.

But for now, you know the basics and you have two new techniques for hooking up signals. Until next time, happy D-coding and may the widgets do wonders to your head-space.

If you'd like to leave a comment...

Until I get time to research and install a commenting system, I ask that you try one of these options:

You can also subscribe via RSS so you won't miss anything.

© Copyright 2019 Ron Tarrant