Haiku API Bindings
Messaging
Not logged in

Documentation | Manual: Messaging

Chapter 3: Messaging

In the previous chapter, the only user input we dealt with was the closing of the Window, and we used Haiku's builtin features to do that. In this tutorial, we're going to do something a little more complicated.

This program for this chapter is called ClickMe.ext.

Message loops

This section is a brief overview of how the messaging system works. If you don't care about understanding this, just jump ahead to the next section.

A Message is essentially a packet of information. For each event (for example, button clicks), the system will create a Message and post it to a Looper.

Each Looper has a message loop running in a thread; Window and Application are subclasses of Looper.

The core of the message loop determines the Message's target Handler and then passes the Message to the Looper's DispatchMessage hook.

DispatchMethod will convert some Messages into hook calls on other objects, but most Messages simply pass through to the target Handler's MessageReceived hook.

Controls, such as Button, are subclasses of Handler. Looper is a subclass of Handler, so a Looper can also handle messages itself.

The default target for most Controls is not the Control itself, but the owning Window. This means you only need one Window subclass, not a subclass for each Control.

Imports

For this program, we will need Application, Window, Button, and Message. As in the previous tutorial, we will also need some constants, so we'll go ahead and list them here right away.

Perl

use HaikuR1::Window qw(B_TITLED_WINDOW B_QUIT_ON_WINDOW_CLOSE);
# since we need the constants, we pull in Window directly
# this will pull in InterfaceKit, which will pull in ApplicationKit
# and there's no need to pull in the individual classes

Python

from HaikuR1.ApplicationKit import Application, Message
use HaikuR1.InterfaceKit import Window, Button, B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE

# we will need this in our Window subclass
from HaikuR1.SupportKit import code_to_int

Window subclass

Most programs will create their message loops by subclassing Window and overriding the MessageReceived hook, which is what we will do here.

The first thing we need is a message constant. These are really 32-bit integers, but since 4 ASCII characters take up the same amount of space and are really handy for debugging, by convention Haiku message constants consist of 4 ASCII characters.

System message codes consist of upper-case ASCII characters and underscores, so if we stick to lower-case characters, we won't collide with any system messages.

In C++, it is easy to define a 32-bit integer constant using an ASCII string, but in native code it's a little more complicated. The Support Kit provides a function to do this for you.

Perl

package ClickMeWindow;
use parent 'HaikuR1::Window';
use HaikuR1::SupportKit qw(code_to_int);
use strict;

my $message_code = code_to_int('clik');

Python

class ClickMeWindow(Window):
    message_code = code_to_int('clik')

(Note that code_to_int translates the code to a 32-bit integer. You don't have to stick to ASCII characters; you can use any encoding you want, but if you exceed 4 bytes, part of your code will be cut off, and then the reverse function, int_to_code, won't generate a string that matches your original input string.)

Next, we need our button. We could make our button variable globally accessible and work outside the class, like we did with the StringView in the previous chapter. But since we need to create a Window subclass anyway, we'll do it all in our window class.

Button

Perl

sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);

    $self->{button} = HaikuR1::Button->new(
        frame        => [10, 10, 100, 25],
        name         => "ClickMeButton",
        label        => "Click Me",
        message      => HaikuR1::Message->new($message_code),
    );
    $self->AddChild($self->{button});
    $self->{count} = 0;

    return $self;
}

Python

    def __init__(self, *args, **kwargs):
        self.button = Button(
            frame        = [10, 10, 100, 25],
            name         = "ClickMeButton",
            label        = "Click Me",
            message      = Message(message_code),
        )
        self.AddChild(self.button)
        self.count = 0

Note the following:

* The Button class takes a label instead of the text parameter that StringView took. It also takes a message parameter; this message will be passed to the window whenever the user clicks the Button.

* In addition to the button, we included a count variable that tracks the number of times the user clicks the button.

MessageReceived hook

Finally, we need to respond to our custom Message.

When implementing custom hooks in a subclass, pay attention to the default behavior of the hook. In some cases, you need to call the base class version or you will break something. In other cases, the base class version simply signals an error and calling it will break something. In the case of MessageReceived, we must call the base class version of the hook for any Message we do not handle.

Perl

sub MessageReceived {
    my ($self, $message) = @_;

    if ($message->what == $message_code) {
        $self->{count}++;
        $self->{button}->SetLabel("Click Me $self->{count}");
        return;
    }

    $self->SUPER::MessageReceived($message);
}

Python

    def MessageReceived(self, message):
        if message.what == message_code:
            self.count += 1
            self.button.SetLabel("Click Me " + str(self.count))
            return

        return super(ClickMeWindow, self).MessageReceived(message)

Running the app

Although this program is little more complicated than the previous one, it is still simple enough that we can get by with the standard Application class. Since we're not subclassing Application, all that's left is to create the objects, show the window, and run the app.

Perl

package main; # leave the subclass namespace

my $app = HaikuR1::Application->new("application/x.vnd-hab.ClickMe");

my $window = ClickMeWindow->new(
    frame      => [50, 50, 300, 300],
    title      => "Hello World",
    type       => B_TITLED_WINDOW,
    flags      => B_QUIT_ON_WINDOW_CLOSE,
);

$window->Show();

$app->Run();

Python

app = Application("application/x.vnd-hab.ClickMe")

window = ClickMeWindow(
    frame      = [50, 50, 300, 300],
    title      = "Hello World",
    type       = B_TITLED_WINDOW,
    flags      = B_QUIT_ON_WINDOW_CLOSE,
)

window.Show()

app.Run()

Once again, this program is simple enough that it doesn't require any cleanup code.