C the WIMP 6
Taking a POLL
Without doubt, the POLL routine has to go down in any multi-tasking application as one of the most important of all the functions. Without a POLL routine, the machine will just sit there.
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.
Wimp Poll | Reason codes | |
In | 0 NULL | 1 Redraw window |
r0 mask | 2 Open window | 3 Close window |
r1 pointer to 256 byte block | 4 Pointer leaving window | 5 Pointer entering window |
r3 pointer to pollword in RMA | 6 Mouse click | 7 User drag box |
Out | 8 Key pressed | 9 Menu selection |
r0 reason code | 10 Scroll request | 11 Lose caret |
r1 block containing returned data | 12 Gain caret | 13 Poll word non zero |
14-16 Reservered | 17-19 User messages | |
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 common ignored codes are to ignore 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. Indeed, the same can be performed under C (infact, it is 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);
}
Alright, 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 of 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 can be a small structure which is the composite of a number of smaller structures.
For example
Here we define the mouse block
typdef 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;
(the other definitions are also just typedef structs to the other reason codes in poll routine).
Something which should strike as being strange is that for the main reason codes above, they are held in an union rather than as part of the struct body itself. Remember back to the C from the Top series, 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 linewimp_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 disasterous - the program won't work!).
Inside the memory definition routine, we have the line
if ((wb=malloc(sizeof(*wb)))==NULL)
{
report_error(1,"Wimp_Block operation failed");
exit(1);
}
This allocates 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.
paulf.johnson@ukonline.co.uk