If you’re familiar with writing single-tasking software – on any platform – then writing an application to work under the RISC OS Wimp can be a confusing experience. Conventional software is linear: execution starts at the top and runs through to the end. The code will follow any functions, subroutines or – perhaps – gotos that it meets, but this is all under the control of the software developer.
Writing for the Wimp – or, indeed, any other windowing system – isn’t like that. The user can interact with an application via its windows and dialogue boxes in a host of different ways: clicking on buttons, scrolling the contents, opening menus, dragging other windows over the top and requiring things to be redrawn. Other software running on the system might also want to interact and exchange information.
The way that software interacts with the Wimp hasn’t really changed since the days of Arthur – although there have been some small changes to accommodate the system of cooperative multitasking, they don’t alter anything in a fundamental way. All access to the Wimp is via SWI calls: using the
SYS command in BASIC or – in the case of this tutorial – OSLib when working in C.
Registering with the Wimp
When an application starts, it must initialise itself: loading any data it needs from disc, setting up any data structures and registering itself with the Wimp. This last point, done with the Wimp_Initialise SWI, is the first thing that differentiates multitasking applications from single-tasking ones. If we were working in BASIC, the initialisation for a very simple application might look like this.
DIM b% 255 quit% = FALSE SYS "Wimp_Initialise", 310, &4B534154, "Example App", 0
The code claims 256 bytes of memory and points a variable b% to it: a lot of Wimp SWI calls need a block of memory to work, and b% is a common feature of Wimp software written in BASIC. It then sets a variable quit% to FALSE, to keep track of when the code should exit.
With its own initialisation complete, the final thing it does is call Wimp_Initialise to register itself as a Wimp task. This call has a number of important parameters, which will affect how the Wimp treats the application.
310, which is the minimum version of the Wimp that the software is able to accept. There are a number of specific values which can be passed here to turn on different functionality: while 310 is the lowest which should be used in modern software, an application might pass 380 in if it required support for nested windows, for example (more of which later).
&4B534154 is a “magic number” – its four bytes actually spell out the word “TASK” in ASCII. It’s sufficiently unlikely to end up in R1 by accident that the Wimp uses its presence to turn off legacy support for Arthur: before RISC OS, no parameter was passed in this register. R2 contains a pointer to the name that we’re giving the task: it will appear in the Task Manager’s listing, amongst other places. R3 controls the behaviour of User Messages – for the time being, passing zero in here will be fine.
Doing what we’re told
With the initialisation complete, the application can move on to the business of multitasking. RISC OS uses a system of ‘cooperative multitasking’: that is, each application must yield control back to the Wimp as soon as it’s finished its current activity. If an application doesn’t yield for more than a few fractions of a second at a time, the desktop can very quickly become extremely sluggish.
This yielding is done via the Wimp_Poll SWI. When an application calls Wimp_Poll, it yields control back to the Wimp and the Wimp, in turn, goes off and services all of the other applications running on the system. When Wimp_Poll returns, it brings with it details of an ‘event’ that the application must deal with. This is enough to make the whole system multitask.
The events returned by Wimp_Poll can report actions from the user: keypresses, mouse clicks or menu selections. They can report the opening, moving, scrolling and closing of windows; they might also be requests to redraw part of the contents of a window. So-called ‘user messages’ – messages arriving from the system or from other applications – can also arrive as events.
This is an important point which separates Wimp applications from single-tasking software: all user input must come via Wimp_Poll or some other route approved by the Wimp. BASIC keywords like
MOUSE, C functions such as scanf(), or SWIs like OS_ReadLine and OS_Mouse must never be used – if they are, they will affect other applications running on the system.
To make an application multitask, therefore, is simply a case of calling the Wimp_Poll SWI repeatedly, and quickly processing the events that it returns.
REPEAT SYS "Wimp_Poll", &3831, b% TO reason% CASE reason% OF WHEN 17, 18 IF b%!16 = 0 THEN quit% = TRUE ENDCASE UNTIL quit%
When working in BASIC, the Wimp_Poll SWI is usually enclosed in a
UNTIL loop. In this example, the loop exits when the variable quit% – which was set to FALSE in the initialisation – becomes TRUE.
The call to Wimp_Poll takes two parameters and returns – for now – one value. The
&3831 passed in R0 is an ‘event mask’ in which individual bits are set to tell the Wimp that we don’t wish to receive particular events – we’ll explain this when we return to look at Wimp_Poll in more detail.
R1 contains a pointer to the 256 bytes of memory that we claimed in the initialisation; before returning from Wimp_Poll, the Wimp will fill this block with information about the event that’s being given to the application. The event is identified by a value returned in R0 and stored in reason% – the so-called ‘reason code’.
It’s fairly common in BASIC to take the reason code returned by Wimp_Poll and use it in a
CASE statement, to allow the program to jump to an appropriate piece of code for the event in question. There’s only one event that every Wimp application must handle, and that’s something called a Message_Quit – as its name suggests, it’s an instruction to terminate immediately.
We’ll look into the details of what’s going in in a later chapter, but for now it’s enough to know that Message_Quit will always arrive with an event reason code of 17 or 18 and with the word at offset 16 in the parameter block set to zero. If both these requirements are satisfied, the application simply sets quit% to TRUE and the
UNTIL loop exits without calling Wimp_Poll again.
Ending a Wimp application is the reverse of starting one up: it must free any resources it has claimed, and de-register itself from the Wimp. The latter is done with the Wimp_CloseDown SWI.
SYS "Wimp_CloseDown" END
Compared to Wimp_Initialise, Wimp_CloseDown is extremely simple: in the case of an application written in BASIC, it takes no parameters (it does take one parameter, but for a ‘simple’ application it’s zero so BASIC’s
SYS command fills it in for us).
In the case of our example application, that’s is: there’s no resources to release. We can therefore just call
END and let the program terminate.
Putting it all together
Putting the pieces together, we end up with the program in Listing 1.1.
REM >Example 1.1 REM REM (c) Stephen Fryatt, 2015 DIM b% 255 quit% = FALSE SYS "Wimp_Initialise", 310, &4B534154, "Example App", 0 REPEAT SYS "Wimp_Poll", &3831, b% TO reason% CASE reason% OF WHEN 17, 18 IF b%!16 = 0 THEN quit% = TRUE ENDCASE UNTIL quit% SYS "Wimp_CloseDown" END
Listing 1.1: A simple Wimp application in BASIC
When run, the application creates an entry in the Task Manager as shown in Figure 1.1 and waits to be asked to quit via the menu (selecting Quit from the Task Manager will send a Message_Quit to the application).
It’s not exactly a useful application, but it does show the main structure required to interact with the Wimp.