User Messages

From RISC OS
Revision as of 23:21, 11 February 2009 by FJG (talk | contribs) (Oops, lost the section heading)
Jump to navigationJump to search

There are many kinds of messages under RISC OS. The WIMP reports events to applications in the form of messages. In fact, each return code from Wimp_Poll is a message. Three of these return codes (17, 18 and 19) have been reserved for User Messages. These allow tasks to communicate with other tasks.

The way the WIMP handles these messages is not always clear, and sometimes counter-intuitive. This page is meant to explain the mechanism to programmers.

On this page, we will refer to two tasks which communicate. Task A will be the task initiating a message, and task B will be receiving that message and possibly reply to it.

Wimp_SendMessage

All messages in the WIMP environment are generated using this call. If an application wishes to send a message (any message, but most often User messages), it must use this call. When sending User Messages to another task, the description of the SWI simplifies to:

 SWI Wimp_SendMessage (&400E7)
 On entry
   R0 = event code (as will be returned by Wimp_Poll - 17, 18 or 19)
   R1 = pointer to message block
   R2 = task handle of destination task (task B), or
        window handle (message sent to window's creator), or
        -2 (icon bar, message sent to creator of icon given by R3), or
        0 (broadcast message, sent to all tasks, including the originator)
   R3 = icon handle (only used if R2 = -2)
 On exit
   R0 corrupted
   R2 = task handle of destination task (task B)
   the message is queued
   the message block is updated (event codes 17 and 18 only)

The message block is laid out as follows:

      +0 = length of the block
      +4 = not used on entry
      +8 = not used on entry
     +12 = your_ref (0 for original message)
     +16 = message action code
     +20 = message data
       ...

On return the block is updated with:

      +4 = task handle of the sender (task A)
      +8 = my_ref

What is important to remember is that the fields at +4 and +8 need not be filled in by the application. They will be filled in by the Wimp, and the block with these fields filled in will be sent to task B. The my_ref code is generated by the Wimp (> 0), and used by the Wimp as a unique identifier of the message.

The length of the block (offset +0) should be set to the size of the message in bytes, including any strings and their terminating characters, rounded up to the nearest whole word (i.e. multiple of 4). It is at least 20 (up to and including the message action code, but without any message data), and at most 256 (the maximum size of a message).

The your_ref field should normally be set to 0, to indicate a message that is not a reply. When and how replies (your_ref > 0) are used is described below.

A Simple Message

Task A can send a message to task B using event code 17 (User_Message) in R0 of the Wimp_SendMessage call. The Wimp will take the data block pointed to by R1, fill in the fields as described in #Wimp_SendMessage and put that block in its message queue. The next time task B is activated, its Wimp_Poll call will return event code 17 and the poll block will be filled with the data from the block from the message queue. (The User Message codes, 17, 18 and 19, have the highest priority in the Wimp, so User Messages will arrive before other Wimp messages.)

Task B now has the message and can act on it. Task A will not automatically get any form of confirmation that the message has indeed arrived at task B.

Question: what happens if the destination task handle is not valid (i.e. there is no corresponding active task). Does Wimp_SendMessage return an error, or is the message silently dropped?

A Simple Broadcast

Task A can send a User_Message (17) to all other tasks by setting the destination task handle (in R2) to zero. The Wimp will pass this message with event code 17 to each task in turn, including to task A itself. Note that task A will not necessarily be the last task to get this message. Task A will usually ignore this message.

Such a broadcast may be useful to inform many tasks of an event in one go. Tasks which are interested in such a message from task A will usually recognise them by the task handle of task A, and/or by the message action code and message data.

There is no guarantee that any given task will receive a broadcast. This is because tasks can register with the Wimp which messages they wish to receive. If a task chooses to do so then only messages on the list presented to the Wimp will be delivered to the task.

A Recorded Message

To get a confirmation that task B has actually seen (and not ignored) the message, task A can send a Recorded Message. This is done by using event code 18 (User_Message_Recorded) rather than code 17 when sending it. The message will arrive in the normal way at task B (with 18 as the Wimp_Poll return code). Task B can then choose to acknowledge the message, or to ignore it.

To ignore the message, task B does not have to do anything. In fact, it does not even have to have routines in place to handle this type of message. Obviously, ignoring a message is the default action for a task. When task B calls Wimp_Poll again, the Wimp will notice that the message was ignored, and deliver the message back to task A with event code 19. This event code is unhelpfully called User_Message_Ack. It should, in this case, be called User_Message_NAck (for negative acknowledge). When task A gets this event code 19, it means that the message it sent was ignored by the target, and it can take further action.

To acknowledge the message, task B can send any User Message to task A, and do it either as an event code 17, 18 or 19. The important thing is that in the acknowledge, it sets the your_ref field (at +12) to the my_ref field (at +8) of the original message. When task B calls Wimp_Poll again, the Wimp will recognise the freshly queued answer by the your_ref field, and not send a code 19 message to task A. For this recognition to work, task B must send the reply after receiving the message from task A before calling Wimp_Poll again. The destination for this acknowledge, i.e. the task handle of task A, can be found at offset +4 in the message block.

Simple Acknowledge: code 19

For a simple acknowledgement task B can use code 19: User_Message_Ack (this time the name fits). The Wimp will recognise this message, not send a code 19 message to task A, and simply drop the code 19 reply from task B. The effect is that the message is acknowledged, and no new message is sent to a task.

Acknowledge with new message: code 17

Task B can acknowledge the message and at the same time send a new message to task A by using code 17 (User_Message) rather than code 19. The Wimp will still recognise it as a reply (due to the your_ref field being filled in) and not send the original message back to task A with code 19. It will also deliver the new message (which may have a completely different message action code and message data) to task A.

Acknowledge with a recorded message: code 18

This is like an acknowledge with code 17, but now the new message from task B to task A is again a recorded message. If task A ignores it, task B will get it back as a code 19 message. For an acknowledge, task A will have the same options as task B had.

In fact, two tasks can send each other a practically unlimited chain of recorded messages, in the process sending lots of data in the messages. The memory data transfer version of the Data transfer protocol works like this, sending RAMFetch and RAMTansmit messages back and forth until all data is transferred (PRM 3-255).


A Recorded Broadcast Message

This is done by sending a User_Message_Recorded (18) to all other tasks by setting the destination task handle (in R2) to zero. The Wimp will send this message to each task in turn, with a Wimp_Poll return code of 18. If none of the tasks acknowledges the message, it will eventually return to task A with a code 19.

If one of the tasks (we'll call that one task B) acknowledges the message as described for recorded messages, then task A will not get the message bounced back with code 19. So far, things are the same as with a normal, non-broadcast, recorded message. An extra trick is that after task B has acknowledged the message, it is no longer passed on to any other tasks. This means only the first task that replies to the message will be able to do so. Any other tasks that might want to reply but have not seen the message yet, will never see the message.

It is important to note that some broadcast messages should never be acknowledged as they need to be received by all (interested) tasks. This includes broadcasts that inform tasks of system wide changes, such as the screen mode changing.

Also note that task A might see its own message pass by as a code 18 message. This will certainly happen if no task acknowledges the message, because in that case the message is offered to all tasks, including task A. But even if another task acknowledges the message, there is a good chance that task A will see its own message coming round as a code 18 message. It is not until task A gets a code 19 message that it knows the message was not acknowledged.

Tasks should ignore any User_Message (17) or User_Message_Recorded (18) that it sent itself. They can recognise them by the task handle of the originator (offset +4) when it is the same as their own task handle.

Sending a recorded broadcast message is a good way to find a task which can perform a certain service for task A. This is for instance used by the OLE protocol to find an OLE server.

The Wimp Filter

The Wimp offers a particular User Message only to applications which have explicitly indicated that they want to receive it. This prevents unnecessary overhead from switching to a task and letting it decide itself that it does not want to do anything with a particular message.

A task switch is rather expensive in resources: the memory manager needs to be reconfigured, cache pages need to be flushed and loaded with code for the new application, etcetera. Each task switch which can be avoided is a speed gain for the whole system.

To get the User Messages it wants to receive, an application should pass a list of desired message action codes (as found at offset +16 in the message block) to Wimp_Initialise, as a zero-terminated list pointed to by R3. Any messages not on the list will not be passed to the application and hence won't cause a context switch.

An application may indicate that it does not want to receive any User Messages by setting R3 to zero. In this case only the Message_Quit(0) message will be passed to the application, to allow the task to be quit from the system task display window.

It is also possible to let the Wimp pass all messages to your application, by passing an empty list to Wimp_Initialise. R3 then points to a list consisting of only the terminating zero word. In short, R3 points to a word containing the value zero.

The list passed to Wimp_Initialise is the initial set of messages that a task wishes to receive. The list can be altered at runtime by using the SWIs Wimp_AddMessages and Wimp_RemoveMessages. However, this does not work on tasks that use the Toolbox.

Passing an empty list should only be done in the development phase of an application, and sparingly too. One possible case would be when the application does not appear to respond to a particular message, and the programmer suspects this is because there is an error in the list of messages.

The application can then be changed to temporary allow all messages. If this works, the error was in the list, and the list should be scrutinized, corrected and re-instated. If it does not help, the error is somewhere else, and the list should also be re-instated.

A stable version of an application, be it for private use or for publication, should always have a proper list of messages set up. Allowing all User Messages to be offered to an application will cause a significant decline in system performance due to unnecessary task switching.

Remarks

Reusing the poll block

Often the same memory block can be re-used when sending a reply to a message. Applications will have a 256 byte block (the poll block) of which they pass a pointer to Wimp_Poll. This will contain the message after Wimp_Poll returns.

For a reply, this same block can usually be passed to Wimp_SendMessage, with only a few changes. For instance, for a simple acknowledge with User_Message_Acknowledge (19), all that needs to be done is copy my_ref (+8) to your_ref (+12). R2 can be set to the task handle of the sender from +4, and R0 set to 19.

If you want to send a different message, don't forget to not only change the message action code and the message data, but also recalculate the length of the block at +0.

The program will never have to fill in the items at +4 and +8, as they are ignored and filled in by the Wimp.

Message priorities

The Wimp does not simply deliver messages in the order in which they are created, but groups them in several priorities (See PRM Wimp_Poll, p3-116). From high to low:

  • User Messages (type 17, 18 and 19) and any events sent using Wimp_SendMessage. Explicitly mentioned are types 11 (Lose_Caret) and 12 (Gain_Caret)
  • All other types, except 0
  • Type 0 (Null_Reason_Code)

This means that User Messages have a high priority.

Finding task handles

You can discover the task handle of a given window or icon bar icon by sending a User_Message_Acknowledge (type 19) with your_ref=0 and R2/R3 set to the window/icon handle. On exit R2 will contain the task handle of the owner, though no message was actually sent.

Similarly, after sending any message the task's own handle can be found at offset +4 in the block.

Window handles vs Task handles

The value passed in R2 to Wimp_SendMessage is either a Window handle or a Task handle. There are no flags or other values passed to the SWI to indicate which is used. This means that the Wimp can tell them apart merely by looking at the value.

External References

  • Wimp_SendMessage: PRM 3-196
  • Wimp_Poll: PRM 3-115 Description of the return codes, including code 17, 18 and 19
  • The Messages section in the Programmer's Reference Manual, PRM 3 pp 230-267. This describes several protocols and their user messages already defined in the WIMP. Among them are System Messages (such as Message_Quit), Filer Messages (to communicate with the Filer), the Data Transfer Protocol, the Printer Protocol and many more.