C The Wimp (part 2)

Hopefully, you will have written a couple of CLA programs and grown happy using the _swi function. By the end of this tutorial, you will see how quickly a (very) basic WIMP application can be written.

All Wimp programs have two features in common - they have to initialise themselves with the Task Manager and they have to use a poll loop. They are both _swi accessed, so will have values in and out.

SWI Wimp_Initialise

On entry:

   r0   Last wimp version * 100
   r1   0x4B534154 ("TASK")
   r2   Task description
   r3   Messages list required

On exit:

   r0   Current wimp version*100
   r1   Task handle

The wimp version relates to the RISC OS version. This is normally 310 (corresponding to OS 3.10), through 350, 360, 370 and 400 are all used. 200 (OS 2.00) is unlikely to be seen now.

r1 is actually just a fudge to filter out all the old pre-RISC OS programs which aren't likely to pass this on initialisation!

The task handle is the name by which the task manager knows what the application is. Every application, be it WIMP or CLA will have had one of these assigned to it on start up.

The poll loop is the manner by which the program itself keeps up-to-date with what events have been received.

Events?

All wimp programs rely on events. These events are the likes of the pointer entering or leaving a window or the mouse being clicked. That said, even when the program is just sitting there, twiddling its electronic thumbs or generally just chilling out, the program is still receiving events, namely NULL events.

SWI Wimp_Poll

On entry:

   r0   event mask
   r1   pointer to a 256 byte block
   r3   poll word pointer in RMA

On exit:

   r0   event reason code
   r1   pointer to block of data   

The event mask allows (or disallows) certain events. For now, we'll happily ignore it and accept all events.

The event reason code is just the result of the current wimp poll. For a NULL event, this will be 0.

How do we code this up though?

Remember, the task handle is unique to the program and may be needed elsewhere, so it's a good idea to set this up as a global variable. The same applies to the task name and some method of generating the 256-byte block of memory. Everything else is really just a matter of calling the correct _swi commands.

The full app looks like this:

/* C the WIMP - part 1.
   This program registers itself with the task manager and does nothing else.
   Note, the swi call is not trap handled with the _swix version of the
   function
*/

#include "swis.h"
#include <stdlib.h>

int task_handle;
int *mem_block;
int quit=0;
#define task_name "C the WIMP 1"
#define TRUE 1
void register_task(void)
{
 _swi(Wimp_Initialise,
 _INR(0,3)|_OUT(1),310,0x4b534154,task_name,NULL,&task_handle);
}
void initialise(void)
{
 mem_block=malloc(256);
 if (!mem_block) exit(1);
}
void event_poll(void)
{
 int reason;
    _swi(Wimp_Poll,_INR(0,1)|_OUT(0),0,mem_block,&reason);
 if (reason==0) event_poll();
}
int main(void)
{
 initialise();
 register_task();
 if (TRUE) event_poll();
 return 0;
}

Huge eh?! When it's compiled, the following will be seen in the task display window:

The program will now just sit there. It won't interfere with your computer (other than taking a chunk of memory - the chunk size will be the same as the NEXT size in the task window).

This isn't a true multitasking program as the only way to kill the task (other than a power reset, which is a tad extreme) is to highlight the registered name (here it is C the WIMP 1), press <menu> and select Quit on the menu.

There is only one other thing missing here which is of utmost importance to a WIMP application - error handling.

A swi which handles errors is Wimp_ReportError. Unfortunately, it's not a multitasking error box, but it does the job.

Wimp_ReportError (edited)

On entry:

   r0   256 byte block of memory
   r1   flags
   r2   application name

On exit:

   r0   corrupt
   r1   0 if nothing selected
        1 if OK selected
        2 if cancel
        3-5 for extra buttons

flags control the buttons on the error box. These vary but are all bit set.

Wimp_ReportError flags

   Bit   Result
   0     OK box
   1     Cancel box
   2     Highlight Cancel (or OK if there is no Cancel box)
   3     Don't do "Press space or click mouse to continue"
   4     Don't prefix title with "Error from "
   5     Return immediately with r1=0 and box open.
   6     Simulate icon click in box according to bits 0 and 1
   7     Don't beep
   8     Use categories (dealt with later)
   9-11  Categories
   12-31 Reserved. Must be 0.

As it stands though, the program, while it has the 256-byte block set up, can't hold the data required for the swi call.

You see, the block should contain the following

bytes 0 - 3 : int 
bytes 4 - 255 : char, \0 terminated.

The simplest way to do this is to declare error_block as a structure.

By the addition of the following lines, the application will now act more like a real application.

Insert at the beginning...

#include "string.h"

Replace the int *mem_block;...

typedef struct error_block {
                        int value;
                        char text[252];
                           } error_block;

Add prior to initialise()...

void report_error(const int error_number,const char *error_message)
{
 error_block error;
 error.value=error_number;
 sprintf(error.text,"%.251s",error_message);
 _swi(Wimp_ReportError,_INR(0,2),error,1,task_name);
}

Remove initialise(); function completely and in the main() routine, the line which calls the function.

Changes in event_poll():

Delete the if (reason==0) line.

switch(reason)
{
 case 0 : report_error(255,"This program does nothing useful");
 break;
}

Recompile and you're away.

This time, the program produces the error box and terminates when you select OK. This can be avoided by adding back in the if (reason==0) line which had just been deleted.

One thing you may have noticed is that, with the exception of main(), all the functions are void. The reason for this is that all of the functions don't need to return any values. It could be argued that wimp_poll should return a value, but as you've seen, the loop is simpler to keep within a void system.

There you have it

You can see that creating an application which will multitask is quite easy - but then, we've not actually hit upon anything difficult!

Next time, we'll have a deeper look at the wimp poll routine and how to make it far more efficient by using a structure instead of an empty memory block. Until then, keep plugging away!


Source: Archive Magazine 14.5
Publication: Archive Magazine
Contributor: Paul Johnson