Chapter 17: Creating an Application Directory
Before we can move on to look at sprite icons in any more detail, there’s a glaring omission with our example application which needs to be fixed. So far, we’ve just been compiling our code into an Absolute executable called ExampleApp, which we then run directly by double-clicking on it. This works, up to a point, but there are a few problems which so far we’ve skirted around.
Almost invariably, applications on RISC OS come packaged in an application directory: the familiar object in a Filer window whose name starts with an exclamation mark. An application directory is just like any other directory – except that double-clicking on it doesn’t open it up, but instead causes other things to happen.
Creating an application
Creating an application directory is surprisingly easy. First, clean the project by double-clicking on the MkClean TaskObey file. Next, click Menu over the folder, slide over New directory, enter the name “!ExamplApp” and press Return. This will create a new directory but, because the first character of the name is an exclamation mark, it automatically becomes an application directory. The project folder at this stage should look similar to that shown in Figure 17.1. Note that the name has been chosen to keep it within a ten character limit which still exists in some parts of the system.
Figure 17.1: Our project, with the new application directory
The next thing to do is to change the Makefile so that the Absolute file is built inside this new application directory. The COMPONENT variable sets the location of the output file, and this can be changed from
!ExamplApp.!RunImage as shown in Listing 17.1.
# Makefile for Example App COMPONENT = !ExamplApp.!RunImage OBJS = main ibar win CINCLUDES = -IC:,OSLib:,SFLib: LIBS = SFLib:o.SFLib OSLib:o.OSLib32 include CApp C_NO_FNAMES = # Dynamic dependencies:
Listing 17.1 (Makefile): Updated Makefile for an application directory
The name !RunImage needs a little explanation. RISC OS has a number of filename conventions when working with the contents of application folders, some of which are fixed within parts of the operating system itself and some of which have just grown up over the years. It’s conventional to call the main executable inside an application !RunImage, and the Filer will actually make use of this by reporting the date of the !RunImage file when the date of the application directory itself is requested.
It’s worth noting before we progress that amongst the example skeleton build environments supplied with the DDE is one called “exampleapp”. Whilst this is for building applications, it takes an approach which is somewhat different to this one. Which one to use is a matter of personal taste, but the method used by this tutorial is probably the easiest to understand – as well as being suitable for most projects.
Our new application directory can be opened up by double-clicking on it while holding down Shift, which should reveal an empty directory. Double-clicking on the Mk TaskObey file should build the application as usual – except that the resulting Absolute file will be called !RunImage and placed within !ExamplApp. This can be seen in Figure 17.2.
Figure 17.2: The project with the new !RunImage file in place
It’s not the end of the story, however, because double-clicking on the !ExamplApp directory without Shift held down gives an error that !ExamplApp.!Run can’t be found. There’s clearly something missing!
The !Run file
When an application directory is double-clicked, the Filer will attempt to execute the !Run file contained within it. By convention, this file is an Obey file: which means that it contains a sequence of * commands which will be executed by RISC OS. We’re going to add the file shown in Listing 17.2.
| !ExamplApp.!Run RMEnsure UtilityModule 3.10 Error This application requires RISC OS 3.10 or later RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 RMLoad System:Modules.CallASWI RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 Error This app requires CallASWI 0.03 or later RMEnsure FPEmulator 4.03 RMLoad System:Modules.FPEmulator RMEnsure FPEmulator 4.03 Error This application requires FPEmulator 4.03 or later RMEnsure SharedCLibrary 5.17 RMLoad System:Modules.CLib RMEnsure SharedCLibrary 5.34 Error This application requires SharedCLibrary 5.34 or later WimpSlot -min 128K -max 128K Run <Obey$Dir>.!RunImage
Listing 17.2 (!Run): Our application’s !Run file
The file does a number of important things which, up to this point, we’ve been ignoring whilst crossing our fingers and hoping for the best. First are a block of *RMEnsure commands which should be considered as mandatory checks before launching any modern C application compiled with the DDE. The first line is:
RMEnsure UtilityModule 3.10 Error This application requires RISC OS 3.10 or later
The *RMensure takes a module name and a version number and checks the system to see if that module, with the specified version or newer, is already loaded. If it isn’t, the rest of the command is executed as a new * command. Here we’re checking to see if a version of the UtilityModule is present with a version of 3.10 or better: since this module is always present in ROM and its version is the same as the installed version of RISC OS, this is a standard way to check the current OS version.
Checking the OS version is a fairly blunt tool, and one which should only be considered a ‘last resort’ if no better alternative is present. In this case, however, we’re only interested in knowing that the OS version is at least RISC OS 3.1 – even Acorn considered this a ‘base’ version of the OS back in the mid-1990s, and many parts of modern software simply won’t work without it. If a suitable version of the UtilityModule isn’t present, the *Error command is called to warn the user and exit.
The remaining *RMEnsure lines come in pairs, the first being:
RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 RMLoad System:Modules.CallASWI RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 Error This app requires CallASWI 0.03 or later
The *RMEnsure against the UtilityModule ensures that this pair of commands is only executed on versions of RISC OS older than RISC OS 5. The first line then uses *RMEnsure to check for version 0.03 or better of the CallASWI module and, if it isn’t loaded, will attempt to *RMLoad it from within the System Resources folder (!System).
The second line repeats the test for CallASWI version 0.03. By now this should have been loaded by the first of the two lines if required, so if it still isn’t present then it can only mean that a suitable version of the module wasn’t found in !System. As before, if the requirement can’t be met, *Error is used to warn the user and exit.
The CallASWI module was introduced by Acorn with the StrongARM updates, and evolved to version 0.03 with the arrival of RISC OS 5. It provides implementations of a handful of low-level SWI calls which are used by the DDE’s C runtime, allowing compiled code to function the same way on all systems.
The next pair of lines is very similar, except that they apply to all versions of RISC OS and not just those older than RISC OS 5:
RMEnsure FPEmulator 4.03 RMLoad System:Modules.FPEmulator RMEnsure FPEmulator 4.03 Error This application requires FPEmulator 4.03 or later
They check for the presence of version 4.03 of the Floating Point Emulator. Since this is also essential, because it provides all floating point number support for C applications compiled using the DDE, the lines again prevent the application from loading if a suitable version of the module can’t be found.
The final pair of lines is subtly different:
RMEnsure SharedCLibrary 5.17 RMLoad System:Modules.CLib RMEnsure SharedCLibrary 5.34 Error This application requires SharedCLibrary 5.34 or later
Once again, they test for the presence of an essential module: this time the 32-bit Shared C Library, which provides all of the standard C libraries for code compiled using the DDE, and so must also be present. The reason for the difference in the style of test is that unlike conventional modules which make all of their services available via SWI calls, the Shared C Library provides only a handful of SWIs to allow applications to register with it. To access the C library routines that it contains, applications create a table of branches direct to the routines within the library module as part of this registration – removing the overhead of calling SWIs for each one.
This causes a problem, however. The first time a new version of the Shared C Library is soft-loaded, the version in ROM which it is replacing remains in place. Any applications which had previously registered with it will continue to branch to the routines in the ROM version of the module, and so will continue to run. Any applications which register after the soft-load will create their branch tables into the newer RAM version of the module, which is also OK – unless we were to then soft-load another copy of the library.
Unlike the first soft-load, when the ROM version remained in place by virtue of it being in ROM, any subsequent attempts to soft-load a different version of the Shared C Library will completely replace the previous soft-load in RAM – but in a different part of the Relocatable Module Area. This might actually be OK for a while, since RISC OS doesn’t garbage collect the RMA that well, but sooner or later the memory used by the old soft-loaded module will begin to be reused by new code or data. At this point, things will go badly wrong for any applications which are still jumping directly to routines within it.
For this reason, it’s dangerous to soft-load the Shared C Library twice in a session, which results in the curious test seen above. Version 5.17 was the first 32-bit version of the library, so using this as a test for loading a new copy prevents us from replacing another version of the library. We then test for the required version, and fail if it isn’t present. The standard RISC OS !Boot structure should soft-load the current version of the Shared C Library as the machine starts, so if it isn’t available, fixing the problem is left as an exercise for the user. It’s better than freezing their machine without warning, however!
All of the items being tested for here should be considered bare minimum requirements for running an application executable compiled using a modern version of the DDE, and the tests should always be present in such an application’s !Run file. There are ways around some of them, but those are beyond the scope of this tutorial.
WimpSlot -min 128K -max 128K
The next line uses the *WimpSlot command to allocate memory for the application. Up to now, the application has been getting whatever amount of memory is currently allocated to Next in the Task Manager at the time when it is run; by specifying an amount in the !Run file, we can ensure that it receives what it actually requires. The -min and -max parameters can be a little counter-intuitive: if the Next slot is less than -min then the memory is increased to -min, whilst if it is greater than -max then it is decreased to -max. In general, the two values will be set the same, so that a predictable amount of memory is allocated.
The final line is the one which actually launches the application, using the *Run command. The Obey$Dir system variable is set by the system to point to the directory containing the !Run file, which allows us to easily locate the application’s !RunImage file.
With this new !Run file in place, double-clicking on the !ExamplApp application directory will correctly load the application on to the iconbar. In addition, we can now be confident that all of the resources necessary for the code to run will be present. A complete set of files for the modified project can be found in Download 17.1.
An application sprite
Now that we have an application directory, there’s something else that we can add to our simple application. So far, we’ve been relying on the ‘application’ sprite from the Wimp Sprite Pool for our iconbar icon as described in Chapter 6, but now we have somewhere to store our own alternative.
The first thing to do is to create a suitable set of sprite files. One possibility is shown in Figure 17.3, but others can easily be created using Paint or a package such as ArtWorks. An application should supply at least one ‘full size’ sprite with the same name as the application directory (in our case ‘!examplapp’); it can optionally supply a second ‘half size’ sprite, with the same name prefixed by ‘sm’ (in our case ‘sm!examplapp’).
The half size sprite is for use in the Filer’s directory viewers, and is optional because if it isn’t present, the Filer will show the full size sprite with the wimp_ICON_HALF_SIZE flag set (as discussed in Section 14.6). Supplying both sprites is desirable, because doing the scaling ourselves gives more control over the result.
Figure 17.3: The sprites to use for our application
Ideally, a modern application will also provide at least two sets of these full size and half size versions of its sprite: these are at different resolutions, and allow the Wimp to cater for different screen displays. As a minimum, an application should have a !Sprites22 file containing designs in a ‘square pixel’ mode, and a !Sprites file containing designs in a old ‘rectangular pixel’ mode (such as Mode 12 or Mode 15). Optionally, a !Sprites11 file can be supplied with larger designs for use in high-resolution modes. Table 17.1 summarises the files and their sprite sizes in pixels.
|File Name||Resolution||Full Size||Half Size|
|!Sprites||90 × 45 dpi||32 × 16 pixels||16 × 8 pixels|
|!Sprites22||90 × 90 dpi||32 × 32 pixels||16 × 16 pixels|
|!Sprites11||180 × 180 dpi||64 × 64 pixels||32 × 32 pixels|
Table 17.1: The standard application sprite sizes in pixels
Our application sprites can end up in the Wimp Sprite Pool through a couple of mechanisms. First, when the Filer first opens a folder containing our ExamplApp directory, it will check inside our folder (and those of any other applications alongside it) for a file called !Boot (that is, !ExamplApp.!Boot). If one is found, it will execute that file and then stop.
However, we don’t have a !Boot file: they are used for setting up things like filetypes that an application might wish to load, and we don’t yet care about any of that. Fortunately, if no !Boot is found, the system will then go on to look for a file called !Sprites or one of the derivatives listed above – it starts with the suffix appropriate to the current mode, then falls back to try the simple !Sprites if the first option fails.
If a suitable !Sprites (or !Sprites22, !Sprites11, or whatever) file is found, and is typed as “Sprite”, then the sprites within it will be added to the Wimp Sprite Pool ready for us to use. In our case, this means that the !examplapp and sm!examplapp sprites will be loaded.
It’s also possible to explicitly load sprites into the Wimp Sprite Pool, by using the *IconSprites command. It’s a good idea to do this when our application is run, just in case our sprites aren’t already in the pool, or some other sprites with the same name are already there. We can achieve this by adding the line
to the !Run file. Just as when the system loaded the file for us, *IconSprites will try all of the suffied versions of the filename first, before falling back to the default. If we did have a !Boot file, we would also need to use *IconSprites within that.
Finding our resources
There is one last thing that we should probably add to our !Run file at this stage, before moving on. We noted above that the Obey$Dir system variable was set to point to the folder containing the file, but didn’t elaborate. In fact, this is something that the system does whenever an Obey file is executed, and it will remain set until either we reach the end of !Run or we call another Obey file from within it.
This is OK for now, but could be a problem if we needed to find our !ExamplApp folder again later. By then, another Obey file could have been run by the system (perhaps another application has been launched by the user), and this would clearly change Obey$Dir and lose the reference that we had been relying on. To protect against this, we can set our own system variable with the value contained in Obey$Dir, so that we can always find our way back. By convention (see Section 17.7 below), we can create and use any variables whose names start with “ExamplApp$”, so we can add a line to our !Run file as follows:
Set ExamplApp$Dir <Obey$Dir>
It makes sense to do this first, then use it in preference to Obey$Dir for the rest of the file. This means that we should also change the *IconSprites command that we added in the previous section:
| !ExamplApp.!Run RMEnsure UtilityModule 3.10 Error This application requires RISC OS 3.10 or later RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 RMLoad System:Modules.CallASWI RMEnsure UtilityModule 5.00 RMEnsure CallASWI 0.03 Error This app requires CallASWI 0.03 or later RMEnsure FPEmulator 4.03 RMLoad System:Modules.FPEmulator RMEnsure FPEmulator 4.03 Error This application requires FPEmulator 4.03 or later RMEnsure SharedCLibrary 5.17 RMLoad System:Modules.CLib RMEnsure SharedCLibrary 5.34 Error This application requires SharedCLibrary 5.34 or later Set ExamplApp$Dir <Obey$Dir> IconSprites <ExamplApp$Dir>.!Sprites WimpSlot -min 128K -max 128K Run <Obey$Dir>.!RunImage
Listing 17.3 (!Run): Our application’s !Run file with *IconSprites included
Our sprite on the iconbar
Although we now have our new sprite design appearing on our application directory, we should also be using it within the application itself – not least for its iconbar icon. Fortunately, since the sprite is now in the Wimp Sprite Pool, this is extremely simple to achieve.
Back in Section 10.4, we restructured things so that the name of the sprite used both on the iconbar and in report boxes was held in a single location. This means that using our own sprite is now just a case of amending the line near the top of c.main to read:
static char *main_application_sprite = "!examplapp";
With this change in place and compiled, our application will finally be able to install itself on the iconbar using its own sprite design.
Figure 17.4: Our application finally stands out on the iconbar
A note about allocation
So far in this tutorial, we have named our application “ExamplApp” without any real consideration of the consequences. Until this chapter that’s not been a problem, but now things have changed a bit: we’ve added some sprites, with the names “!examplapp” and “sm!examplapp” into the Wimp Sprite Pool, and set an ExamplApp$Dir system variable.
RISC OS is a very ‘flat’ operating system, and there is no segregation between different applications. The Wimp Sprite Pool is just a single collection of sprites shared by all of the applications on the system, and if another application tries to call itself “ExamplApp” then its “!examplapp” and “sm!examplapp” sprites would clash with ours. Similarly, when that other application set the ExamplApp$Dir system variable, it would overwrite whatever value we might have previously set the variable to.
To get around this, RISC OS has a system of resource allocation, which allows developers to register things like application names in a central database – ensuring that they are unique. For applications which will never leave your computer (such as our ExamplApp, one assumes), this process can be ignored. However, before sending an application to anyone else – whether that’s emailing it to a friend or publishing it on the internet – it is essential that the name is registered.
Fortunately, the registration process is simple. We’re going to walk through it here for “ExamplApp”, but it goes without saying that you should not actually submit the registration shown below. The name “ExamplApp” has already been registered on your behalf!
Supplied as part of the DDE is an application called Allocate – it comes in the Utilities folder, so if you have installed things in the standard locations it will be found inside this folder in the root of your hard disc. It allows requests for resource allocations to be assembled quickly into a form that can be emailed to the people who look after the database: once this was Acorn, but these days RISC OS Open are responsible for it. Remember that despite this, the database also includes software which is only intended for use on older 26-bit systems.
When run for the first time, Allocate will ask for some personal details (name and contact information) so that any resources allocated to you can be identified – as seen in Figure 17.5. The Dev No. field is largely obsolete in the post-Acorn world.
Figure 17.5: Entering our details into Allocate
With this complete and saved for future use, Allocate will open up a List of allocations window as seen in Figure 17.6.
Figure 17.6: Allocating our application name
We need an application name reservation for “ExamplApp”, so we click on Reservation and select AppName in the Type field. Enter
ExamplApp into the Text field, and click on Add. The List of allocations window should update to show the allocation, as seen in Figure 17.7. All that we now need to do is save the allocation out as a file which can be attached to an email and sent to the allocation address. See !Allocate.!Help for full details of the process.
Figure 17.7: Saving the allocation to disc
This application name reservation covers us for the !ExamplApp folder, the associated sprites (“!examplapp” and “sm!examplapp”) that we will put into the Wimp sprite pool, and any system variable with a name which begins with “ExamplApp$”.
Getting an allocation usually takes a week or two, so don’t leave it until the last minute if you have a release deadline looming! A name will be refused if someone else has already claimed it, so try not to pick things which are too generic and do a bit of research first. It pays to have a backup idea in case your first request turns out not to be available.
Finally, don’t be tempted to skip the process and release applications using unallocated resources on the basis that it can’t hurt. The RISC OS system only works because its developers avoid clashes of resource allocations. It’s much easier to get a safe name to start with than to have to change it afterwards when problems come to light, and much less likely to incur the wrath of users and other developers!