Copyright (c) Hyperion Entertainment and contributors.

Application Library

From AmigaOS Documentation Wiki
Revision as of 16:46, 13 July 2012 by Alexandre Balaban (talk | contribs) (→‎PrefsObjects: Added links to BOOPSI and Tag ITem, created section Supported Data Types.)
Jump to navigation Jump to search
WIP.png This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally.

Introduction

The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of application is a relatively recent addition to AmigaOS. Before, the system only distinguished between different types of program on a very low level, seeing them as either tasks or processes. This distinction might have been useful in the past when tasks (which require fewer resources in return for not being able to access DOS functions) could improve system performance. But it can hardly make a difference on today’s hardware so the trade-offs are no longer worth it. Nowadays it makes more sense to discriminate between programs that operate without the user even noticing (e.g. drivers, handlers, filesystems and other background services), and genuine full-blown applications with GUI and all.

AmigaOS alone cannot make such a distinction: it uses the Application Library as a mediator through which applications introduce themselves to the system. This process is called application registration, during which the application receives a unique identifier and is added to a public list among other applications. Once registered, the application can make use of the library’s many features:

  • It can send/receive messages to/from other registered applications. The library supports a set of common control messages (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows custom messages designed for an application’s particular needs; in this respect the Application Library provides an alternative to ARexx control.
  • It can use PrefsObjects, an XML-based, object-oriented system for handling program preferences. Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.
  • It can notify the user about, for example, completed tasks via automatic pop-up messages. These represent a practical, less obtrusive alternative to traditional requesters.
  • It can easily create and manage lists of recently-used documents.
  • It can register as a unique application, preventing other instances of itself from running.
  • It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.
  • It can control the behaviour of screen-blankers. Applications that don’t want to be disturbed may prevent the blanker from kicking in, or tell other applications to “keep quiet”.

Library opening chores

Just like other AmigaOS libraries, the Application Library must be opened before it is used. Further, at least one of its interfaces must be obtained, depending on the functionality you require. The Application Library has two interfaces, called “application” and “prefsobjects”. You always need to obtain the “application” interface because it provides access to most library functions including application registration. You’ll only need to open the “prefsobjects” interface if you intend to make use of the PrefsObjects preferences system.

struct Library *ApplicationBase = NULL;
struct ApplicationIFace *IApplication = NULL;
struct PrefsObjectsIFace *IPrefsObjects = NULL;
 
if ( (ApplicationBase = IExec->OpenLibrary("application.library", 52)) )
{
   IApplication = (APTR)IExec->GetInterface(ApplicationBase, "application", 1, NULL);
   IPrefsObjects = (APTR)IExec->GetInterface(ApplicationBase, "prefsobjects", 1, NULL);
}
 
if ( !ApplicationBase || !IApplication || !IPrefsObjects )
{
   /* handle library opening error */
}

Note that there is no interface called “main” like older, single-interface libraries have.

When your application has run its course, don’t forget to clean up and close both the library and its interface(s):

IExec->DropInterface((struct Interface *)IPrefsObjects);
IExec->DropInterface((struct Interface *)IApplication);
IExec->CloseLibrary(ApplicationBase);

Registering the application

Application registration is a simple process during which a program informs AmigaOS that it should be treated as an application, and provides some basic information about itself: the program name, an associated URL address, or a short description. Also, certain application-related parameters can be set at registration time (although some of these may be provided or changed later). Registration typically takes place at program startup; unregistration is normally done at the end of program runtime.

Application identifiers

A successfully registered application receives a numeric identifier, which in this documentation will be referred to as appID. Other registered applications can obtain the appID from the library and use it to communicate with the respective application. AppIDs are unique numbers: the Application Library generates them incrementally on a per-registration basis. They are never used again during the same AmigaOS session, which prevents programs from incidentally addressing the wrong application after the original appID holder unregisters.

Apart from the numeric appID an application can be referred to by its name (that is, a string-type identifier). As programs can get registered in an arbitrary order, it is the name identifier that other applications must use to retrieve the correct appID. To construct a unique name, the Application Library uses a special naming scheme that combines the application name with a related URL identifier (for example, the domain name of the application’s homepage). The latter is optional but it’s recommended to provide it at registration time, to avoid possible application name conflicts. The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.

Registration

Now let’s say we have a program called Best App made by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration might look like this:

uint32 appID;
 
appID = IApplication->RegisterApplication(“BestApp”,
           REGAPP_URLIdentifier, “supercoders.com,
           REGAPP_Description, “The best application there is, really”,
           TAG_DONE);
 
if (!appID)
{
   /* report registration error and quit */
}

Note that we’ve had to alter the application name to “BestApp”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme, the application will now become registered under the name “BestApp.supercoders.com”. (Should a previous instance of BestApp be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “BestApp_1.supercoders.com”, the third will register as “BestApp_2.supercoders.com”, and so on.)

The REGAPP_Description tag in the registration function tells the system what Best App is all about; it’s just an example of an optional parameter that can be provided. There are parameters that can only be applied at registration time, others may be set or changed later using the function SetApplicationAttrs(). Please refer to the Application Library autodoc for a complete list and description of registration/configuration parameters and their corresponding tags.

Unique applications

A program can register as a unique application, thus only allowing one instance of itself to run. While the multitasking nature and tradition of AmigaOS would suggest not imposing such limits, there can be good reasons to do so. For example, the developer of a song player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several songs at the same time. Multiple program instances would only compete for screen space and system resources, possibly jeopardizing OS performance on lower-specification computers.

The following function call registers our Best App as a unique application:

appID = IApplication->RegisterApplication(“BestApp”,
           REGAPP_URLIdentifier, “supercoders.com,
           REGAPP_Description, “The best application there is, really”,
           REGAPP_UniqueApplication, TRUE,
           TAG_DONE);

If the user now tries to launch a second instance of the program, it will fail on RegisterApplication() and the library will send a special message to the first instance informing it about the attempt. It is the developer’s responsibility to react to this message in a sensible way. Do not show error messages here: the user doesn’t need to know (or care) that an application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to front and activate its window. See the Messaging section below for more information.

Finding applications

The Application Library maintains a list of all registered applications. Certain special-purpose programs – let’s call them application managers – will also keep track of applications registering and unregistering. Nevertheless, most programs won’t ever have to do anything like this. If the need arises to talk to another application, they can simply find this particular application in the system and then start sending messages to it.

“Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following:

  • the application name, ie. the one under which it was registered via RegisterApplication();
  • the application name identifier, ie. the unique combination of the application’s name, instance number (should there be more instances running) and URL identifier – see Application identifiers above;
  • the pathname pointing to the program file on disk, e.g. “Work:Utils/BestApp”.

Based on this information, the respective piece of code that will find our BestApp in the system might look like this:

uint32 appID;
 
/* if you only know the application name */
appID = IApplication->FindApplication(FINDAPP_Name, “BestApp”, TAG_DONE);
 
/* if you know the application name identifier */
appID = IApplication->FindApplication(FINDAPP_AppIdentifier, “BestApp.supercoders.com, TAG_DONE);
 
/* if you specifically want to talk to the second running instance */
appID = IApplication->FindApplication(FINDAPP_AppIdentifier, “BestApp_1.supercoders.com, TAG_DONE);
 
/* if you know the pathname to the program file */
appID = IApplication->FindApplication(FINDAPP_FileName, “Work:Utils/BestApp”, TAG_DONE);

Once you have obtained the appID you can start communicating with the respective application.

Messaging

Modern software applications often need to communicate with other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, the operating system provides the necessary means. AmigaOS is no exception: inter-program communication has been supported in the OS on the low level (through Exec Library’s messages and ports) as well as on the high level (using the ARexx-language scripting features). The Application Library has introduced yet another means of communication, which can be seen as lying somewhere in between the two levels.

Provided that you know its appID, you can send messages to any running application that is ready to accept them. Basic application control can be achieved via a set of pre-defined messages that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports custom messages, thus allowing for much more sophisticated control or information exchange. Furthermore, apart from this “invisible”, abstract communication taking place between application ports, you can use the library to provide real and visible information in the form of pop-up notification messages. At the same time, the extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement.

Before we get any further with Application Library messages, it must be made clear that they should not be confused with the similarly-named AppMessages. The latter are a completely different breed, governed by the Workbench Library. They represent a specific way of communication between the Workbench desktop environment and running applications. It’s strictly one-way communication because Workbench can send AppMessages to applications but applications cannot send AppMessages to Workbench. (If you want to learn more about the use of AppMessages, consult the Workbench Library section of the AmigaOS documentation wiki).

Pop-up notifications (Ringhio messages)

As from the introduction of the Ringhio server in AmigaOS 4.1 Update 1, registered applications can inform the user via notifications displayed in a small pop-up box. These are sometimes called Ringhio messages because the server provides means through which the messages are communicated visually (in other words, Ringhio handles the actual display of messages sent by the Application Library).

Ringhio Notification sample

The pop-ups function similarly to requesters in that they show a text message; but unlike requesters, Ringhio messages do not require user interaction or acknowledgement. They just show up briefly and disappear – which makes them great for informing about less significant, matter-of-fact events such as that a certain task has been completed. This is especially helpful if the application is hidden or runs on a different screen, as the user is kept informed about something that is currently beyond his/her visual control.

Of all the types of Application Library messages, pop-up notifications are surely the easiest to program. They don’t require any setup or event handling; all it takes is a single function call:

uint32 result;
 
result = IApplication->Notify(appID,
               APPNOTIFY_Title, “Important Message”,
               APPNOTIFY_Text, “Your socks need changing!,
               APPNOTIFY_PubScreenName, "FRONT",
               TAG_DONE);

The first parameter is your application’s appID received from the registration function. The APPNOTIFY_Title tag specifies a short heading for the pop-up box while APPNOTIFY_Text contains the actual message text. Certain limits to text length apply to ensure that the pop-up remains easy to read: 64 characters for the heading (title) and 128 characters for the text. This particular message will display on the frontmost public screen (as specified in the APPNOTIFY_PubScreenName tag), which may as well be the right setting for most applications. You can of course provide any other public screen name – or you can call Notify() without this tag and let the library use the default, which is the Workbench screen.

The result value shows whether the call was successful; 0 means that an error has occurred and Ringhio failed to display the pop-up. Depending on the significance of the message, you may want to react upon the failure and inform the user through other means of communication, such as a requester.

The look and position of the pop-up box is configurable from the Notifications editor located in the Prefs drawer on your system partition. This is Ringhio’s preferences editor: as we have explained above, it is Ringhio that is responsible for the actual display of notification messages. The pop-up box can also show a small custom image (like the one in the picture above) to make the message more easily identifiable as related to a particular application. The maximum size of the image is 32x32 pixels. It can be any format, provided that there is a datatype for it installed in the system. Being application-specific, these images logically cannot be configured system-wide through the Notifications editor; instead, they are specified as part of the Notify() call. Note that a full path to the image file must be provided:

IApplication->Notify(appID,
               APPNOTIFY_Title, “Important Message”,
               APPNOTIFY_Text, “Your socks need changing!,
               APPNOTIFY_PubScreenName, "FRONT",
               APPNOTIFY_ImageFile, “PROGDIR:Images/BestApp.jpg,
               APPNOTIFY_BackMsg, "All right, ma!",
               TAG_DONE);

But what is this APPNOTIFY_BackMsg thing? It has been mentioned above that notification messages do not require user interaction: they display some text and go away. Nevertheless, Ringhio can send the application a custom message (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. This custom message is sent to the same event stream as other Application Library messages – see the following section (sorry, not written yet!) for information about how to process it within the program.

Whether receiving a “back message” would be useful for a particular application (and whether it would make sense to react upon it) is decided by the programmer. However, remember that Ringhio messages are not requesters, and double-clicking on the message box does not really equal pressing a requester button. Therefore, receiving the message could possibly be taken as user acknowledgement of what Ringio has said, but never as a selection of an option! Do not use the back-message feature to request input: the text of the message should be a statement, not a question or an offer of choice. The double click effectively means “I understand”; it doesn't mean “Yes”.


(to be continued)


PrefsObjects

Introduction

Before AmigaOS 4.x no real standard existed for storing preferences: some developers used icon tooltypes, some used proprietary formats, text or binary. The Application Library provides a format that is human-readable and easily editable in a simple text editor; that is comprehensive enough to cover even very complex settings structures; and that is fully controllable via the library, without the need to laboriously implement data parsing and verification.

All methods to handle the preferences are grouped into a separate specific interface of the Application Library: "PrefsObjects". Prior the use of any of these methods and even if he's already using other methods of the Application Library; the programmer is required to properly obtain this interface, see Library opening chores to know how.

Note
Obtaining the other interface is not required to use methods from the interface PrefsObjects, however in practice the programmer will use both because he will surely want to benefit from some of its features too.

PrefsObjects model is highly object inspired, any manipulated type has its own construction function, and a set of associated methods. One should probably be familiar with BOOPSI philosophy before reading the following section. Similarly PrefsObjects makes high usage of Tag Items so it's recommended to be comfortable with this technique too.

Supported Data Types

The PrefsObjects system supports the most basic data types, that will be sufficient to describe any more complex types (i.e. structured data).

PrefsObjects supported data types
Common type name PrefsObjects name
boolean PrefsNumber
long integer (32 bits) PrefsNumber
double PrefsNumber
string PrefsString
date/time PrefsDate
other (binary) PrefsBinary

In addition to these basic data types, PrefsObjects also supports two containers: Arrays (PrefsArray) and Dictionaries (PrefsDictionary). The main difference between the two being that in arrays elements are accessible by their index in the array, while in a dictionary they are accessible by a key string.