Programming Conventions
There's all sorts of things to know when you write a program, and not all of them are obvious or easily discovered - particularly if you don't know what you don't know! This guide is aimed at introducing you to some conventions and resources which you might or might not already know about. There are a few references dotted about: "PRM" refers to the Programmer's Reference Manual and "SG" refers to the Style Guide.
I will assume you are already familiar with "how" to do things, just not
necessarily what the end result ought to be. Of course, this is all just
a guide and you may decide that you have a better way of doing things.
You would be wise to understand the ideas set out below before doing
things your own way, though. ;-)
!Boot
Choices
Since, well, ages ago when hard-discs became commonplace, it's been recommended that you store the choices for your application in a central place on the system. This makes it easy for a user to upgrade the program, allows it to run happily from a read only medium and on modern, multi-user OSes means each user can have different choices without you, as author, needing to worry about anything at all. You should not, therefore, store choices within your application directory.
So how can you deal with this? Briefly, you just write your choices to
<Choices$Write>.AppName
and read them from
Choices:AppName
. (This is a general rule: if you are
reading from Example:Path
, you would write to
<Example$Write>.Path
.) For a more detailed explanation,
take a look here. You should
register AppName
with the [#Allocations 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, your
programs should fail gracefully if they find they can't write to
<Choices$Write>
. Ideally, you would also provide some
mechanism whereby the location of your choices can be chosen by the
user.
Installing Things
You should be very cautious when fiddling about inside your users' boot sequences. Try to ensure that the users know what your program is going to do, keep a log of what happened and maybe provide a means to roll it back if it goes wrong.
To install a stand-alone program in the boot sequence, use the system
variable Boot$ToBeTasks
(unless your program is necessary
for the completion of the boot sequence, in which case it should go in
Boot$ToBeLoaded
).
Scrap usage
Well, this is a short one. If you want to use the Scrap directory, just
read and write to <Wimp$ScrapDir>.AppName
. (Where
AppName
is the name of your program, and/or has been
[#Allocations allocated] to you specifically.)
Distribution of Modules
In the majority of cases you should distribute your module inside your
application. However, if your module is likely to be used by other
programs, you might want to consider 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 your module. So, for example, if your module is only
suitable for an Iyonix, <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 you require the use of the nested window manager, you should check for version 3.80 of WindowManager and if it's not there you might wish to point users in the [http://acorn.riscos.com/riscos/releases/UniBoot/ right direction].
Sprites
As a minimum, your application should have a !Sprites
file
in it which includes a mode 12 sprite called !AppName
. Note
that sprite names are limited to 12 characters so, if you give your
program a long name, and the first 12 characters match another program,
then the sprites will 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's no point in
just shrinking your sprite without touching it up, however as the filer
will automatically scale the large version of your sprite if
sm!AppName
is not present. If present, and if the user's
screen mode supports it, !Sprites22
will be used in
preference to !Sprites
. It should contain a mode 20 sprite.
Likewise, you may include a !Sprites11
file which has a
180dpi sprite. From RISC OS 4 onwards, you can use 256 colour sprites.
If ic_taskname
is present, it will be used when your
windows are iconised to the pinboard. (taskname
is the
string given in Wimp_Initialise
.)
Application sprites should be irregularly shaped, with transparent
backgrounds and document sprites should have a solid outline, preferably
with the snazzy little folded-corner bit! This helps the user
distinguish easily and quickly between the two types of object.
(SG:17,89,93)
Your icons should not attempt to be photographic quality pictures! They
should be clear, iconic representations of whatever it is you 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 your application
sprite, iconised icon and any filetype sprites should be
*IconSprite
ed. If you wish to use further sprites in your
program, load them into a bit of your own memory with
OS_SpriteOp,&10A
.
Window Design
To keep things consistent between applications you should try to stick to the standard sizes and spacings for icons and buttons in your windows.
- Ensure that you check your window designs bearing in mind that users
can (and do) customise their desktop font. At the very least, you should
test your templates in 12-point Homerton. Ideally you should make use of
the Wimp_TextOp
SWI where your icons are likely to vary
considerably in size.
- You should clearly separate the user-changeable
icons from the "action" buttons, perhaps with a horizontal ruler. In any case, your "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. You might use a small button only 140 units wide and if you need more width, adjust it in steps of 16 OS units.
Internationalisation
It is good practise to design your program 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 you can ensure your
program deals with this appropriately by using
ResFind. The full instructions for
ResFind are included in its archive and are very thorough. Here's a
brief description of the process to illustrate that it shouldn't involve
too much work on your part :-)
Firstly, you must make sure that any user-facing messages are supplied
in an editable form, away from your program's executable. (Of course,
this is good practise anyhow.) You might wish to look at the
MessageTrans
SWIs if you're not already familiar with them.
In your !Run
file (and maybe your !Boot
file
if you're setting Castles [#Help system help variables]),
you run a little basic program called ResFind
which assumes
a certain directory structure within your application directory and
intelligently sets up a couple of system variables depending on what it
finds. From then on, you can simply refer to all your resources using
the AppNameRes:
path variable.
Application Structure
A common structure for your application would be:
!AppName !Boot !Help !Run !RunImage !Sprites !Sprites22 Resources
!Boot:This file is executed when an application's directory viewer is opened, or the application is Filer_Boot-ed. !Help:This file is executed when the 'Help' item from the Filer menu is selected. !Run:This file is executed when an application is run. !RunImage:This is the conventional name given to the main executable for your application. !Sprites:Either this or '!SpritesXX' is used for the application's filer sprite if no '!Boot' file is present. Resources:This is a directory containing any resources your program might need.
Note that !Boot
, !Help
, !Run
and
!Sprites
have special roles and should always be present.
This structure certainly isn't set in stone however and for simple
programs probably isn't that important. If you're using the
ResFind
system [#Internationalisation described above] then
you must have the Resources
directory and it should contain
a directory named UK
. Further territories can be added like
this:
!AppName Resources Japan Germany France etc...
Any territory-independent resources can go in !AppName.Resources
Help
Castle have published a web page which makes various recommendations regarding implementing a help system in your programs. You can see the full document by [http://www.iyonix.com/32bit/help.shtml clicking on this link], or you can read on for a summary...
- 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 file
AppName$Version
:Application's version number
AppName$Web
:URL for application's home page on the Web
AppName$Title
:Application's full title
AppName$Publisher
:Your name!
AppName$Description
:Concise description of the application
Note: it would be a good idea to make these variables dependent on
territory too. You can do this by including them in an obey
file which lives inside Resources.<territory>
.
Setting System Variables
You should only set system variables which are currently unset in
your !Boot
file. For instance, your application should
unilaterally set AppName$Dir
in its !Run
file
but should be more careful about setting it in the !Boot
file. For instance, you could use:
If "<AppName$Dir>" = "" Then Set AppName$Dir <Obey$Dir>
The same applies if you implement the "help" system variables.
You should make sure that you do not rely on anything which is set up in
the !Boot
file, as it is entirely possible that
!Boot
won't be seen before your program is run.
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), you should
ensure that your application name is allocated to you before it is
released to the big wide world. This is not limited to your program's
name however - there's a whole range of things which your should
register, from SWI chunks to scrap locations.
For more detail, see this page.
In particular, there is a link at the bottom of the page to a program
which will make the whole process a a little easier.
Naming Conventions
Here's a list of a few naming conventions which you may care to follow:
- 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 "..." and if they do end in "..." they shouldn't have submenus. *SWI names should have each word capitalised with acronyms all in capitals.
General Hints
Now for a few general hints and tips to hopefully improve your programs...
Avoid Wimp_ReportError:Although Wimp_ReportError provides a quick and
easy way of reporting errors and, indeed, asking questions of the user -
it is not a great way of dealing with the problem. Just because your
program has tied itself in knots or the user has entered something
stupid, or a file is missing, or whatever, isn't really a good reason to
bring the whole system to a grinding halt. Hopefully, with a little
lateral thinking, you should be able to come up with a more friendly
(and multitasking) scheme for giving messages to, and asking questions
of, your users. (In order to address this problem in my !Run
files, I
wrote MultiError, which
you might want to investigate.)
Don't Make Assumptions:Don't assume things about your user's systems. If
there's a specific feature which your program required, test for it
explicitly rather than guessing 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 modules etc.
Backward Compatibility:It is probably reasonable, nowadays, to require at least RISC OS 4 for your programs. However, most things will still work fine on RISC OS 3.5 (especially if the user has installed [#!Boot UniBoot) so you might consider that as your lowest common denominator. It is not worth making any extra effort to support RISC OS 3.1.
Use the Global Clipboard:If you're writing a program which has content which could conceivably be useful in other programs (or visa versa), then make the effort to implement the global clipboard. Various programs make use of it already, such as
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 [http://www.starfighter.acornarcade.com/mysite/articles/Drag-and-Drop.html the accompanying page on drag and drop].
Setting Choices:Lots of people don't want to spend lots of time designing and implementing elaborate configuration systems for their programs. But don't worry - there is a solution! Confix has been written specifically to address this problem. Have a look and see what you think...
How to Distribute Your Programs
Finally we come to how to distribute your programs once you've got them all polished off and ready to go! Ideally you'll have some webspace which you can use to publish your programs. If you refer to this in your documentation, it will be easy for users to find upgrades. If you're looking for free webspace, simply register with [http://www.drobe.co.uk/ drobe] and they'll give you some on their server.
To distribute your programs compress them into a zip file and upload it
to your website. Make sure the filename ends in ".zip" to ensure that
your website server delivers it correctly to end-users. This will also
mean that the file is set to the correct type once it arrives on RISC OS
(for those users with a working MimeMap
installation).
There are several free tools available to do the compression for you,
mostly based on the open-source "InfoZip" routines. You could try try
[http://www.starfighter.acornarcade.com/mysite/utilities.htm#infozip
InfoZip], ZipEE, or
even the [http://www.sbellon.de/sw-ports.html command line InfoZip
port]. It's a good idea to test your archive in the different programs
to make sure all is well and also in
SparkPlug since many
people will use that to unzip your archive and it's not based on
"InfoZip" so is the most likely to muck you about!
The End
Hopefully something in there has tickled your fancy. To finish off, here's a few links to some further resources which you probably already know about...
- The PRMs can be bought pretty cheaply nowadays from your friendly
local (OK, by mail order) RISC OS dealer. You should
also find the [http://sudden.recoil.org/stronghelp/ StrongHelp copy of
them] useful though.
- Have a look at the tofla site
for 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.)
- Erik Groenhuis has some information on his website about
implimenting OLE (Object Linking and Embedding) in your programs.
- Here's some [http://www.xs4all.nl/~erikgrnh/riscos/nested2a.html
info on the nested wimp], also hosted by Erik.
- The Google
[http://groups.google.co.uk/groups?num=30&hl=en&lr=&q=group%3Acomp.sys.acorn.programmer archive] or [http://www.aether.demon.co.uk/faqs/programmer.html 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) [http://www.iconbar.com/forums/viewforum.php?forum=programming Iconbar programming forum] is well worth a visit.
--Adamr 20:12, 7 Jan 2007 (GMT)