Programming Conventions: Difference between revisions

From RISC OS
Jump to navigationJump to search
(→‎Internationalisation: Add link to RISC OS Translated page)
(Re-phrased to make language more formal. Added sentence on RiscPkg.)
Line 1: Line 1:
''The following is opinion of [[User:Adamr|Adam Richardson]] and others and may or may not be agreed with by other RISC OS programmers. Use the [[Talk:Programming_Conventions|discussion page]] to disagree/comment on it''
''The following is opinion of [[User:Adamr|Adam Richardson]] and others and may or may not be agreed with by other RISC OS programmers. Use the [[Talk:Programming_Conventions|discussion page]] to disagree/comment on it''


==Introduction==
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]]''.


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 (which it is assumed the reader is already familiar with) 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]]''.
This article assumes that 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. It's a good idea to understand the conventions set out below before doing things your own way, though. ;-)

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.




Line 9: Line 11:
===Choices===
===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.
It is recommended that the choices for an application are stored in a dedicated place on the system. This makes it easy for a user to upgrade the program whithout changing their 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.


So how can you deal with this? Briefly, you just write your choices to <code><Choices$Write>.AppName</code> and read them from <code>Choices:AppName</code>. (This is a general rule: if you are reading from <code>Example:Path</code>, you would write to <code><Example$Write>.Path</code>.) For a more detailed explanation, take a look [http://gerph.org/info/choices.html here]. You should register <code>AppName</code> with the [[Programming_Conventions#Allocations|allocation service]].
To achieve this, user choices should be written to <code><Choices$Write>.AppName</code> and read from <code>Choices:AppName</code>. (This is a general rule: where reading takes place from <code>Example:Path</code>, the corresponding write location is <code><Example$Write>.Path</code>.) For a more detailed explanation, take a look [http://gerph.org/info/choices.html here]. <code>AppName</code> should be registered with the [[Programming_Conventions#Allocations|allocation service]].
<br>(PRM5a:530-531,539)
<br>(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 <code><Choices$Write></code>. Ideally, you would also provide some mechanism whereby the location of your choices can be chosen by the user.
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 <code><Choices$Write></code> and ideally, should also provide some mechanism whereby the location of the application's choices can be chosen by the user.




===Installing Things===
===Modifications===


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.
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 <code>Boot$ToBeTasks</code> (unless your program is necessary for the completion of the boot sequence, in which case it should go in <code>Boot$ToBeLoaded</code>).
To install a stand-alone program in the boot sequence, use the system variable <code>Boot$ToBeTasks</code> (unless the program is necessary for the completion of the boot sequence, in which case it should go in <code>Boot$ToBeLoaded</code>).




===Scrap usage===
===Scrap usage===


Well, this is a short one. If you want to use the Scrap directory, just read and write to <code><Wimp$ScrapDir>.AppName</code>. (Where <code>AppName</code> is the name of your program, and/or has been [[Programming_Conventions#Allocations|allocated]] to you specifically.)
To make use of the Scrap directory, programs should read and write to <code><Wimp$ScrapDir>.AppName</code>. (Where <code>AppName</code> is the name of the program, and/or has been [[Programming_Conventions#Allocations|allocated]] specifically.)




===Distribution of Modules===
===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 <code>!System</code>. 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.
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 <code>!System</code>. 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 <code>!System</code> should be supplied in a dummy <code>!System</code> application which the user can then use with their computer's in-built "System Merge" utility. The structure of
Modules which are destined for <code>!System</code> should be supplied in a dummy <code>!System</code> application which the user can then use with their computer's in-built "System Merge" utility. The structure of
the dummy application will be:
the dummy application will be:
!System.Modules.<minOS>.YourModule
!System.Modules.<minOS>.YourModule
where <code><minOS></code> 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, <code><minOS></code> will be 500.
where <code><minOS></code> 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, <code><minOS></code> will be 500.




===UniBoot===
===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].
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 [http://acorn.riscos.com/riscos/releases/UniBoot/ RISC OS Ltd website] where it can be downloaded and installed.




==Sprites==
==Sprites==


As a minimum, your application should have a <code>!Sprites</code> file in it which includes a mode 12 sprite called <code>!AppName</code>. 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.
As a minimum, applications should have a <code>!Sprites</code> file which includes a mode 12 sprite called <code>!AppName</code>. 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.


<code>!Sprites</code> can also contain <code>sm!AppName</code> 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 <code>sm!AppName</code> is not present. If present, and if the user's screen mode supports it, <code>!Sprites22</code> will be used in preference to <code>!Sprites</code>. It should contain a mode 20 sprite. Likewise, you may include a <code>!Sprites11</code> file which has a 180dpi sprite. From RISC OS 4 onwards, you can use 256 colour sprites. If <code>ic_taskname</code> is present, it will be used when your windows are iconised to the pinboard. (<code>taskname</code> is the string given in <code>Wimp_Initialise</code>.)
<code>!Sprites</code> can also contain <code>sm!AppName</code> 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 the sprite without touching it up, however, as the filer will automatically scale the large version of the sprite if <code>sm!AppName</code> is not present. If present, and if the user's screen mode supports it, <code>!Sprites22</code> will be used in preference to <code>!Sprites</code>. It should contain a mode 20 sprite. Likewise, a <code>!Sprites11</code> file may be included which has a 180dpi sprite. From RISC OS 4 onwards, 256 colour sprites may be used. If <code>ic_taskname</code> is present, it will be used when the application windows are iconised to the pinboard or iconbar. (<code>taskname</code> is the string given in <code>Wimp_Initialise</code>.)


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.
Application sprites should be irregularly shaped, with transparent backgrounds and document sprites should 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.
<br>(SG:17,89,93)
<br>(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.
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.
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 <code>*IconSprites</code>ed. The command is always <code>*IconSprites <YourApp$Dir>.!Sprites</code> : the Wimp will itself add the numeric suffix for the appropriate resolution and colour depth. If you wish to use further sprites in your program, load them into a bit of your own memory with <code>OS_SpriteOp,&10A</code>.
To avoid cluttering up the Wimp sprite pool, only application sprites, iconised icon and any filetype sprites should be <code>*IconSprites</code>ed. The command is always <code>*IconSprites <YourApp$Dir>.!Sprites</code> : 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 <code>OS_SpriteOp,&10A</code> SWI.)


RISC OS will IconSprites the appropriate <code>!Sprites</code> or <code>!SpritesXX</code> file when an application without a !Boot file is <code>Filer_Boot</code>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 (eg from the command line) without having first been <code>Filer_Boot</code>ed.
RISC OS will <code>IconSprites</code> the appropriate <code>!Sprites</code> or <code>!SpritesXX</code> file when an application without a !Boot file is <code>Filer_Boot</code>ed. If a !Boot file is present it must <code>IconSprites</code> the !Sprites file manually; the same is necessary for the !Run file since the application may be run without having first been <code>Filer_Boot</code>ed (eg from the command line).
<br>(SG:106)
<br>(SG:106)


==Window Design==


==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.


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 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 <code>Wimp_TextOp</code> 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.
*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 <code>Wimp_TextOp</code> SWI where your icons are likely to vary considerably in size in order to ensure adequate spacing and layout in different modes and fonts.
*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.
*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.




==Internationalisation==
==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 <code>Territory</code> 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 [http://www.gag.de/software/ResFind/ ResFind]. The full instructions for <code>ResFind</code> 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 :-)
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 <code>Territory</code> module, RISC OS is able to deal with operating in different countries, timezones etc. and applications can cope with this appropriately by using [http://www.gag.de/software/ResFind/ ResFind]. The full instructions for <code>ResFind</code> 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 <code>MessageTrans</code> SWIs.
*In the <code>!Run</code> file (and maybe the <code>!Boot</code> file if Castle's help system variables are being used), <code>ResFind</code> 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 the <code>AppNameRes:</code> path variable.


See also [[RISC OS Translated]].
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 <code>MessageTrans</code> SWIs if you're not already familiar with them.


In your <code>!Run</code> file (and maybe your <code>!Boot</code> file if you're setting Castle's help system variables), you run a little basic program called <code>ResFind</code> 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 <code>AppNameRes:</code> path variable.

See also [[RISC OS Translated]].


==Application Structure==
==Application Structure==


A common structure for your application would be:
A common structure for applications is:


!AppName
!AppName
Line 101: Line 103:
;Resources:This is a directory containing any resources your program might need.
;Resources:This is a directory containing any resources your program might need.


Note that <code>!Boot</code>, <code>!Help</code>, <code>!Run</code> and <code>!Sprites</code> 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 <code>ResFind</code> system for internationalisation (described above) then you must have the <code>Resources</code> directory and it should contain a directory named <code>UK</code>. Further territories can be added like this:
Note that <code>!Boot</code>, <code>!Help</code>, <code>!Run</code> and <code>!Sprites</code> have special roles and should always be present. If <code>ResFind</code> is being used for [[Programming_Conventions#Internationalisation|internationalisation]] then the application must have the <code>Resources</code> directory and it should contain a directory named <code>UK</code>. Further territories can be added like this:


!AppName
!AppName
Line 109: Line 111:
France
France
etc...
etc...
Any territory-independent resources can go in <code>!AppName.Resources</code>
Any territory-independent resources go in <code>!AppName.Resources</code>




==Help==
==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...
Castle have published a web page which makes various recommendations regarding implementing a help system in RISC OS programs. The full document is available [http://www.iyonix.com/32bit/help.shtml here]. A summary is presented below:


*The second entry down on an application's icon bar menu should be a <code>Help</code> entry which launches the program's help file. (E.g. with <code>*Filer_Run <AppName$Dir>.!Help</code>)
*The second entry down on an application's icon bar menu should be a <code>Help</code> entry which launches the program's help file. (E.g. with <code>*Filer_Run <AppName$Dir>.!Help</code>)
Line 128: Line 130:
:<code>AppName$Description</code>: Concise description of the application
:<code>AppName$Description</code>: 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 <code>obey</code> file which lives inside <code>Resources.<territory></code>.
Note: it is good idea to make these variables dependent on territory too. This can be achieved by including them in an <code>obey</code> file which lives inside <code>Resources.<territory></code>.




==Setting System Variables==
==Setting System Variables==


You should only set system variables which are currently '''unset''' in your <code>!Boot</code> file. For instance, your application should unilaterally set <code>AppName$Dir</code> in its <code>!Run</code> file but should be more careful about setting it in the <code>!Boot</code> file. For instance, you could use:
Only system variables which are currently '''unset''' should be set in <code>!Boot</code> file. Applications should unilaterally set <code>AppName$Dir</code> in their <code>!Run</code> file but should be more careful about setting it in the <code>!Boot</code> file. Example code for <code>!Boot</code> files is:
If "<AppName$Dir>" = "" Then Set AppName$Dir <Obey$Dir>
If "<AppName$Dir>" = "" Then Set AppName$Dir <Obey$Dir>
The same applies if you implement the "help" system variables.
The same applies to the "help" system variables.


You should make sure that you do not rely on anything which is set up in the <code>!Boot</code> file, as it is entirely possible that <code>!Boot</code> '''won't''' be seen before your program is run.
As applications can potentially be run without their <code>!Boot</code> files having been executed, they should not rely on anything which is set up in the <code>!Boot</code> file.




==Allocations==
==Allocations==


In order to avoid clashes between programs with similar names (for instance they might be resetting <code>AppName$Dir</code> 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.
In order to avoid clashes between programs with similar names (for instance they might be resetting <code>AppName$Dir</code> 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.


For more detail, see [http://www.riscos.com/news/news_items/PR090801.htm this page]. In particular, there is a link at the bottom of the page to a program which will make the whole process a little easier.
For more detail, see [http://www.riscos.com/news/news_items/PR090801.htm this page]. In particular, there is a link at the bottom of the page to a program which automates the allocation process.




==Naming Conventions==
==Naming Conventions==


Here's a list of a few naming conventions which you may care to follow:
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 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.
*Menu items which open new windows should end in "..." and if they do end in "..." they shouldn't have submenus.
Line 156: Line 157:




==General Hints==
==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. [http://www.snowstone.org.uk/riscos/multierror/ MultiError] provides such an alternative.)
Now for a few general hints and tips to hopefully improve your programs...


;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 <code>OS_ReadSysInfo</code> and check for the existence of specific modules etc.
;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 <code>!Run</code> files, I wrote [http://www.snowstone.org.uk/riscos/multierror/ MultiError], which you might want to investigate.)


;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.
;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 <code>OS_ReadSysInfo</code> 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 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, including
;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, including
Line 174: Line 173:
:For details, have a look [http://www.starfighter.acornarcade.com/mysite/articles/SelectionModel.html 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].
:For details, have a look [http://www.starfighter.acornarcade.com/mysite/articles/SelectionModel.html 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! [http://www.xat.nl/en/riscos/sw/confix/ Confix] has been written specifically to address this problem. Have a look and see what you think...
;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 [http://www.xat.nl/en/riscos/sw/confix/ Confix].



==How to Distribute Your Programs==


==Distributing Applications==
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.


Applications should be published online. The RISC OS news portal, [http://www.drobe.co.uk Drobe], provides free webspace for registered users. It is recommended that you refer to the internet homepage of the application in the helpdocumentation to ensure users can easily find support and upgrades if needed.
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 <code>MimeMap</code> 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], [http://www.pettigrew.org.uk/john/riscos/zipee.html 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 [http://pilling.users.netlink.co.uk/free.html SparkPlug] since many people will use that to unzip your archive and it's not based on "InfoZip" so is the most likely to cause you strife!


Programs should be compressed as zip files for download from the internet and the archive filename should end in <code>.zip</code> (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: [http://www.starfighter.acornarcade.com/mysite/utilities.htm#infozip InfoZip], [http://www.pettigrew.org.uk/john/riscos/zipee.html ZipEE], and the [http://www.sbellon.de/sw-ports.html command line InfoZip port]. Archives should be tested with [http://pilling.users.netlink.co.uk/free.html 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 [http://www.riscpkg.org/ RiscPkg].
==The End: Further Reading==


==Further Reading==
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 <strike>local</strike> (OK, by mail order) RISC OS dealer. They can also be downloaded in HTML and StrongHelp format using [http://www.drobe.co.uk/riscos/artifact1688.html this procedure]. You should also find the shorter [http://sudden.recoil.org/stronghelp/ StrongHelp paraphrase of them] useful for quick reference.
*The PRMs can be bought by mail order from a RISC OS dealer. They can also be downloaded, for free, in HTML and StrongHelp format using [http://www.drobe.co.uk/riscos/artifact1688.html this procedure]. You should also find the shorter [http://sudden.recoil.org/stronghelp/ StrongHelp paraphrase of them] useful for quick reference.
*Have a look at the [http://www.tofla.iconbar.com/main.htm tofla] site for an extensive collection of programming articles and resources.
*The [http://www.tofla.iconbar.com/main.htm tofla] site has an extensive collection of programming articles and resources.
*Chris Bazley has a few items of interest [http://www.starfighter.acornarcade.com/mysite/programming.htm here]. (Including the global clipboard docs referred to above.)
*Chris Bazley has a few items of interest [http://www.starfighter.acornarcade.com/mysite/programming.htm here]. (Including the global clipboard docs referred to above.)
*Erik Groenhuis has some information on his website about [http://www.xs4all.nl/~erikgrnh/riscos/ole.html implimenting OLE] (Object Linking and Embedding) in your programs.
*Erik Groenhuis has some information on his website about [http://www.xs4all.nl/~erikgrnh/riscos/ole.html implimenting OLE] (Object Linking and Embedding) in your programs.

Revision as of 13:26, 24 January 2007

The following is opinion of Adam Richardson and others and may or may not be agreed with by other RISC OS programmers. 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 (which it is assumed the reader is already familiar with) 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 are stored in a dedicated place on the system. This makes it easy for a user to upgrade the program whithout changing their 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, <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 RISC OS 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's no point 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, !Sprites22 will be used in preference to !Sprites. It should contain a mode 20 sprite. Likewise, a !Sprites11 file may be included which has a 180dpi sprite. From RISC OS 4 onwards, 256 colour sprites may be used. 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.)

Application sprites should be irregularly shaped, with transparent backgrounds and document sprites should 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 any filetype sprites should be *IconSpritesed. 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_Booted. 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_Booted (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.


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'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 the AppNameRes: path variable.

See also RISC OS Translated.


Application Structure

A common structure for applications is:

!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. 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
  Japan
  Germany
  France
  etc...

Any territory-independent resources go in !AppName.Resources


Help

Castle 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 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 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 file. Applications should unilaterally set AppName$Dir in their !Run file but should be more careful about setting it in the !Boot file. Example code for !Boot files is:

If "<AppName$Dir>" = "" Then Set AppName$Dir <Obey$Dir>

The same applies to the "help" system variables.

As applications can potentially be run without their !Boot files having been executed, they should not rely on anything which is 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.

For more detail, see this page. In particular, there is a link at the bottom of the page to a program which automates the allocation process.


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 "..." and if they do end in "..." they shouldn't 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 visa 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 helpdocumentation 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, ZipEE, and the command line InfoZip port. 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.

Further Reading

  • The PRMs can be bought by mail order from a RISC OS dealer. They can also be downloaded, for free, in HTML and StrongHelp format using this procedure. You should also find the shorter StrongHelp paraphrase of them useful for quick reference.
  • 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.)
  • Erik Groenhuis has some information on his website about implimenting OLE (Object Linking and Embedding) in your programs.
  • Here's some info on the nested wimp, also hosted by Erik.
  • 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.