First Steps in Programming RISC OS Computers
A RISCWorld reprint of the updated second edition
Chapter 15 :
Although by now you can doubtless produce some weird and wonderful programs of your own, you may be envious of commercially-produced packages which operate in the desktop environment with their windows, menus and icons on the icon bar.
The programs we've met so far have not exploited one of the most powerful features of RISC OS - its ability to multi-task, that is have several programs running at the same time, using its Wimp environment. Wimp stands for Windows, Icons, Menus and Pointers.
Moving into the multi-tasking environment is where this guide gives way to A Beginner's Guide to Wimp Programming but we will allow ourselves a little dabble at it in this section; enough to produce a multi-tasking 'front end' containing our Munchie program. We will use the version that we produced in the previous section which introduced a user sprite area. You could probably use this technique to run any program you like, with a little modification. It won't multi-task while the game is running but it will allow it to sit on the icon bar so that you can call it in the middle of word-processing or whatever else you're doing. It will also get round the problem of having to keep the sprites file in the currently selected directory.
From Munchie to !MunchieYou may already have discovered that an application consists of a directory whose name begins with an exclamation mark (!). Within this directory are the files used by the application. To open an ordinary directory, we double-click on it but, of course, doing this on an application directory runs the application. To open such a directory, hold down Shift while double-clicking.
If you do this on any application, you will find a variety of files inside, depending on how the application works. Almost certainly there will be three files called !Run, !RunImage and !Sprites. The !Sprites file contains the sprite used to represent the application directory in its parent directory window. The !RunImage file contains the body of the program and will probably be either a Basic or machine code file.
You will find versions of the !Munchie application in the mode 12 and mode 27 subdirectories. The mode 12 version contains, in addition to the MunSprites file, another sprite file called !Sprites which itself contains one sprite; a copy of the 'mun_right' sprite but with the name '!munchie'. When RISC OS opens the parent directory containing the application, it looks for this file, then looks inside it for a sprite with the same name as the application. If it finds one, it loads it into a special sprite area called the Wimp sprite area and uses it to represent the application directory in the parent directory window.
... there will be three files ...
The mode 27 version has the MunSprites and !Sprites files, and also another one called !Sprites22. This file also contains a '!munchie' sprite but there is a difference between the two. The one in the !Sprites file has rectangular pixels, as used in mode 12, and the one in the !Sprites22 file has square ones, as used in mode 27. If the computer is operating in a rectangular pixel mode when you open the directory window, it loads the !Sprites file; if it's in a square pixel mode, it loads the !Sprites22 file. In this way, it uses whichever sprite would look best on the screen.
If you wish to create your own application directory, you start in the same way as for an ordinary one, by clicking Menu over a filing system window and choosing the appropriate option. If you call it '!Munchie', it will be represented by an application icon. Clearly, the first thing we need is a sprite file so that our application will be properly represented in its parent directory window.
If you created your own Munchie sprites, measuring 34 pixels by 17 in mode 12 or 34 by 34 in mode 27, you can use the one called 'mun_right' that you already have. It's exactly the right size and will look good in any desktop mode. If you have the mode 27 version, you can use this sprite in the !Sprites22 file and also make a copy of it with rectangular pixels (using Paint's 'Use sprite as brush' option) for the !Sprites file. Open your !Munchie directory and copy the munsprites file into it, then load the file into Paint, keeping the directory window open. Click Menu over the Mun_right sprite, go to the Sprite 'mun_right' submenu and through to Save. This will let you save a file containing just this sprite. Before saving, change its filename to '!Sprites' (or '!Sprites22' - don't forget the '!'), then drag it into the !Munchie directory window. Load this new file into Paint, change the sprite name to '!munchie' and resave it. The sprite should have exactly the same name as the application except that, like all sprite names, it should all be lower case.
The colours for the game sprites were chosen to look correct using non-desktop colours because that is how they are used in the game. The new !munchie sprite will be used in an icon on the desktop so you may have to change its colours using Paint so that it looks correct.
Provided you have the sprite name correct and its file is called !Sprites, the application icon will now be replaced in the parent directory window by your Munchie sprite, though you may have to reset the machine first for this to happen.
Your application directory will now contain more than one sprite files. We're keeping the sprites used by the game separate from the one in the !Sprites file for a very good reason. The game sprites will be loaded into a user sprite area using the technique which we learnt in the previous section and will not continue to occupy any memory once we've quit the game. The sprite in the !Sprites file will go into the Wimp sprite pool, as we shall see shortly, and will remain there until we switch off or reset the machine.
The !Run FileIf you were creating an application directory from scratch and tried clicking on it as though you were trying to run the application, you would get an error message saying something like 'File 'ADFS::Games.$.!Munchie.!Run' not found'. The operating system would have tried to run a file called '!Run' in the !Munchie directory and, of course, it couldn't find it!
A !Run file is almost always an Obey file. This is a special type of file containing commands which are executed by the command line interpreter, just like the star commands which we used at the beginning of this guide. To create such a file, start up your text editor and choose the appropriate option. If you're using Edit, click Menu on its icon and go to the Create submenu. This presents you with a choice of filetypes. Choose Obey and a window will appear, ready for you to type in the following listing:
|Run file for Munchie Set Munchie$Dir <Obey$Dir> IconSprites <Munchie$Dir>.!Sprites WimpSlot -min 192k -max 192k Run <Munchie$Dir>.!RunImage
The first line is a comment, like a REM line in Basic. The '|' character (a shifted backslash) makes the CLI ignore the rest of the line.
The second line sets a system variable. You can see all the system variables by typing;
Once a system variable has been set up, it stays there until either it is removed with a *Unset command or the machine is reset. You may notice that any application which the machine 'knows' about will probably have a system variable named after it. The convention in RISC OS appears to be to put a dollar ($) sign in the middle of the name, so we will call our system variable Munchie$Dir.
Learning to Obey$DirOne of the existing system variables is called Obey$Dir. If you can find it in the list, you'll see that its contents (after the colon) are the pathname of a directory. In fact, it contains the pathname of the last Obey file that was executed. Every time the filing system runs an Obey file, it sets Obey$Dir to the pathname of the directory containing the file.
When the CLI executes a line containing angular brackets (<>), it looks for the name of a system variable between then and replaces it with its contents. This means that our new variable Munchie$Dir will have the same contents as Obey$Dir. Because the line creating it is part of an Obey file, this is the pathname of the directory containing the file being executed, which is also our application directory. This is important because it gives our application a way of finding its files wherever it's located, whether in the root directory of a floppy disc or deep in the directory structure of a hard disc.
The next line loads the !Sprites file into the Wimp sprite pool. It is probably not necessary to do this as it will have been done by the filer when it opened the parent directory but it's best to make sure. The !munchie sprite is the one we're going to put on the icon bar, so we need it in the Wimp pool and this line ensures that it will be there if you should happen to run the application from the command line.
If we were to call the !RunImage file at this point, the program would run and occupy all the memory which the task manager has allocated to the 'next' application, even though it may not need that much. This would be very wasteful and tie up memory which other applications could use so we must first set the size of the 'slot' which our program will occupy. This is the job of the WimpSlot command in the next line of the !Run file. The two suffixes '-min 192k' and '-max 192k' together ensure that this is the amount allocated. It may seem a lot for what is not a very long program but most of it is taken up by the sprite user area. In the mode 12 version, these figures are changed to 128k because the sprite area is smaller.
Incidentally, if your machine has 1Mbyte of RAM, a Wimp slot is allocated in blocks of 8k. For a 2Mbyte machine, this becomes 16k and, for 4Mbytes, 32k. The Risc PC and later machines use blocks of 4k, whatever the size of their memory.
If you try to run the application again, the error message will change to something like 'File 'ADFS::Games.$.!Munchie.!RunImage' not found' because, of course, the last line of the !Run file calls a file which we haven't yet written. You will find, though, that your system variable has been set up.
The !RunImage FileMost of this file will be copied from the last version of the Munchie program but we need to add a bit to the beginning to make it multi-task.
If you're working from scratch, create a new Basic file and type in the listing called !RunImage. This is the minimum that will allow the program to run without crashing.
Lines 160 to 240 can be copied from PROCinit in the Munchie program but line 170 has a bit added to it.
The beginning of PROCinit does the work of part of Munchie's PROCinit. This consists of actions such as loading the Mun_sprites file, which should only be done once, before you start to play the game. We've left out the commands which modify the sound system for now - we don't want to change the beep yet!
The addition to line 170 creates a data block of 100 bytes. We'll use this several times with the SYS commands that control the Wimp. Line 250 creates variable quit%. The application continues to run as long as it remains FALSE.
At line 260, we meet the first of these SYS commands. The first thing to do is initialise our program in the multi-tasking environment which we do with the Wimp_Initialise call. The number in R0 is the version number of the operating system that we're writing for, multiplied by 100. Unless you're using special new features that only RISC OS 3 and later versions can handle, always make this number 200 so that your program could run under RISC OS 2.
The hexadecimal number &4B534154 in R1 is, in fact, the ASCII code for 'TASK' (the number is stored in memory low byte first, &54 being the code for 'T' and &4B the code for 'K'. This special number tells the Wimp that the program was written to run under RISC OS and not the older Arthur operating system. The string in R2 is the program name for showing in the Task manager window.
The number returned in R1 is the task handle for this program. We store it in variable task% in case we need it later.
Following initialisation, the next job is to create an icon on the icon bar. PROCicon does this by setting up the data block and calling Wimp_CreateIcon with the address of the start of the block in R1. Line 300 sets up the various four-byte words of the block. The first of these at b% contains the window handle or number of the window in which we want to put the icon. The 'window' in this case is the icon bar which has two handles when it comes to creating icons; -1 for an icon on the right-hand side and -2 for one on the left.
The next four words, from b%+4 to b%+16, contain the minimum and maximum x and y coordinates of the icon. We don't actually know where on the bar our icon will be but in this situation we can make the minimum x and y coordinates zero and the maximum ones the width and height of the sprite - both 68 OS units.
The next word at b%+20 contains 32 bits called flags which describe the nature of the sprite. We won't go into them in this guide except to say that bit 1 is set, meaning that the icon contains a sprite, and the number 3 in bits 12 - 15 means that our application is called whenever the mouse is clicked over the icon. Finally, the bytes from b%+24 onwards contain the sprite name, entered in line 310.
Having set up all this information, we put the address of the data block in R1 and call Wimp_CreateIcon in line 320. Although the information in the data block will still be there after the icon has been created, we don't need it any more and the data block can be used for other things.
Polling the WimpReturning to the main part of the program, we now enter a loop between lines 60 and 120 which we keep going round as long as quit% remains FALSE. Line 70 calls Wimp_Poll, passing it the address of the data block in R1.
When the call returns, the number in R0 is put into variable r%. This number is a reason code which tells us if anything has happened that our program needs to know about. We check for various values of reason code using the CASE ... OF structure between lines 80 and 110, then check that quit% is still FALSE and go back to the top of the loop to call Wimp_Poll again.
That, at least, is what appears to happen. In practice, between our call to Wimp_Poll and its return, the machine may have handed control to every multi-tasking application that is running. Each one in turn checks its reason code, does whatever it has to do and calls Wimp_Poll again, allowing the machine to pas on control to the next application. Provided all the programs behave themselves, the system works. This is the heart of the multi-tasking system and the secret of how several programs can run at the same time, but our program is blissfully unaware of all this as it goes round and round its polling loop.
The CASE ... OF structure only checks for the reason codes that we're interested in and does nothing if any others arrive. Codes 17 and 18 both mean that the operating system has sent us a message, the details of which are in the data block. If either of these reason codes is received, line 100 calls PROCmessage to deal with the situation.
The nature of the message is indicated by the message action code which is the number in the word at b%+16. The only message we're interested in is one with action code zero which is an instruction to our application to close down. If this number is found in b%+16, line 360 simply sets quit% to TRUE. The next time the program gets to the end of the polling loop at line 120, it carries straight on. Line 130 calls Wimp_CloseDown which does the opposite of Wimp_Initialise. We pass it our task handle in R0 and the ASCII code for 'TASK' in R1. The program then ends and our icon disappears from the icon bar.
If you now double-click on the !Munchie icon, the program should load and its icon will appear on the icon bar. If it doesn't appear, it could be because the sprite name in line 310 doesn't match the name of the sprite in the !Sprites file or because the program isn't going round the Wimp_Poll loop properly.
Avoid the temptation for now to click the mouse on your new icon. Instead, open the Task manager window. You can do this, if you have RISC OS 3 onwards, by clicking on the icon at the right-hand end of the icon bar. If you have RISC OS 2, the icon will be an 'A' character. You need to click Menu on this and choose the Task display option.
... its icon will appear on the icon bar ...
The top section of the window is headed 'Application tasks' and shows the names of any applications which are running including, hopefully, Munchie with 128k or 192k allocated to it (this is where the operating system makes use of the name in R2 when we called Wimp_Initialise). If you click Menu anywhere along this line, you will get a menu including an option called Task 'Munchie'. This leads to a submenu with one item, Quit. If you select this, you should find that the Munchie line will disappear from the Task manager window and your icon from the icon bar. Your application has closed down.
What has happened is that the operating system has sent the application a message through Wimp_Poll and PROCmessage has dealt with it.
Dealing with Mouse ClicksIf you're typing in your own version. there is another way of shutting down the application at this point. If you start it up again and click the mouse over the icon, you will get an error message saying 'No such function/procedure at line 90'. When you click the mouse, Wimp_Poll returns with a reason code of 6, meaning that the mouse has been clicked over a window or icon belonging to us. Line 90 tries to send the program to a procedure which we haven't yet written. That is the next part to add:
390 DEFPROCmouseclick 400 CASE b%!8 OF 410 WHEN 1:quit%=TRUE 420 WHEN 4:PROCplay 430 ENDCASE 440 ENDPROC 450 :
The data block contains information such as which button was clicked, the window and icon the pointer was over when the mouse was clicked and its x and y coordinates. Because we're keeping things simple in this program, we only need to know which button was clicked. This information is contained in the word at b%+8, which is examined by the CASE ... OF structure.
This number is the same as the third number returned by the MOUSE command which we met in the MouseLines program in section 6. The three least significant, or right-hand, bits reflect the state of the mouse buttons, each one being a 1 if the corresponding button was pressed. Thus, if you click Adjust, bit zero will be set, putting 1 in b%+8. If Menu is clicked, b%+8 will be 2 and if Select is clicked, it will be 4. Lines 410 and 420 check for Adjust and Select respectively.
This arrangement allows us to either play the game by clicking Select over our icon or quit the application by clicking Adjust. If we do the latter, line 410 sets quit% to TRUE, resulting in the program closing down, as we saw earlier. Clicking Select leads us to PROCplay and the start of the game itself:
460 DEFPROCplay 470 mode%=MODE 480 MODE 27:REM use mode 12 for standard resolution monitor 490 VOICES 2 500 VOICE 2,"WaveSynth-Beep" 510 VOICE 1,"StringLib-Hard" 520 BEATS 200 530 bar_sent%=FALSE 540 REPEAT 550 PROCgame 560 UNTIL (char% AND &DF)<>ASC("Y") 570 VOICES 1 580 VOICE 1,"WaveSynth-Beep" 590 SYS "Wimp_SetMode",mode% 600 ENDPROC 610 :
This may look familiar; most of it consists of the remainder of the PROCinit and main loop of Munchie. We leave it until now to redefine the voices because, of course, we don't want to modify the beep while our icon is just sitting on the icon bar!
We have added two new lines to the procedure; line 470 to keep a note of the screen mode before we change it and line 590 to restore the desktop. When we get tired of playing the game, the loop between lines 540 and 560 stops repeating, the sound system is restored to normal and the screen mode returned to what it was. It is important to do this with the Wimp_SetMode command rather than using the Basic MODE keyword because this command restores the desktop palette (the colour definitions) and redraws everything in the desktop.
The Remainder of the GameSo far, we've accounted for the main part of the old Munchie program, together with its PROCinit. The rest of the procedures can be copied into the new version of the program without alteration, except for a few changes to the end of PROCgame:
800 PROCmove 810 UNTIL dotleft%=0 OR char%=27 820 IF char%<>27 THEN 830 PRINT TAB(10,25)"You took ";(TIME-start%)/100" seconds" 840 PRINT TAB(10,26)"Another go? (y/n)" 850 REPEAT 860 char%=GET 870 UNTIL (char% AND &DF)=ASC("Y") OR (char% AND &DF)=ASC("N") 880 ENDIF 890 ENDPROC 900 :
In our previous, non-multitasking version, if we wanted to stop playing the game halfway through, we could simply press Esc. In the Wimp environment, the escape key is usually disabled and just acts as a normal key, producing ASCII code 27.
In order to be able to stop the game with Esc, we need to detect code 27. When we get to line 810, the variable char% contains the code for the last key pressed, produced by PROCmove. We've modified this line to stop the loop repeating when we press Esc and added lines 820 and 880 to skip the last part of PROCgame and return us straight to PROCplay. Line 560 in PROCplay stops its own loop repeating under these circumstances (27 AND &DF is not equal to the ASCII code for 'Y') and we return to the desktop.
ConclusionThis is the beginning of Wimp programming. We haven't used any windows, menus or icons other than the one on the icon bar, or made any use of the mouse pointer, apart from detecting a button click over the icon, but your program can co-exist in the memory with others and be available for you to run at any time - a multi-tasking application.
And that's it. You've taken your first steps in programming your RISC OS computer. We've covered quite a lot of ground and seen what a great many of the Basic keywords do and how to use them. For a fuller description of all the keywords, see Appendix 1.
Hopefully, while working through this guide, you experimented with writing your own programs. If you did, you'll realise that your RISC OS computer is not only a very powerful tool capable of running sophisticated software; it's also very easy to bend it to your will and make it do something special which only you want it to.
And it's also great fun!