Chapter 4: Messages and Mouse Clicks
It may seem odd that we have to begin writing our application with the procedure for closing it down, but we don't really want to reset the machine every time we get rid of it. It's a bit like learning to drive a car - one of the first things you have to learn is how to stop! We shall also learn a couple of very useful techniques along the way.
There are usually two ways of closing down an application. One is internally, for example from a menu option; the other is externally, by means of a message through the Wimp system. This is sent to the application when you exit from the desktop or shut down the machine, or select Quit from a Task Manager window menu. We will deal with the second method first, beginning by adding some lines to the Wimp polling loop.
... or select Quit from a Task Manager window menu
In the next listing (file page_022), we add a new procedure, PROCreceive, and some extra lines in PROCpoll.
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 CASE r% OF 290 WHEN 6:PROCmouseclick 300 WHEN 17,18:PROCreceive 310 ENDCASE 320 ENDPROC 330 : 340 DEFPROCinit 350 REM initialisation before polling loop starts 360 DIM b% 1023 370 quit%=FALSE 380 ENDPROC 390 : 400 DEFPROCreceive 410 REM handles messages received from the Wimp with reason codes 17 or 18 420 CASE b%!16 OF 430 WHEN 0:quit%=TRUE 440 ENDCASE 450 ENDPROC 460 :
We are beginning to make use of the reason code, which the Wimp_Poll call returns to variable r%.
We're also making our first use of a CASE ... OF statement. The part of the program between 'CASE ... OF' and 'ENDCASE' takes the variable following 'CASE' and compares it with the numbers following the 'WHEN' keywords. If it finds a match on one line, it carries out the instruction on that line, then jumps to 'ENDCASE', ignoring the other 'WHEN' lines.
This CASE ... OF statement checks r% and takes appropriate action according to its value. Line 290 has a call to a procedure we haven't yet written, but line 300 handles reason codes 17 and 18, which are returned when the Wimp sends a message to our application. We need not concern ourselves with the difference between 17 and 18 at this stage.
Message ReceivedPROCreceive handles messages. The message is contained in the data block returned by Wimp_Poll, beginning at address b%, and we are particularly concerned with the message action code, which is the number in the word at b%+16. We are using another CASE ... OF statement here so that we can add extra lines later, dealing with other message action codes.
At the moment, though, we are only concerned with an action code of zero, which is the instruction from the Wimp to close down. When the program detects this, it simply sets variable quit% to TRUE and returns to the polling loop.
Because quit% is now TRUE, the loop stops repeating and carries straight on to call PROCclose and end the program. We saw this procedure in the previous section but didn't make use of it then. It works rather like PROCinitialise in reverse; again we have to put the ASCII code for 'TASK' in R1 and we put the task handle, which we obtained when we initialised the task, in R0.
When you have copied this version as the !RunImage file, start up the application again, then open the Task Manager window. Click Menu on the name of your application, then follow the menu tree to Task 'Test' and choose Quit. Your application will disappear from the icon bar and the window. The Wimp has sent it a message with an action code of zero, meaning 'Close Down', and it has done as it was told.
Handling Mouse ClicksThere is another way to close down the application at this stage. If you start it up again then click any of the mouse buttons on its icon, you will get an error message saying 'No such function/procedure'. This is because of line 290. We have clicked the mouse over our application's icon, which has caused Wimp_Poll to return with a reason code of 6. This led to a call to PROCmouseclick and an error message, because we haven't written it yet. Basic's response to an error at this stage is to close down the application.
Normally, an application would have a Quit option on its main menu. Creating a menu, however, is a rather complicated business, which we will deal with later. For now, we will use a simpler option - making the task close down when we click the Adjust button on its icon.
We can handle mouse clicks by adding a procedure to the !RunImage file, making a new listing called page_024:
470 DEFPROCmouseclick 480 REM handles mouse clicks in response to Wimp_Poll reason code 6 490 REM b%!0=mousex,b%!4=mousey:b%!8=buttons: 495 REM b%!12=window handle (-2 for icon bar):b%!16=icon handle 500 CASE b%!12 OF 510 WHEN -2:CASE b%!8 OF 520 WHEN 1:quit%=TRUE 530 ENDCASE 540 ENDCASE 550 ENDPROC 560 :
This procedure will handle all the mouse clicks connected with our application. Although it's fairly simple at the moment, it is destined to grow considerably by the time we've finished with it.
Because the procedure deals with various buttons clicked on various icons in various windows, we shall use multiple nested CASE ... OF statements. It may look pointless, using a CASE ... OF with only one WHEN, but we shall be adding a lot more later.
When Wimp_Poll returns with a reason code of 6, this procedure is called and the data block contains the following:
The REM statement at the beginning of the procedure serves as a reminder of this.
The first thing to find out is which window the mouse was clicked on. At the moment this has to be the icon bar, but we will be adding other windows later on, so we must be sure.
We do this by checking the number in b%+12, which is the window handle. The icon bar has a window handle of -2. This is found in line 510, which leads to another CASE ... OF statement, checking the number in b%+8.
This number tells us the state of the mouse buttons. Think of it as a binary number whose three right-hand bits mirror the state of the three mouse buttons. If the Adjust button is held down, the number will be 1; the Menu button will produce 2 and the Select button 4.
It is a simple matter to set quit% to TRUE when Adjust is clicked and to return to the polling loop, causing the application to close down.
By now, you should be getting a good idea of how multi-tasking programs work. The polling loop, with its repeated calls to Wimp_Poll, is the centre of the whole operation. Each time the program returns from Wimp_Poll, we check the reason code, take whatever action is necessary and dive back into Wimp_Poll again to let other applications have a go.
Ideally, anything our program does between returning from Wimp_Poll and calling it again should take as little time as possible, as none of the other applications can function while it's doing it. If you want to do something that may last for some time, such as loading a file from a disc, it's best to switch on the Hourglass before you start, and switch it off again when you have finished. This indicates that the machine is still functioning, but that multi-tasking has temporarily been suspended.
In the process of learning how to close down our application, we have set in place the procedures for handling Wimp messages and dealing with mouse clicks. We shall make much more use of both of these later on.