C The Wimp (part 6 - Taking A Poll)

Without doubt, the POLL routine has to be one of the most important of all the functions in any multitasking application. Without a POLL routine, the machine will just sit there doing nothing.

Under RISC OS, the POLL swi looks at the application and, when an event is generated (such as a request to open a window), a reason code is generated and the application goes off and does what is asked - simple enough.

SWI Wimp_Poll

In:

Out:

Reason codes:

Priority (High - Low): 17-19,1-6, 8, 9, rest, 0

The wimp mask

This is a 32-bit number which tells the poll routine which reason codes the program wants to ignore. For instance, if you just wish to ignore all NULL polls (as is quite often the case), this value would be 1.

The most commonly ignored codes are the NULL reasons and pointer entering and leaving a window.

The poll routine

It is quite a handy thing to know that the block passed in to the swi in r1 is also the same block as is passed out in r1. This does pose a bit of a problem - how do you address this memory block?

In Basic, it would be performed using something like the following snippet:

DEF PROCpoll_routine
DIM poll_block% 256 : REM allocates 256 bytes of memory
SYS "Wimp_Poll",1,block% TO reason%
CASE reason% OF
 WHEN 0
  PROCnullreason
ENDCASE

Simple, and the same can be performed under C (in fact, it's almost identical).

void poll_routine(void)
{
 char *memory_block;
 int reason;

 if ((memory_block=malloc(256))==NULL)
 {
  report_error(1,"Memory allocation failed");
  exit (1);
 }

 _swi(Wimp_Poll,_INR(0,1)|_OUT(0),1,memory_block,&reason);

 switch (reason)
 {
  case 0 : null_routine();
           break;
 }

 free (memory_block);
}

OK, it does the job - but how do you interpret the data back from r1? Remember, all you have done is allocate a block of memory. Up to reason code 13, with the exception of reason 0 (NULL), all the other codes return a block of data inside r1. This makes Basic programs (and the above C method) look a complete mess and you end up (in Basic) with passing a block which has the same name around a multitude of PROCs. Using this method can result in a complete nightmare when a program needs to be debugged.

Given that we know that in C we have the facility to create data structures (we did this last time in defining the window block), the same can be applied to the value passed into r1. The poll structure can be either one very large structure or a small structure which is the composite of a number of smaller structures.

Example

Here we define the mouse block:

typedef struct mouse
{
  int mouse_x;
  int mouse_y;
  int buttons;
  int win_handle;
  int icon_handle;
  int prev_mouse_state;
} mouse;

This is then incorporated into the main poll block structure as:

typedef struct wimp_block
{
  union
  {
    int null;
    pointer redraw;
    poll_win open_win;
    poll_win close_win;
    pointer point_out;
    pointer point_in;
    mouse mouse_click;
    drag_box user_drag;
    key key_pressed;
    menu_select menu;
    scroll_req scroll;
    caret lose_caret;
    caret gain_caret;
    int poll_nonzero;
    int reserved[2];
    message_block user_mess;
    message_block user_mess_rec;
    message_block user_mess_ack;
  } poll;

 int nmem[64];
 char mem[256];

} wimp_block;

(other definitions are also just typedef structs to the other reason codes in the poll routine).

Something which may strike you as being strange is that, for the main reason codes above, they are held in a union rather than as part of the struct body itself. In the C from the Top series, I explained that, with a struct of this type, it means that the values will be either the contents of the union or will fill the nmem and mem values. If there wasn't a union, the data returned would fill from the top down making a mess of the order attempting to be made.

So how is this used?

Firstly, we need to allocate memory to the wimp_block. This is performed at the same time as allocating memory to any other structure (such as a window or icon data block). At the start of the program (prior to any functions), the line

wimp_block *wb;

is entered. This tells the program that the pointer wb points to the typedef'd structure wimp_block. No memory has been allocated to this block at present, so using it would be dangerous (and even disastrous - the program won't work!).

Inside the memory definition routine, we have the lines:

if ((wb=malloc(sizeof(*wb)))==NULL)
{
 report_error(1,"Wimp_Block operation failed");
 exit(1);
}

These allocate the required amount of memory.

The poll routine now becomes:

void event_poll(void)
{
 int reason,mask = 0x31;
 _swix(Wimp_Poll,_INR(0,1)|_OUT(0),mask,wb,&reason);
 switch (reason)
 {
  case 6 : mouse_click(wb->poll.mouse_click.icon_handle);
 break;
 }
}

which is simpler to understand than the Basic version which, though simpler in the poll routine, can be a pain in the mouse routine.

I will start to cover the reason codes next month.


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