User Messages

From RISC OS

(Difference between revisions)
Jump to: navigation, search
Erik Groenhuis (Talk | contribs)
(Set up a page describing User Messages (code 17, 18 and 19) to explain the acknowledge mechanism.)
Newer edit →

Revision as of 09:26, 2 February 2009

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 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.

Contents

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 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.

Task A may assume that all tasks will eventually get this message.

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 above, then task A will not get the message bounced back with code 19. So far, things are the same as with a normal 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.

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.

This 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.


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 delivers 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 even 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 vs Task handles

The value passed in R2 to Wimp_SendMessage is either a Window handle or a Task handle. This means that the Wimp can tell these 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
Personal tools