Index


RISC World

Programming for non programmers

Part 7 - Working with the Wimp

David Holden continues his series.

So far you should have learned how to write simple programs in Basic, and we could continue to improve these skills and produce more complex and sophisticated applications, but if you want to write 'real' programs then that means learning to operate in the multi-tasking desktop environment. The method described in the last part, running a program in a Taskwindow, is not really good enough.

This is the point at which so many people give up. They may have learned to program on a BBC computer and perhaps write non-desktop programs in the early days of the Archimedes before RISC OS appeared, but they have never made the jump to writing desktop applications.

Don't be put off. It's not difficult, really it's not. If you have followed this series until now you have already learned all the important things you need to know. Everything else is just applying that knowledge plus a basic understanding of the way the Wimp works. There is nothing magical about it, and no massive mountain to climb before you can get even the simplest program to run. You just need to understand how the 'system' works.

In this session I shall describe in general terms how a multi tasking Wimp program 'works'. We wont go into any specific programming details, but in the next session we will put this overview together with some straightforward code to produce a 'real' Wimp program.

A multi-tasking environment

The most important difference between a single-tasking program like the ones we have written so far and a Wimp program is that a Wimp program has to co-exist with others. It cant PRINT directly to the screen or get user input via INPUT because it has to share the screen, keyboard, mouse, and all the rest of the hardware with other programs. All input and output therefore has to be channelled via SWI calls to the Wimp or Operating System. As you might expect, there are lots of special SWI's to do this.

The method used by the RISC OS desktop to manage all these programs is highly complex, but from the point of view of each individual program it's quite straightforward. When it starts up the program 'registers' itself with the Wimp and is given a unique number or task handle. This unique ID allows the program to communicate with the Wimp and with other programs, as we saw in the last part of the series.

With lots of programs and one set of hardware there has to be some way of dividing it between them, letting each program in turn do some work. There are various methods of achieving this but they fall into two main types. One allocates each task a proportion of the processing time and then leaves it and moves on to the next, the other goes to each task in turn and lets it have as much (or as little) time as it needs. This isnt the place to discuss the merits of the two systems, they each have good and bad points. The important fact is that RISC OS uses the second method.

Once a program is installed and has registered itself with the Wimp it repeatedly calls a SWI called Wimp_Poll. This passes control from the program to the Wimp. During this period the Wimp can carry out all the other work it has to do, like redrawing windows, checking for keyboard input and mouse button clicks, etc. and, of course, giving all the other applications their chance to do some work. When the Wimp_Poll SWI comes back to the program it will return various information about all the things that have happened since the last time it was there; what keys or mouse buttons were pressed, whether anything has happened with any of the program's windows or icons, if there are any messages from any of the other programs or the OS, and much more.

It is then up to the program to decide what, if anything, it needs to do, and then, having done whatever is necessary (or nothing, if that is what is required) call Wimp_Poll again and the cycle repeats.

As you can imagine, with several tasks running the Wimp actually works on a 'round robin' system, so each task is called in turn. When Wimp_Poll returns the program once again has (effectively) complete control of the computer. In the normal course of events it will retain this control until it calls Wimp_Poll again, so the program can take as much time as it needs. In reality it should take only the absolute minimum of time, as until it calls Wimp_Poll again all other programs are 'shut out', so, to the user, the computer will appear to 'freeze'. Of course, this would normally only be a fraction of a second, so on a Strong ARM computer with well written programs there might be two or three hundred Wimp_Polls per second. To the user things will appear to happen immediately. That key you pressed may have been 'offered' to several other tasks before the program that, to you, was the 'only' one that should have been taking an interest gets to hear about it, but to an unbelievably sluggish human the response will appear instantaneous.

Things that can be done quickly should therefore be done during a single poll, but anything that will take longer should be split up into chunks so that Wimp_Poll can be called while the program is working. This allows other programs to get a look�in and the user wont feel that that something has gone wrong.

There has to be some way for the Wimp to return data to a program and this is done both in the registers and in in a block of RAM. When you call Wimp_Poll you must put a pointer to a suitable block of RAM in R1. This RAM, of course, must be somewhere in your program's workspace. It doesnt need to be very big, the Wimp message system can only deal with 256 bytes of data.

To avoid the Wimp including your program in its round�robin of Wimp_Poll 'returns' you can set a mask when you call it. This is placed in R0 and tells the Wimp which type of events your program is not interested in. This is important, because if the Wimp has to poll every application even when nothing of interest to that application has happened it will be wasting a great deal of time so other programs will have less time to work and the desktop will run at a snail's pace. However, we shall leave this for the moment.

When Wimp_Poll returns control to the program R1 will still contain the pointer to the data block but R0 will hold the event code. What your program does and the contents of the data block will depend upon this event code. The codes returned are:

  • 0 Null (ie. do nothing)
  • 1 Redraw Window request
  • 2 Open Window request
  • 3 Close Window request
  • 4 Mouse pointer leaving window
  • 5 Mouse pointer entering window
  • 6 Mouse button click
  • 7 User drag box
  • 8 Key pressed
  • 9 Menu selection
  • 10 Window scroll request
  • 11 Lose caret
  • 12 Gain caret
  • 13 Poll word non-zero
  • 17 User message
  • 18 User message recorded
  • 19 User message acknowledge

Don't worry if you don't understand what some of these mean, they will be explained as we progress. The main thing is that you can see that Wimp_Poll will return things like mouse click and keypresses and in so doing tell us which window and icon these happen in. We can check if these are 'owned' by our program and if so take the appropriate action.

Practicalities

You might think that this will make a wimp program very different from the non multi�tasking ones we have looked at so far. In fact, it's not so very different. The general system is similar. You may remember from our early sessions we defined a simple system to describe what a program does.

  1. Get data
  2. Process data
  3. Output results
  4. Go back to 1

When we translate this into a working program we normally have to add another step before 1, so what we get is this.

  1. Set up program
  2. Get data
  3. Process data
  4. Output results
  5. Go back to 2

A Wimp program has to follow the same pattern. The main difference is that item 2 will be based around Wimp_Poll, and will in fact be itself a closed loop branching off to various processing routines and other routines to display results, probably in windows. So a sort of skeleton structure of a Wimp program would normally look something like this.

PROCsetup
REPEAT
 SYS "Wimp_Poll".... TO event
 CASE event OF
  WHEN 2: (open window request)
  WHEN 3: (close window request)
  WHEN 6: PROCmouse_click
  WHEN 8: PROCkey_pressed
  WHEN 9: PROCmenu_selection
  When 17,18: PROCmessage
 ENDCASE
UNTIL FALSE

DEFPROCsetup
  Define all the main system variables.
  DIM all the arrays required
  Define windows and menus
  Anything else required to get the program ready to run
ENDPROC

DEFPROCmouse_click
  Check if the click occurred in one of our program's
  windows or icons and if so deal with it
ENDPROC

DEFPROCmenu_click
  Find out which menu and item the mouse click occurred
  on and take appropriate action.
ENDPROC

To explain how this would work in slightly more detail let's return to our old VAT calculator program and see how it might look. We could implement it with a single window, something like this:

The VAT Calculator

To use this you would enter the amount in the top writable icon, click on either the Add or Subtract button and the amount would be calculated and displayed in the lower icon.

Applying this to the skeleton program, text entry in the top icon is actually handled by the Wimp, so we don't have to do anything. As soon as the user clicks on one of the buttons Wimp_Poll will tell us that this has happened and which button has been clicked. It does this by returning the window handle and the icon number within that window. The window handle is allocated by the Wimp when the window is created and registered with the Wimp (which would be done in PROCsetup) and we will have to record this. The icon numbers are set when the window is created and begin at zero. Whatever method is used to design the window we will know and record the numbers of the icons.

So, the CASE statement will pass the mouse click on to PROCmouse_click, which will first check that it's our window that's been clicked in, then work out whether it's the Add or Subtract button that's been clicked and pass control to procedures to do the actual work.

Their job is going to be to read the text from the top icon, calculate the answer and then display it in the bottom icon. We are therefore going to need two more routines to deal with reading and writing text to and from icons.

DEFFNread_icon_text (window%,icon%)
  Read text from designated window and icon
= text$

DEFPROCwrite_icon_text (window%,icon%,text$)
  Display 'text$' in designated window and icon
ENDPROC

To make these general purpose routines the information they require, the window handle, icon number and, when writing text, the text itself, is passed as parameters.

So, we have the skeleton of a Wimp program. In the next session we will flesh out these rather bare bones and turn it into a fully working Wimp application.

David Holden

 Index