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 since 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 applications (and the Wimp) 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 like this:
struct wimp_message { int size; /* Offset 0 */ wimp_t sender; /* Offset 4 */ int my_ref; /* Offset 8 */ int your_ref; /* Offset 12 */ bits action; /* Offset 16 */ byte reserved[236]; /* Offset 20 */ };
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.
The action member contains a numeric code which identifies the message which has been received. Of the messages that we have met so far, Message_Quit is 0x0u, Message_SubmenuWarning is 0x400C0u and Message_MenusDeleted is 0x400C9u. Thankfully, it’s rare to need to remember these, as OSLib defines constants for many of them – 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 tracked and to to form conversations with replies. When the Wimp takes a message through the Wimp_SendMessage SWI, it replaces whatever is in my_ref with a unique value before delivering the message to its recipients. The sender should put zero into your_ref, unless it is sending the message in response to an incoming message; if the message is a response, the sender should instead copy the value from my_ref into your_ref so that everyone can see that it is is reply. We will see this in action later.
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 an action (like the user attempting to load a particular type of file) and hoping that an application will claim and handle it.
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 they 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, helpfully, the description of Wimp_Initialise in the Programmer’s Reference Manual gets it wrong. There are, in fact, three things that we can do:
- If we pass a pointer to an empty zero-terminated list of message numbers (that is, where messages[0] is message_QUIT), then the application wishes to receive every message that it may be sent. This is strongly discouraged for performance reasons.
- If we pass a pointer to a zero-terminated list of message numbers in a wimp_message_list structure, then the application will receive only the messages included in the list. Since Message_Quit is message number zero, it is included in the list by default as the terminator.
- If we pass NULL, then the application only wishes to receive Message_Quit: no other messages will be delivered.
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 working, as we have already seen that we can receive Message_MenusDeleted, but 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() 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^rsquo;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.
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 should be apparent, is that we would need to curate a list of all of the messages that our code – and 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 doesn’t work as expected in several versions of the Wimp). And because we’re registering messages using the event_add_message_handler() function from SFLib’s event library, this is adding 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, 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.
As with the other event handlers that we’ve met in more detail, user message handlers take a single parameter which is a pointer to a wimp_message structure. As with the other events dispatched by SFLib’s event library, this is in practice the block of memory that we pass to Wimp_Poll and then to event_process_event().
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 wimp_message structure doesn’t contain a window handle to allow it to be routed, so the 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. 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 process is internal to the application, and does not have any connection to the concept of ‘claiming’ messages at the Wimp level.


