Writing OmniDesk-compatible utilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This article is about creating utilities which interface with the OmniDesk utility manager. If you don't know how to write programs which multi-task in the RISC OS desktop, then stop reading now! All the OmniDesk utilities are complete, self-contained programs in their own right: OmniDesk does not look after their Wimp routines for them, and so you must be familiar with writing for Acorn's desktop before you can contemplate a full OmniDesk utility.

However, if you are undaunted by the prospect of writing a utility which must conform not only with RISC OS guidelines but with a few others as well, then read on! Having a set of utilities which appear as entries in a single manager utility's menu is very convenient, and the more OmniDesk utilities that appear the better! Acorn User will be happy to publish readers' submissions, and will of course pay for them.

The Basics
~~~~~~~~~~

As a series published over several months, we felt that it was best to make every OmniDesk utility completely self-sufficient. It would have been possible to create a common library of many of the routines used by the utilities, but this would have made them less independent and less understandable. As they now exist, all the OmniDesk utilities can be copied to any disc and used on their own just like any other RISC OS program. They only become dependant on OmniDesk if they are started up by it in the first place.

A second aim was to make the listings intelligible. To this end, the listings all follow exactly the same structure and contain plenty of comments. They all start off with specialised Wimp routines for decoding menus, handling mouse clicks, etc., followed by a set of non-specific but very useful routines which are identical in all programs. These cover things like updating icons and ticking menus, and are tailored to reduce screen flicker wherever possible. Finally, each listing ends with a set of program-specific routines.

Whilst it would certainly be possible to convert an existing utility to converse with OmniDesk, it is really better to write a new utility from scratch, basing it around the edited framework of an existing OmniDesk utility.

Inner Workings
~~~~~~~~~~~~~~

When OmniDesk starts up, it goes through all the utilities stored in the <OmniDesk$Dir>.Utilities directory and runs each one with some information tacked on the end of its start-up string. This information is of the form "StartupOmniDesk#" (no spaces after the first), where "#" is the task handle of OmniDesk itself. If the "OmniDesk#" bit is not present, it means the user has run the utility by double-clicking on it in a filer window in the normal way. If this is the case, the utility must install an icon on the icon bar in the normal way and ignore any messages from OmniDesk. This is most important, as only one copy of any utility must be under OmniDesk's control.

Whenever OmniDesk starts a utility, the start-up string will always end with "OmniDesk#". There are two additional options, either of which may also be present: "Startup" means that OmniDesk is starting up all the utilities, and just requires some information, but does not require the utility to actually run. Therefore, if "Startup" is specified, the utility must declare itself to OmniDesk (see later) and exit immediately. If "Exit" is specified, it means that the utility must open its main window and run as normal, but quit as soon as its main window is closed. If neither "Startup" nor "Exit" is specified, then the utility must allow its window to be opened and closed freely, and must only quit when told to do so by OmniDesk. Note that if a utility is started by OmniDesk (as opposed to being run from a directory by the user) it must not install an icon on the icon bar.

Sending messages
~~~~~~~~~~~~~~~~

OmniDesk and its utilities converse via a set of messages (which have officially allocated numbers from Acorn). These are as follows:

Message_UtilDeclare (&825C0)
----------------------------

This message is very important, as it tells OmniDesk all the major details of the utility which sends it. When a utility is run it must read its start-up string, and if it ends with "OmniDesk#", it must read the OmniDesk task handle (the # part) and immediately send a Message_UtilDeclare message back to OmniDesk. The message block is comprised as follows:

Block+20 (4 bytes) contains the task handle of the utility.

Block+24 (20 bytes, including ASCII 13 terminator) contains the task name of the utility. This is used both in the OmniDesk Utilities menu and in the Preferences window.

Block+44 (11 bytes, including ASCII 13 terminator) contains the file (leaf) name of the utility, which will of course begin with "!".

Block+55 (77 bytes with terminator) contains the help text about what the utility does if you activate it with the Select or Adjust mouse buttons.

Block+132 (77 bytes with terminator) contains the help text about what the utility does if you drag a file into it.

The two help messages are of huge importance, as their presence determines how OmniDesk treats the utility. A utility is free to accept either dragged files or icon bar clicks or both. It should not distinguish between clicks with the Select and Adjust buttons, since either button may be configured to start it up via the OmniDesk preferences window. (If installed on the icon bar, a utility should ignore Adjust-clicks.)

So, if the utility provides a mouse-button help message, OmniDesk knows it will start immediately when selected from the Utilities menu, or when the OmniDesk icon is clicked (if this has been configured in the preferences window.) Note that the mouse-button help message must start in mid-sentence: OmniDesk itself will prefix it with the "Click Select/Adjust to " as appropriate.

If the utility provides a drag help message, it must be of the form "Drag whatever file here to ...". OmniDesk will then know that the utility can accept a file dragged into it, and will unshade the 'Drag' radio icon in the Preferences window.

If both mouse-button and drag help messages are provided, then it will be possible to configure OmniDesk to start up a utility in two ways: (a) via a mouse-click on the OmniDesk icon, and (b) via a file being dragged to the OmniDesk icon.

If only a drag-help message is supplied, then it will still be possible to configure a mouse-button start in OmniDesk, but this will result in a small window opening (containing the drag help message) which will accept a file dragged into it.

Every utility should therefore supply at least one help message. If neither is supplied, then the utility will not be configurable in OmniDesk (its radio icons will all be shaded), and will only be accessible via the Utilities menu. Furthermore, this will result in a drag window opening (containing no help message), which may not be desirable.

Interactive help is very important in OmniDesk and is provided on everything, including menus in RISC OS 3.

Message_UtilOpen (&825C1)
-------------------------

OmniDesk sends this message to a utility when it wants it to start up. The contents are as follows:

Block+20 (4 bytes) is the action required: if the value is 4, this means "behave as though Select has been clicked on the icon bar icon"; if the value is 2, this means "behave as though a file has been dragged to the icon bar icon".

Block+24 (20 bytes including ASCII 13 terminator) is the task name of the receiver utility required. It is very important that a utility checks that the task name at Block+24 is its own taskname; if not, the message should be ignored. Under some circumstances, this message has to be broadcast by OmniDesk, which means that all the utilities will receive it, whether or not it was intended for them.

If the contents of Block+20 are the value 4, the utility must simply open its main window (or perform whatever task it normally performs when activated). If the contents are the value 2, some additional information will be supplied:

Block+44 (4 bytes): estimated size of data to load in bytes.

Block+48 (4 bytes): filetype of file dragged to OmniDesk

Block+52: full pathname of the file.

If the filetype at Block+48 is inappropriate, the utility should issue an error message, send a Message_UtilQuitting (see below) to OmniDesk and quit; otherwise it should process the file as normal and, when finished, send a Message_UtilLoadAck (see below) back to OmniDesk. Note that OmniDesk will deal with <Wimp$Scrap> transfer protocols itself, so nothing 'clever' is needed here. However, if the application is being run independently of OmniDesk (and has installed and icon), it must deal with such complexities itself.

Message_UtilQuitting (&825C2)
-----------------------------

This has three uses:

(a) When OmniDesk is quit, it tells all its utilities to quit by broadcasting this message

(b) When updating the choices set in the Preferences window, any utility which has not had its 'load' switch set On is told to quit.

(c) If a utility quits unexpectedly, or is quit via the task manager, it must send this message to OmniDesk so that its internal utility record can be updated.

The contents are: Block+20 (4 bytes): task handle. In cases (a) and (b), the task handle will be that of OmniDesk; in case (c), it will be that of a utility.

Message_UtilReside (&825C3)
---------------------------

This is a simple message, which has no contents other than the normal ones up to Block+20. If a utility receives it, it means that its 'load' switch in the OmniDesk Preferences window has been set to On, and therefore it should not quit until specifically told to do so by OmniDesk. If the "Exit" option was specified in the start-up string, this condition must be negated. NB: when the "-Exit" option is in force, the utility must quit when the user closes its main window, but before doing so it must send OmniDesk a Message_UtilQuitting.

Message_UtilLoadAck (&825C4)
----------------------------

This message must be sent to OmniDesk once the file from the UtilOpen message has been processed. OmniDesk will perform any tidying-up necessary (such as deleting <Wimp$Scrap>). As with the UtilReside message, this message has no contents.

Further details about all these messages are supplied in the "Docs" directory within OmniDesk. Although they might initially seem fairly complex, these messages are actually quite simple to deal with, and many cases can be dealt with by single procedures within a program. It is very important to bear in mind that if a utility has been started up manually by the user, and has installed an icon on the icon bar (i.e. it does not know an OmniDesk task handle), it must ignore any OmniDesk messages it receives. Also, if not being run by OmniDesk, it should obey the RISC OS 3 desktop save protocol (but should ignore it if under OmniDesk's control).

Stylistic points
~~~~~~~~~~~~~~~~

All the OmniDesk utilities should present a common appearance. There is no size limit on the utilities which can be run from OmniDesk, but they should generally have a single main window with associated menu. Obviously, you would not write a complex multi-document editor to run with OmniDesk!

If run separately from OmniDesk, the icon bar menu should have Info and Quit options, but no others. The icon bar icon should also update itself as appropriate to the mode it is running in, between high and low resolution colour and high resolution monochrome versions. (See the PROCUpdateIconForMode routine in any utility for details of how this works.) It should respond to Select-clicks, but ignore Adjust ones.

Every utility which has a main window (this is actually not an essential requirement) must present a main menu which consists of a bare minimum of three items: 'Info', 'Window position', and 'Save settings', and these three items should be separated by two red dashed lines. Extra information items should go in the top third (below 'Info'), extra save items in the bottom third (above 'Save settings') and any other items necessary should go above 'Window position' in the middle of the menu.

All utilities should include the window-snap feature from the 'Window position' menu option, and it should be possible to disable the snapping if the small window icon is removed (by clicking Adjust on it) from the 'Location of window' dialogue. The grid coordinates should be saved (if nothing else) by the 'Save settings' menu option.

A further related point is that when a window is snapped to the right-most column of the grid, its main menu should work backwards (right to left) under RISC OS 3, so that it does not collide with the edge of the screen. If necessary, long (indirected) menu titles should be provided under RISC OS 3, but they should still make sense under RISC OS 2. See the !RunImage file of any utility to see how this can be done.

An icon should be provided for use with the Pinboard, and all windows should make use of version 2 of the public domain Interface Manager to present a 3D appearance to both RISC OS 2 and 3 users.

Although all the above requirements may seem rather daunting, there is really not a huge amount of work needed to make a program converse with OmniDesk. Furthermore, many routines (such as the ones to deal with the window-snapping features) are identical in all utilities and, providing you are programming in Basic, you can copy the necessary bits out of an existing utility.

So, why not have a go? Earn yourself a bit of fame and a small fortune, and have your OmniDesk compliant utilities published by Acorn User!

