Programming Conventions
This article is a collaborative attempt to summarise some RISC OS programming conventions. It may not represent every programmer's opinion however. Use the discussion page to disagree/comment on it.
Introduction
This guide will introduce some of the conventions and techniques associated with writing a "well-behaving" RISC OS program. It does not address the technical aspects of programming under RISC OS (with which it is assumed the reader is already familiar) but deals with some of the "softer" issues. Where possible, further resources and references are given. In particular, "PRM" refers to the Programmer's Reference Manual and "SG" refers to the Style Guide.
The guidance given in this article is not cast in stone, but should be adherred to unless there are convincing reasons why a certain situation demands a different solution.
!Boot
Choices
It is recommended that the choices for an application be stored in a dedicated place on the system. This makes it easy for a user to upgrade the program whithout changing settings, allows the application to run from a read-only medium, and, on modern multi-user OSes, means each user can easily have different saved choices. Choices and settings should not, therefore, be stored within the application directory.
To achieve this, user choices should be written to <Choices$Write>.AppName
and read from Choices:AppName
. (This is a general rule: where reading takes place from Example:Path
, the corresponding write location is <Example$Write>.Path
.) For a more detailed explanation, take a look here. AppName
should be registered with the allocation service.
(PRM5a:530-531,539)
While it is generally good practice to write (and read) choices to the locations referenced by the variables outlined above, there is one caveat. It is entirely possible for a user to be running on a system where they have no write access to most of the hard drive. (Consider FSLock in Configure.) For example, this may be in a school or office situation where the system administrator wants to prevent users from altering too much on the machines. As a minimum, therefore, programs should fail gracefully if they find they can't write to <Choices$Write>
and ideally, should also provide some mechanism whereby the location of the application's choices can be chosen by the user.
Modifications
Great care must be taken when modifying a user's boot sequence. If this is absolutely necessary, the user should be informed (and given a choice) about what the application intends to do. The application should keep a log of what happened and, ideally, provide a means to roll back the changes.
To install a stand-alone program in the boot sequence, use the system variable Boot$ToBeTasks
(unless the program is necessary for the completion of the boot sequence, in which case it should go in Boot$ToBeLoaded
).
Scrap usage
To make use of the Scrap directory, programs should read and write to <Wimp$ScrapDir>.AppName
. (Where AppName
is the name of the program, and/or has been allocated specifically.)
Distribution of Modules
In the majority of cases modules should be distributed inside the applications which use them. However, if a module is likely to be used by other programs, it might be worth considering distributing it for installation into !System
. This has two benefits: it means that each program which uses it does not have to supply it separately and also makes keeping the module up to date (when newer versions are released) much easier for the user.
Modules which are destined for !System
should be supplied in a dummy !System
application which the user can then use with their computer's in-built "System Merge" utility. The structure of
the dummy application will be:
!System.Modules.<minOS>.YourModule
where <minOS>
is the minimum version of the OS which is required to run the module. So, for example, if a module is only suitable for an IYONIX pc, <minOS>
will be 500.
UniBoot
Most end-users ought to be using the UniBoot distribution for their boot sequence, but this should not be assumed. If the use of the nested window manager is required, an application should check for version 3.80 of WindowManager. If it's not present the application should direct users to the RISCOS Ltd website where it can be downloaded and installed.
Sprites
As a minimum, applications should have a !Sprites
file which includes a mode 12 sprite called !AppName
. Note that sprite names are limited to 12 characters so, if a program has a long name, and the first 12 characters match another program, the sprites may get confused.
!Sprites
can also contain sm!AppName
which should be half the size of the main sprite, for use when the user has "small icons" showing in their directory viewer. There is no value in just shrinking the sprite without touching it up, however, as the filer will automatically scale the large version of the sprite if sm!AppName
is not present. If present, and if the user's screen mode supports it, !SpritesXX
will be used in preference to !Sprites
when the file is called using IconSprites
. (The suffix can be found manually by calling Wimp_ReadSysInfo,2
.) The possible values for XX
are given below.
DPI | Standard Dimensions | 16 colours | 256 colours | 32K/16M colours | |
---|---|---|---|---|---|
!Sprites | 90x45 | 34x17 | 12 | 15 | new format |
!Sprites22 | 90x90 | 34x34 | 20 or 27 | 21 or 28 | new format |
!Sprites23 | (obsolete hi res mono mode 23) | ||||
!Sprites11 | 180x180 | 68x68 | new format | new format | new format |
From RISC OS 4 onwards, 256 colour sprites may be used (preferably with an optimised palette). Sprites with a full alpha mask do not work on RISC OS 5.xx and should be avoided. If ic_taskname
is present, it will be used when the application windows are iconised to the pinboard or iconbar. (taskname
is the string given in Wimp_Initialise
.)
(For more details on sprite specifications, see this discussion on usenet.)
Application sprites should be irregularly shaped (they can be wider than the 'standard' size above, but should not be taller, so as to fit onto the icon bar), with transparent backgrounds. Document sprites should be the standard size and have a solid outline, preferably with the curled corner present in many default sprite sets. This helps the user distinguish easily and quickly between the two types of object.
(SG:17,89,93)
Icons should not attempt to be photographic quality pictures. They should be clear, iconic representations of whatever it is they are trying to communicate.
Application icons should go on the right of the icon bar and not have text under them, unless they are for devices. They should only be animated with good reason.
To avoid cluttering up the Wimp sprite pool, only application sprites, iconised icon and filetype sprites should be *IconSprites
ed. The command is always *IconSprites <YourApp$Dir>.!Sprites
: the Wimp will itself add the numeric suffix for the appropriate resolution and colour depth. If an application requires the use of further sprites, they should be loaded into its own workspace. (For instance, by using the OS_SpriteOp,&10A
SWI.)
RISC OS will IconSprites
the appropriate !Sprites
or !SpritesXX
file when an application without a !Boot file is Filer_Boot
ed. If a !Boot file is present it must IconSprites
the !Sprites file manually; the same is necessary for the !Run file since the application may be run without having first been Filer_Boot
ed (eg from the command line).
(SG:106)
Window Design
In order to maintain the "look and feel" of the desktop, the spacings for icons and buttons within window should be consistent between applications.
- Ensure that window designs are checked bearing in mind that users can (and do) customise their desktop font. At the very least, templates should be tested in 12-point Homerton. Ideally, applications should make use of the
Wimp_TextOp
SWI where your icons are likely to vary considerably in size in order to ensure adequate spacing and layout in different modes and fonts. - User-changeable icons should be clearly separated from "action" buttons, perhaps with a horizontal ruler. In any case, "Cancel" and "OK" buttons (in that order), should be at the bottom of the window, aligned to the right-hand side.
- The default (e.g. responds to the
Return
key) button on a window should be 204x68 OS units big, while other action buttons are 188x52 OS units. If only a very small button is needed an alternative is to have it only 140 units wide. If extra width is needed, it should be added in multiples of 16 OS units.
Keyboard Shortcuts
Applications should provide keyboard shortcuts. If the action is a common one, the shortcut should be consistent with the OS and other applications, as follows:
Shortcut | Action |
---|---|
F1 | Help |
F3 | Save |
^F2 | Close window |
F4 | Find |
F5 | Goto |
F8 | Undo |
F9 | Redo |
^Z | Clear selection |
^C | Copy selection |
^X | Cut selection |
^V | Paste selection |
^B | Bold |
^I | Italic |
Internationalisation
It is good practise to design programs in such a way that third parties will be able to translate it if they choose. Through the use of the Territory
module, RISC OS is able to deal with operating in different countries, timezones etc. and applications can cope with this appropriately by using ResFind. The full instructions for ResFind
are included in its archive and are very thorough. A brief description of the process is given here to illustrate that it is a fairly straightforward process:
- Any user-facing messages should be supplied in an editable form, away from the program's executable. (Of course, this is good practise anyhow.) A good way to implement this is through the use of the
MessageTrans
SWIs. - In the
!Run
file (and maybe the!Boot
file if Castle Technology's help system variables are being used),ResFind
is called which assumes a certain directory structure within the application directory and intelligently sets up a couple of system variables depending on what it finds. From then on, all the application resources can be simply referenced using theAppNameRes:
path variable.
See also RISC OS Translated.
Application Structure
A common structure for applications is:
!AppName !Boot !Help !Run !RunImage !Sprites !Sprites22 Resources
- !Boot
- (optional) This file is executed when an application's directory viewer is opened, or the application is Filer_Boot-ed. You don't need to have
!Boot
if it is only going to contain the linesIconSprites ...
andSet AppName$Dir ...
. As the latter is not useful on its own and the former will be done by the Filer by default on!Boot
's absence. - !Help
- (strongly recommended) This file is executed when the 'Help' item from the Filer menu is selected.
- !Run
- (required) This file is executed when an application is run.
- !RunImage
- (strongly recommended) This is the conventional name given to the main executable for your application. When present, the filer will use
!RunImage
's date as date for!AppName
. - !Sprites
- (required) Either this or
!SpritesXX
is used for the application's Filer sprite if no!Boot
file is present. - Resources
- (optional) This is a directory containing any resources your program might need.
Note that !Boot
, !Help
, !Run
and !Sprites
have special defined roles. If ResFind
is being used for internationalisation then the application must have the Resources
directory and it should contain a directory named UK
. Further territories can be added like this:
!AppName Resources France Germany Japan UK etc...
Any territory-independent resources go in !AppName.Resources
.
Help
Castle Technology have published a web page which makes various recommendations regarding implementing a help system in RISC OS programs. The full document is available here. A summary is presented below:
- The second entry down on an application's icon bar menu should be a
Help
entry which launches the program's help file. (E.g. with*Filer_Run <AppName$Dir>.!Help
) - The application's main menu should include a
Help
entry as its last item. This may lead to a submenu of help sections. - If help is provided in some "marked up" form (e.g. HTML, StrongHelp) then a text alternative should be provided. This can be catered for by having the
!Help
file provide a small script which launches the appropriate file. - Authors should set up a number of "help" system variables, specifically:
AppName$Help
: Path to help fileAppName$Version
: Application's version numberAppName$Web
: URL for application's home page on the WebAppName$Title
: Application's full titleAppName$Publisher
: Your name!AppName$Description
: Concise description of the application
Note: it is good idea to make these variables dependent on territory too. This can be achieved by including them in an obey
file which lives inside Resources.<territory>
.
Setting System Variables
Only system variables which are currently unset should be set in !Boot
files. Applications can unilaterally set the relevant system variables in their !Run
file but should be more careful about setting them in the !Boot
file. Example code for !Boot
files is:
If "<Alias$@RunType_XXX>" = "" Then Set Alias$@RunType_XXX Run <AppName$Dir>.!Run %%*0
As applications can potentially be run without their !Boot
files having been executed, they should not rely on anything which is only set up in the !Boot
file.
Allocations
In order to avoid clashes between programs with similar names (for instance they might be resetting AppName$Dir
every time they're run, or might write over each other's choices), the application name should be "allocated" before it is released. This is not limited to the program's name however - there's a whole range of things which should be registered, from SWI chunks to scrap locations.
Visit this page for more details.
Naming Conventions
Here are some miscellaneous naming conventions:
- Menu items and windows should have only their first word capitalised, unless they are proper nouns or acronyms.
- Menu items which open new windows should end in "...".
- Menu items which end in "..." should not have submenus.
- SWI names should have each word capitalised with acronyms all in capitals.
General Guidance
- Avoid
Wimp_ReportError
- Although
Wimp_ReportError
provides a quick and easy way of reporting errors and, indeed, asking questions of the user, its use is discouraged as it brings the desktop's multi-tasking to a halt. Alternative solutions should be found for delivering messages to, and getting input from, users. (One potential solution is to generate a multi-tasking equivalent of the error box. MultiError provides such an alternative.)
- Don't Make Assumptions
- Don't assume things about the user's systems. If there's a specific feature which a program required, it should be tested for explicitly rather than guessed-at from the machine or OS version. (For example, do not assume that a user is running a 26bit machine just because the OS version is less than 5.) Make use of
OS_ReadSysInfo
and check for the existence of specific modules etc.
- Backward Compatibility
- It is reasonable to require at least RISC OS 4 for your programs. However, many functions will work under RISC OS 3.5, especially if the user has kept their boot sequence up to date ad installed third-party utilities so support for this older generation of the OS may still be feasible.
- Use the Global Clipboard
- If you're writing a program which has content which could conceivably be useful in other programs (or vice versa), then make the effort to implement the global clipboard. Various programs make use of it already, including
- For details, have a look here. If you'd like to go a step further and implement direct dragging of selections between applications, then look at the accompanying page on drag and drop.
- Setting Choices
- To save time implementing a GUI for the user choices in a program, it may be worth considering a third party solution such as Confix.
Distributing Applications
Applications should be published online. The RISC OS news portal, Drobe, provides free webspace for registered users. It is recommended that you refer to the internet homepage of the application in the help documentation to ensure users can easily find support and upgrades if needed.
Programs should be compressed as zip files for download from the internet and the archive filename should end in .zip
(this can ensure that the archive is served correctly by the hosting server). There are several free tools available which provide zip compression, mostly based on the open-source "InfoZip" routines, including: InfoZip and ZipEE. Archives should be tested with SparkPlug as many people will use that to unzip the archive. (It's not based on "InfoZip" so is the most likely to cause compatibility problems.)
As an alternative (or in addition to) publishing application archives online, it is worth considering providing the application as a "package" for use with RiscPkg.
See Also
- User Messages - an explanation how user messages (type 17, 18 and 19) work.
- Object Linking and Embedding - how to use OLE in your application.
Further Reading
- RISC OS Documentation
- The tofla site has an extensive collection of programming articles and resources.
- Chris Bazley has a few items of interest here. (Including the global clipboard docs referred to above.)
- Here's some info on the nested wimp.
- The Google archive or FAQ of the comp.sys.acorn.programmer usenet group is well worth a trawl and if you can't find what you're after, you can always ask a question too. If you prefer a web forum, then the friendly (though low-traffic) Iconbar programming forum is well worth a visit.