15: Loading the RISC OS Way
We are now entering the realm of Wimp message passing, and things are getting more complicated. We'll deal with loading first, as it's simplest, so first save a file using the old method.
You can rename the file, if you wish, and move it to a more convenient directory.
We will no longer need a Load menu option, so delete the line from PROCmenuclick which calls PROCload and amend the DATA line in PROCwindow_menu to read:
2680 DEFPROCwindow_menu 2690 RESTORE +1 2700 DATA Shapes,Options,Clear,Save,* 2710 wmenu%=FNmake_menu 2720 ENDPROC 2730 :
The divider line under Clear is a bit superfluous without the Load item, so we have removed that as well, by deleting the '_D' suffix from 'Clear'.
We're going to completely rewrite PROCload, so delete the old version.
Loading and saving both involve the passing of messages between the Filer and our application, so insert two new lines in PROCreceive, so that it reads as follows:
1600 DEFPROCreceive 1610 REM handles messages received from the Wimp with reason codes 17 or 18 1620 CASE b%!16 OF 1630 WHEN 0:quit%=TRUE 1640 WHEN 2:PROCsave 1650 WHEN 3:PROCload 1660 ENDCASE 1670 ENDPROC 1680 :
Start up the application and drag any file icon either to your application's icon or to its window. The result will be an error message saying 'No such function/procedure at line 1650' (or some other line number). If this doesn't happen, it could be because you rewrote the line which calls Wimp_Initialise, putting 300 or 310 into R1 instead of 200. We will see why this makes a difference shortly.
PROCreceive, you will recall, handles Wimp messages which arrive on a return from Wimp_Poll with a reason code of 17 or 18, and works by looking at the message action code in b%+16. Up until now, we've only been concerned with the message whose action code is zero, which means 'Close Down', but the filing system has now sent us one with a code of 3, inviting us to load a file. Before we write our new PROCload, let's have a look at messages in general.
Wimp Message PassingMessages are passed to and from applications using the system call Wimp_SendMessage. A message comes in as a return from Wimp_Poll, as we saw in the case of the 'Close Down' instruction.
When we send or receive a message, R0 contains the Wimp_Poll reason code. Indeed, it is possible for one application to tell another to open or close one of its windows, by sending a message with a reason code of 2 or 3! Usually, though, we send messages with reason codes of 17, 18 or 19.
The difference between these codes is that code 18 is used for a 'Recorded Delivery' type message - the sender expects a reply. If we receive such a message, we can reply to it by sending it straight back with reason code 19.
The address of the data block is sent in R1 - the contents of the block are the same as for the corresponding Wimp_Poll call. R2 contains a handle to tell the Wimp which task to send the message to: this could be the task handle of the destination task, or it could be one of its window handles, in which case, by the time the message gets to its destination, it has been turned into a task handle.
When we looked at closedown messages, the only part of the data block that concerned us was the word at byte 16 - the message action code. It's now time to look at the rest of the block. If we are sending or receiving a message with a reason code of 17, 18 or 19, the beginning of the block looks like this:
We don't put anything into the words at bytes 4 and 8 when we send the message - by the time it reaches its destination, the Wimp has put the sender's task handle into byte 4 and generated a number called my_ref which it puts into byte 8. If we want to send a message back, acknowledging something, we take the my_ref number, which arrived in byte 8, and put it into byte 12, calling it your_ref. That way, the system knows that our message is a reply.
Differences Between RISC OS 2 and 3+We are now starting to use message action codes other than zero (Close Down), and this shows up a difference between RISC OS 2 and 3 (and later versions of RISC OS, which behave in the same way as RISC OS 3). You will recall that when we called Wimp_Initialise at the start of the program, we put a number in R1 to tell it which version of the Wimp we were writing for; 200 because our program is compatible with RISC OS 2.
RISC OS 3 allows us to specify which message action codes our program will respond to by putting a list of them in a buffer and the address of the buffer in R3 when we call Wimp_Initialise. If we put 300 or 310 in R1, the Wimp will expect the list to be where R3 says it is. As we do not specify the contents of R3 in our SYS command, it will contain zero, which points to the machine's hardware vectors. None of these vectors contains 2 or 3, with the result that the loading and saving procedures in this section and in Section 16 will not work (try it). Because we put 200 in R1, the Wimp assumes that our program was written for RISC OS 2, so R3 is ignored.
If you want to make use of this facility, you could bring forward the DIM statement which creates the block at b% to a point before we call Wimp_Initialise and include a line such as:
The block contains a list of message action codes which our program will respond to, followed by a zero. You would, of course, have to keep this list up to date if you add further message codes. If you have RISC OS 3.10 or later (not 3.00), putting zero at the start of the list will make the Wimp pass all messages to your program.
Data Transfer MessagesThere are a great many different types of Wimp message, ranging from those informing us that such things as the screen mode or the palette are being changed to one telling us that another application is about to close down.
Most of these need not concern us. If you want further details, they are all described in the Programmer's Reference Manual. We're concerned here with the four types used for data transfer:
As our application is interacting with another task, namely the filing system, it's important to observe the correct protocol. Certain parts of this protocol may seem a bit pointless, but they have been designed so that data may be transferred from one application to another, always using the same procedure.
In other words, there is really very little difference between transferring data to and from a disc and to and from another application. Both operations use the same protocols, and this is one of the most important ideas behind RISC OS.
When we load a file from disc, the following things happen:
The error message which we received when we dragged a file icon to our application was as a result of receiving a DataLoad message. Line 1650 tried to call PROCload which, at the moment, doesn't exist.
The Rest of the Data BlockSo far, we've seen what is in the first 20 bytes of a message data block. This information is common to all messages, but the rest of the block, from byte 20 onwards, takes various forms, depending on the nature of the message. All four types of data transfer message have similar contents. Before we can write our new PROCload, we need to have a look at the contents of the rest of the block:
When we receive this message, we will have sufficient information to be able to load the file, as we will know its full pathname and filetype, so we can now write our new PROCload:
3640 DEFPROCload 3650 IF b%!40<>&012 ERROR 1<<30,"Filetype not recognised" 3660 PROCterm(b%+44) 3670 SYS "XOS_CLI","LOAD "+$(b%+44)+" "+STR$~list% TO err%;flags% 3680 IF (flags% AND 1)<>0 !err%=1<<30:SYS "OS_GenerateError",err% 3690 b%!12=b%!8 3700 b%!16=4:REM Message_DataLoadAck 3710 SYS "Wimp_SendMessage",17,b%,b%!4 3720 !b%=main% 3730 SYS "Wimp_GetWindowState",,b% 3740 IF ((b%!32) AND 1<<16)=0 THEN 3750 SYS "Wimp_OpenWindow",,b% 3760 ELSE 3770 PROCforce_redraw(main%) 3780 ENDIF 3790 ENDPROC 3800 :
Because we are told the filetype, in b%+40, we can reject any files that are not the correct type. Line 3650 does this, with a non-fatal error message.
Although the filename is in the string starting at b%+44, we cannot go ahead and use it yet, because it ends not with a Return, but with a zero, and Basic expects all its strings to end with a Return.
This is a situation we are going to meet many times, so it's worth having a procedure to replace the zero with a Return character, ASCII code 13:
3810 DEFPROCterm(a%) 3820 LOCAL n% 3830 WHILE a%?n%>31 3840 n%+=1 3850 ENDWHILE 3860 a%?n%=13 3870 ENDPROC 3880 :
The local variable n% is automatically set to zero when it is created.
We simply have to call this procedure, passing it the address where the string starts, and it does the rest. Although Basic requires a Return on the end of its strings, the operating system doesn't seem too bothered about whether the string ends in a zero or a Return.
If you meet a situation where RISC OS does not work properly, because your string ends in a Return instead of a zero, you could always add a zero by putting '+CHR$0' on the end of the string.
Loading the FileWe now know the full pathname of our file from the string at b%+44, so we can load it. In case of problems, we're using a system call name preceded by an 'X', as we saw in our old PROCload, and we're handling any errors produced in a way that makes them non-fatal.
Next, we have to send the DataLoadAck message, which we do by transferring the incoming 'my_ref' to the 'your_ref' word, in line 3690, changing the message action code to 4 in line 3700, and sending the message. We get the task handle of the filing system from b%+4 and put it in R2.
All that remains now is to redraw the window if it is open, or open it if it is not. We can find this out by calling Wimp_GetWindowState, in line 3730 and examining the flags, in b%+32. If the window is open, bit 16 is set.
The remaining lines of the procedure either open the window or redraw it, as appropriate.
The version of the listing up to this point can be found as page_147.
If you drag your file onto your icon or into your window, you should see your wonderful artwork appear in the window. Now for the hard part - saving it.
... drag your file onto your icon ...