Beginner's Guide to WIMP Programming
Everything you need to know to start writing your own applications.
Chapter 3: Getting on the Icon Bar
We come now to writing the !RunImage file which, as we discovered at the end of the last section, contains the major part of the application. This file will be progressively developed during the whole of this guide so, if you're using the software supplied with the guide rather than typing in your own, you will find a great many different versions of the file.
The application itself is supplied without a !RunImage file, but with a set of files whose filenames consist of the page number where they are described in this guide. To run the application, make a copy of it, drag the appropriate file into its directory and rename the file "!RunImage". To see what this first file does, for example, open the '!Test' application directory, locate the page_015 file inside the 'Steps' directory, copy it into the '!Test' application directory and rename it as "!RunImage". Then run the application to see what happens.
If you're typing in your own files, create a new Basic file using your text editor and type in the following (without the line numbers):
10 REM >!RunImage 20 REM (C) Martyn Fox 30 REM Wimp training program 40 REM version 0.01 (date) 50 SYS "Wimp_Initialise",200,&4B534154,"Test" TO ,task% 60 PROCinit 70 PROCcreateicon 80 REPEAT 90 PROCpoll 100 UNTIL quit% 110 PROCclose 120 END 130 : 140 DEFPROCcreateicon 150 REM creates the application's icon and puts it on the icon bar 160 !b%=-1:b%!4=0:b%!8=0:b%!12=68:b%!16=68:b%!20=&3002 170 $(b%+24)="!Test":SYS"Wimp_CreateIcon",,b% TO i% 180 ENDPROC 190 : 200 DEFPROCclose 210 REM tells the Wimp to quit the application 220 SYS "Wimp_CloseDown",task%,&4B534154 230 ENDPROC 240 : 250 DEFPROCpoll 260 REM main program Wimp polling loop 270 SYS "Wimp_Poll",,b% TO r% 280 ENDPROC 290 : 300 DEFPROCinit 310 REM initialisation before polling loop starts 320 DIM b% 1023 330 quit%=FALSE 340 ENDPROC 350 :
The REM statements in the first four lines can, of course, contain anything you like. Save the file in the application directory, using the filename '!RunImage'.
InitialisationTo some extent, this next section is a recap of what we discovered when we looked at the one-line multi-tasking application earlier.
At line 50 we encounter the SYS command, whose job is to introduce our program to the Wimp system as a multi-tasking application.
Register R0 is loaded with the version number of RISC OS for which our software is being written, multiplied by 100. This allows us to include the two digits in the version number after the decimal point and still end up with a whole number. This number serves two purposes. Firstly, if we were to run our software on a machine with an earlier version of RISC OS, it would warn us that it might not work. Secondly, it tells the machine which version our software was written for, so that it can adjust itself accordingly.
The software in this guide will be written to be compatible with RISC OS 2 as well as RISC OS 3 and later. For this reason, we put 200 into R0 so that it can run on a machine still using RISC OS 2 (known as Backward Compatibility).
Register R1 contains a 32-bit number which is the ASCII code for 'TASK' with the least significant byte having the lowest address and the most significant byte having the highest. As we saw earlier, this is a way of telling the Wimp that we are starting up a proper multi-tasking application, written for RISC OS, and not one written for the older 'Arthur' operating system.
Once again, the string in R2 is the name of the application for use in the Task Manager window. In this case the number which the SYS command puts into the register is the address where Basic has put the string.
After the call has finished, R1 contains the task handle - a number by which the Wimp knows this application. We will probably not make much use of this, except when we tell the Wimp that the application is closing down.
Having initialised the Wimp, we can now set up various parameters of the program, with PROCinit, which is currently at the end of the listing.
We shall be making a great deal of use of a data block. Most system calls to the Wimp require the passing of a lot of information, often much more than could be contained in the processor's registers. To do this, we use a block of memory containing all the details, and we simply have to put the address of the start of the block into one register, usually R1.
The DIM command sets up this block for us. It sets aside 1,024 bytes (1K), which should be more than adequate for our purposes, and puts the address of the first byte into variable b%. We shall be referring to this variable a great many times, which is why we give it a very short name. Because the number in b% is an address, we say that b% is a pointer, or that it points to the data block.
Bits, Bytes and WordsAn address number refers to the location of one byte, or eight bits. Because your computer is a 32-bit machine, most of its data is handled four bytes at a time. Note, by the way, that a four-byte, or 32-bit, number is called a word.
We will frequently refer to individual bits in a word by their bit number. The least significant, or right-hand, bit is bit zero, and the most significant, or left-hand, bit is bit 31. For example, if we say that bit 31 is set, it means it is a 1; if we say it is clear, it means it is a zero.
Incidentally, '!b%=X' means the value of X is put into the word (four bytes) at b%, and 'b%!4' means the same as '!(b%+4)'. If you only wanted to put a number into one byte, you would use a question mark, for example 'b%?4=X'.
To put a string into the data block at, say, b%+12, we use the command '$(b%+12)="Main window"' or '$(b%+12)=title$'.
We can see the first use of this data block in PROCcreateicon.
The first four-byte word of the block contains the handle of the window in which we wish to create the icon. This is a number the Wimp gives to each window when it's created. In this case, the 'window' where we want to put our icon is actually the icon bar. When it comes to creating icons, the icon bar has a window handle of -1 for icons on the right-hand side, and -2 for those on the left, such as filing systems and printer drivers. We want our icon on the right, so we put -1 into the first four-byte word of the block.
At other times the icon bar always has a window handle of -2.
The next four words contain the positions of the sides of the icon; the minimum and maximum x (horizontal) and y (vertical) coordinates. We do not know where our icon is going to sit on the icon bar, so we give min x and min y values of zero, and max x and max y are the width and height of the icon.
It is worth noting, by the way, the order of those coordinates. In Wimp programming, we often have to put the coordinates of a rectangle such as a window into a data block. The standard order is: minimum x, minimum y, maximum x, maximum y.
The next word, at b%+20, contains the icon flags; 32 separate bits which govern the icon's behaviour. We will see what all the bits mean later, but the number we are entering here, &3002, means the icon contains a sprite and that it notifies the application if it is clicked on once, which is what we want.
The string starting at b%+24 is the name of the sprite, which may have up to 12 characters. If you wanted to use a sprite with a longer name, you would have to use indirected data, which we will deal with at a later stage.
Notice that we call Wimp_CreateIcon with the pointer to the start of the block, b%, in R1. This is a very common practice in Wimp system calls. The Wimp gives the icon a number, called the icon handle, which it sends back in R0 and which the SYS statement puts into variable i%
We shall be looking at Wimp_CreateIcon in greater detail later.
Polling the WimpAfter creating the icon our program enters the main polling loop, which is the heart of all multi-tasking programs running under RISC OS. As with our one-line program, each time the program goes round the loop, it calls Wimp_Poll. As far as our program is concerned, control comes straight back again, but, in fact, the system has returned control to other applications running in the meantime.
On return from Wimp_Poll, R0 contains a reason code, which tells the application precisely why control is being returned to it at this time. We will learn to interpret these reason codes later.
After you have typed in the !RunImage file, check it and save it in your application's directory. After you have finished test-running it, you may wish to make a copy of it, called 'page_015' so that you can keep a copy of this version of the file before you modify it.
If you now double-click Select on the application directory icon, you should see the application's icon appear on the icon bar and, if you open the Task Manager window, you will see your application listed under 'Application tasks', with 32K, or whatever you chose, allocated to it.
Unfortunately, there is no way of removing the application, short of resetting the machine. Most applications have a Quit option on their main menu but we haven't given ours a menu yet. The Quit option that can be selected from a menu on the Task Manager window does not work and, if you try to close down the desktop with the Exit option on the Task Manager's main menu, or even choose Shutdown, you may find (depending on which version of RISC OS you have) that the application remains obstinately in place, even after the rest of the desktop has disappeared!
If you have RISC OS 3.5 or later, you can, of course, shut down the program by pressing Alt-Break.
... the application remains obstinately in place ...
The trouble is that the loop in lines 80 to 100 keeps repeating until the variable quit%, which was set to FALSE by PROCinit, changes to TRUE, and there is nothing to make that happen!
Your program has not crashed the machine. In one sense it has, as there is no way of getting out of it, but both the machine and the program are still working properly. If you want to prove this, try adding a couple of extra lines to make the program beep once every so often, rather like the one-line program did:
250 DEFPROCpoll 260 REM main program Wimp polling loop 270 SYS "Wimp_Poll",,b% TO r% 280 count%+=1:IF count%=500:count%=0:VDU 7 290 ENDPROC 300 : 310 DEFPROCinit 320 REM initialisation before polling loop starts 330 DIM b% 1023 340 count%=0 350 quit%=FALSE 360 ENDPROC 370 :
The new lines are temporary; delete them after you have finished this little experiment. They just set up a counter which is incremented by 1 each time the Wimp polling loop is run. After every 500th call to Wimp_Poll, the program beeps and the counter is reset. If you run it (the listing is provided as page_020), you will find that your machine works as normal, except that it beeps frequently. This proves that you have created a real, multi-tasking application; you just can't easily get rid of it!