Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Application Library"
(80 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
− | {{WIP}} |
||
− | |||
== Introduction == |
== 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. |
||
+ | 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 [[Exec_Tasks|tasks]] or [[AmigaDOS_Data_Structures#Process_Data_Structures|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 [[UI_Style_Guide_Glossary#GUI|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 [[#Registering the application|application registration]], during which the application receives a unique [[#Application identifiers|identifier]] and is added to a public list among other applications. Once registered, the application can make use of the library’s many features: |
||
+ | |||
+ | 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 [[#Registering the Application|application registration]], during which the application receives a [[#Application Identifiers|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|control messages]] (commands) such as those telling an application to quit, iconify or bring its window to front. But it also allows [[#Custom Messages|custom messages]] designed for an application’s particular needs; in this respect the Application Library provides an alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]] control. |
||
+ | * It can turn into a “watchdog” and get notified when other applications register. |
||
− | * 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 |
+ | * 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 notify the user about, for example, completed tasks via automatic [[#Pop-up Notifications (Ringhio Messages)|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 easily create and manage lists of recently-used documents. |
||
− | * It can register as a |
+ | * 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 show its icon or display the current program state in taskbar-like applications, such as AmiDock. |
||
Line 20: | Line 21: | ||
* 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”. |
* 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”. |
||
− | == |
+ | === Frequently Asked Questions === |
+ | |||
+ | {| class="wikitable" |
||
+ | |- |
||
+ | |''Q: Should my programs register with the Application Library?'' |
||
+ | |A: Yes, that would make a lot of sense. Apart from access to the handy features mentioned above, the implementation of certain library features may improve the behaviour of your application within the AmigaOS ecosystem. |
||
+ | |- |
||
+ | |''Q: After registering with the library, will my application become controlled by the OS or by other applications?'' |
||
+ | |A: No. The registration process alone does not turn on any features. There is a recommendation to allow a [[#Minimum Application Library Support|minimum degree of control]], but in the end it is the programmer who decides what functionality offered by the library will be provided and supported in his/her application. That also includes the scope of external control: if you don't want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming [[#Control Messages|control messages]]. |
||
+ | |- |
||
+ | |''Q: Doesn't the Application Library duplicate functionality already present in commodities?'' |
||
+ | |A: No. The only similarity between the two is that programs register with some system library, which then acts as a control centre. [[Commodities_Exchange_Library|Commodities]] provide a way to install custom input handlers into the [[Input_Device|Input Device]]'s event stream. The Application Library's field and scope of operation is completely different (and much wider). |
||
+ | |- |
||
+ | |''Q: Why is this a library? Wouldn't it be wiser to implement it as a class, like MUI does it?'' |
||
+ | |A: This would tie the functionality to the object-oriented (BOOPSI) API – applications designed using [[GUI_Programming#The_Two_Frameworks|other APIs or toolkits]] would not be able to use it. |
||
+ | |- |
||
+ | |''Q: Can a program be registered both as an application and as a commodity?'' |
||
+ | |A: Yes, that's possible – although few programs would probably benefit from that. Exceptions include application managers and taskbars (e.g. AmiDock), which may need to control other applications and, at the same time, behave like a commodity. |
||
+ | |- |
||
+ | |''Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system?'' |
||
+ | |A: Hardly any. The [[PrefsObjects]] .xml preference files are, typically, only read when the application starts and written at user request, so there is minimum overhead involved. Accessing the prefs file during program runtime doesn't put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory. |
||
+ | |} |
||
+ | |||
+ | === Minimum Application Library Support === |
||
+ | |||
+ | In the foreseeable future, AmigaOS will feature an application manager to control running applications. (A third-party solution called [http://wiki.amiga.org/index.php?title=Exchanger Exchanger] is already available.) The idea is to provide a single, universal control point for both commodities and applications. In order to allow easy, consistent and predictable program control, developers are encouraged to [[#Registering the Application|register their applications]] and implement the following suggested minimum Application Library support: |
||
+ | |||
+ | * Provide a short [[#Description|description]] for a better identification of the application. |
||
+ | * Tell the OS whether your application supports iconification, that is, hiding/showing its GUI. If iconification is supported: |
||
+ | ** set the REGAPP_HasIconifyFeature registration tag to TRUE; |
||
+ | ** make the application respond to the [[#Control Messages|APPLIBMT_Hide and APPLIBMT_Unhide]] control messages; |
||
+ | ** update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies. |
||
+ | * Allow the application to be shut down externally in a safe and graceful manner in reaction to the [[#Control Messages|APPLIBMT_Quit]] message. |
||
+ | |||
+ | Not implementing this suggested minimum means that the application manager will be unable to control your application properly and consistently (some of the manager's control buttons or menu items may appear ineffective). This could cause confusion on the part of the users and degrade their AmigaOS experience. |
||
+ | |||
+ | == Library Opening Chores == |
||
+ | |||
+ | {{Note|title=Note|text=Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2. This is due to changes in the Application Library API which had broken standard tag support until version 2 interfaces were introduced.}} |
||
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. |
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. |
||
Line 31: | Line 70: | ||
if ( (ApplicationBase = IExec->OpenLibrary("application.library", 52)) ) |
if ( (ApplicationBase = IExec->OpenLibrary("application.library", 52)) ) |
||
{ |
{ |
||
− | IApplication = ( |
+ | IApplication = (struct ApplicationIFace *) IExec->GetInterface(ApplicationBase, |
+ | "application", 2, NULL); |
||
− | IPrefsObjects = (APTR)IExec->GetInterface(ApplicationBase, "prefsobjects", 1, NULL); |
||
+ | IPrefsObjects = (struct PrefsObjectsIFace *) IExec->GetInterface(ApplicationBase, |
||
+ | "prefsobjects", 2, NULL); |
||
} |
} |
||
Line 41: | Line 82: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
− | Note that there is no interface called “main” like older, single-interface libraries have. |
+ | Note that there is no interface called “main” like older, single-interface libraries have. The number in the GetInterface() call above refers to the version of the interface. Library interfaces are not backwards or forwards compatible so they must be specified precisely. '''Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2.''' This is due to certain changes in the Application Library API. |
When your application has run its course, don’t forget to clean up and close both the library and its interface(s): |
When your application has run its course, don’t forget to clean up and close both the library and its interface(s): |
||
Line 51: | Line 92: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
− | == Registering the |
+ | == 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 |
+ | 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 properties 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. |
+ | *hint* Avoid using the _ underscore character in the registered name. These may cause "charsetconvert" errors when the system boots up later. |
||
− | === Application identifiers === |
||
+ | === 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. |
||
+ | A successfully registered application receives a numeric identifier, which in this documentation will be referred to as ''appID''. The identifier is public: any registered application can obtain another application’s appID (see [[#Finding Applications|Finding Applications]] below) and use it to communicate with the respective application. AppIDs are unique integer numbers: the library generates them incrementally on a per-registration basis. They are never assigned 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. |
||
+ | 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 (and the same application will have a different appID each time), 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: |
||
− | === Registration === |
||
+ | * the application name; |
||
− | 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: |
||
+ | * an instance count (if more than one instance of the same application is started); |
||
+ | * a URL identifier (for example, the domain name of the application’s homepage – optional). |
||
+ | |||
+ | The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file. |
||
+ | |||
+ | === Registration Functions === |
||
+ | |||
+ | Now let’s say we have a program, a media player called Audio Monster created by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration (and unregistration) might look like this: |
||
<syntaxhighlight> |
<syntaxhighlight> |
||
uint32 appID; |
uint32 appID; |
||
− | appID = IApplication->RegisterApplication( |
+ | appID = IApplication->RegisterApplication("AudioMonster", |
− | REGAPP_URLIdentifier, |
+ | REGAPP_URLIdentifier, "supercoders.com", |
− | REGAPP_Description, |
+ | REGAPP_Description, "A media player", |
− | + | TAG_END); |
|
if (!appID) |
if (!appID) |
||
Line 77: | Line 126: | ||
/* report registration error and quit */ |
/* report registration error and quit */ |
||
} |
} |
||
+ | |||
+ | /* |
||
+ | do whatever your program does |
||
+ | */ |
||
+ | |||
+ | IApplication->UnregisterApplication(appID, NULL); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
− | Note that we’ve had to alter the application name to |
+ | Note that we’ve had to alter the application name to “AudioMonster”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme (outlined in the previous section) the application will now become registered under the name “AudioMonster.supercoders.com”. (Should a previous instance of Audio Monster be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “AudioMonster_1.supercoders.com”, the third will register as “AudioMonster_2.supercoders.com”, and so on.) |
+ | |||
+ | Also note that the URL identifier is just the domain name, i.e. without the “www.” part. The identifier is only used to distinguish between applications, not to access their homepages. The domain name is, therefore, sufficient; the resulting name identifier needn’t be long and quirky. |
||
+ | |||
+ | After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available. |
||
+ | |||
+ | == Application Attributes == |
||
+ | |||
+ | An application is described by a set of ''attributes''. There are attributes that describe the application's properties or feature set, indicate its current state, or determine its behaviour. Some attributes are only specified at registration time and cannot be altered once they are set, while others can be changed freely (as long as the application remains registered, of course). The general recommendation is to specify at registration time all properties that are not going to change during program lifetime: i.e. define all "static" application features in one place. |
||
+ | === Description === |
||
− | 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. |
||
+ | The REGAPP_Description tag, used in the registration example code above, tells the system what the application is all about. Although the description is an optional attribute, it is recommended to always provide it, as it will allow application manager or taskbar programs to provide meaningful information to the user. Keep the description short, matter-of-fact and serious. |
||
+ | The application description cannot be changed after registration. |
||
− | === Unique applications === |
||
+ | === Uniqueness === |
||
− | 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. |
||
+ | 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 sometimes can be good reasons to do so. For example, the developer of the Audio Monster player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several media files 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 |
+ | The following function call will register Audio Monster as a unique application: |
<syntaxhighlight> |
<syntaxhighlight> |
||
− | appID = IApplication->RegisterApplication( |
+ | appID = IApplication->RegisterApplication("AudioMonster", |
− | REGAPP_URLIdentifier, |
+ | REGAPP_URLIdentifier, "supercoders.com", |
− | REGAPP_Description, |
+ | REGAPP_Description, "A media player", |
REGAPP_UniqueApplication, TRUE, |
REGAPP_UniqueApplication, TRUE, |
||
− | + | TAG_END); |
|
</syntaxhighlight> |
</syntaxhighlight> |
||
− | 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 |
+ | 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 Messages|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 the application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window. |
+ | Quite logically, uniqueness is an attribute that cannot be changed after registration. |
||
− | == Finding applications == |
||
+ | === Feature Set / Control Scope === |
||
− | 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. |
||
+ | Different applications can implement different control features, and can be designed for a different scope of external control. For example, a text editor may respond to a [[#Control Messages|control message]] (command) that tells it to create a new, blank, unnamed document, whereas in a media player such a command would be best ignored. Similarly, a simple tool with no configuration capability would want to stay clear of messages that control program settings. It is always good manners to inform the system about the scope of control your application supports, as other applications may query about (and rely on) this information when sending control messages. Always set the following boolean tags to TRUE in the RegisterApplication() call if your application implements support for the respective feature (the default value is FALSE): |
||
+ | {| class="wikitable" |
||
− | “Finding an application” basically means obtaining its appID from the library. To do this you need to know at least one of the following: |
||
+ | !Tag |
||
+ | !Description |
||
+ | !Implementation notes |
||
+ | |- |
||
+ | |REGAPP_HasIconifyFeature |
||
+ | |The application supports iconification and uniconification. |
||
+ | |Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages. |
||
+ | |- |
||
+ | |REGAPP_HasPrefsWindow |
||
+ | |The application has a dedicated Preferences (Settings) window. |
||
+ | |Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message. |
||
+ | |- |
||
+ | |REGAPP_CanCreateNewDocs |
||
+ | |The application can create new documents (projects). |
||
+ | |Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message. |
||
+ | |- |
||
+ | |REGAPP_CanPrintDocs |
||
+ | |The application can print documents. |
||
+ | |Set to TRUE if your application responds to the APPLIBMT_PrintDoc message. |
||
+ | |} |
||
+ | |||
+ | These four attributes can be changed after registration. |
||
+ | |||
+ | === Receiving Notifications === |
||
+ | |||
+ | The Application Library can send certain notification messages that may be of interest to special-purpose programs like application managers, taskbars or screen blankers. If you are developing such a program, you may want to register for the following notifications: |
||
+ | |||
+ | {| class="wikitable" |
||
+ | !Tag |
||
+ | !Description |
||
+ | !Implementation notes |
||
+ | |- |
||
+ | |REGAPP_AppNotifications |
||
+ | |Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list. |
||
+ | |Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs. |
||
+ | |- |
||
+ | |REGAPP_BlankerNotifications |
||
+ | |Receive notifications concerning blanker activity. |
||
+ | |Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers. |
||
+ | |} |
||
+ | |||
+ | Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream. |
||
+ | |||
+ | These two attributes can be changed after registration. |
||
+ | |||
+ | === Changing Application Attributes === |
||
+ | |||
+ | Certain attributes describe "dynamic" application properties and, as such, can be set or changed after registration. Some of these are ''state attributes'' that need to be updated each time your program changes state (shows/hides its GUI, or enters the game mode; see below in the table). Some are not really attributes but, rather, commands that invoke an application-related action. |
||
+ | |||
+ | {| class="wikitable" |
||
+ | !Tag |
||
+ | !Description |
||
+ | !Implementation notes |
||
+ | |- |
||
+ | |APPATTR_AllowsBlanker |
||
+ | |Same as REGAPP_AllowsBlanker above. |
||
+ | |You may want to be able to enable/disable blanking dynamically during application runtime – this what the APPATTR_AllowsBlanker tag is for. For example, a presentation program may allow blankers while the presentation is being worked on, and disable them when the presentation is started. |
||
+ | |- |
||
+ | |APPATTR_AppOpenedDocument |
||
+ | |Adds a new entry to the application's Last Used Documents list. |
||
+ | |Set this tag each time your application has successfully opened a named document or project. The parameter to this tag is a pointer to the name string (i.e. a STRPTR). |
||
+ | |- |
||
+ | |APPATTR_AppNotifications<br />APPATTR_BlankerNotifications |
||
+ | |Same as the two corresponding [[#Receiving Notifications|registration tags]] above. |
||
+ | | |
||
+ | |- |
||
+ | |APPATTR_CanCreateNewDocs<br />APPATTR_CanPrintDocs<br />APPATTR_HasIconifyFeature<br />APPATTR_HasPrefsWindow |
||
+ | |Same as the four corresponding [[#Feature Set / Control Scope|registration tags]] above. |
||
+ | |Prefer setting these properties at registration time. |
||
+ | |- |
||
+ | |APPATTR_ClearLastUsedDocs |
||
+ | |Clears the application's list of last used documents. |
||
+ | |Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.) |
||
+ | |- |
||
+ | |APPATTR_FlushPrefs |
||
+ | | |
||
+ | | |
||
+ | |- |
||
+ | |APPATTR_Hidden |
||
+ | |A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not. |
||
+ | |Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information. |
||
+ | |- |
||
+ | |APPATTR_IconType |
||
+ | |Changes the application icon type. |
||
+ | | |
||
+ | |- |
||
+ | |APPATTR_MainPrefsDict |
||
+ | |Allows changing the application's Prefs dictionary. |
||
+ | | |
||
+ | |- |
||
+ | |APPATTR_NeedsGameMode |
||
+ | |A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode. |
||
+ | |By the "game mode" we understand a mode in which an application doesn't want to be "disturbed" by other applications. It normally assumes full screen operation, and possibly taking over the audio system. Games or presentation programs are examples of applications that may want to implement the game mode, in which other programs are simply asked to “keep quiet”: not try to play sounds, open windows, requesters, etc. This feature assumes discipline and cooperation on the part of other applications; please use it moderately and only enter the game mode when it is really needed. Also note that setting APPATTR_NeedsGameMode to TRUE does not guarantee that other applications will comply. |
||
+ | |- |
||
+ | |APPATTR_SavePrefs |
||
+ | |Same as REGAPP_SavePrefs above. |
||
+ | | |
||
+ | |} |
||
+ | |||
+ | The function to set or change application attributes is SetApplicationAttrs(). It is very similar to SetAttrs() used in Intuition programming, only the first parameter is not a pointer to a BOOPSI object but an application identifier. The appID is followed by a tag list, so several attributes can be set at a time. The following example call will inform the system that the application has iconified (or otherwise hidden its GUI) and that the screen blanker can now come to front freely (assuming that the blanker was forbidden before for some reason). The result value indicates whether the call was successful or not: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | BOOL result; |
||
+ | |||
+ | result = IApplication->SetApplicationAttrs(appID, |
||
+ | APPATTR_Hidden, TRUE, |
||
+ | APPATTR_AllowsBlanker, TRUE, |
||
+ | TAG_END); |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | == Finding Applications == |
||
+ | |||
+ | The Application Library maintains a public list of all registered applications. Certain special-purpose programs – ''application managers'' – will read this list (also keeping track of all subsequent registrations and unregistrations) and offer some degree of control: display information about applications and/or send commands telling them to do something. Quite naturally, most programs will not (nor are they supposed to!) act as managers but a need to talk to another application may arise. How do you find it, then? |
||
+ | |||
+ | Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library's messaging system. 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, 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 |
+ | * 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|Application identifiers]] above; |
− | * the pathname pointing to the program file on disk, e.g. “Work:Utils/ |
+ | * the pathname pointing to the program file on disk, e.g. “Work:Utils/AudioMonster”. |
− | Based on this information, the respective piece of code that will find our |
+ | Based on this information, the respective piece of code that will find our application in the system might look like this: |
<syntaxhighlight> |
<syntaxhighlight> |
||
Line 115: | Line 296: | ||
/* if you only know the application name */ |
/* if you only know the application name */ |
||
− | appID = IApplication->FindApplication(FINDAPP_Name, |
+ | appID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", |
+ | TAG_END); |
||
/* if you know the application name identifier */ |
/* if you know the application name identifier */ |
||
− | appID = IApplication->FindApplication(FINDAPP_AppIdentifier, |
+ | appID = IApplication->FindApplication(FINDAPP_AppIdentifier, "AudioMonster.supercoders.com", |
+ | TAG_END); |
||
/* if you specifically want to talk to the second running instance */ |
/* if you specifically want to talk to the second running instance */ |
||
− | appID = IApplication->FindApplication(FINDAPP_AppIdentifier, |
+ | appID = IApplication->FindApplication(FINDAPP_AppIdentifier, "AudioMonster_1.supercoders.com", |
+ | TAG_END); |
||
/* if you know the pathname to the program file */ |
/* if you know the pathname to the program file */ |
||
− | appID = IApplication->FindApplication(FINDAPP_FileName, |
+ | appID = IApplication->FindApplication(FINDAPP_FileName, "Work:Utils/AudioMonster", |
+ | TAG_END); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
Line 131: | Line 316: | ||
== Messaging == |
== Messaging == |
||
− | Modern software applications often |
+ | Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported on the low level (through Exec Library’s [[Exec Messages and Ports|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. You can even send a message to all applications at once! Basic application control can be achieved via a set of [[#Control Messages|predefined messages]] that correspond to common program commands and functions (such as New, Open, Print, Iconify or Quit). But the library also supports [[#Custom Messages|custom messages]], thus allowing for more sophisticated control or information exchange. The extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement (it has already been mentioned in the [[#Frequently Asked Questions|Frequently Asked Questions]] section above that registration alone does not magically turn on any features). |
||
+ | |||
+ | 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. However, as these are different and not really within the scope of the Application Library messaging framework, they are dealt with in a [[#Pop-up Notifications (Ringhio Messages)|separate section]] of this document. |
||
+ | |||
+ | {{Note|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|Workbench Library]] section of the AmigaOS documentation wiki). |
||
+ | }} |
||
+ | |||
+ | === Data Structures === |
||
+ | |||
+ | Rather than introduce yet another system for communication, the Application Library builds upon the existing [[Exec_Messages_and_Ports|Exec messaging framework]]. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of [[Exec_Messages_and_Ports#Getting_a_Message|GetMsg()]] and [[Exec_Messages_and_Ports#Replying|ReplyMsg()]] takes place when processing incoming Application Library messages (see section [[#Message Handling|Message Handling]] below) – although the library implements its own method for [[#Sending Messages|sending messages]]. |
||
+ | |||
+ | The following three structures are defined for carrying Application Library message data: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | struct ApplicationMsg |
||
+ | { |
||
+ | struct Message msg; |
||
+ | uint32 senderAppID; // the appID of the sender application |
||
+ | uint32 type; // identifies the type of message |
||
+ | }; |
||
+ | |||
+ | struct ApplicationOpenPrintDocMsg |
||
+ | { |
||
+ | struct ApplicationMsg almsg; |
||
+ | STRPTR fileName; |
||
+ | }; |
||
+ | |||
+ | struct ApplicationCustomMsg |
||
+ | { |
||
+ | struct ApplicationMsg almsg; |
||
+ | STRPTR customMsg; |
||
+ | }; |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | As you can see, structure ''ApplicationMsg'' contains a standard ''[[Exec_Messages_and_Ports#Messages|struct Message]]'', the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ''ApplicationOpenPrintDocMsg'' is used by certain [[#Control Messages|control messages]] that require a pointer to a filename; the ''ApplicationCustomMsg'' structure is used for [[#Custom Messages|custom messages]]. |
||
+ | |||
+ | Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to [[#Sending Messages|send a message]] to an application you specify it in the “type” field. |
||
+ | |||
+ | === Control Messages === |
||
+ | |||
+ | The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided: |
||
+ | |||
+ | {| class="wikitable" |
||
+ | !Message name |
||
+ | !Description |
||
+ | !Implementation notes |
||
+ | |- |
||
+ | |APPLIBMT_Quit |
||
+ | |The application is asked to shut down itself and quit. |
||
+ | |Basically, upon receiving this message you should react as if your main program window received the WMHI_CLOSEWINDOW event. When implementing support for APPLIBMT_Quit, the programmer is required to design the program exit sequence in such a way that no unexpected data loss can occur (for example, by displaying a confirmation requester that allows the user to save work or cancel the quit command). |
||
+ | |- |
||
+ | |APPLIBMT_ForceQuit |
||
+ | |Same as before but this time the application shall quit immediately, without asking for saving documents etc. |
||
+ | |Providing this command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely. So implement APPLIBMT_ForceQuit with care, or don't implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications. |
||
+ | |- |
||
+ | |APPLIBMT_Hide |
||
+ | |The application shall hide its interface and iconify on the Workbench screen. |
||
+ | |rowspan="2"|An application that supports APPLIBMT_Hide and APPLIBMT_Unhide is expected to register itself with REGAPP_HasIconifyFeature set to TRUE. Basically, upon receiving this message you should react as if your main program window received the WMHI_ICONIFY / WMHI_UNICONIFY event. |
||
+ | |- |
||
+ | |APPLIBMT_Unhide |
||
+ | |The application shall come back from the iconified (hidden) state. |
||
+ | |- |
||
+ | |APPLIBMT_ToFront |
||
+ | |The application window shall come to front. |
||
+ | |Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. See the end of the [[#Message Handling|Message Handling]] section for a note on APPLIBMT_ToFront. |
||
+ | |- |
||
+ | |APPLIBMT_OpenPrefs |
||
+ | |The application shall open its preferences window. |
||
+ | |Only implement if your application has a dedicated Preferences (Settings) window. An application that supports APPLIBMT_OpenPrefs is expected to register itself with the REGAPP_HasPrefsWindow set to TRUE. |
||
+ | |- |
||
+ | |APPLIBMT_ReloadPrefs |
||
+ | |The application shall reload its preferences. |
||
+ | |Whatever this command is useful for. |
||
+ | |- |
||
+ | |APPLIBMT_NewBlankDoc |
||
+ | |The application shall open a new, blank document or project. |
||
+ | |Applications such as web browsers can, too, make use of this command to open new program windows. An application that supports APPLIBMT_NewBlankDoc is expected to register itself with the REGAPP_CanCreateNewDocs set to TRUE. |
||
+ | |- |
||
+ | |APPLIBMT_OpenDoc |
||
+ | |The application shall try to open a specific document or project. |
||
+ | |The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data Structures|Data Structures]] above). |
||
+ | |- |
||
+ | |APPLIBMT_PrintDoc |
||
+ | |The application shall try to print a specific document. |
||
+ | |The name of the document is passed as part of the message data structure (''struct ApplicationOpenPrintDocMsg'': see [[#Data Structures|Data Structures]] above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE. |
||
+ | |} |
||
+ | |||
+ | An application will only react to messages that are implemented and supported. If you [[#Sending Messages|send a message]] to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are encouraged to provide the [[#Minimum Application Library Support|minimum suggested support]] as outlined above. Implement the rest according to your application’s features and needs, and to the highest standards of security. |
||
+ | |||
+ | === Custom Messages === |
||
+ | |||
+ | In contrast to a [[#Control Messages|control message]] (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to [[UI_Style_Guide_Glossary#ARexx|ARexx]], while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to [[#Finding Applications|find]] the receiver application and [[#Sending Messages|send a message]] to it. |
||
+ | |||
+ | The pointer to the message text string is contained in a special [[#Data Structures|data structure]], ''struct ApplicationCustomMsg''. |
||
+ | |||
+ | === Special Messages === |
||
+ | |||
+ | There are also some special messages that an application can receive from the library or another running application: |
||
+ | |||
+ | {| class="wikitable" |
||
+ | ! Message name !! Description !! Implementation notes |
||
+ | |- |
||
+ | | APPLIBMT_Unique || If an application is registered as [[#Uniqueness|unique]] and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. || All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message. |
||
+ | |- |
||
+ | | APPLIBMT_GameModeEntered || Sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. || The message is sent around whenever an application sets its APPATTR_NeedsGameMode attribute to TRUE. |
||
+ | |- |
||
+ | | APPLIBMT_GameModeLeft || Informs all running application that the sender has left the game mode. || The message is sent around whenever an application sets its APPATTR_NeedsGameMode attribute to FALSE. |
||
+ | |} |
||
+ | |||
+ | === Message Handling === |
||
+ | |||
+ | You already know that messaging in AmigaOS takes place between [[Exec_Messages_and_Ports#Message_Ports|message ports]]. Being a superset of standard Exec messages, Application Library notifications (be they predefined [[#Control Messages|control messages]], [[#Custom Messages|custom messages]] or [[#Special Messages|special messages]]) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the ''notification port''. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s [[Intuition_Input_and_Output_Methods#Receiving_Input_Events_from_Intuition|IDCMP messages]]). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications. |
||
+ | |||
+ | A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands. |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | void event_loop(uint32 appID) |
||
+ | { |
||
+ | struct MsgPort *notificationPort = NULL; |
||
+ | struct ApplicationMsg *appLibMsg = NULL; |
||
+ | struct ApplicationCustomMsg *customMsg = NULL; |
||
+ | uint32 appLibSignal = 0, sigGot = 0; |
||
+ | BOOL done = FALSE; |
||
+ | |||
+ | /* Obtain pointer to Application Library's notification port and set the signal bit. */ |
||
+ | IApplication->GetApplicationAttrs(appID, APPATTR_Port, ¬ificationPort, TAG_END); |
||
+ | appLibSignal = (1L << notificationPort->mp_SigBit); |
||
+ | |||
+ | /* Go into the event loop. */ |
||
+ | while (!done) |
||
+ | { |
||
+ | /* Wait for a signal to arrive. */ |
||
+ | sigGot = IExec->Wait(appLibSignal); |
||
+ | |||
+ | /* Process all Application Library messages. */ |
||
+ | if ( sigGot & appLibSignal ) |
||
+ | { |
||
+ | /* Obtain pointer to the message. */ |
||
+ | while ( (appLibMsg = (struct ApplicationMsg *) IExec->GetMsg(notificationPort)) ) |
||
+ | { |
||
+ | /* Identify the type of message. */ |
||
+ | switch (appLibMsg->type) |
||
+ | { |
||
+ | case APPLIBMT_Quit: |
||
+ | case APPLIBMT_ForceQuit: |
||
+ | done = TRUE; |
||
+ | break; |
||
+ | |||
+ | case APPLIBMT_ToFront: |
||
+ | /* Make the program window come to front. */ |
||
+ | break; |
||
+ | |||
+ | case APPLIBMT_Hide: |
||
+ | /* Iconify the program. */ |
||
+ | break; |
||
+ | |||
+ | case APPLIBMT_Unhide: |
||
+ | /* Uniconify the program. */ |
||
+ | break; |
||
+ | |||
+ | /* Process the custom message as you like. |
||
+ | Here we just use printf() to output the message text. */ |
||
+ | case APPLIBMT_CustomMsg: |
||
+ | customMsg = (struct ApplicationCustomMsg *) appLibMsg; |
||
+ | printf("The message text is: %s\n", customMsg-> customMsg); |
||
+ | break; |
||
+ | |||
+ | /* |
||
+ | Process any other Application Library messages. |
||
+ | */ |
||
+ | |||
+ | } |
||
+ | /* Return the processed message to the sender so that it can be freed. */ |
||
+ | IExec->ReplyMsg( (struct Message *) appLibMsg); |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | Like all [[Exec_Messages_and_Ports#Messages|Exec messages]] obtained via the GetMsg() function, Application Library messages must be [[Exec_Messages_and_Ports#Replying|replied to]], i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own. |
||
+ | |||
+ | Also remember that the notifications are addressed to an abstract ''application'' – this makes them rather different from IDCMP messages, which are always sent to a particular ''window'' (Intuition doesn't know what an application is). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, this following piece of code |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | /* Don't do this!!! */ |
||
+ | case APPLIBMT_ToFront: |
||
+ | IIntuition->ScreenToFront(win->WScreen); |
||
+ | IIntuition->WindowToFront(win); |
||
+ | IIntuition->ActivateWindow(win); |
||
+ | break; |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | is broken because if an APPLIBMT_ToFront command were sent to your iconified application, there would be no window to refer to. You need to check for iconified state first, uniconify the window if needed, and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make! |
||
+ | |||
+ | === Sending Messages === |
||
+ | |||
+ | While incoming notifications are [[#Message Handling|processed]] pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application: |
||
+ | |||
+ | # Prepare the respective [[#Data Structures|data structure]]: specify the type of message and supply the sender's [[#Application Identifiers|identifier]] (appID). |
||
+ | # [[#Finding Applications|Find the receiver]] application. |
||
+ | # Send a message to it via the SendApplicationMsg() function. |
||
+ | |||
+ | For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | struct ApplicationMsg appMsg; // message data structure |
||
+ | uint32 audioMonsterID; // identifier of the receiver |
||
+ | |||
+ | /* Step 1: Prepare the message data structure. */ |
||
+ | appMsg.senderAppID = appID; // identifier of the sender |
||
+ | appMsg.type = APPLIBMT_Quit; // type of message |
||
+ | |||
+ | /* Step 2: Find the receiver application. */ |
||
+ | if ( (audioMonsterID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", TAG_END)) ) |
||
+ | { |
||
+ | /* Step 3: Send the message. */ |
||
+ | IApplication->SendApplicationMsg(appID, |
||
+ | audioMonsterID, |
||
+ | &appMsg, |
||
+ | APPLIBMT_Quit); |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0. |
||
+ | |||
+ | Sending [[#Custom Messages|custom messages]] is done in a similar fashion, the only difference is that you must use a dedicated [[#Data Structures|data structure]] instead of the generic ''struct ApplicationMsg''. As our Audio Monster application is a media player, it may as well have defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell Audio Monster to start playing, it will need to send the custom message like this: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | struct ApplicationCustomMsg customMsg; // custom message data structure |
||
+ | uint32 audioMonsterID; // identifier of the receiver |
||
+ | |||
+ | /* Prepare the message data structure. */ |
||
+ | customMsg.almsg.senderAppID = appID; // identifier of the sender |
||
+ | customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message |
||
+ | customMsg.customMsg = "Start playback"; // message text |
||
+ | |||
+ | /* Find the receiver application. */ |
||
+ | if ( (audioMonsterID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", TAG_END)) ) |
||
+ | { |
||
+ | /* Send the message. */ |
||
+ | IApplication->SendApplicationMsg(appID, |
||
+ | audioMonsterID, |
||
+ | (struct ApplicationMsg *) &customMsg, |
||
+ | APPLIBMT_CustomMsg); |
||
+ | } |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | == 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). [[File:RinghioNotification.png|frame|Ringhio notification example]] The pop-ups function similarly to [[Intuition Requesters|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: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | uint32 result; |
||
+ | |||
+ | result = IApplication->Notify(appID, |
||
+ | APPNOTIFY_Title, "Important Message", |
||
+ | APPNOTIFY_Text, "Your socks need changing!", |
||
+ | APPNOTIFY_PubScreenName, "FRONT", |
||
+ | TAG_END); |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | The first parameter is your application’s appID received from the [[#Registration Functions|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 (160 characters as of Ringhio 53.23). 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 [[Public_Screen_Type#The_Default_Public_Screen_and_Workbench|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: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | IApplication->Notify(appID, |
||
+ | APPNOTIFY_Title, "Important Message", |
||
+ | APPNOTIFY_Text, "Your socks need changing!", |
||
+ | APPNOTIFY_PubScreenName, "FRONT", |
||
+ | APPNOTIFY_ImageFile, "PROGDIR:Images/AudioMonster.jpg", |
||
+ | APPNOTIFY_BackMsg, "All right, ma!", |
||
+ | TAG_END); |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | 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 Messages|custom message]] (called “back message” – hence the name of the tag) if the user has double-clicked on the message box. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, [[#Custom Messages|custom messages]] are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the [[#Message Handling|Message Handling]] section for how to go about it. |
||
+ | |||
+ | 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. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note 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 be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”. |
||
+ | |||
+ | If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open Audio Monster’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click. |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | IApplication->Notify(appID, |
||
+ | APPNOTIFY_Title, "Important Message", |
||
+ | APPNOTIFY_Text, "Your socks need changing!", |
||
+ | APPNOTIFY_PubScreenName, "FRONT", |
||
+ | APPNOTIFY_ImageFile, "PROGDIR:Images/AudioMonster.jpg", |
||
+ | APPNOTIFY_BackMsg, "URL:http://www.supercoders.com", |
||
+ | APPNOTIFY_CloseOnDC, TRUE, |
||
+ | TAG_END); |
||
+ | </syntaxhighlight> |
||
+ | |||
+ | === Pop-up Message Style Guide === |
||
+ | * Keep the message text short. Give the user chance to read the entire message before it disappears. |
||
− | 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 complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement. |
||
+ | * Do not use pop-up messages to report errors. An error is usually a serious situation, and as such it cannot be dismissed by simply displaying an automatic message (which can easily get missed or overlooked). |
||
+ | * Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user. |
||
+ | == Function Reference == |
||
− | 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 [[Workbench and Icon Library#The Workbench Library|the Workbench Library section]] of the AmigaOS documentation wiki). |
||
+ | The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call. |
||
− | (to be continued) |
||
+ | |||
+ | {| class="wikitable" |
||
+ | ! Function |
||
+ | ! Description |
||
+ | |- |
||
+ | | FindApplication() |
||
+ | | Searches for a previously registered application. |
||
+ | |- |
||
+ | | FreeApplicationList() |
||
+ | | Frees the list of applications generated by GetApplicationList(). |
||
+ | |- |
||
+ | | GetAppLibAttrs() |
||
+ | | Obtains global Application Library attributes. |
||
+ | |- |
||
+ | | GetApplicationAttrs() |
||
+ | | Obtains attributes of a registered application. |
||
+ | |- |
||
+ | | GetApplicationList() |
||
+ | | Obtains the list of all currently registered applications. |
||
+ | |- |
||
+ | | LockApplicationIcon() |
||
+ | | Attempts to lock an application icon. |
||
+ | |- |
||
+ | | Notify() |
||
+ | | Displays a pop-up message box. |
||
+ | |- |
||
+ | | RegisterApplication() |
||
+ | | Registers an application. |
||
+ | |- |
||
+ | | SendApplicationMsg() |
||
+ | | Sends a message to a registered application. |
||
+ | |- |
||
+ | | SetAppLibAttrs() |
||
+ | | Sets or changes global Application Library attributes. |
||
+ | |- |
||
+ | | SetApplicationAttrs() |
||
+ | | Sets or changes application attributes. |
||
+ | |- |
||
+ | | UnlockApplicationIcon() |
||
+ | | Unlocks an application icon. |
||
+ | |- |
||
+ | | UnregisterApplication() |
||
+ | | Unregisters an application. |
||
+ | |} |
Latest revision as of 21:10, 9 February 2020
Contents
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 turn into a “watchdog” and get notified when other applications register.
- 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”.
Frequently Asked Questions
Q: Should my programs register with the Application Library? | A: Yes, that would make a lot of sense. Apart from access to the handy features mentioned above, the implementation of certain library features may improve the behaviour of your application within the AmigaOS ecosystem. |
Q: After registering with the library, will my application become controlled by the OS or by other applications? | A: No. The registration process alone does not turn on any features. There is a recommendation to allow a minimum degree of control, but in the end it is the programmer who decides what functionality offered by the library will be provided and supported in his/her application. That also includes the scope of external control: if you don't want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming control messages. |
Q: Doesn't the Application Library duplicate functionality already present in commodities? | A: No. The only similarity between the two is that programs register with some system library, which then acts as a control centre. Commodities provide a way to install custom input handlers into the Input Device's event stream. The Application Library's field and scope of operation is completely different (and much wider). |
Q: Why is this a library? Wouldn't it be wiser to implement it as a class, like MUI does it? | A: This would tie the functionality to the object-oriented (BOOPSI) API – applications designed using other APIs or toolkits would not be able to use it. |
Q: Can a program be registered both as an application and as a commodity? | A: Yes, that's possible – although few programs would probably benefit from that. Exceptions include application managers and taskbars (e.g. AmiDock), which may need to control other applications and, at the same time, behave like a commodity. |
Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system? | A: Hardly any. The PrefsObjects .xml preference files are, typically, only read when the application starts and written at user request, so there is minimum overhead involved. Accessing the prefs file during program runtime doesn't put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory. |
Minimum Application Library Support
In the foreseeable future, AmigaOS will feature an application manager to control running applications. (A third-party solution called Exchanger is already available.) The idea is to provide a single, universal control point for both commodities and applications. In order to allow easy, consistent and predictable program control, developers are encouraged to register their applications and implement the following suggested minimum Application Library support:
- Provide a short description for a better identification of the application.
- Tell the OS whether your application supports iconification, that is, hiding/showing its GUI. If iconification is supported:
- set the REGAPP_HasIconifyFeature registration tag to TRUE;
- make the application respond to the APPLIBMT_Hide and APPLIBMT_Unhide control messages;
- update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies.
- Allow the application to be shut down externally in a safe and graceful manner in reaction to the APPLIBMT_Quit message.
Not implementing this suggested minimum means that the application manager will be unable to control your application properly and consistently (some of the manager's control buttons or menu items may appear ineffective). This could cause confusion on the part of the users and degrade their AmigaOS experience.
Library Opening Chores
Note |
---|
Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2. This is due to changes in the Application Library API which had broken standard tag support until version 2 interfaces were introduced. |
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 = (struct ApplicationIFace *) IExec->GetInterface(ApplicationBase, "application", 2, NULL); IPrefsObjects = (struct PrefsObjectsIFace *) IExec->GetInterface(ApplicationBase, "prefsobjects", 2, NULL); } if ( !ApplicationBase || !IApplication || !IPrefsObjects ) { /* handle library opening error */ }
Note that there is no interface called “main” like older, single-interface libraries have. The number in the GetInterface() call above refers to the version of the interface. Library interfaces are not backwards or forwards compatible so they must be specified precisely. Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2. This is due to certain changes in the Application Library API.
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 properties 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.
- hint* Avoid using the _ underscore character in the registered name. These may cause "charsetconvert" errors when the system boots up later.
Application Identifiers
A successfully registered application receives a numeric identifier, which in this documentation will be referred to as appID. The identifier is public: any registered application can obtain another application’s appID (see Finding Applications below) and use it to communicate with the respective application. AppIDs are unique integer numbers: the library generates them incrementally on a per-registration basis. They are never assigned 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 (and the same application will have a different appID each time), 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;
- an instance count (if more than one instance of the same application is started);
- a URL identifier (for example, the domain name of the application’s homepage – optional).
The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.
Registration Functions
Now let’s say we have a program, a media player called Audio Monster created by the fictitious software company SuperCoders, Inc. The piece of C code to handle its registration (and unregistration) might look like this:
uint32 appID; appID = IApplication->RegisterApplication("AudioMonster", REGAPP_URLIdentifier, "supercoders.com", REGAPP_Description, "A media player", TAG_END); if (!appID) { /* report registration error and quit */ } /* do whatever your program does */ IApplication->UnregisterApplication(appID, NULL);
Note that we’ve had to alter the application name to “AudioMonster”; it is because the Application Library doesn’t allow spaces in application names. According to the naming scheme (outlined in the previous section) the application will now become registered under the name “AudioMonster.supercoders.com”. (Should a previous instance of Audio Monster be already running, the library will automatically append an instance counter to the application name: the second instance will therefore be called “AudioMonster_1.supercoders.com”, the third will register as “AudioMonster_2.supercoders.com”, and so on.)
Also note that the URL identifier is just the domain name, i.e. without the “www.” part. The identifier is only used to distinguish between applications, not to access their homepages. The domain name is, therefore, sufficient; the resulting name identifier needn’t be long and quirky.
After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available.
Application Attributes
An application is described by a set of attributes. There are attributes that describe the application's properties or feature set, indicate its current state, or determine its behaviour. Some attributes are only specified at registration time and cannot be altered once they are set, while others can be changed freely (as long as the application remains registered, of course). The general recommendation is to specify at registration time all properties that are not going to change during program lifetime: i.e. define all "static" application features in one place.
Description
The REGAPP_Description tag, used in the registration example code above, tells the system what the application is all about. Although the description is an optional attribute, it is recommended to always provide it, as it will allow application manager or taskbar programs to provide meaningful information to the user. Keep the description short, matter-of-fact and serious.
The application description cannot be changed after registration.
Uniqueness
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 sometimes can be good reasons to do so. For example, the developer of the Audio Monster player might decide to make his/her program a unique application because the user would most likely gain nothing from playing several media files 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 will register Audio Monster as a unique application:
appID = IApplication->RegisterApplication("AudioMonster", REGAPP_URLIdentifier, "supercoders.com", REGAPP_Description, "A media player", REGAPP_UniqueApplication, TRUE, TAG_END);
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 the application is unique, so an error message would scold them for doing nothing wrong. The recommended behaviour is to bring the first instance to view and activate its window.
Quite logically, uniqueness is an attribute that cannot be changed after registration.
Feature Set / Control Scope
Different applications can implement different control features, and can be designed for a different scope of external control. For example, a text editor may respond to a control message (command) that tells it to create a new, blank, unnamed document, whereas in a media player such a command would be best ignored. Similarly, a simple tool with no configuration capability would want to stay clear of messages that control program settings. It is always good manners to inform the system about the scope of control your application supports, as other applications may query about (and rely on) this information when sending control messages. Always set the following boolean tags to TRUE in the RegisterApplication() call if your application implements support for the respective feature (the default value is FALSE):
Tag | Description | Implementation notes |
---|---|---|
REGAPP_HasIconifyFeature | The application supports iconification and uniconification. | Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages. |
REGAPP_HasPrefsWindow | The application has a dedicated Preferences (Settings) window. | Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message. |
REGAPP_CanCreateNewDocs | The application can create new documents (projects). | Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message. |
REGAPP_CanPrintDocs | The application can print documents. | Set to TRUE if your application responds to the APPLIBMT_PrintDoc message. |
These four attributes can be changed after registration.
Receiving Notifications
The Application Library can send certain notification messages that may be of interest to special-purpose programs like application managers, taskbars or screen blankers. If you are developing such a program, you may want to register for the following notifications:
Tag | Description | Implementation notes |
---|---|---|
REGAPP_AppNotifications | Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list. | Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs. |
REGAPP_BlankerNotifications | Receive notifications concerning blanker activity. | Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers. |
Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream.
These two attributes can be changed after registration.
Changing Application Attributes
Certain attributes describe "dynamic" application properties and, as such, can be set or changed after registration. Some of these are state attributes that need to be updated each time your program changes state (shows/hides its GUI, or enters the game mode; see below in the table). Some are not really attributes but, rather, commands that invoke an application-related action.
Tag | Description | Implementation notes |
---|---|---|
APPATTR_AllowsBlanker | Same as REGAPP_AllowsBlanker above. | You may want to be able to enable/disable blanking dynamically during application runtime – this what the APPATTR_AllowsBlanker tag is for. For example, a presentation program may allow blankers while the presentation is being worked on, and disable them when the presentation is started. |
APPATTR_AppOpenedDocument | Adds a new entry to the application's Last Used Documents list. | Set this tag each time your application has successfully opened a named document or project. The parameter to this tag is a pointer to the name string (i.e. a STRPTR). |
APPATTR_AppNotifications APPATTR_BlankerNotifications |
Same as the two corresponding registration tags above. | |
APPATTR_CanCreateNewDocs APPATTR_CanPrintDocs APPATTR_HasIconifyFeature APPATTR_HasPrefsWindow |
Same as the four corresponding registration tags above. | Prefer setting these properties at registration time. |
APPATTR_ClearLastUsedDocs | Clears the application's list of last used documents. | Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.) |
APPATTR_FlushPrefs | ||
APPATTR_Hidden | A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not. | Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information. |
APPATTR_IconType | Changes the application icon type. | |
APPATTR_MainPrefsDict | Allows changing the application's Prefs dictionary. | |
APPATTR_NeedsGameMode | A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode. | By the "game mode" we understand a mode in which an application doesn't want to be "disturbed" by other applications. It normally assumes full screen operation, and possibly taking over the audio system. Games or presentation programs are examples of applications that may want to implement the game mode, in which other programs are simply asked to “keep quiet”: not try to play sounds, open windows, requesters, etc. This feature assumes discipline and cooperation on the part of other applications; please use it moderately and only enter the game mode when it is really needed. Also note that setting APPATTR_NeedsGameMode to TRUE does not guarantee that other applications will comply. |
APPATTR_SavePrefs | Same as REGAPP_SavePrefs above. |
The function to set or change application attributes is SetApplicationAttrs(). It is very similar to SetAttrs() used in Intuition programming, only the first parameter is not a pointer to a BOOPSI object but an application identifier. The appID is followed by a tag list, so several attributes can be set at a time. The following example call will inform the system that the application has iconified (or otherwise hidden its GUI) and that the screen blanker can now come to front freely (assuming that the blanker was forbidden before for some reason). The result value indicates whether the call was successful or not:
BOOL result; result = IApplication->SetApplicationAttrs(appID, APPATTR_Hidden, TRUE, APPATTR_AllowsBlanker, TRUE, TAG_END);
Finding Applications
The Application Library maintains a public list of all registered applications. Certain special-purpose programs – application managers – will read this list (also keeping track of all subsequent registrations and unregistrations) and offer some degree of control: display information about applications and/or send commands telling them to do something. Quite naturally, most programs will not (nor are they supposed to!) act as managers but a need to talk to another application may arise. How do you find it, then?
Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library's messaging system. 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/AudioMonster”.
Based on this information, the respective piece of code that will find our application in the system might look like this:
uint32 appID; /* if you only know the application name */ appID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", TAG_END); /* if you know the application name identifier */ appID = IApplication->FindApplication(FINDAPP_AppIdentifier, "AudioMonster.supercoders.com", TAG_END); /* if you specifically want to talk to the second running instance */ appID = IApplication->FindApplication(FINDAPP_AppIdentifier, "AudioMonster_1.supercoders.com", TAG_END); /* if you know the pathname to the program file */ appID = IApplication->FindApplication(FINDAPP_FileName, "Work:Utils/AudioMonster", TAG_END);
Once you have obtained the appID you can start communicating with the respective application.
Messaging
Messages are used extensively in AmigaOS: the inner workings of Exec or Intuition actually involve a good deal of message passing. But it’s not just the operating system that needs to communicate. Modern software applications often want to talk to other running applications. Regardless of whether this communication will, in real use, entail a simple command-driven action or an intricate exchange of data, AmigaOS provides the necessary means: inter-program communication is supported 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. You can even send a message to all applications at once! Basic application control can be achieved via a set of predefined 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 more sophisticated control or information exchange. The extent and complexity of messaging is fully up to the programmer: your application will only send/receive messages that you will implement (it has already been mentioned in the Frequently Asked Questions section above that registration alone does not magically turn on any features).
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. However, as these are different and not really within the scope of the Application Library messaging framework, they are dealt with in a separate section of this document.
Note |
---|
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). |
Data Structures
Rather than introduce yet another system for communication, the Application Library builds upon the existing Exec messaging framework. Programmers will, therefore, find working with Application Library messages rather familiar. For instance, the library uses data structures that are in fact extensions of the standard Exec message structure. Also, the usual procedure of GetMsg() and ReplyMsg() takes place when processing incoming Application Library messages (see section Message Handling below) – although the library implements its own method for sending messages.
The following three structures are defined for carrying Application Library message data:
struct ApplicationMsg { struct Message msg; uint32 senderAppID; // the appID of the sender application uint32 type; // identifies the type of message }; struct ApplicationOpenPrintDocMsg { struct ApplicationMsg almsg; STRPTR fileName; }; struct ApplicationCustomMsg { struct ApplicationMsg almsg; STRPTR customMsg; };
As you can see, structure ApplicationMsg contains a standard struct Message, the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: ApplicationOpenPrintDocMsg is used by certain control messages that require a pointer to a filename; the ApplicationCustomMsg structure is used for custom messages.
Upon message arrival you identify the message by reading the “type” field of the respective data structure. Similarly, if you want to send a message to an application you specify it in the “type” field.
Control Messages
The Application Library allows registered applications to send messages that control other applications. There is a set of predefined messages (or rather, commands) that can be sent to a running application, telling it – for example – that it should come to front, quit, open a document, create a new one, and so on. As these actions are common program functions, the library offers a practical and easy-to-implement way to control applications externally. The following control messages (commands) are currently provided:
Message name | Description | Implementation notes |
---|---|---|
APPLIBMT_Quit | The application is asked to shut down itself and quit. | Basically, upon receiving this message you should react as if your main program window received the WMHI_CLOSEWINDOW event. When implementing support for APPLIBMT_Quit, the programmer is required to design the program exit sequence in such a way that no unexpected data loss can occur (for example, by displaying a confirmation requester that allows the user to save work or cancel the quit command). |
APPLIBMT_ForceQuit | Same as before but this time the application shall quit immediately, without asking for saving documents etc. | Providing this command as part of the Application Library messaging framework was surely meant well but there is an inherent risk: a malevolent application could hamper the use of other applications by sending them this message and making them quit prematurely. So implement APPLIBMT_ForceQuit with care, or don't implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications. |
APPLIBMT_Hide | The application shall hide its interface and iconify on the Workbench screen. | An application that supports APPLIBMT_Hide and APPLIBMT_Unhide is expected to register itself with REGAPP_HasIconifyFeature set to TRUE. Basically, upon receiving this message you should react as if your main program window received the WMHI_ICONIFY / WMHI_UNICONIFY event. |
APPLIBMT_Unhide | The application shall come back from the iconified (hidden) state. | |
APPLIBMT_ToFront | The application window shall come to front. | Programmatically that entails calling the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. See the end of the Message Handling section for a note on APPLIBMT_ToFront. |
APPLIBMT_OpenPrefs | The application shall open its preferences window. | Only implement if your application has a dedicated Preferences (Settings) window. An application that supports APPLIBMT_OpenPrefs is expected to register itself with the REGAPP_HasPrefsWindow set to TRUE. |
APPLIBMT_ReloadPrefs | The application shall reload its preferences. | Whatever this command is useful for. |
APPLIBMT_NewBlankDoc | The application shall open a new, blank document or project. | Applications such as web browsers can, too, make use of this command to open new program windows. An application that supports APPLIBMT_NewBlankDoc is expected to register itself with the REGAPP_CanCreateNewDocs set to TRUE. |
APPLIBMT_OpenDoc | The application shall try to open a specific document or project. | The name of the document is passed as part of the message data structure (struct ApplicationOpenPrintDocMsg: see Data Structures above). |
APPLIBMT_PrintDoc | The application shall try to print a specific document. | The name of the document is passed as part of the message data structure (struct ApplicationOpenPrintDocMsg: see Data Structures above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE. |
An application will only react to messages that are implemented and supported. If you send a message to an application that is registered but does not perform any Application Library event handling, there will be no reaction at all. Further, many applications will only implement a subset of the available control commands: if a program does not have a Print function, an APPLIBMT_PrintDoc message will quite logically be ignored. There is no rule saying which or how many control messages should be implemented but you are encouraged to provide the minimum suggested support as outlined above. Implement the rest according to your application’s features and needs, and to the highest standards of security.
Custom Messages
In contrast to a control message (see above), a custom message has no meaning or purpose predefined by the library. It is a simple text string the actual meaning of which is determined by the application. There is some undeniable beauty in this concept. For instance, the application can define a set of “publicly available” commands that call a corresponding function whenever a particular command (text string) arrives from another application. This kind of external control is very easy to implement and represents a practical alternative to ARexx, while requiring less setup. Also, sender applications need not care about internals (such as knowing the ARexx port name) – all it takes is to find the receiver application and send a message to it.
The pointer to the message text string is contained in a special data structure, struct ApplicationCustomMsg.
Special Messages
There are also some special messages that an application can receive from the library or another running application:
Message name | Description | Implementation notes |
---|---|---|
APPLIBMT_Unique | If an application is registered as unique and the user attempts to start a second instance of the program, the attempt will fail and the library will send an APPLIBMT_Unique message to the first instance. | All unique applications should listen for this message and react to it: not doing so might leave the user puzzled as to why the program hasn’t started. The recommended reaction is to bring the first instance to view and focus. This may entail a couple of steps, like uniconifying (if the program is iconified at the moment), swapping screen (if the program runs on a dedicated screen), bringing the program window to front, and activating it. Basically, the APPLIBMT_Unique message should be treated in the same way as the APPLIBMT_ToFront message. |
APPLIBMT_GameModeEntered | Sent to all currently running applications if another application enters the “game mode” – that is, a state in which it doesn’t want to be disturbed. | The message is sent around whenever an application sets its APPATTR_NeedsGameMode attribute to TRUE. |
APPLIBMT_GameModeLeft | Informs all running application that the sender has left the game mode. | The message is sent around whenever an application sets its APPATTR_NeedsGameMode attribute to FALSE. |
Message Handling
You already know that messaging in AmigaOS takes place between message ports. Being a superset of standard Exec messages, Application Library notifications (be they predefined control messages, custom messages or special messages) are, too, sent to a message port. We’ll call this dedicated port – in order to distinguish it from other ports possibly used by the program – the notification port. The port is automatically created by the library at registration time and freed as part of the UnregisterApplication() call. Messages arriving at the port are meant to be processed within the program’s main event loop, together with other events (such as Intuition’s IDCMP messages). To process incoming messages you first need to obtain the notification port pointer from the library and then set the port’s signal bit. The signal bit is used to identify messages as Application Library notifications.
A simplified event loop code could look like the one below. Note that this example loop only waits for and processes Application Library messages. In real use you'll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.
void event_loop(uint32 appID) { struct MsgPort *notificationPort = NULL; struct ApplicationMsg *appLibMsg = NULL; struct ApplicationCustomMsg *customMsg = NULL; uint32 appLibSignal = 0, sigGot = 0; BOOL done = FALSE; /* Obtain pointer to Application Library's notification port and set the signal bit. */ IApplication->GetApplicationAttrs(appID, APPATTR_Port, ¬ificationPort, TAG_END); appLibSignal = (1L << notificationPort->mp_SigBit); /* Go into the event loop. */ while (!done) { /* Wait for a signal to arrive. */ sigGot = IExec->Wait(appLibSignal); /* Process all Application Library messages. */ if ( sigGot & appLibSignal ) { /* Obtain pointer to the message. */ while ( (appLibMsg = (struct ApplicationMsg *) IExec->GetMsg(notificationPort)) ) { /* Identify the type of message. */ switch (appLibMsg->type) { case APPLIBMT_Quit: case APPLIBMT_ForceQuit: done = TRUE; break; case APPLIBMT_ToFront: /* Make the program window come to front. */ break; case APPLIBMT_Hide: /* Iconify the program. */ break; case APPLIBMT_Unhide: /* Uniconify the program. */ break; /* Process the custom message as you like. Here we just use printf() to output the message text. */ case APPLIBMT_CustomMsg: customMsg = (struct ApplicationCustomMsg *) appLibMsg; printf("The message text is: %s\n", customMsg-> customMsg); break; /* Process any other Application Library messages. */ } /* Return the processed message to the sender so that it can be freed. */ IExec->ReplyMsg( (struct Message *) appLibMsg); } } }
Like all Exec messages obtained via the GetMsg() function, Application Library messages must be replied to, i.e. returned to the sender after they have been processed. This is what the last command does in the code above. Remember that all message resources are freed after ReplyMsg() so should you need to use the message data (for example, the custom message text string) beyond the event loop, you must copy it to a memory storage of your own.
Also remember that the notifications are addressed to an abstract application – this makes them rather different from IDCMP messages, which are always sent to a particular window (Intuition doesn't know what an application is). Whereas Intuition stops sending IDCMP messages as soon as the window becomes closed/iconified, the Application Library keeps passing messages as long as the application is registered, regardless of program window state. So be smart in your code when processing Application Library notifications: check for the availability of the program window and perform uniconification when necessary. For example, this following piece of code
/* Don't do this!!! */ case APPLIBMT_ToFront: IIntuition->ScreenToFront(win->WScreen); IIntuition->WindowToFront(win); IIntuition->ActivateWindow(win); break;
is broken because if an APPLIBMT_ToFront command were sent to your iconified application, there would be no window to refer to. You need to check for iconified state first, uniconify the window if needed, and only then call the ScreenToFront(), WindowToFront() and ActivateWindow() sequence. The mistake of referencing a non-existing window is as silly as it is easy to make!
Sending Messages
While incoming notifications are processed pretty much like normal Exec messages, the Application Library implements its own method for message sending. It takes three simple steps to send a message to another application:
- Prepare the respective data structure: specify the type of message and supply the sender's identifier (appID).
- Find the receiver application.
- Send a message to it via the SendApplicationMsg() function.
For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this:
struct ApplicationMsg appMsg; // message data structure uint32 audioMonsterID; // identifier of the receiver /* Step 1: Prepare the message data structure. */ appMsg.senderAppID = appID; // identifier of the sender appMsg.type = APPLIBMT_Quit; // type of message /* Step 2: Find the receiver application. */ if ( (audioMonsterID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", TAG_END)) ) { /* Step 3: Send the message. */ IApplication->SendApplicationMsg(appID, audioMonsterID, &appMsg, APPLIBMT_Quit); }
Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0.
Sending custom messages is done in a similar fashion, the only difference is that you must use a dedicated data structure instead of the generic struct ApplicationMsg. As our Audio Monster application is a media player, it may as well have defined a set of commands for external control, one of them being “Start playback”. Now if another application wants to tell Audio Monster to start playing, it will need to send the custom message like this:
struct ApplicationCustomMsg customMsg; // custom message data structure uint32 audioMonsterID; // identifier of the receiver /* Prepare the message data structure. */ customMsg.almsg.senderAppID = appID; // identifier of the sender customMsg.almsg.type = APPLIBMT_CustomMsg; // type of message customMsg.customMsg = "Start playback"; // message text /* Find the receiver application. */ if ( (audioMonsterID = IApplication->FindApplication(FINDAPP_Name, "AudioMonster", TAG_END)) ) { /* Send the message. */ IApplication->SendApplicationMsg(appID, audioMonsterID, (struct ApplicationMsg *) &customMsg, APPLIBMT_CustomMsg); }
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).
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_END);
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 (160 characters as of Ringhio 53.23). 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/AudioMonster.jpg", APPNOTIFY_BackMsg, "All right, ma!", TAG_END);
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. As the code snippet shows, this custom message takes the form of a text string (within the Application Library messaging context, custom messages are always text strings). It is sent to the same event stream, and is supposed to be processed within the same event loop, as other Application Library messages – see the Message Handling section for how to go about it.
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. The reaction (if any – often none is needed) should be sensible and logical. Make sure not to misuse the feature! Note 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 be interpreted as the user’s acknowledgement of what Ringhio has said, but never as a selection of an option. Do not use Ringhio to request input via the back-message feature: the text of the message should be a statement, not a question or an offer of choice. Considering this logic, the double click means “I understand”; it doesn't mean “Yes” or “No”.
If the text string provided in the APPNOTIFY_BackMsg tag is an URL, the feature works rather differently. Instead of sending the message to the event stream, this particular URL is opened in your default web browser. Because the process is done through the Launch Handler, your system should be recent enough to have it (the Launch Handler was introduced in AmigaOS 4.1 Update 1). The following piece of code will open Audio Monster’s homepage if the user double-clicks on the Ringhio box. The last in the tag list, APPNOTIFY_CloseOnDC, causes the message box to disappear right after the double click.
IApplication->Notify(appID, APPNOTIFY_Title, "Important Message", APPNOTIFY_Text, "Your socks need changing!", APPNOTIFY_PubScreenName, "FRONT", APPNOTIFY_ImageFile, "PROGDIR:Images/AudioMonster.jpg", APPNOTIFY_BackMsg, "URL:http://www.supercoders.com", APPNOTIFY_CloseOnDC, TRUE, TAG_END);
Pop-up Message Style Guide
- Keep the message text short. Give the user chance to read the entire message before it disappears.
- Do not use pop-up messages to report errors. An error is usually a serious situation, and as such it cannot be dismissed by simply displaying an automatic message (which can easily get missed or overlooked).
- Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user.
Function Reference
The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call.
Function | Description |
---|---|
FindApplication() | Searches for a previously registered application. |
FreeApplicationList() | Frees the list of applications generated by GetApplicationList(). |
GetAppLibAttrs() | Obtains global Application Library attributes. |
GetApplicationAttrs() | Obtains attributes of a registered application. |
GetApplicationList() | Obtains the list of all currently registered applications. |
LockApplicationIcon() | Attempts to lock an application icon. |
Notify() | Displays a pop-up message box. |
RegisterApplication() | Registers an application. |
SendApplicationMsg() | Sends a message to a registered application. |
SetAppLibAttrs() | Sets or changes global Application Library attributes. |
SetApplicationAttrs() | Sets or changes application attributes. |
UnlockApplicationIcon() | Unlocks an application icon. |
UnregisterApplication() | Unregisters an application. |