Chapter 34: User Messages

Since the start of this tutorial, there has been something of an elephant hiding in plain sight in the corner of the room. All the way from Chapter 1 and Chapter 2 our application has been listening for something called Message_Quit (or message_QUIT) which might arrive via the Wimp_Poll reason codes of wimp_USER_MESSAGE and wimp_USER_MESSAGE_RECORDED, but we never really introduced it properly. Instead, we glibly stated that if one arrived, our application must exit immediately.

Then, in Chapter 23, we introduced Message_MenusDeleted in order to enable us to detect when our menu trees were closed. This time we needed a bit more context, to allow us to extract some more information from the communication – but it was still somewhat tangential to the main focus of the code. Finally, in Chapter 26, we briefly mentioned the existence of Message_SubmenuWarning without giving any more information about it.

What’s in a message?

All of these messages are what the Wimp describes as user messages. In the general sense, messages are anything that our application can receive through Wimp_Poll – although we have usually been referring to these as “events” in our exploration because this is probably the more common term for the concept these days. Any time that Wimp_Poll returns to our application’s poll loop with a new reason code for us to handle, something somewhere has called the Wimp_SendMessage SWI to send that code and its associated data block to us.

Usually the thing calling Wimp_SendMessage will be the Wimp itself, although there are scenarios in which one application might wish to send an event like Mouse_Click, Key_Pressed or Open_Window_Request to another. However, there are three Wimp_Poll reason codes specifically set aside for both the Wimp and applications to send general messages to each other: User_Message, User_Message_Recorded and User_Message_Acknowledge. OSLib defines these as wimp_USER_MESSAGE, wimp_USER_MESSAGE_RECORDED and wimp_USER_MESSAGE_ACKNOWLEDGE. When one of these events arrives at our application, the payload is a user message.

OSLib packages the payload up as a wimp_message structure, and back in Chapter 2 we said that this was ‘almost’ defined as follows:

struct wimp_message {
        int     size;
        wimp_t  sender;
        int     my_ref;
        int     your_ref;
        bits    action;
        byte    reserved[236];
};

typedef struct wimp_message wimp_message;

Sticking with the ‘almost’ for the time being, all user messages come with a standard 20 byte header but can optionally hold additional data after this within the reserved array of bytes. To assist the recipient with decoding the contents, the size member holds the number of bytes which are in use in the message, including the 20 byte header, rounded up to a complete number of 4-byte words.

The action member contains a numeric code which identifies the action of the message which has been received. Of the messages that we have met so far, action Message_Quit has the value 0x0u, action Message_SubmenuWarning has the value 0x400C0u and action Message_MenusDeleted has the value 0x400C9u. Thankfully, it’s rare to need to remember these, as OSLib defines constants for many of the actions – for example:

#define message_QUIT            0x0u
#define message_MENU_WARNING    0x400C0u
#define message_MENUS_DELETED   0x400C9u

The other three members of the wimp_message structure give us some context about the message. The sender member holds the task handle of the sending task, which will be zero if the Wimp sent the message. This can be useful information if a message requires a response, or some other action which would need our application to know its origin.

Finally, the my_ref and your_ref members hold numerical reference numbers which allow messages to be easily identified. We will look at these in the next chapter.

Being more honest

It has been convenient to tell a white lie about the internals of the wimp_message structure since we introduced it back in Chapter 2, because it has saved us from looking too closely at the slightly messy way that OSLib handles user message data blocks. However, it’s now time to come clean: what we find if we look inside oslib/wimp.h isn’t what we showed above but is, in fact, this.

#define wimp_MESSAGE_HEADER_MEMBERS \
        int size; \
        wimp_t sender; \
        int my_ref; \
        int your_ref; \
        bits action;

struct wimp_message {
        wimp_MESSAGE_HEADER_MEMBERS
        union {
                wimp_message_data_xfer data_xfer;
                wimp_message_ram_xfer ram_xfer;
                wimp_message_prequit prequit;
                wimp_message_save_desktop save_desktop;
                wimp_message_save_desktopw save_desktopw;
                wimp_message_device device;
                wimp_message_data_saved data_saved;
                wimp_message_shutdown shutdown;
                wimp_message_claim_entity claim_entity;
                wimp_message_data_request data_request;
                wimp_message_dragging dragging;
                wimp_message_drag_claim drag_claim;
                wimp_message_release_entity release_entity;
                wimp_message_app_control app_control;
                byte reserved[236];
        } data;
};

With the exception of reserved[], the members of the standard header that we described above are packaged up as a macro called wimp_MESSAGE_HEADER_MEMBERS and then this is used to define the wimp_message structure alongside a union named data. This union contains a collection of other structures and the actual definition of reserved[]. The dimension of 236 comes from the fact that there are five 4-byte fields in the standard part of the block (size, sender, my_ref, your_ref and action); 5 × 4 gives us 20 bytes, and 20 + 236 gives us the 256 bytes that make up the standard Wimp message block. From this, we can conclude that a user message payload can never be more than 236 bytes long – remember this!

The wimp_message_... types listed within the union represent the message payloads for what are referred to as the ‘core’ message actions. Like Message_Quit, these have low action numbers in the range 0x0u to 0x15u and are defined as part of RISC OS itself. Beyond that, there are some messages found in ranges like 0x400u, 0x500u and 0x800u, before we get into ranges which are tied to module SWI base numbers and other blocks which can be registered through the Allocations system. It’s quite rare to write an application which only uses ‘core’ message actions, so OSLib provides us with some other ways to get into the message payloads.

Back in Chapter 23 we introduced Message_MenusDeleted and described a couple of structures which were associated with it: wimp_message_menus_deleted and wimp_full_message_menus_deleted. At the time, we stuck with our little white lie around how they were defined, but in fact the definitions make use of wimp_MESSAGE_HEADER_MEMBERS to define the common structure elements – which reduces duplication across all of the structure definitions which are required.

struct wimp_message_menus_deleted {
        wimp_menu       *menu;
};

typedef struct wimp_message_menus_deleted wimp_message_menus_deleted;

struct wimp_full_message_menus_deleted {
        wimp_MESSAGE_HEADER_MEMBERS
        wimp_menu       *menu;
};

typedef struct wimp_full_message_menus_deleted wimp_full_message_menus_deleted;

Now that we know about the internals of the wimp_message structure, we can see that wimp_message_menus_deleted defines the payload for Message_MenusDeleted and could be used within the data union alongside the structures for the ‘core’ actions. In contrast, wimp_full_message_menus_deleted defines the whole of the Wimp_Poll block. As we saw at the time, we can cast a pointer to a wimp_message structure into a pointer to a wimp_full_message_menus_deleted structure in order to access its contents – as we did in Listing 23.7 in order to handle Message_MenusDeleted actions.

static osbool menu_message_menus_deleted(wimp_message *message)
{
        wimp_full_message_menus_deleted *deleted = (wimp_full_message_menus_deleted *) message;

        /* Check that the deleted menu is ours. */

        if (deleted == NULL || deleted->menu != menu_current_menu)
                return FALSE;

        /* Clear our saved menu details. */

        menu_current_menu = NULL;
        menu_current_callback = NULL;

        return TRUE;
}

When we come to send messages ourselves, we can also use the full structures to claim space for new message blocks from the stack. We will see this in action in the coming chapters.

Getting the message

On a normal RISC OS system there will be a lot of user messages flowing around. The ones that we have mentioned above are sent to individual applications, but messages can also be broadcast to all of the applications which are currently running. Broadcasts fall into two categories: those which are intended to notify every application of an event (such as a task starting, changing its memory allocation or exiting, or of a screen mode change), and those which are announcing something they hope an application will be able to claim and handle.

On RISC OS 2, every application would receive every event which might come its way. This was easy for application authors, but wasted a lot of time paging applications in and out again just to pass on messages that most applications didn’t even look at. As a result, RISC OS 3 introduced a different approach: if an application passes wimp_VERSION_RO3 as a minimum version to Wimp_Initialise, then the *messages parameter to wimp_initialise() will come into effect.

When we originally introduced this code back in Chapter 2, we simply said that we would pass NULL for the *messages parameter, but didn’t really explain why that was the case! As a reminder, the parameters passed to wimp_initialise() are as follows:

extern wimp_t wimp_initialise(
        wimp_version_no         version,
        char const              *name,
        wimp_message_list const *messages,
        wimp_version_no         *version_out
);

OSLib defines the parameter in question as a pointer to a wimp_message_list structure, and this turns out to contain an array of user message numbers and is defined as follows (where UNKNOWN is defined as 1 to provide a C90 form of flexible array):

struct wimp_message_list {
        int messages[UNKNOWN];
};

typedef struct wimp_message_list wimp_message_list;

Given the unconventional nature of the definition, there are also a couple of handy macros provided to help with allocating space for a message list in our application.

#define wimp_MESSAGE_LIST(N) \
        struct { \
                int messages[N]; \
        }

#define wimp_SIZEOF_MESSAGE_LIST(N) \
        ((N)*sizeof((wimp_message_list *) NULL)->messages)

The Wimp expects either a NULL or a list of messages to be passed in this parameter. The way that it works is a little counter-intuitive and, somewhat unhelpfully, the description of Wimp_Initialise in the Programmer’s Reference Manual gets it wrong. There are, in fact, three things that we can do:

It should be apparent from this that there is no way for an application to refuse to accept Message_Quit. We can safely ignore most of the messages that fly around the system, but we must shut the application down when told to do so!

Clearly our application is receiving more message actions than just Message_Quit despite passing NULL to Wimp_Initialise, as we have already seen that we can receive Message_MenusDeleted. However, before we explain why that’s the case, it’s worth taking a quick look at how we might pass lists of messages to Wimp_Initialise. If we go back to Listing 4.1 in Chapter 4 – which is the first time that a stand-alone main_initialise() function appeared in our code – we were calling Wimp_Initialise like this:

static void main_initialise(void)
{
        wimp_initialise(wimp_VERSION_RO3, "Example App", NULL, NULL);
}

If we were to pass a pointer to an empty message list in to wimp_initialise(), then we would end up with the following main_initialise(). We’re using the wimp_MESSAGE_LIST() macro to allocate the memory required for a single entry on the stack, then casting a pointer to this into the *messages parameter.

static void main_initialise(void)
{
        wimp_MESSAGE_LIST(1) message_list = { message_QUIT };

        wimp_initialise(wimp_VERSION_RO3, "Example App", (wimp_message_list *) &message_list, NULL);
}

If we were to run this code and watch the messages arriving from Wimp_Poll, we would see a lot of action codes besides Message_Quit. Our application will still work because it will just ignore them, but a great deal of time will be wasted each time it gets paged in and back out again.

An improvement would be to take the second option from above, and pass in a list of the messages that we are actually interested in. Remembering to allocate one more slot in the array to hold the reference to Message_MenusDeleted, we could call wimp_initialise() as follows:

static void main_initialise(void)
{
        wimp_MESSAGE_LIST(2) message_list = {
                        message_MENUS_DELETED,
                        message_QUIT
        };

        wimp_initialise(wimp_VERSION_RO3, "Example App", (wimp_message_list *) &message_list, NULL);
}

This version would actually perform the same as our application currently does, only receiving Message_MenusDeleted and Message_Quit. If we needed more messages, we could simply allocate more space in the wimp_message_list structure and add their codes to the list. The problem, which is probably already apparent, is that we would need to curate a list of all of the messages that our code – not to mention the libraries that is uses – requires.

As we have already seen, however, our application is actually using the third option for calling wimp_initialise(): passing NULL to the *messages parameter. This automatically gets us Message_Quit, but since Chapter 23 we have also been receiving Message_MenusDeleted without any problems. The reason for this is that the Wimp has a couple of additional SWIs – Wimp_AddMessages and Wimp_RemoveMessages – which can add or remove messages from the list which an application wishes to receive after the call to Wimp_Initialise.

extern void wimp_add_messages(
        wimp_message_list const *messages
);

extern void wimp_remove_messages(
        wimp_message_list const *messages
);

These both take pointers to wimp_message_list structures in the same format as wimp_initialise(), and allow messages to be added and removed from the list that an application will receive (although it’s worth remembering that Wimp_RemoveMessages may not always work as expected in some versions of the Wimp). And because we’re registering messages using the event_add_message_handler() function from SFLib’s event library, this is calling Wimp_AddMessages to add messages to the list for us.

This is a much cleaner solution compared to passing an exhaustive list of all of the messages that we will need to receive as part of the call to Wimp_Initialise and, after our short diversion above, we will continue to use it.

Messages as events

We first met SFLib’s event_add_message_handler() when we set up an event handler for Message_Quit, and then again when we were looking at Message_MenusDeleted in the context of menu event dispatch. It allows us to register a function of our choosing as an event handler for user messages of a specific type.

Just like the other event handlers from SFLib that we’ve alredy looked at in more detail, user message handlers take a single parameter which is a pointer to a wimp_message structure. And as with the other events dispatched by SFLib’s event library, this is in practice the block of memory that we passed both to Wimp_Poll and then to the event_process_event() function.

static osbool main_message_quit(wimp_message *message)
{
        main_quit_flag = TRUE;

        return TRUE;
}

One difference from the other event handlers is that these return an osbool value. The common pary of the wimp_message structure doesn’t contain a window handle to allow it to be routed, so the event library allows more than one handler to be registered for a message type. When a message is received, it will be passed to each of the registered handlers in turn so long as they each return FALSE from the function. As soon as one handler returns TRUE, however, the message is ‘claimed’ and it will not be passed to any other handlers which might remain in the list. The order in which the handlers will be called is not defined.

This ‘claiming’ process is internal to the application, and does not have any connection to the very different concept of ‘claiming’ messages at the Wimp level. We will see the Wimp process later on.

The event_add_message_handler() function is defined as follows:

osbool event_add_message_handler(
        unsigned int            message,
        enum event_message_type type,
        osbool                  (*message_action)(wimp_message *message)
);

The message parameter is the action code for the message that we wish to claim: for example, message_QUIT. The *message_action parameter is a pointer to the callback function that we wish to use to handle the message: for example, the main_message_quit() defined above. The type parameter is one of the following enum values.

enum event_message_type {
        EVENT_MESSAGE_NONE = 0,
        EVENT_MESSAGE = 1,              /* User_Message.                */
        EVENT_MESSAGE_RECORDED = 2,     /* User_Message_Recorded        */
        EVENT_MESSAGE_INCOMING = 3,
        EVENT_MESSAGE_ACKNOWLEDGE = 4   /* User_Message_Acknowledge     */
};

The EVENT_MESSAGE, EVENT_MESSAGE_RECORDED and EVENT_MESSAGE_ACKNOWLEDGE values map to the three Wimp_Poll reason codes that we introduced above. The values are flags, so it is possible to combine them with the | (bitwise or) operator; it isn’t unusual to treat User_Message and User_Message_Recorded in a similar way, so EVENT_MESSAGE_INCOMING can be used to represent a combination of these two without the need to explicitly or them together each time.

We can register the main_message_quit() function above as a handler for Message_Quit by calling event_add_message_handler() like this:

event_add_message_handler(message_QUIT, EVENT_MESSAGE_INCOMING, main_message_quit);

Neither of the two messages that we have used so far require us to respond to them, so it’s very likely that they will have arrived at our application on a User_Message reason code. This is, however, very much at the discretion of the sender, so it doesn’t hurt to also accept them if they arrive via a User_Message_Recorded – hence the use of EVENT_MESSAGE_INCOMING for the type parameter.

The difference between the two reason codes lies in the “Recorded” at the end of User_Message_Recorded: like recorded mail, if an application sends a message via the User_Message_Recorded reason code then it expects some kind of response or, at the very least, proof of delivery. The Wimp looks after the mechanics of this, and will let the sending application know if there is no response to a recorded message using the User_Message_Acknowledge reason code.

We will look at this in a coming chapter, but for now it isn’t important. Neither of the messages that our application listens for – Message_Quit and Message_MenusDeleted – expect any kind of response, so we need do nothing more than listen out for them and act on the information that they contain if they arrive.