

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Daniel+Jedlicka</id>
	<title>AmigaOS Documentation Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.amigaos.net/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Daniel+Jedlicka"/>
	<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/wiki/Special:Contributions/Daniel_Jedlicka"/>
	<updated>2026-06-25T04:15:48Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12588</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12588"/>
		<updated>2026-04-13T07:11:45Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Unified parameter reference style.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 1200 Hz, so clock pulses are delivered approximately every 0.83 ms. This is good enough even for timing-critical applications such as MIDI sequencing.&lt;br /&gt;
&lt;br /&gt;
There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_AlarmSigTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(player,&lt;br /&gt;
           PLAYER_AlarmTime, 1000,&lt;br /&gt;
           PLAYER_Ready, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up Player structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a Player with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_AlarmSigTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState(); [[#More About Conductors|see below]]. This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct Player *player, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;player&#039;&#039; parameter indicates which Player structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
        PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
        PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
        PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
        PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pl-&amp;gt;pl_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 1200 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pl-&amp;gt;pl_Userdata)) &amp;lt; pl-&amp;gt;pl_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by Player structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct Player *player, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;player&#039;&#039; parameter is a pointer to a Player structure that is linked to the Conductor you want to change. The &#039;&#039;ti&#039;&#039; parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CONDSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (Players) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their Player to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each Player has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CONDSTATE_LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the Player is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12587</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12587"/>
		<updated>2026-04-13T07:08:22Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed internal link.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 1200 Hz, so clock pulses are delivered approximately every 0.83 ms. This is good enough even for timing-critical applications such as MIDI sequencing.&lt;br /&gt;
&lt;br /&gt;
There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_AlarmSigTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(player,&lt;br /&gt;
           PLAYER_AlarmTime, 1000,&lt;br /&gt;
           PLAYER_Ready, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up Player structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a Player with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_AlarmSigTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState(); [[#More About Conductors|see below]]. This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct Player *player, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter indicates which Player structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
        PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
        PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
        PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
        PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pl-&amp;gt;pl_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 1200 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pl-&amp;gt;pl_Userdata)) &amp;lt; pl-&amp;gt;pl_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by Player structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct Player *player, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter is a pointer to a Player structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CONDSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (Players) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their Player to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each Player has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CONDSTATE_LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the Player is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12586</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12586"/>
		<updated>2026-04-13T07:05:41Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Minor corrections&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 1200 Hz, so clock pulses are delivered approximately every 0.83 ms. This is good enough even for timing-critical applications such as MIDI sequencing.&lt;br /&gt;
&lt;br /&gt;
There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_AlarmSigTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(player,&lt;br /&gt;
           PLAYER_AlarmTime, 1000,&lt;br /&gt;
           PLAYER_Ready, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up Player structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a Player with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_AlarmSigTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState(); [[More About Conductors|see below]]. This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct Player *player, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter indicates which Player structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
        PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
        PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
        PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
        PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pl-&amp;gt;pl_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 1200 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pl-&amp;gt;pl_Userdata)) &amp;lt; pl-&amp;gt;pl_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by Player structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct Player *player, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter is a pointer to a Player structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CONDSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (Players) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their Player to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each Player has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CONDSTATE_LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the Player is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12585</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12585"/>
		<updated>2026-04-13T06:59:52Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Corrected wrong information in the More About Conductors section.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 1200 Hz, so clock pulses are delivered approximately every 0.83 ms. This is good enough even for timing-critical applications such as MIDI sequencing.&lt;br /&gt;
&lt;br /&gt;
There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_AlarmSigTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(player,&lt;br /&gt;
           PLAYER_AlarmTime, 1000,&lt;br /&gt;
           PLAYER_Ready, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up Player structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a Player with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_AlarmSigTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState(); see below. This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct Player *player, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter indicates which Player structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
        PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
        PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
        PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
        PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pl-&amp;gt;pl_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 1200 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pl-&amp;gt;pl_Userdata)) &amp;lt; pl-&amp;gt;pl_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by Player structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct Player *player, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter is a pointer to a Player structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CONDSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (Players) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CONDSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their Player to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each Player has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CONDSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the Player is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12584</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12584"/>
		<updated>2026-04-13T06:53:06Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Corrected wrong information in the Getting Clock Ticks section.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 1200 Hz, so clock pulses are delivered approximately every 0.83 ms. This is good enough even for timing-critical applications such as MIDI sequencing.&lt;br /&gt;
&lt;br /&gt;
There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_AlarmSigTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(player,&lt;br /&gt;
           PLAYER_AlarmTime, 1000,&lt;br /&gt;
           PLAYER_Ready, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up Player structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a Player with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_AlarmSigTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState(); see below. This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct Player *player, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The player parameter indicates which Player structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
        PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
        PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
        PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
        PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (player != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(player, CONDSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pl-&amp;gt;pl_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 1200 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct Player *pl)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pl-&amp;gt;pl_Userdata)) &amp;lt; pl-&amp;gt;pl_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by PlayerInfo structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct PlayerInfo *pi, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter is a pointer to a PlayerInfo structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLOCKSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (PlayerInfos) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their PlayerInfo to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each PlayerInfo has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CLOCKSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the PlayerInfo is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12583</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12583"/>
		<updated>2026-04-13T06:34:17Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Removed references to struct PlayerInfo because it&amp;#039;s in fact called Player.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a Player is set up by each client application that wants to get timing information from the Conductor. There is typically one Player for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Player structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single Player structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a Player structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Player structure you want to create. (A complete list of all the tags available can be found in the Autodoc for the SetPlayerAttrs() function.) It returns a pointer to the Player structure. Here’s a fragment showing how to set up a Player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Player *player = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (player != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(player);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a Player will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their Players to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any Players you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the Players attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a Player and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 600 Hz so clock pulses are delivered approximately every 1.66 ms. There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_SignalTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(pPlayerInfo,&lt;br /&gt;
        PLAYER_AlarmTime, 1000,&lt;br /&gt;
        PLAYER_Ready, TRUE,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up PlayerInfo structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a PlayerInfo with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_SignalTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState() (discussed below). This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter indicates which PlayerInfo structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is &lt;br /&gt;
requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) &lt;br /&gt;
whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
    PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pi­&amp;gt;pi_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When &lt;br /&gt;
Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 600 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pi-&amp;gt;pi_Userdata)) &amp;lt; pi-&amp;gt;pi_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by PlayerInfo structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct PlayerInfo *pi, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter is a pointer to a PlayerInfo structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLOCKSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (PlayerInfos) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their PlayerInfo to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each PlayerInfo has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CLOCKSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the PlayerInfo is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12582</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12582"/>
		<updated>2026-04-13T06:27:43Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Conductors and Players */  - fixed section heading&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Players =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a PlayerInfo is set up by each client application that wants to get timing information from the Conductor. There is typically one PlayerInfo for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and PlayerInfo structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single PlayerInfo structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a PlayerInfo structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct PlayerInfo *pi = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the PlayerInfo structure you want to create. (A complete list of all the tags available can be found in the Autodoc for SetPlayerAttrs().) It returns a pointer to the PlayerInfo. Here’s a fragment showing how to set up a PlayerInfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (pPlayerInfo != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a PlayerInfo will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their PlayerInfos to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any PlayerInfos you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the PlayerInfos attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a PlayerInfo and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 600 Hz so clock pulses are delivered approximately every 1.66 ms. There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_SignalTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(pPlayerInfo,&lt;br /&gt;
        PLAYER_AlarmTime, 1000,&lt;br /&gt;
        PLAYER_Ready, TRUE,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up PlayerInfo structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a PlayerInfo with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_SignalTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState() (discussed below). This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter indicates which PlayerInfo structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is &lt;br /&gt;
requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) &lt;br /&gt;
whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
    PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pi­&amp;gt;pi_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When &lt;br /&gt;
Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 600 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pi-&amp;gt;pi_Userdata)) &amp;lt; pi-&amp;gt;pi_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by PlayerInfo structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct PlayerInfo *pi, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter is a pointer to a PlayerInfo structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLOCKSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (PlayerInfos) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their PlayerInfo to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each PlayerInfo has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CLOCKSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the PlayerInfo is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=GadTools_Library&amp;diff=12396</id>
		<title>GadTools Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=GadTools_Library&amp;diff=12396"/>
		<updated>2024-12-07T07:59:40Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added note that GadTools menus are now replaced by the Menu Class.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GadTools Library ==&lt;br /&gt;
&lt;br /&gt;
Originally, programming even straightforward user interfaces in [[Intuition_Library|Intuition]] could be rather complicated, and certainly difficult for first-time programmers. The GadTools toolkit was introduced in AmigaOS 2.x to simplify the task. It provided relatively easy-to-use, higher-level chunks to build [http://en.wikipedia.org/wiki/Graphical_user_interface GUIs] from, and to help programmers through what used to be a difficult chore.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Note|text=As of AmigaOS 4.1 Final Edition, GadTools is basically a legacy toolkit retained for compatibility. Due to its inherent limitations, using GadTools in modern applications can no longer be recommended. Prefer using the object-oriented GUI programming framework based on [[BOOPSI]].}}&lt;br /&gt;
&lt;br /&gt;
== Elements of GadTools ==&lt;br /&gt;
&lt;br /&gt;
GadTools is the easy way to program gadgets and menus. With GadTools, the system handles the detail work required to control gadgets and menus so the application uses less code and simpler data structures.&lt;br /&gt;
&lt;br /&gt;
Another key benefit of GadTools is its standardized and elegant look. All applications that use GadTools will share a similar appearance and behavior. Users will appreciate a sense of instant familiarity even the first time they use a product.&lt;br /&gt;
&lt;br /&gt;
GadTools provides a significant degree of visual consistency across multiple applications that use it. There is also internal consistency between different elements of GadTools; the look is clean and orderly. Depth is used not just for visual embellishment, but as an important cue. For instance, the user is free to select symbols that appear inside a &amp;quot;raised&amp;quot; area, but &amp;quot;recessed&amp;quot; areas are informational only, and clicking in them has no effect.&lt;br /&gt;
&lt;br /&gt;
GadTools is not amenable to creative post-processing or hacking by programmers looking to achieve a result other than what GadTools currently offers. Software developers whose needs extend beyond the standard features of GadTools should create custom gadgets that share the look and feel of GadTools by using either BOOPSI or by directly programming gadgets at a lower level. See [[Intuition_Gadgets|Intuition Gadgets]] and [[BOOPSI_-_Object_Oriented_Intuition|BOOPSI]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== GadTools Tags ===&lt;br /&gt;
&lt;br /&gt;
Many of the GadTools functions use TagItem arrays or &#039;&#039;tag lists&#039;&#039; to pass information across the function interface. These tag-based functions come in two types, one that takes a pointer to an array of tag items and one that takes a variable number of tag item arguments directly in the function call. In general, the second form, often called the &#039;&#039;varargs&#039;&#039; form because the call takes a variable number of arguments, is provided for convenience and is internally converted to the first form. When looking through the Autodocs or other Amiga reference material, the documentation for both forms is usually available in the array-based function description.&lt;br /&gt;
&lt;br /&gt;
All GadTools tags begin with a leading &amp;quot;GT&amp;quot;. In general, they also have a two-letter mnemonic for the kind of gadget in question. For example, slider gadgets recognize tags such as &amp;quot;GTSL_Level&amp;quot;. The GadTools tags are defined in &amp;amp;lt;libraries/gadtools.h&amp;amp;gt;. Certain GadTools gadgets also recognize other Intuition tags such as GA_Disabled and PGA_Freedom, which can be found in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For more information on tags and tag-based functions, be sure to see the [[Utility_Library|Utility Library]].&lt;br /&gt;
&lt;br /&gt;
== GadTools Gadgets ==&lt;br /&gt;
&lt;br /&gt;
[[GadTools Gadgets]] are now largely superseded by [[BOOPSI Gadgets|gadgets]] based on the object-oriented [[BOOPSI - Object Oriented Intuition|BOOPSI framework]].&lt;br /&gt;
&lt;br /&gt;
== GadTools Menus ==&lt;br /&gt;
&lt;br /&gt;
[[GadTools Menus]] are now largely superseded by [[Intuition Menu Class|menus]] based on the object-oriented [[BOOPSI - Object Oriented Intuition|BOOPSI framework]].&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions discussed in this article. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CreateGadgetA()&amp;lt;br/&amp;gt;CreateGadget()&lt;br /&gt;
| Allocate GadTools gadget.&lt;br /&gt;
|-&lt;br /&gt;
| FreeGadgets()&lt;br /&gt;
| Free all GadTools gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| GT_SetGadgetAttrsA()&amp;lt;br/&amp;gt;GT_SetGadgetAttrs()&lt;br /&gt;
| Update gadget.&lt;br /&gt;
|-&lt;br /&gt;
| CreateContext()&lt;br /&gt;
| Create a base for adding GadTools gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| CreateMenusA()&amp;lt;br/&amp;gt;CreateMenus()&lt;br /&gt;
| Allocate GadTools menu structures.&lt;br /&gt;
|-&lt;br /&gt;
| FreeMenus()&lt;br /&gt;
| Free menus allocated with CreateMenus().&lt;br /&gt;
|-&lt;br /&gt;
| LayoutMenuItemsA()&amp;lt;br/&amp;gt;LayoutMenuItems()&lt;br /&gt;
| Format GadTools menu items.&lt;br /&gt;
|-&lt;br /&gt;
| LayoutMenusA()&amp;lt;br/&amp;gt;LayoutMenus()&lt;br /&gt;
| Format GadTools menus.&lt;br /&gt;
|-&lt;br /&gt;
| GT_GetIMsg()&lt;br /&gt;
| GadTools gadget compatible version of GetMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_ReplyIMsg()&lt;br /&gt;
| GadTools gadget compatible version of ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_FilterIMsg()&lt;br /&gt;
| Process GadTools gadgets with GetMsg()/ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_PostFilterIMsg()&lt;br /&gt;
| Process GadTools gadgets with GetMsg()/ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_RefreshWindow()&lt;br /&gt;
| Display GadTools gadget imagery after creation.&lt;br /&gt;
|-&lt;br /&gt;
| GT_BeginRefresh()&lt;br /&gt;
| GadTools gadget compatible version of BeginRefresh().&lt;br /&gt;
|-&lt;br /&gt;
| GT_EndRefresh()&lt;br /&gt;
| GadTools gadget compatible version of EndRefresh().&lt;br /&gt;
|-&lt;br /&gt;
| DrawBevelBoxA()&amp;lt;br/&amp;gt;DrawBevelBox()&lt;br /&gt;
| Draw a 3D box.&lt;br /&gt;
|-&lt;br /&gt;
| GetVisualInfoA()&amp;lt;br/&amp;gt;GetVisualInfo()&lt;br /&gt;
| Get drawing information for GadTools.&lt;br /&gt;
|-&lt;br /&gt;
| FreeVisualInfo()&lt;br /&gt;
| Free drawing information for GadTools.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Libraries&amp;diff=12395</id>
		<title>Libraries</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Libraries&amp;diff=12395"/>
		<updated>2024-12-07T07:50:03Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Moved the Menu Class page to the BOOPSI section where it really belongs.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
A library is a set of related functions and data. For example, the AmigaOS&#039;s DOS library contains functions for accessing files and directories, and the Intuition library contains the graphical user interface functions.&lt;br /&gt;
&lt;br /&gt;
There are three types of libraries: &#039;&#039;&#039;dynamically-loaded shared libraries&#039;&#039;&#039;, &#039;&#039;&#039;dynamically linked libraries&#039;&#039;&#039;, and &#039;&#039;&#039;static libraries&#039;&#039;&#039;. Shared libraries are shared by the running programs – only one copy of a library exists in the memory, no matter how many programs are using it. All AmigaOS&#039;s libraries are shared libraries. The shared library files can be recognized from the &#039;&#039;&#039;.library&#039;&#039;&#039; suffix.&lt;br /&gt;
&lt;br /&gt;
A dynamically linked library is attached to a program during the load time, just before the program is executed. Linked library functions can&#039;t be shared by programs, and several copies of the library functions may exist in the memory. The linked library files can be recognized from the &#039;&#039;&#039;.so&#039;&#039;&#039; suffix.&lt;br /&gt;
&lt;br /&gt;
A static library is like a linked library except it is attached permanently to a program when it is created. This makes it impossible for the user to update the library the program is using. Static libraries are supplied with programming languages.&lt;br /&gt;
&lt;br /&gt;
== Shared Libraries ==&lt;br /&gt;
=== AmigaDOS ===&lt;br /&gt;
&lt;br /&gt;
[[AmigaDOS Introduction]]&lt;br /&gt;
&lt;br /&gt;
[[DOS Library]]&lt;br /&gt;
:[[AmigaDOS Data Structures|Data Structures]]&lt;br /&gt;
:[[Program Startup]]&lt;br /&gt;
:[[Basic Input and Output Programming]]&lt;br /&gt;
:[[Executing External Programs]]&lt;br /&gt;
:[[Cooperative Record Locking]]&lt;br /&gt;
:[[Notification]]&lt;br /&gt;
:[[Path Name Handling]]&lt;br /&gt;
:[[Pattern Matching]]&lt;br /&gt;
:[[Multiple Assigns]]&lt;br /&gt;
:[[AmigaDOS Packets|Packets]]&lt;br /&gt;
:[[AmigaDOS Vector-Port|Vector-Port]]&lt;br /&gt;
:[[Hard and Soft Links]]&lt;br /&gt;
:[[Writing a UserShell]]&lt;br /&gt;
:[[AmigaDOS Device Input and Output|Device Input and Output]]&lt;br /&gt;
&lt;br /&gt;
=== User Interface Libraries ===&lt;br /&gt;
&lt;br /&gt;
[[Intuition Library]]&lt;br /&gt;
:[[Intuition Screens|Screens]]&lt;br /&gt;
::[[Custom_Screen_Type|Custom Screen Type]]&lt;br /&gt;
::[[Public_Screen_Type|Public Screen Type]]&lt;br /&gt;
:[[Intuition Windows|Windows]]&lt;br /&gt;
::[[Window Types|Types]]&lt;br /&gt;
::[[Window Structures and Functions|Structures and Functions]]&lt;br /&gt;
::[[Window Communication|Communicating with Intuition]]&lt;br /&gt;
::[[Window Display Preservation|Display Preservation]]&lt;br /&gt;
:[[Intuition Gadgets|Gadgets]]&lt;br /&gt;
::[[Boolean_Gadget_Type|Boolean Gadget Type]]&lt;br /&gt;
::[[Proportional_Gadget_Type|Proportional Gadget Type]]&lt;br /&gt;
::[[String_Gadget_Type|String Gadget Type]]&lt;br /&gt;
:[[Intuition Menus|Menus]]&lt;br /&gt;
::[[Intuition Classic Menus|Classic Menus]]&lt;br /&gt;
::[[Intuition Context Menus|Context Menus]]&lt;br /&gt;
:[[Intuition Requesters|Requesters]]&lt;br /&gt;
:[[Intuition Alerts|Alerts]]&lt;br /&gt;
:[[Intuition Images, Line Drawing and Text|Images, Line Drawing and Text]]&lt;br /&gt;
::[[Intuition Images|Images]]&lt;br /&gt;
::[[Intuition Borders|Borders]]&lt;br /&gt;
::[[Intuition Text|Text]]&lt;br /&gt;
:[[Intuition Input and Output Methods|Input and Output Methods]]&lt;br /&gt;
:[[Intuition Mouse|Mouse]]&lt;br /&gt;
:[[Intuition Keyboard|Keyboard]]&lt;br /&gt;
:[[Intuition Pointer|Pointer]]&lt;br /&gt;
:[[Intuition Special Functions|Special Functions]]&lt;br /&gt;
:[[BOOPSI - Object Oriented Intuition]]&lt;br /&gt;
::[[BOOPSI Gadgets]]&lt;br /&gt;
::[[BOOPSI Images]]&lt;br /&gt;
::[[Intuition Menu Class|BOOPSI Menus]]&lt;br /&gt;
::[[BOOPSI Class Reference]]&lt;br /&gt;
&lt;br /&gt;
[[Workbench Library]]&lt;br /&gt;
&lt;br /&gt;
[[Icon Library]]&lt;br /&gt;
&lt;br /&gt;
[[Locale Library]]&lt;br /&gt;
&lt;br /&gt;
[[GadTools Library]]&lt;br /&gt;
:[[GadTools Menus]]&lt;br /&gt;
:[[GadTools Gadgets]]&lt;br /&gt;
&lt;br /&gt;
[[ASL Library]]&lt;br /&gt;
:[[ASL File Requester|File Requester]]&lt;br /&gt;
:[[ASL Font Requester|Font Requester]]&lt;br /&gt;
:[[ASL Screen Mode Requester|Screen Mode Requester]]&lt;br /&gt;
&lt;br /&gt;
[[Application Library]]&lt;br /&gt;
:[[PrefsObjects]]&lt;br /&gt;
&lt;br /&gt;
[[Preferences]]&lt;br /&gt;
&lt;br /&gt;
=== Graphics Libraries ===&lt;br /&gt;
&lt;br /&gt;
[[Graphics Library]]&lt;br /&gt;
:[[Display Database]]&lt;br /&gt;
:[[Graphics Primitives|Primitives]]&lt;br /&gt;
::[[Classic Graphics Primitives|Classic Primitives]]&lt;br /&gt;
:[[Graphics Sprites, Bobs and Animation|Sprites, Bobs and Animation]]&lt;br /&gt;
::[[Hardware Sprites]]&lt;br /&gt;
::[[Virtual Sprites|Virtual Sprites (VSprites)]]&lt;br /&gt;
::[[Blitter Objects|Blitter Objects (Bobs)]]&lt;br /&gt;
:[[Graphics Library and Text|Text]]&lt;br /&gt;
:[[Graphics Regions|Regions]]&lt;br /&gt;
:[[Graphics Composited Video|Composited Video]]&lt;br /&gt;
:[[Graphics Video Overlay|Video Overlay]]&lt;br /&gt;
:[[Graphics Minterms|Minterms]]&lt;br /&gt;
&lt;br /&gt;
[[Layers Library]]&lt;br /&gt;
&lt;br /&gt;
=== Additional Libraries ===&lt;br /&gt;
&lt;br /&gt;
[[AmigaGuide Library]]&lt;br /&gt;
&lt;br /&gt;
[[Camd Library]]&lt;br /&gt;
&lt;br /&gt;
[[Commodities Exchange Library]]&lt;br /&gt;
&lt;br /&gt;
[[Datatypes Library]]&lt;br /&gt;
:[[Writing Datatype Classes]]&lt;br /&gt;
&lt;br /&gt;
[[IFFParse Library]]&lt;br /&gt;
:[[Parsing IFF]]&lt;br /&gt;
:[[Writing IFF]]&lt;br /&gt;
&lt;br /&gt;
[[Keymap Library]]&lt;br /&gt;
&lt;br /&gt;
[[Newlib Library]]&lt;br /&gt;
&lt;br /&gt;
[[PThreads Library]]&lt;br /&gt;
&lt;br /&gt;
[[RealTime Library]]&lt;br /&gt;
&lt;br /&gt;
[[Translator Library]]&lt;br /&gt;
&lt;br /&gt;
[[DiskIO Library|Disk I/O Library]]&lt;br /&gt;
&lt;br /&gt;
=== 68k Libraries ===&lt;br /&gt;
&lt;br /&gt;
[[Math Libraries]]&lt;br /&gt;
&lt;br /&gt;
=== Third-Party Libraries ===&lt;br /&gt;
&lt;br /&gt;
[[Expat Library]]&lt;br /&gt;
&lt;br /&gt;
[[Filesysbox Library]]&lt;br /&gt;
&lt;br /&gt;
== Linked Libraries ==&lt;br /&gt;
&lt;br /&gt;
=== Audio Libraries ===&lt;br /&gt;
libao&lt;br /&gt;
&lt;br /&gt;
libogg&lt;br /&gt;
&lt;br /&gt;
libvorbis&lt;br /&gt;
&lt;br /&gt;
libvorbisenc&lt;br /&gt;
&lt;br /&gt;
libvorbisfile&lt;br /&gt;
&lt;br /&gt;
=== C/C++ Programming Language Libraries ===&lt;br /&gt;
libc&lt;br /&gt;
&lt;br /&gt;
libgcc&lt;br /&gt;
&lt;br /&gt;
libgcov&lt;br /&gt;
&lt;br /&gt;
libicudata&lt;br /&gt;
&lt;br /&gt;
libicuuc&lt;br /&gt;
&lt;br /&gt;
libstdc++&lt;br /&gt;
&lt;br /&gt;
=== Compression Libraries ===&lt;br /&gt;
libz&lt;br /&gt;
&lt;br /&gt;
libbz2&lt;br /&gt;
&lt;br /&gt;
=== Database Libraries ===&lt;br /&gt;
libsqlite&lt;br /&gt;
&lt;br /&gt;
=== Dynamic Linking Libraries ===&lt;br /&gt;
libdl&lt;br /&gt;
&lt;br /&gt;
=== File System Libraries ===&lt;br /&gt;
libntfs&lt;br /&gt;
&lt;br /&gt;
=== File Transfer Libraries ===&lt;br /&gt;
libcurl&lt;br /&gt;
&lt;br /&gt;
libssl&lt;br /&gt;
&lt;br /&gt;
=== Graphics Libraries ===&lt;br /&gt;
libcairo&lt;br /&gt;
&lt;br /&gt;
libfontconfig&lt;br /&gt;
&lt;br /&gt;
libfreetype&lt;br /&gt;
&lt;br /&gt;
libjpeg&lt;br /&gt;
&lt;br /&gt;
libpixman&lt;br /&gt;
&lt;br /&gt;
libpng&lt;br /&gt;
&lt;br /&gt;
libpng12&lt;br /&gt;
&lt;br /&gt;
libSDL&lt;br /&gt;
&lt;br /&gt;
libSDL_gfx&lt;br /&gt;
&lt;br /&gt;
=== Parallel Execution Libraries ===&lt;br /&gt;
libpthread&lt;br /&gt;
&lt;br /&gt;
=== Python Programming Language Libraries ===&lt;br /&gt;
libpython&lt;br /&gt;
&lt;br /&gt;
=== XML Libraries ===&lt;br /&gt;
libexpat&lt;br /&gt;
&lt;br /&gt;
libxml&lt;br /&gt;
&lt;br /&gt;
libxslt&lt;br /&gt;
&lt;br /&gt;
== Static Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[Linker Libraries]]&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12357</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=12357"/>
		<updated>2023-04-06T08:42:05Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed typos that probably resulted from OCR.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and PlayerInfos =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a PlayerInfo is set up by each client application that wants to get timing information from the Conductor. There is typically one PlayerInfo for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and PlayerInfo structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single PlayerInfo structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a PlayerInfo structure you call CreatePlayer():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct PlayerInfo *pi = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the PlayerInfo structure you want to create. (A complete list of all the tags available can be found in the Autodoc for SetPlayerAttrs().) It returns a pointer to the PlayerInfo. Here’s a fragment showing how to set up a PlayerInfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (pPlayerInfo != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a PlayerInfo will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their PlayerInfos to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any PlayerInfos you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the PlayerInfos attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a PlayerInfo and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 600 Hz so clock pulses are delivered approximately every 1.66 ms. There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_SignalTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(pPlayerInfo,&lt;br /&gt;
        PLAYER_AlarmTime, 1000,&lt;br /&gt;
        PLAYER_Ready, TRUE,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up PlayerInfo structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a PlayerInfo with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_SignalTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState() (discussed below). This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter indicates which PlayerInfo structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is &lt;br /&gt;
requested using the PLAYER_AlarmTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) &lt;br /&gt;
whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
    PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pi­&amp;gt;pi_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When &lt;br /&gt;
Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 600 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pi-&amp;gt;pi_Userdata)) &amp;lt; pi-&amp;gt;pi_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by PlayerInfo structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating – see below) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct PlayerInfo *pi, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter is a pointer to a PlayerInfo structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLOCKSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (PlayerInfos) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_LOCATE || This is the same as running with one exception: the clock does not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their PlayerInfo to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each PlayerInfo has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CLOCKSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the PlayerInfo is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=11085</id>
		<title>Application Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=11085"/>
		<updated>2020-02-09T20:10:09Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Minimum Application Library Support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of &#039;&#039;application&#039;&#039; 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 &#039;&#039;background services&#039;&#039;), and genuine full-blown &#039;&#039;applications&#039;&#039; with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can turn into a “watchdog” and get notified when other applications register.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can easily create and manage lists of recently-used documents.&lt;br /&gt;
&lt;br /&gt;
* It can register as a &#039;&#039;unique application&#039;&#039;, preventing other instances of itself from running.&lt;br /&gt;
&lt;br /&gt;
* It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.&lt;br /&gt;
&lt;br /&gt;
* 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”.&lt;br /&gt;
&lt;br /&gt;
=== Frequently Asked Questions ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Should my programs register with the Application Library?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: After registering with the library, will my application become controlled by the OS or by other applications?&#039;&#039;&lt;br /&gt;
|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&#039;t want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming [[#Control Messages|control messages]].&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Doesn&#039;t the Application Library duplicate functionality already present in commodities?&#039;&#039;&lt;br /&gt;
|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]]&#039;s event stream. The Application Library&#039;s field and scope of operation is completely different (and much wider).&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Why is this a library? Wouldn&#039;t it be wiser to implement it as a class, like MUI does it?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Can a program be registered both as an application and as a commodity?&#039;&#039;&lt;br /&gt;
|A: Yes, that&#039;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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system?&#039;&#039;&lt;br /&gt;
|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&#039;t put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Minimum Application Library Support ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* Provide a short [[#Description|description]] for a better identification of the application.&lt;br /&gt;
* Tell the OS whether your application supports iconification, that is, hiding/showing its GUI. If iconification is supported:&lt;br /&gt;
** set the REGAPP_HasIconifyFeature registration tag to TRUE;&lt;br /&gt;
** make the application respond to the [[#Control Messages|APPLIBMT_Hide and APPLIBMT_Unhide]] control messages;&lt;br /&gt;
** update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies.&lt;br /&gt;
* Allow the application to be shut down externally in a safe and graceful manner in reaction to the [[#Control Messages|APPLIBMT_Quit]] message.&lt;br /&gt;
&lt;br /&gt;
Not implementing this suggested minimum means that the application manager will be unable to control your application properly and consistently (some of the manager&#039;s control buttons or menu items may appear ineffective). This could cause confusion on the part of the users and degrade their AmigaOS experience.&lt;br /&gt;
&lt;br /&gt;
== Library Opening Chores ==&lt;br /&gt;
&lt;br /&gt;
{{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.}}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Library *ApplicationBase = NULL;&lt;br /&gt;
struct ApplicationIFace *IApplication = NULL;&lt;br /&gt;
struct PrefsObjectsIFace *IPrefsObjects = NULL;&lt;br /&gt;
&lt;br /&gt;
if ( (ApplicationBase = IExec-&amp;gt;OpenLibrary(&amp;quot;application.library&amp;quot;, 52)) )&lt;br /&gt;
{&lt;br /&gt;
   IApplication  = (struct ApplicationIFace *)  IExec-&amp;gt;GetInterface(ApplicationBase, &lt;br /&gt;
                                                                    &amp;quot;application&amp;quot;, 2, NULL);&lt;br /&gt;
   IPrefsObjects = (struct PrefsObjectsIFace *) IExec-&amp;gt;GetInterface(ApplicationBase,&lt;br /&gt;
                                                                    &amp;quot;prefsobjects&amp;quot;, 2, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if ( !ApplicationBase || !IApplication || !IPrefsObjects )&lt;br /&gt;
{&lt;br /&gt;
   /* handle library opening error */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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. &#039;&#039;&#039;Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2.&#039;&#039;&#039; This is due to certain changes in the Application Library API.&lt;br /&gt;
&lt;br /&gt;
When your application has run its course, don’t forget to clean up and close both the library and its interface(s):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IPrefsObjects);&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IApplication);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(ApplicationBase);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registering the Application ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
*hint* Avoid using the  _ underscore character in the registered name. These may cause &amp;quot;charsetconvert&amp;quot; errors when the system boots up later.&lt;br /&gt;
&lt;br /&gt;
=== Application Identifiers ===&lt;br /&gt;
&lt;br /&gt;
A successfully registered application receives a numeric identifier, which in this documentation will be referred to as &#039;&#039;appID&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* the application name;&lt;br /&gt;
* an instance count (if more than one instance of the same application is started);&lt;br /&gt;
* a URL identifier (for example, the domain name of the application’s homepage – optional).&lt;br /&gt;
&lt;br /&gt;
The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.&lt;br /&gt;
&lt;br /&gt;
=== Registration Functions ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (!appID)&lt;br /&gt;
{&lt;br /&gt;
   /* report registration error and quit */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    do whatever your program does&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
IApplication-&amp;gt;UnregisterApplication(appID, NULL);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.)&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available.&lt;br /&gt;
&lt;br /&gt;
== Application Attributes ==&lt;br /&gt;
&lt;br /&gt;
An application is described by a set of &#039;&#039;attributes&#039;&#039;. There are attributes that describe the application&#039;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 &amp;quot;static&amp;quot; application features in one place.&lt;br /&gt;
&lt;br /&gt;
=== Description ===&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The application description cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Uniqueness ===&lt;br /&gt;
A program can register as a &#039;&#039;unique application&#039;&#039;, 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.&lt;br /&gt;
&lt;br /&gt;
The following function call will register Audio Monster as a unique application:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_UniqueApplication, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Quite logically, uniqueness is an attribute that cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Feature Set / Control Scope ===&lt;br /&gt;
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):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasIconifyFeature&lt;br /&gt;
|The application supports iconification and uniconification.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasPrefsWindow&lt;br /&gt;
|The application has a dedicated Preferences (Settings) window.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanCreateNewDocs&lt;br /&gt;
|The application can create new documents (projects).&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanPrintDocs&lt;br /&gt;
|The application can print documents.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_PrintDoc message.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These four attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Receiving Notifications ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_AppNotifications&lt;br /&gt;
|Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_BlankerNotifications&lt;br /&gt;
|Receive notifications concerning blanker activity.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream.&lt;br /&gt;
&lt;br /&gt;
These two attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Changing Application Attributes ===&lt;br /&gt;
&lt;br /&gt;
Certain attributes describe &amp;quot;dynamic&amp;quot; application properties and, as such, can be set or changed after registration. Some of these are &#039;&#039;state attributes&#039;&#039; 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.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AllowsBlanker&lt;br /&gt;
|Same as REGAPP_AllowsBlanker above.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppOpenedDocument&lt;br /&gt;
|Adds a new entry to the application&#039;s Last Used Documents list.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppNotifications&amp;lt;br /&amp;gt;APPATTR_BlankerNotifications&lt;br /&gt;
|Same as the two corresponding [[#Receiving Notifications|registration tags]] above.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_CanCreateNewDocs&amp;lt;br /&amp;gt;APPATTR_CanPrintDocs&amp;lt;br /&amp;gt;APPATTR_HasIconifyFeature&amp;lt;br /&amp;gt;APPATTR_HasPrefsWindow&lt;br /&gt;
|Same as the four corresponding [[#Feature Set / Control Scope|registration tags]] above.&lt;br /&gt;
|Prefer setting these properties at registration time.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_ClearLastUsedDocs&lt;br /&gt;
|Clears the application&#039;s list of last used documents.&lt;br /&gt;
|Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.)&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_FlushPrefs&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_Hidden&lt;br /&gt;
|A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not.&lt;br /&gt;
|Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_IconType&lt;br /&gt;
|Changes the application icon type.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_MainPrefsDict&lt;br /&gt;
|Allows changing the application&#039;s Prefs dictionary.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_NeedsGameMode&lt;br /&gt;
|A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode.&lt;br /&gt;
|By the &amp;quot;game mode&amp;quot; we understand a mode in which an application doesn&#039;t want to be &amp;quot;disturbed&amp;quot; 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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_SavePrefs&lt;br /&gt;
|Same as REGAPP_SavePrefs above.&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;SetApplicationAttrs(appID,&lt;br /&gt;
            APPATTR_Hidden, TRUE,&lt;br /&gt;
            APPATTR_AllowsBlanker, TRUE,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finding Applications ==&lt;br /&gt;
&lt;br /&gt;
The Application Library maintains a public list of all registered applications. Certain special-purpose programs – &#039;&#039;application managers&#039;&#039; – 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?&lt;br /&gt;
&lt;br /&gt;
Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library&#039;s messaging system. To do this you need to know at least one of the following:&lt;br /&gt;
&lt;br /&gt;
* the application name, ie. the one under which it was registered via RegisterApplication();&lt;br /&gt;
* 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;&lt;br /&gt;
* the pathname pointing to the program file on disk, e.g. “Work:Utils/AudioMonster”.&lt;br /&gt;
&lt;br /&gt;
Based on this information, the respective piece of code that will find our application in the system might look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
/* if you only know the application name */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the application name identifier */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you specifically want to talk to the second running instance */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster_1.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the pathname to the program file */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_FileName, &amp;quot;Work:Utils/AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have obtained the appID you can start communicating with the respective application.&lt;br /&gt;
&lt;br /&gt;
== Messaging ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
{{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 &#039;&#039;AppMessages&#039;&#039;. 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).&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Data Structures ===&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
The following three structures are defined for carrying Application Library message data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg&lt;br /&gt;
{&lt;br /&gt;
	struct Message msg;&lt;br /&gt;
	uint32 senderAppID;  // the appID of the sender application&lt;br /&gt;
	uint32 type;         // identifies the type of message&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationOpenPrintDocMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR fileName;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationCustomMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR customMsg;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, structure &#039;&#039;ApplicationMsg&#039;&#039; contains a standard &#039;&#039;[[Exec_Messages_and_Ports#Messages|struct Message]]&#039;&#039;, the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: &#039;&#039;ApplicationOpenPrintDocMsg&#039;&#039; is used by certain [[#Control Messages|control messages]] that require a pointer to a filename; the &#039;&#039;ApplicationCustomMsg&#039;&#039; structure is used for [[#Custom Messages|custom messages]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Control Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Message name&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Quit&lt;br /&gt;
|The application is asked to shut down itself and quit.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ForceQuit&lt;br /&gt;
|Same as before but this time the application shall quit immediately, without asking for saving documents etc.&lt;br /&gt;
|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&#039;t implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Hide&lt;br /&gt;
|The application shall hide its interface and iconify on the Workbench screen.&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Unhide&lt;br /&gt;
|The application shall come back from the iconified (hidden) state.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ToFront&lt;br /&gt;
|The application window shall come to front.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenPrefs&lt;br /&gt;
|The application shall open its preferences window.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ReloadPrefs&lt;br /&gt;
|The application shall reload its preferences.&lt;br /&gt;
|Whatever this command is useful for.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_NewBlankDoc&lt;br /&gt;
|The application shall open a new, blank document or project.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenDoc&lt;br /&gt;
|The application shall try to open a specific document or project.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_PrintDoc&lt;br /&gt;
|The application shall try to print a specific document.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Custom Messages ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The pointer to the message text string is contained in a special [[#Data Structures|data structure]], &#039;&#039;struct ApplicationCustomMsg&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Special Messages ===&lt;br /&gt;
&lt;br /&gt;
There are also some special messages that an application can receive from the library or another running application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Message name !! Description !! Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Message Handling ===&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;notification port&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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&#039;ll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void event_loop(uint32 appID)&lt;br /&gt;
{&lt;br /&gt;
 struct MsgPort              *notificationPort = NULL;&lt;br /&gt;
 struct ApplicationMsg       *appLibMsg = NULL;&lt;br /&gt;
 struct ApplicationCustomMsg *customMsg = NULL;&lt;br /&gt;
 uint32 appLibSignal = 0, sigGot = 0;&lt;br /&gt;
 BOOL   done = FALSE;&lt;br /&gt;
&lt;br /&gt;
 /* Obtain pointer to Application Library&#039;s notification port and set the signal bit. */&lt;br /&gt;
 IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;amp;notificationPort, TAG_END);&lt;br /&gt;
 appLibSignal = (1L &amp;lt;&amp;lt; notificationPort-&amp;gt;mp_SigBit);&lt;br /&gt;
 &lt;br /&gt;
 /* Go into the event loop. */&lt;br /&gt;
 while (!done)&lt;br /&gt;
  {&lt;br /&gt;
   /* Wait for a signal to arrive. */&lt;br /&gt;
   sigGot = IExec-&amp;gt;Wait(appLibSignal);&lt;br /&gt;
&lt;br /&gt;
   /* Process all Application Library messages. */&lt;br /&gt;
   if ( sigGot &amp;amp; appLibSignal )&lt;br /&gt;
    {&lt;br /&gt;
     /* Obtain pointer to the message. */&lt;br /&gt;
     while ( (appLibMsg = (struct ApplicationMsg *) IExec-&amp;gt;GetMsg(notificationPort)) )&lt;br /&gt;
      {&lt;br /&gt;
       /* Identify the type of message. */&lt;br /&gt;
       switch (appLibMsg-&amp;gt;type)&lt;br /&gt;
        {&lt;br /&gt;
         case APPLIBMT_Quit:&lt;br /&gt;
         case APPLIBMT_ForceQuit:&lt;br /&gt;
           done = TRUE;&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_ToFront:&lt;br /&gt;
           /* Make the program window come to front. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Hide:&lt;br /&gt;
           /* Iconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Unhide:&lt;br /&gt;
           /* Uniconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*  Process the custom message as you like.&lt;br /&gt;
             Here we just use printf() to output the message text. */&lt;br /&gt;
         case APPLIBMT_CustomMsg:&lt;br /&gt;
           customMsg = (struct ApplicationCustomMsg *) appLibMsg;&lt;br /&gt;
           printf(&amp;quot;The message text is: %s\n&amp;quot;, customMsg-&amp;gt; customMsg);&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*&lt;br /&gt;
             Process any other Application Library messages.&lt;br /&gt;
          */&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
       /* Return the processed message to the sender so that it can be freed. */&lt;br /&gt;
       IExec-&amp;gt;ReplyMsg( (struct Message *) appLibMsg);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Also remember that the notifications are addressed to an abstract &#039;&#039;application&#039;&#039; – this makes them rather different from IDCMP messages, which are always sent to a particular &#039;&#039;window&#039;&#039; (Intuition doesn&#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Don&#039;t do this!!! */&lt;br /&gt;
case APPLIBMT_ToFront:&lt;br /&gt;
  IIntuition-&amp;gt;ScreenToFront(win-&amp;gt;WScreen);&lt;br /&gt;
  IIntuition-&amp;gt;WindowToFront(win);&lt;br /&gt;
  IIntuition-&amp;gt;ActivateWindow(win);&lt;br /&gt;
break;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
=== Sending Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
# Prepare the respective [[#Data Structures|data structure]]: specify the type of message and supply the sender&#039;s [[#Application Identifiers|identifier]] (appID).&lt;br /&gt;
# [[#Finding Applications|Find the receiver]] application.&lt;br /&gt;
# Send a message to it via the SendApplicationMsg() function.&lt;br /&gt;
&lt;br /&gt;
For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg appMsg;    // message data structure&lt;br /&gt;
uint32 audioMonsterID;           // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Step 1: Prepare the message data structure. */&lt;br /&gt;
appMsg.senderAppID = appID;      // identifier of the sender&lt;br /&gt;
appMsg.type = APPLIBMT_Quit;     // type of message&lt;br /&gt;
&lt;br /&gt;
/* Step 2: Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Step 3: Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 &amp;amp;appMsg,&lt;br /&gt;
                 APPLIBMT_Quit);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0.&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;struct ApplicationMsg&#039;&#039;. 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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationCustomMsg customMsg;      // custom message data structure&lt;br /&gt;
uint32 audioMonsterID;                      // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Prepare the message data structure. */&lt;br /&gt;
customMsg.almsg.senderAppID = appID;        // identifier of the sender&lt;br /&gt;
customMsg.almsg.type = APPLIBMT_CustomMsg;  // type of message&lt;br /&gt;
customMsg.customMsg = &amp;quot;Start playback&amp;quot;;     // message text&lt;br /&gt;
&lt;br /&gt;
/* Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 (struct ApplicationMsg *) &amp;amp;customMsg,&lt;br /&gt;
                 APPLIBMT_CustomMsg);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pop-up Notifications (Ringhio Messages) ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;Ringhio messages&#039;&#039; 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).&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;All right, ma!&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&#039;t mean “Yes” or “No”.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;URL:http://www.supercoders.com&amp;quot;,&lt;br /&gt;
               APPNOTIFY_CloseOnDC, TRUE,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pop-up Message Style Guide ===&lt;br /&gt;
&lt;br /&gt;
* Keep the message text short. Give the user chance to read the entire message before it disappears.&lt;br /&gt;
* 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).&lt;br /&gt;
* Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| FindApplication()&lt;br /&gt;
| Searches for a previously registered application.&lt;br /&gt;
|-&lt;br /&gt;
| FreeApplicationList()&lt;br /&gt;
| Frees the list of applications generated by GetApplicationList().&lt;br /&gt;
|-&lt;br /&gt;
| GetAppLibAttrs()&lt;br /&gt;
| Obtains global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationAttrs()&lt;br /&gt;
| Obtains attributes of a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationList()&lt;br /&gt;
| Obtains the list of all currently registered applications.&lt;br /&gt;
|-&lt;br /&gt;
| LockApplicationIcon()&lt;br /&gt;
| Attempts to lock an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| Notify()&lt;br /&gt;
| Displays a pop-up message box.&lt;br /&gt;
|-&lt;br /&gt;
| RegisterApplication()&lt;br /&gt;
| Registers an application.&lt;br /&gt;
|-&lt;br /&gt;
| SendApplicationMsg()&lt;br /&gt;
| Sends a message to a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| SetAppLibAttrs()&lt;br /&gt;
| Sets or changes global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| SetApplicationAttrs()&lt;br /&gt;
| Sets or changes application attributes.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockApplicationIcon()&lt;br /&gt;
| Unlocks an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| UnregisterApplication()&lt;br /&gt;
| Unregisters an application.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=9872</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=9872"/>
		<updated>2018-12-30T21:04:42Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed spelling, added two sub-headings.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RealTime Library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. The library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.&lt;br /&gt;
&lt;br /&gt;
= Conductors and Playerlnfos =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e. a group of applications that want to be synced together). &lt;br /&gt;
&lt;br /&gt;
Each Conductor can have one or more client applications. A second structure called a PlayerInfo is set up by each client application that wants to get timing information from the Conductor. There is typically one Playerlnfo for each task that wants to get timing information (a task could have more than one but this would be unusual).&lt;br /&gt;
&lt;br /&gt;
Both the Conductor and Playerlnfo structures are dynamic. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also, the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.&lt;br /&gt;
&lt;br /&gt;
An application will usually create a single PlayerInfo structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a PlayerInfo structure you call CreatePlayerO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct PlayerInfo *pi = IRealTime-&amp;gt;CreatePlayer(Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This call takes a list of tag items that describe the attributes of the Playerlnfo structure you want to create. (A complete list of all the tags available can be found in the Autodoc for SetPlayerAttrs().) It returns a pointer to the PlayerInfo. Here’s a fragment showing how to set up a PlayerInfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
  PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
  PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (pPlayerInfo != NULL )&lt;br /&gt;
{&lt;br /&gt;
  // Your real-time application goes here...&lt;br /&gt;
&lt;br /&gt;
  IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above, a PlayerInfo will be created with the name of &amp;quot;My_player&amp;quot;. It will be attached to the Conductor structure named &amp;quot;My_conductor&amp;quot;. If a Conductor structure named &amp;quot;My_conductor&amp;quot; does not already exist, the system will create one. Other applications could also attach their PlayerInfos to &amp;quot;My_conductor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
When your application finishes, you should delete any PlayerInfos you have created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however, this won’t happen until &#039;&#039;&#039;all&#039;&#039;&#039; the PlayerInfos attached to a Conductor are deleted).&lt;br /&gt;
&lt;br /&gt;
Once you have a PlayerInfo and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.&lt;br /&gt;
&lt;br /&gt;
= Getting Clock Ticks =&lt;br /&gt;
&lt;br /&gt;
The RealTime Library uses a tick frequency of 600 Hz so clock pulses are delivered approximately every 1.66 ms. There are two ways to get this timing information:&lt;br /&gt;
&lt;br /&gt;
* An alarm&#039;s signal &lt;br /&gt;
* A clock tick callback hook &lt;br /&gt;
&lt;br /&gt;
== Using the alarm facility ==&lt;br /&gt;
&lt;br /&gt;
You can ask the RealTime Library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.&lt;br /&gt;
&lt;br /&gt;
The fragment below shows how to set up the library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the RealTime Library interface is already obtained).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// This fragment assumes the that IRealTime is already obtained.&lt;br /&gt;
int8 midiSignal = IExec-&amp;gt;AllocSignal(-1);  // Allocate a wake-up signal bit&lt;br /&gt;
if (midiSignal != -1)&lt;br /&gt;
{&lt;br /&gt;
  struct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_SignalTask, IExec-&amp;gt;FindTask(NULL),&lt;br /&gt;
    PLAYER_AlarmSigBit, midiSignal,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Start the realtime clock running.&lt;br /&gt;
    int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
    if (!res)&lt;br /&gt;
    {&lt;br /&gt;
      BOOL timerr = IRealTime-&amp;gt;SetPlayerAttrs(pPlayerInfo,&lt;br /&gt;
        PLAYER_AlarmTime, 1000,&lt;br /&gt;
        PLAYER_Ready, TRUE,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
      if (timerr)&lt;br /&gt;
      {&lt;br /&gt;
        // You could do some other job before&lt;br /&gt;
        // calling Wait() on the alarm signal.&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; midiSignal);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Print(&amp;quot;Couldn&#039;t set alarm\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t start clock\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t set up PlayerInfo structure.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t allocate signal.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the fragment above, the calling task requests a PlayerInfo with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the RealTime Library when the alarm goes off. The PLAYER_SignalTask tag indicates which task will be signaled.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is then started by calling SetConductorState() (discussed below). This is important since any alarm requests made when the clock is stopped will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result = SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter indicates which Playerlnfo structure is to have its attributes changed. The Tag items indicate the attributes and their new values. If the change is made successfully, then TRUE is returned. FALSE indicates failure. In the fragment above, a wake-up time is &lt;br /&gt;
requested using the PLAYER_A1armTime tag. Also, the calling task indicates to the Conductor that it is ready by using the PLAYER_Ready tag (more on this below).&lt;br /&gt;
&lt;br /&gt;
At this point, the call to Wait(1UL &amp;lt;&amp;lt; midiSignal) causes the task to sleep until time = 1000 ticks.&lt;br /&gt;
&lt;br /&gt;
== Using the clock tick callback facility ==&lt;br /&gt;
&lt;br /&gt;
The discussion so far has concentrated on the alarm facility of the RealTime Library. An even finer level of control over time is available using the clock tick callback hook facility. Instead of setting an alarm to signal your task at some future time, the hook facilities allow your application code to be invoked whenever Conductor time is updated.&lt;br /&gt;
&lt;br /&gt;
The realtime clock is driven by an interrupt that simply increments the base time and then uses software interrupts to distribute the time to any Conductors. By using a callback hook, you can have your custom code invoked at the software interrupt level (before tasks) &lt;br /&gt;
whenever Conductor time is refreshed.&lt;br /&gt;
&lt;br /&gt;
To set up a callback hook, you use the PLAYER_Hook tag with the address of a standard Hook structure as defined in &amp;lt;utilities/hook.h&amp;gt;. This structure contains the address of the code you want to be invoked whenever the RealTime Library updates your Conductor. Here’s a code fragment showing how this is done:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Task *My_task;&lt;br /&gt;
int8 My_signal;&lt;br /&gt;
&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // ...Open the RealTime Library and do other set up here...&lt;br /&gt;
&lt;br /&gt;
  struct Hook My_hook;&lt;br /&gt;
  My_hook.h_Entry = (HOOKFUNC)My_hookFunc;&lt;br /&gt;
  &lt;br /&gt;
  uint32 ticks = 1000;&lt;br /&gt;
  stuct PlayerInfo *pPlayerInfo = IRealTime-&amp;gt;CreatePlayer(&lt;br /&gt;
    PLAYER_Name, &amp;quot;My_player&amp;quot;,&lt;br /&gt;
    PLAYER_Conductor, &amp;quot;My_conductor&amp;quot;,&lt;br /&gt;
    PLAYER_Hook, &amp;amp;My_hook,&lt;br /&gt;
    PLAYER_UserData, &amp;amp;ticks,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
    &lt;br /&gt;
  if (pPlayerInfo != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    My_task   = IExec-&amp;gt;FindTask(NULL);  // Initialize these globals so that&lt;br /&gt;
    My_signal = IExec-&amp;gt;AllocSignal(-1); // the hook function can signal us.&lt;br /&gt;
    if (My_signal != -1)&lt;br /&gt;
    {&lt;br /&gt;
      // Start the clock running.&lt;br /&gt;
      int32 res = IRealTime-&amp;gt;SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);&lt;br /&gt;
      if (!res)&lt;br /&gt;
      {&lt;br /&gt;
        // ...your code goes here...&lt;br /&gt;
        IExec-&amp;gt;Wait(1UL &amp;lt;&amp;lt; My_signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      IExec-&amp;gt;FreeSignal(My_signal);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    IRealTime-&amp;gt;DeletePlayer(pPlayerInfo);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ...Close the library, etc...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the code above the function named My_hookFunc() will be called by the RealTime Library whenever it updates Conductor time.&lt;br /&gt;
&lt;br /&gt;
Here’s the callback hook function itself. This simply compares Conductor time to a variable, ticks, whose address is pointed to by pi­&amp;gt;pi_UserData. Notice how this address was filled in using the PLAYER_UserData tag in the call to SetPlayerAttrs() in main() above. When &lt;br /&gt;
Conductor time equals or exceeds ticks, the hook function signals the main task.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// Hook code called whenever the RealTime Library updates Conductor time.&lt;br /&gt;
// Normally this is 600 times/sec.&lt;br /&gt;
uint32 My_hookFunc(struct Hook *hook, struct pmTime *msg, struct PlayerInfo *pi)&lt;br /&gt;
{&lt;br /&gt;
  switch (msg-&amp;gt;pmt_Method)&lt;br /&gt;
  {&lt;br /&gt;
   case PM_TICK:&lt;br /&gt;
     // Test whether Conductor time has exceeded the number in *ticks*.&lt;br /&gt;
     // If it has, then signal the parent task.&lt;br /&gt;
     if ((*uint32*)(pi-&amp;gt;pi_Userdata)) &amp;lt; pi-&amp;gt;pi_Source-&amp;gt;cdt_ClockTime)&lt;br /&gt;
       IExec-&amp;gt;Signal(My_task, 1UL &amp;lt;&amp;lt; My_signal);&lt;br /&gt;
     break;&lt;br /&gt;
&lt;br /&gt;
   default:&lt;br /&gt;
     break;     &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= More About Conductors =&lt;br /&gt;
&lt;br /&gt;
It is the job of the Conductor to act as middleman between the Amiga&#039;s timing hardware and client tasks represented by PlayerInfo structures. Each Conductor keeps track of:&lt;br /&gt;
* an Exec list of all its client players &lt;br /&gt;
* the state of the conductor (i.e. running, stopped, paused, locating) &lt;br /&gt;
* what time it is relative to start time&lt;br /&gt;
* whether the Conductor is using the Amiga’s internal hardware for its timing pulses or an external source&lt;br /&gt;
&lt;br /&gt;
As shown in the examples above, the &amp;quot;state&amp;quot; of the Conductor can be changed at any time by calling SetConductorState(). If the call succeeds, zero is returned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 res = IRealTime-&amp;gt;SetConductorState(struct Playerlnfo *pi, int32 newstate, int32 ti);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pi parameter is a pointer to a PlayerInfo structure that is linked to the Conductor you want to change. The ti parameter is a time offset used for special cases (typically set to zero). The &#039;&#039;newstate&#039;&#039; parameter is one of the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLOCKSTATE_STOPPED || The clock is not running&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_PAUSED || To the RealTime Library, this is exactly the same as stopped. This is provided as a convenience to those applications that wish to make a distinction between the two. &lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_RUNNING || The clock is running and time pulses are being distributed to any client applications (Playerlnfos) that are ready.&lt;br /&gt;
|-&lt;br /&gt;
| CLOCKSTATE_LOCATE || This is the same as running with one exception: the clock docs not actually start until &#039;&#039;all&#039;&#039; client applications have indicated that they are ready by setting the PLAYER_Ready attribute of their PlayerInfo to TRUE. This allows applications that cannot start immediately to set up before timing pulses actually begin.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The difference between locating and running requires some explanation. Each Playerlnfo has a &amp;quot;ready bit&amp;quot; which is used to tell the RealTime Library that the player is ready to receive ticks. This bit is reset each time the library relocates the clock to a new time.&lt;br /&gt;
&lt;br /&gt;
The reason for the ready bit is that some applications need to find the appropriate location in the multimedia sequence or score before they can start playing at the new location. In some cases this can take a considerable amount of time. Hence, CLOCKSTATE__LOCATE is used with the ready bit to allow all players that are sharing a timing context to be synchronized together.&lt;br /&gt;
&lt;br /&gt;
Players can set the state of their ready bit by using the PLAYER_Ready tag attribute either when the Playerlnfo is created with CreatePlayer() or later with SetPlayerAttrs(). See the first code fragment above for an example of this.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BOOPSI_101&amp;diff=9337</id>
		<title>BOOPSI 101</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BOOPSI_101&amp;diff=9337"/>
		<updated>2018-06-02T20:02:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Minor corrections (typos, dead link).&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In this section we will explore a simple example of creating, interacting with and disposing a simple BOOPSI object - a requester window.  With these simple objects you can see the basic operations that are common to almost all BOOPSI objects.  When using BOOPSI gadget objects there may be some changes in syntax (f.e., using &#039;&#039;&#039;SetGadgetAttrs()&#039;&#039;&#039; instead of &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039;), but the concepts remain the same.&lt;br /&gt;
&lt;br /&gt;
== Creating an Object ==&lt;br /&gt;
&lt;br /&gt;
The Intuition functions &#039;&#039;&#039;NewObject()&#039;&#039;&#039; / &#039;&#039;&#039;NewObjectA()&#039;&#039;&#039; create a BOOPSI object using either &amp;quot;stack&amp;quot; of tags or predefined a tag list and returns a pointer to a new BOOPSI object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Object *object = NewObject(Class *cl, ClassID classID, Tag tag1, ...);&lt;br /&gt;
Object *object = NewObjectA(Class *cl, ClassID classID, const struct TagItem *tags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As mentioned previously (see [[BOOPSI#BOOPSI_and_Tags|BOOPSI and Tags]]), the &#039;&#039;&#039;NewObject()&#039;&#039;&#039; function has a variant that uses predefined tag lists (&#039;&#039;&#039;NewObjectA()&#039;&#039;&#039;), but here we will continue with the more commonly used stack-based variant.&lt;br /&gt;
&lt;br /&gt;
In general, BOOPSI objects are &amp;quot;black boxes&amp;quot;. This means the inner workings of BOOPSI objects are not visible to the application programmer, so the programmer does not know what goes on inside it. This really means the inner workings of these objects are none of your business. Unless otherwise documented, only use an object pointer as a handle to the object.&lt;br /&gt;
&lt;br /&gt;
To create an object, &#039;&#039;&#039;NewObject()&#039;&#039;&#039; first needs to know whether we are creating an object from a public or private class. Typically any objects created from system gadgets are all public classes. In such cases, the &#039;&#039;&#039;cl&#039;&#039;&#039; argument (a class pointer) would be set to NULL and &#039;&#039;&#039;classID&#039;&#039;&#039; argument set to the public name of the class (a text string like &amp;quot;requesterclass&amp;quot; or &amp;quot;string.gadget&amp;quot;). If one wanted to create an object of a private class, the &#039;&#039;&#039;cl&#039;&#039;&#039; argument would be a pointer to that class and the &#039;&#039;&#039;classID&#039;&#039;&#039; would be ignored.&lt;br /&gt;
&lt;br /&gt;
Regardless of whether the tag list is defined elsewhere or within this function call, some list of &amp;quot;tags&amp;quot; is next proivided to define the characteristics of the new object.  Each &amp;quot;tag&amp;quot; consists of a pair of values - each with specifically named &amp;quot;attribute&amp;quot; and it&#039;s proposed &amp;quot;value&amp;quot;.  Some general examples of such tags might be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     GA_Left,         100,&lt;br /&gt;
     REQ_TitleText,   &amp;quot;Testing...&amp;quot;,&lt;br /&gt;
     WA_DragBar,      TRUE,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, we can use the &#039;&#039;&#039;NewObject{}&#039;&#039;&#039; function along with a &amp;quot;stack&amp;quot; of &amp;quot;tags&amp;quot; containing basic parameters to create a new requester object like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     reqobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;requester.class&amp;quot;,&lt;br /&gt;
          REQ_Type,        REQTYPE_INFO,&lt;br /&gt;
          REQ_TitleText,   &amp;quot;Requesters Example&amp;quot;,&lt;br /&gt;
          REQ_BodyText,    buffer,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, the variable &#039;&#039;&#039;reqobj&#039;&#039;&#039; will contain a pointer to new a BOOPSI requester object.  If unsuccessful, &#039;&#039;&#039;reqobj&#039;&#039;&#039; will be NULL.  This is a place for a developer to apply some error trapping when an object couldn&#039;t be created - maybe our application ran out of memory? Maybe some of the parameters (tags) were invalid?&lt;br /&gt;
&lt;br /&gt;
Once the requester object has been created, we still won&#039;t see anything yet.  Our new object is waiting in the wings for a BOOPSI message to show itself.  This leads us to the next sections where we interact with the object we created.&lt;br /&gt;
&lt;br /&gt;
== Passing messages to an Object ==&lt;br /&gt;
&lt;br /&gt;
To communicate with BOOPSI objects we send &amp;quot;BOOPSI Messages&amp;quot;.  Unlike traditional AmigaOS &amp;quot;Messages&amp;quot; used by the Exec, ARexx and for interprocess communications, BOOPSI messages are different and frequently rely on tag lists for their content.  To &amp;quot;send&amp;quot; a BOOPSI Message to an existing object, we use the &#039;&#039;&#039;IDoMethod&#039;&#039;&#039; function (or its tag list based variant):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG IDoMethod(Object *myobject, ULONG methodID, ...);&lt;br /&gt;
ULONG IDoMethodA(Object *myobject, Msg boopsimessage);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The return value is class-dependent. The first argument to both of these functions points to the object that will receive the BOOPSI message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IDoMethod()&#039;&#039;&#039; then depends on the class and method being used for the contents of the rest of the message. Those elements may include a method name, tags, text or pointers. The class documentation will described those as well as whether the class expects any certain order of tags, etc.&lt;br /&gt;
&lt;br /&gt;
For a simple example, we can ask the BOOPSI requester object &#039;&#039;&#039;reqobj&#039;&#039;&#039; (created above) to show itself by sending this message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, the &#039;&#039;&#039;RM_OPENREQ&#039;&#039;&#039; method of the Requester Class expects the elements of its &#039;&#039;&#039;orRequest&#039;&#039;&#039; structure.  The class docs point us to a header file that explains we need to start with the &#039;&#039;&#039;MethodID&#039;&#039;&#039; and then pointers to a tag list, window and a screen structures. In our simple example above, we are passing no arguments, so our call has three NULL&#039;s for these elements.  &lt;br /&gt;
&lt;br /&gt;
When one calls &#039;&#039;IDoMethod()&#039;&#039;&#039; with the &#039;&#039;&#039;RM_OPENREQ&#039;&#039;&#039; method on a requester object, the requester appears and the application waits for the user input.  Given the simple &amp;quot;information&amp;quot; style requester we created (with default buttons), the result will simply numerically indicate which button was selected by the user.&lt;br /&gt;
&lt;br /&gt;
With the &#039;&#039;&#039;IDoMethodA()&#039;&#039;&#039; variant, all of the above arguments have to be combined into a complete BOOPSI Message structure, &#039;&#039;&#039;boopsimessage&#039;&#039;&#039;.  Again the layout of depends on the class and method.  Every method&#039;s message starts off with a &#039;&#039;&#039;Msg&#039;&#039;&#039; (from &amp;amp;lt;intuition/classusr.h&amp;amp;gt;) and is followed by the class specific tags:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
    ULONG MethodID; /* Method-specific data may follow this field */&lt;br /&gt;
} *Msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As mentioned above, there are variants of the &#039;&#039;&#039;IDoMethod()&#039;&#039;&#039; functions for specific uses, such as the &#039;&#039;&#039;DoGadgetMethod()&#039;&#039;&#039; function.  In that case, the function passes additional information pertinent to GUI gadget objects.&lt;br /&gt;
&lt;br /&gt;
== Setting an Object&#039;s Attributes ==&lt;br /&gt;
&lt;br /&gt;
Every BOOPSI object is defined by a number of attributes.  In the above example we defined our requester when we created the object with &#039;&#039;&#039;NewObject()&#039;&#039;&#039;.  But an object&#039;s attributes are not necessarily static.  An application can use the  &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to change or set other attributes on an object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 IIntuition-&amp;gt;SetAttrs( Object * object, Tag tag1, ... );&lt;br /&gt;
     uint32 IIntuition-&amp;gt;SetAttrsA( Object * object, const struct TagItem * tagList );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In each case we have to provide a pointer to our object and then tag pairs with the Attribute identifiers and the replacement values.  As an example, we can use the &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to reset the type and contents of our requester object from above and reuse the object to open a second, different requester for the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     strcpy(buffer,&amp;quot;default string&amp;quot;);&lt;br /&gt;
     result = IIntuition-&amp;gt;SetAttrs( reqobj,&lt;br /&gt;
          REQ_Type,				REQTYPE_STRING,&lt;br /&gt;
          REQ_BodyText,			&amp;quot;please enter some text&amp;quot;,&lt;br /&gt;
          REQS_Buffer,			buffer,&lt;br /&gt;
          REQS_MaxChars,			sizeof(buffer) - 1,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
     result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these changes, we&#039;ve converted our simple requester with a couple buttons into a string requester.  For any attributes we did not reset here, the previous settings or the default values of the class will continue to be used.&lt;br /&gt;
&lt;br /&gt;
Not all object attributes can be set with &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; and this is dependent on each class.  For more information about which attributes can be set in specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] and the class AutoDocs and look for those attributes with a &#039;&#039;&#039;OM_SET&#039;&#039;&#039; listed as an &amp;quot;applicability&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Again there is variant of the &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to be used with BOOPSI GUI gadgets: &#039;&#039;&#039;SetGadgetAttrs()&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Getting an Object&#039;s Attributes ==&lt;br /&gt;
&lt;br /&gt;
Just as we can set attributes of an existing BOOPSI object, we also have the ability to get the current settings from a object&#039;s attributes.  Inquiring about a specific setting is done using the BOOPSI function &#039;&#039;&#039;GetAttr()&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 attr = GetAttr(uint32 attrID, Object *object, uint32 *storage);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We start by specifying the &#039;&#039;&#039;attrID&#039;&#039;&#039; is the attribute&#039;s identifier we are interested in, we then have to identify object to get the attribute from (&#039;&#039;&#039;object&#039;&#039;&#039;), and finally provide an area that will hold the value of the attribute. This function returns a 0 if the object doesn&#039;t recognize or return the attribute, otherwise it returns some non-zero value, the meaning of which depends on the class. In most cases, GetAttr() returns a 1 when it is successful.&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, there are only a few attributes to which values can be obtained.  The following example will obtain the result code from the last use of the object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 lastresult;&lt;br /&gt;
     result = IIntuition-&amp;gt;GetAttr(REQ_ReturnCode, reqobj,&amp;amp;lastresult);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another example of the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; function would be to retrieve the value a user provided in an Interger type requester.  This information could be restrieved like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     int32 userInt;&lt;br /&gt;
     result = IIntuition-&amp;gt;GetAttr(REQI_Number, reqobj,&amp;amp;userInt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While the simple requester class is not a great example for getting a lot of attributes, BOOPSI provides a second pair of functions that uses taglists for obtaining multiple attributes in one call:  &#039;&#039;&#039;GetAttrs()&#039;&#039;&#039; and &#039;&#039;&#039;GetAttrsA&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 retval = GetAttrs(Object *object, Tag tag1, ...);&lt;br /&gt;
     uint32 retval = GetAttrsA(Object *object, struct TagItem *attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with the previous tag functions mentioned above, these functions are the same except for how the tag list is handled.  In the case of each tag pair, the value will be set to the current value in the object queried.&lt;br /&gt;
&lt;br /&gt;
As with setting attributes, not all object attributes are obtainable using the &#039;&#039;&#039;GetAttr()&#039;&#039;&#039; function, depending on each class.  For more information about which attributes can be obtained in specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] and the class AutoDocs and look for those attributes with a &#039;&#039;&#039;OM_GET&#039;&#039;&#039; listed as an &amp;quot;applicability&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Unlike when creating and setting attributes on BOOPSI GUI gadgets, there are no GUI gadgets specific versions of the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; family of functions.  Instead, the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; functions can also be used to obtain GUI gadget attributes where they are available (see the Class Reference and autodocs).&lt;br /&gt;
&lt;br /&gt;
== Disposing of an Object ==&lt;br /&gt;
&lt;br /&gt;
When an application is finally done with an object, it has to dispose of the object. To dispose of an object, use the Intuition function &#039;&#039;&#039;DisposeObject()&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void DisposeObject(Object *obj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As usual, we use a pointer to our object to indicate the object to be disposed.&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, we would simply call:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     IIntuition-&amp;gt;DisposeObject(reqobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In many cases - complex gadget GUI&#039;s being the prime example - when a BOOPSI object has connect &amp;quot;child&amp;quot; objects and it is disposed, then all children are also disposed.  An example would be a window object and all the gadgets within that window.  In any case, you must be careful not to dispose of an object that already been disposed.&lt;br /&gt;
&lt;br /&gt;
== What About the BOOPSI Messages and Methods? ==&lt;br /&gt;
&lt;br /&gt;
In the above sections of this page, the functions used reflect BOOPSI messages and the underlying basic &amp;quot;methods&amp;quot; performed by a BOOPSI object.  In the [[BOOPSI#OOP_Overview|&amp;quot;OOP Overview&amp;quot;]] section we see how these saw how these common methods are practically universal to all BOOPSI classes.  The above functions simply provide a wrapper for those common methods like this:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NewObject() || OM_NEW&lt;br /&gt;
|-&lt;br /&gt;
| SetAttrs()/SetGadgetAttrs() || OM_SET&lt;br /&gt;
|-&lt;br /&gt;
| GetAttr() || OM_GET&lt;br /&gt;
|-&lt;br /&gt;
| DisposeObject() || OM_DISPOSE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These methods are defined on the rootclass level, so all BOOPSI classes inherit them. The Intuition functions that correspond to these methods take care of constructing and sending a BOOPSI message with the appropriate method ID and parameters.&lt;br /&gt;
&lt;br /&gt;
One last function had a more direct relationship to operation and methods of BOOPSI objects - in this case, causing our requester object to present itself :&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| IDoMethod() || RM_OPENREQ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of course, in the case of different classes and objects, the method would change with how the &#039;&#039;&#039;IDoMethod&#039;&#039;&#039; function was called on those objects.&lt;br /&gt;
&lt;br /&gt;
== Example Program ==&lt;br /&gt;
&lt;br /&gt;
Below is the complete example program that includes the above excerpts.  This program will create a requester object, display a requester with buttons and one with a string gadget and print the results in the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Requesters Example&lt;br /&gt;
gcc -o Requesters Requesters.c -lauto&lt;br /&gt;
quit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
*&lt;br /&gt;
************************************************************&lt;br /&gt;
**&lt;br /&gt;
** Created by: CodeBench 0.31 (20.5.2013)&lt;br /&gt;
**&lt;br /&gt;
** Project: OS4ex-Requesters&lt;br /&gt;
**&lt;br /&gt;
** A simple requester.class example used to demonstrate functionality.&lt;br /&gt;
**&lt;br /&gt;
** File: Requesters&lt;br /&gt;
**&lt;br /&gt;
** Date: 20-04-2013 20:43:23&lt;br /&gt;
**&lt;br /&gt;
** Version:   02&lt;br /&gt;
**&lt;br /&gt;
************************************************************&lt;br /&gt;
*&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*		includes&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/requester.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/requester.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*		declarations&lt;br /&gt;
 */&lt;br /&gt;
Object *reqobj;&lt;br /&gt;
struct orRequest reqmsg;&lt;br /&gt;
char buffer[100];&lt;br /&gt;
uint32 result = 0;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
	/*		create a new requester object&lt;br /&gt;
	 */&lt;br /&gt;
	strcpy(buffer,&amp;quot;Welcome to your\nfirst BOOPSI requester&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	reqobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;requester.class&amp;quot;,&lt;br /&gt;
		REQ_Type,				REQTYPE_INFO,&lt;br /&gt;
		REQ_TitleText,			&amp;quot;Requesters Example&amp;quot;,&lt;br /&gt;
		REQ_BodyText,			buffer,&lt;br /&gt;
		TAG_END);&lt;br /&gt;
	&lt;br /&gt;
	// Was the requester object created?&lt;br /&gt;
	if (reqobj)&lt;br /&gt;
	{&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;   Requester object created\n&amp;quot;);&lt;br /&gt;
		/*		tell the object to show itself&lt;br /&gt;
		 */&lt;br /&gt;
		result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
		// How did things go?&lt;br /&gt;
		if (result)&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #1 - result code = %ld\n&amp;quot;,result);&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Req #1 - result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		reset object characteristics&lt;br /&gt;
		 */&lt;br /&gt;
		&lt;br /&gt;
		strcpy(buffer,&amp;quot;default string&amp;quot;);&lt;br /&gt;
		result = IIntuition-&amp;gt;SetAttrs( reqobj,&lt;br /&gt;
			REQ_Type,				REQTYPE_STRING,&lt;br /&gt;
			REQ_BodyText,			&amp;quot;please enter some text&amp;quot;,&lt;br /&gt;
			REQS_Buffer,			buffer,&lt;br /&gt;
			REQS_MaxChars,			sizeof(buffer) - 1,&lt;br /&gt;
			TAG_END);&lt;br /&gt;
		&lt;br /&gt;
		/*		tell object to show itself again&lt;br /&gt;
		 */&lt;br /&gt;
		result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
		// Did we get a good response?&lt;br /&gt;
		if (result&amp;gt;0)&lt;br /&gt;
		{&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #2 - result code = %ld\n&amp;quot;,result);&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #2 - result text = %s\n&amp;quot;,buffer);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Req #2 - result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		Lets check some object attributes&lt;br /&gt;
		 */&lt;br /&gt;
		uint32 lastresult;&lt;br /&gt;
		&lt;br /&gt;
		result = IIntuition-&amp;gt;GetAttr(REQ_ReturnCode, reqobj,&amp;amp;lastresult);&lt;br /&gt;
		// Did we get some value?&lt;br /&gt;
		if (result&amp;gt;0)&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Last result code = %ld\n&amp;quot;,lastresult);&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   This result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		Dispose of our requester object&lt;br /&gt;
		 */&lt;br /&gt;
		IIntuition-&amp;gt;DisposeObject(reqobj);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;   ERROR: Failed to create Requester object\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BOOPSI_101&amp;diff=9336</id>
		<title>BOOPSI 101</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BOOPSI_101&amp;diff=9336"/>
		<updated>2018-06-02T19:45:39Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed the size of chapter headings.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In this section we will explore a simple example of creating, interacting with and disposing a simple BOOPSI object - a requester window.  With these simple objects you can see the basic operations that are common to almost all BOOPSI objects.  When using BOOPSI gadget objects there may be some changes in syntax (f.e., using &#039;&#039;&#039;SetGadgetAttrs()&#039;&#039;&#039; instead of &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039;), but the concepts remain the same.&lt;br /&gt;
&lt;br /&gt;
== Creating an Object ==&lt;br /&gt;
&lt;br /&gt;
The Intuition functions &#039;&#039;&#039;NewObject()&#039;&#039;&#039; / &#039;&#039;&#039;NewObjectA()&#039;&#039;&#039; create a BOOPSI object using either &amp;quot;stack&amp;quot; of tags or predefined a tag list and returns a pointer to a new BOOPSI object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Object *object = NewObject(Class *cl, ClassID classID, Tag tag1, ...);&lt;br /&gt;
Object *object = NewObjectA(Class *cl, ClassID classID, const struct TagItem *tags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As mentioned previously (see [[BOOPSI#OOPSI_and_Tags|BOOPSI and Tags]]), the &#039;&#039;&#039;NewObject()&#039;&#039;&#039; function has a variant that uses predefined tag lists (&#039;&#039;&#039;NewObjectA()&#039;&#039;&#039;), but here we will continue with the more commonly used stack based variant.&lt;br /&gt;
&lt;br /&gt;
In general, BOOPSI objects are &amp;quot;black boxes&amp;quot;. This means the inner workings of BOOPSI objects are not visible to the application programmer, so the programmer does not know what goes on inside it. This really means the inner workings of these objects are none of your business. Unless otherwise documented, only use an object pointer as a handle to the object.&lt;br /&gt;
&lt;br /&gt;
To create an object, &#039;&#039;&#039;NewObject()&#039;&#039;&#039; first needs to know whether we are creating an object from a public or private class.  Typically any objects created from system gadgets are all public classes.   In such cases, the &#039;&#039;&#039;cl&#039;&#039;&#039; argument (a pointer) would be set to NULL and &#039;&#039;&#039;classID&#039;&#039;&#039; argument set to the text name of the class (like &amp;quot;requesterclass&amp;quot; or &amp;quot;string.gadget&amp;quot;).  If one wanted to create an object of a private class, the &#039;&#039;&#039;cl&#039;&#039;&#039; argument would be a pointer to that class and the &#039;&#039;&#039;classID&#039;&#039;&#039; would be ignored.&lt;br /&gt;
&lt;br /&gt;
Regardless of whether the tag list is defined elsewhere or within this function call, some list of &amp;quot;tags&amp;quot; is next proivided to define the characteristics of the new object.  Each &amp;quot;tag&amp;quot; consists of a pair of values - each with specifically named &amp;quot;attribute&amp;quot; and it&#039;s proposed &amp;quot;value&amp;quot;.  Some general examples of such tags might be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     GA_Left,         100,&lt;br /&gt;
     REQ_TitleText,   &amp;quot;Testing...&amp;quot;,&lt;br /&gt;
     WA_DragBar,      TRUE,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, we can use the &#039;&#039;&#039;NewObject{}&#039;&#039;&#039; function along with a &amp;quot;stack&amp;quot; of &amp;quot;tags&amp;quot; containing basic parameters to create a new requester object like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     reqobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;requester.class&amp;quot;,&lt;br /&gt;
          REQ_Type,        REQTYPE_INFO,&lt;br /&gt;
          REQ_TitleText,   &amp;quot;Requesters Example&amp;quot;,&lt;br /&gt;
          REQ_BodyText,    buffer,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, the variable &#039;&#039;&#039;reqobj&#039;&#039;&#039; will contain a pointer to new a BOOPSI requester object.  If unsuccessful, &#039;&#039;&#039;reqobj&#039;&#039;&#039; will be NULL.  This is a place for a developer to apply some error trapping when an object couldn&#039;t be created - maybe our application ran out of memory?  maybe some of the parameters (tags) were invalid?&lt;br /&gt;
&lt;br /&gt;
Once the requester object has been created, we still won&#039;t see anything yet.  Our new object is waiting in the wings for a BOOPSI message to show itself.  This leads us to the next sections where we interact with the object we created.&lt;br /&gt;
&lt;br /&gt;
== Passing messages to an Object ==&lt;br /&gt;
&lt;br /&gt;
To communicate with BOOPSI objects we send &amp;quot;BOOPSI Messages&amp;quot;.  Unlike traditional AmigaOS &amp;quot;Messages&amp;quot; used by the Exec, ARexx and for interprocess communications, BOOPSI messages are different and frequently rely on tag lists for their content.  To &amp;quot;send&amp;quot; a BOOPSI Message to an existing object, we use the &#039;&#039;&#039;IDoMethod&#039;&#039;&#039; function (or its tag list based variant):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG IDoMethod(Object *myobject, ULONG methodID, ...);&lt;br /&gt;
ULONG IDoMethodA(Object *myobject, Msg boopsimessage);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The return value is class-dependent. The first argument to both of these functions points to the object that will receive the BOOPSI message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IDoMethod()&#039;&#039;&#039; then depends on the class and method being used for the contents of the rest of the message. Those elements may include a method name, tags, text or pointers. The class documentation will described those as well as whether the class expects any certain order of tags, etc.&lt;br /&gt;
&lt;br /&gt;
For a simple example, we can ask the BOOPSI requester object &#039;&#039;&#039;reqobj&#039;&#039;&#039; (created above) to show itself by sending this message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, the &#039;&#039;&#039;RM_OPENREQ&#039;&#039;&#039; method of the Requester Class expects the elements of its &#039;&#039;&#039;orRequest&#039;&#039;&#039; structure.  The class docs point us to a header file that explains we need to start with the &#039;&#039;&#039;MethodID&#039;&#039;&#039; and then pointers to a tag list, window and a screen structures. In our simple example above, we are passing no arguments, so our call has three NULL&#039;s for these elements.  &lt;br /&gt;
&lt;br /&gt;
When one calls &#039;&#039;IDoMethod()&#039;&#039;&#039; with the &#039;&#039;&#039;RM_OPENREQ&#039;&#039;&#039; method on a requester object, the requester appears and the application waits for the user input.  Given the simple &amp;quot;information&amp;quot; style requester we created (with default buttons), the result will simply numerically indicate which button was selected by the user.&lt;br /&gt;
&lt;br /&gt;
With the &#039;&#039;&#039;IDoMethodA()&#039;&#039;&#039; variant, all of the above arguments have to be combined into a complete BOOPSI Message structure, &#039;&#039;&#039;boopsimessage&#039;&#039;&#039;.  Again the layout of depends on the class and method.  Every method&#039;s message starts off with a &#039;&#039;&#039;Msg&#039;&#039;&#039; (from &amp;amp;lt;intuition/classusr.h&amp;amp;gt;) and is followed by the class specific tags:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
    ULONG MethodID; /* Method-specific data may follow this field */&lt;br /&gt;
} *Msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As mentioned above, there are variants of the &#039;&#039;&#039;IDoMethod()&#039;&#039;&#039; functions for specific uses, such as the &#039;&#039;&#039;DoGadgetMethod()&#039;&#039;&#039; function.  In that case, the function passes additional information pertinent to GUI gadget objects.&lt;br /&gt;
&lt;br /&gt;
== Setting an Object&#039;s Attributes ==&lt;br /&gt;
&lt;br /&gt;
Every BOOPSI object is defined by a number of attributes.  In the above example we defined our requester when we created the object with &#039;&#039;&#039;NewObject()&#039;&#039;&#039;.  But an object&#039;s attributes are not necessarily static.  An application can use the  &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to change or set other attributes on an object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 IIntuition-&amp;gt;SetAttrs( Object * object, Tag tag1, ... );&lt;br /&gt;
     uint32 IIntuition-&amp;gt;SetAttrsA( Object * object, const struct TagItem * tagList );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In each case we have to provide a pointer to our object and then tag pairs with the Attribute identifiers and the replacement values.  As an example, we can use the &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to reset the type and contents of our requester object from above and reuse the object to open a second, different requester for the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     strcpy(buffer,&amp;quot;default string&amp;quot;);&lt;br /&gt;
     result = IIntuition-&amp;gt;SetAttrs( reqobj,&lt;br /&gt;
          REQ_Type,				REQTYPE_STRING,&lt;br /&gt;
          REQ_BodyText,			&amp;quot;please enter some text&amp;quot;,&lt;br /&gt;
          REQS_Buffer,			buffer,&lt;br /&gt;
          REQS_MaxChars,			sizeof(buffer) - 1,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
     result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these changes, we&#039;ve converted our simple requester with a couple buttons into a string requester.  For any attributes we did not reset here, the previous settings or the default values of the class will continue to be used.&lt;br /&gt;
&lt;br /&gt;
Not all object attributes can be set with &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; and this is dependent on each class.  For more information about which attributes can be set in specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] and the class AutoDocs and look for those attributes with a &#039;&#039;&#039;OM_SET&#039;&#039;&#039; listed as an &amp;quot;applicability&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Again there is variant of the &#039;&#039;&#039;SetAttrs()&#039;&#039;&#039; function to be used with BOOPSI GUI gadgets: &#039;&#039;&#039;SetGadgetAttrs()&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Getting an Object&#039;s Attributes ==&lt;br /&gt;
&lt;br /&gt;
Just as we can set attributes of an existing BOOPSI object, we also have the ability to get the current settings from a object&#039;s attributes.  Inquiring about a specific setting is done using the BOOPSI function &#039;&#039;&#039;GetAttr()&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 attr = GetAttr(uint32 attrID, Object *object, uint32 *storage);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We start by specifying the &#039;&#039;&#039;attrID&#039;&#039;&#039; is the attribute&#039;s identifier we are interested in, we then have to identify object to get the attribute from (&#039;&#039;&#039;object&#039;&#039;&#039;), and finally provide an area that will hold the value of the attribute. This function returns a 0 if the object doesn&#039;t recognize or return the attribute, otherwise it returns some non-zero value, the meaning of which depends on the class. In most cases, GetAttr() returns a 1 when it is successful.&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, there are only a few attributes to which values can be obtained.  The following example will obtain the result code from the last use of the object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 lastresult;&lt;br /&gt;
     result = IIntuition-&amp;gt;GetAttr(REQ_ReturnCode, reqobj,&amp;amp;lastresult);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another example of the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; function would be to retrieve the value a user provided in an Interger type requester.  This information could be restrieved like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     int32 userInt;&lt;br /&gt;
     result = IIntuition-&amp;gt;GetAttr(REQI_Number, reqobj,&amp;amp;userInt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While the simple requester class is not a great example for getting a lot of attributes, BOOPSI provides a second pair of functions that uses taglists for obtaining multiple attributes in one call:  &#039;&#039;&#039;GetAttrs()&#039;&#039;&#039; and &#039;&#039;&#039;GetAttrsA&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     uint32 retval = GetAttrs(Object *object, Tag tag1, ...);&lt;br /&gt;
     uint32 retval = GetAttrsA(Object *object, struct TagItem *attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with the previous tag functions mentioned above, these functions are the same except for how the tag list is handled.  In the case of each tag pair, the value will be set to the current value in the object queried.&lt;br /&gt;
&lt;br /&gt;
As with setting attributes, not all object attributes are obtainable using the &#039;&#039;&#039;GetAttr()&#039;&#039;&#039; function, depending on each class.  For more information about which attributes can be obtained in specific classes, see the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] and the class AutoDocs and look for those attributes with a &#039;&#039;&#039;OM_GET&#039;&#039;&#039; listed as an &amp;quot;applicability&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Unlike when creating and setting attributes on BOOPSI GUI gadgets, there are no GUI gadgets specific versions of the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; family of functions.  Instead, the &#039;&#039;&#039;GetAttr&#039;&#039;&#039; functions can also be used to obtain GUI gadget attributes where they are available (see the Class Reference and autodocs).&lt;br /&gt;
&lt;br /&gt;
== Disposing of an Object ==&lt;br /&gt;
&lt;br /&gt;
When an application is finally done with an object, it has to dispose of the object. To dispose of an object, use the Intuition function &#039;&#039;&#039;DisposeObject()&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void DisposeObject(Object *obj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As usual, we use a pointer to our object to indicate the object to be disposed.&lt;br /&gt;
&lt;br /&gt;
In the case of our requester example, we would simply call:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     IIntuition-&amp;gt;DisposeObject(reqobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In many cases - complex gadget GUI&#039;s being the prime example - when a BOOPSI object has connect &amp;quot;child&amp;quot; objects and it is disposed, then all children are also disposed.  An example would be a window object and all the gadgets within that window.  In any case, you must be careful not to dispose of an object that already been disposed.&lt;br /&gt;
&lt;br /&gt;
== What About the BOOPSI Messages and Methods? ==&lt;br /&gt;
&lt;br /&gt;
In the above sections of this page, the functions used reflect BOOPSI messages and the underlying basic &amp;quot;methods&amp;quot; performed by a BOOPSI object.  In the [[BOOPSI#OOP_Overview|&amp;quot;OOP Overview&amp;quot;]] section we see how these saw how these common methods are practically universal to all BOOPSI classes.  The above functions simply provide a wrapper for those common methods like this:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NewObject() || OM_NEW&lt;br /&gt;
|-&lt;br /&gt;
| SetAttrs()/SetGadgetAttrs() || OM_SET&lt;br /&gt;
|-&lt;br /&gt;
| GetAttr() || OM_GET&lt;br /&gt;
|-&lt;br /&gt;
| DisposeObject() || OM_DISPOSE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These methods are defined on the rootclass level, so all BOOPSI classes inherit them. The Intuition functions that correspond to these methods take care of constructing and sending a BOOPSI message with the appropriate method ID and parameters.&lt;br /&gt;
&lt;br /&gt;
One last function had a more direct relationship to operation and methods of BOOPSI objects - in this case, causing our requester object to present itself :&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| IDoMethod() || RM_OPENREQ&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of course, in the case of different classes and objects, the method would change with how the &#039;&#039;&#039;IDoMethod&#039;&#039;&#039; function was called on those objects.&lt;br /&gt;
&lt;br /&gt;
== Example Program ==&lt;br /&gt;
&lt;br /&gt;
Below is the complete example program that includes the above excerpts.  This program will create a requester object, display a requester with buttons and one with a string gadget and print the results in the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Requesters Example&lt;br /&gt;
gcc -o Requesters Requesters.c -lauto&lt;br /&gt;
quit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
*&lt;br /&gt;
************************************************************&lt;br /&gt;
**&lt;br /&gt;
** Created by: CodeBench 0.31 (20.5.2013)&lt;br /&gt;
**&lt;br /&gt;
** Project: OS4ex-Requesters&lt;br /&gt;
**&lt;br /&gt;
** A simple requester.class example used to demonstrate functionality.&lt;br /&gt;
**&lt;br /&gt;
** File: Requesters&lt;br /&gt;
**&lt;br /&gt;
** Date: 20-04-2013 20:43:23&lt;br /&gt;
**&lt;br /&gt;
** Version:   02&lt;br /&gt;
**&lt;br /&gt;
************************************************************&lt;br /&gt;
*&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*		includes&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/requester.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/requester.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*		declarations&lt;br /&gt;
 */&lt;br /&gt;
Object *reqobj;&lt;br /&gt;
struct orRequest reqmsg;&lt;br /&gt;
char buffer[100];&lt;br /&gt;
uint32 result = 0;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
	/*		create a new requester object&lt;br /&gt;
	 */&lt;br /&gt;
	strcpy(buffer,&amp;quot;Welcome to your\nfirst BOOPSI requester&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	reqobj = IIntuition-&amp;gt;NewObject(NULL,&amp;quot;requester.class&amp;quot;,&lt;br /&gt;
		REQ_Type,				REQTYPE_INFO,&lt;br /&gt;
		REQ_TitleText,			&amp;quot;Requesters Example&amp;quot;,&lt;br /&gt;
		REQ_BodyText,			buffer,&lt;br /&gt;
		TAG_END);&lt;br /&gt;
	&lt;br /&gt;
	// Was the requester object created?&lt;br /&gt;
	if (reqobj)&lt;br /&gt;
	{&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;   Requester object created\n&amp;quot;);&lt;br /&gt;
		/*		tell the object to show itself&lt;br /&gt;
		 */&lt;br /&gt;
		result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
		// How did things go?&lt;br /&gt;
		if (result)&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #1 - result code = %ld\n&amp;quot;,result);&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Req #1 - result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		reset object characteristics&lt;br /&gt;
		 */&lt;br /&gt;
		&lt;br /&gt;
		strcpy(buffer,&amp;quot;default string&amp;quot;);&lt;br /&gt;
		result = IIntuition-&amp;gt;SetAttrs( reqobj,&lt;br /&gt;
			REQ_Type,				REQTYPE_STRING,&lt;br /&gt;
			REQ_BodyText,			&amp;quot;please enter some text&amp;quot;,&lt;br /&gt;
			REQS_Buffer,			buffer,&lt;br /&gt;
			REQS_MaxChars,			sizeof(buffer) - 1,&lt;br /&gt;
			TAG_END);&lt;br /&gt;
		&lt;br /&gt;
		/*		tell object to show itself again&lt;br /&gt;
		 */&lt;br /&gt;
		result = IIntuition-&amp;gt;IDoMethod( reqobj, RM_OPENREQ, NULL, NULL, NULL );&lt;br /&gt;
		// Did we get a good response?&lt;br /&gt;
		if (result&amp;gt;0)&lt;br /&gt;
		{&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #2 - result code = %ld\n&amp;quot;,result);&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;  Req #2 - result text = %s\n&amp;quot;,buffer);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Req #2 - result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		Lets check some object attributes&lt;br /&gt;
		 */&lt;br /&gt;
		uint32 lastresult;&lt;br /&gt;
		&lt;br /&gt;
		result = IIntuition-&amp;gt;GetAttr(REQ_ReturnCode, reqobj,&amp;amp;lastresult);&lt;br /&gt;
		// Did we get some value?&lt;br /&gt;
		if (result&amp;gt;0)&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   Last result code = %ld\n&amp;quot;,lastresult);&lt;br /&gt;
		else&lt;br /&gt;
			IDOS-&amp;gt;Printf(&amp;quot;   This result = 0\n&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
		/*		Dispose of our requester object&lt;br /&gt;
		 */&lt;br /&gt;
		IIntuition-&amp;gt;DisposeObject(reqobj);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		IDOS-&amp;gt;Printf(&amp;quot;   ERROR: Failed to create Requester object\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UserDoc:Main&amp;diff=9328</id>
		<title>UserDoc:Main</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UserDoc:Main&amp;diff=9328"/>
		<updated>2018-01-25T21:07:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* AmigaOS platform targets */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Welcome to AmigaOS ==&lt;br /&gt;
&lt;br /&gt;
AmigaOS was born in 1985 and delivered what contemporary personal computer operating systems could only dream of.  As the first &amp;quot;multimedia&amp;quot; operating system, it was trivial for AmigaOS computers to display animations while playing music and reading data from disks.  Such multimedia and multitasking finesse drew many people to this system.  Some of them are famous: [http://www.amigahistory.co.uk/warhol.html Andy Warhol], Sir Arthur C. Clarke, [http://www.polyphoto.com/upchug/AEcastro.html NASA], Hollywood and the TV broadcasting industry, and many others that thought [http://www.youtube.com/watch?v=PWeO5IkCssk only Amiga makes it possible].&lt;br /&gt;
&lt;br /&gt;
Today many people still think AmigaOS has something special that makes it more interesting and rewarding than other systems. This system allows the user to control the computer, not the other way around. It is a system you fully understand that is easier and more flexible to use.  In other words, AmigaOS is &#039;&#039;&#039;more fun&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== AmigaOS:  The flexible operating system ===&lt;br /&gt;
AmigaOS is an operating system:  a collection of efficient programs written to start the computer, let the user control the computer, and present feedback to the user.&lt;br /&gt;
&lt;br /&gt;
AmigaOS is designed with ease of use and flexibility in mind. To begin with, AmigaOS provides a clear view of your computer, your applications and files. A number of methods are available to let your computer serve you, whether graphically with a mouse, using the &amp;quot;Shell&amp;quot; command line, or by some other means the user prefers.&lt;br /&gt;
&lt;br /&gt;
AmigaOS strives to avoid stupid limitations that can be found on other systems. AmigaOS users can organise their files the way they like. There are few limits on file hierarchy, locations and file names.  Drives don&#039;t have to be named with a letter or cryptic names (such as C:, or sda1), your files don&#039;t have to reside in your &amp;quot;Documents&amp;quot; folder and your hard drives aren&#039;t hidden from you. If you&#039;re not writing to drives and you want to &amp;quot;shut down&amp;quot;, why wait for the OS to allow that?  With AmigaOS, just hit the power switch. Done.&lt;br /&gt;
&lt;br /&gt;
An Amiga does not start with pre-installed applications serving some sales conglomerate, marketing organization or their big brother. AmigaOS does not do actions behind the user&#039;s back. As unique as it is today, the AmigaOS computer serves the user and not the other way around. With one of the largest proportions of user-programmers around, the trustworthy AmigaOS user-friendly ethic is mirrored in AmigaOS applications.&lt;br /&gt;
&lt;br /&gt;
Since the first versions more than twenty-five years ago, AmigaOS has also been designed to serve efficiently. Optimizing  applications and OS code has always been the goal of programmers and developers of this operating system.  The result is an operating system and applications that take less space on your hard drives, waste less time loading, consume less memory, require less processing power, and respond more quickly to the user.&lt;br /&gt;
&lt;br /&gt;
And every update of AmigaOS doesn&#039;t demand you must buy newer, more powerful hardware. AmigaOS currently runs on twenty year old 200MHz computers or brand new dual core 1,800MHz computers.  It&#039;s the user&#039;s choice how they want to &#039;&#039;&#039;enjoy&#039;&#039;&#039; AmigaOS.&lt;br /&gt;
&lt;br /&gt;
=== Some AmigaOS features ===&lt;br /&gt;
Here are some of the features of AmigaOS that make it easy to control your computer. Some of these concepts were copied by other operating systems which tend to show they are the correct way of doing things.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Small footprint:&#039;&#039;&#039; AmigaOS can work with 64 MB of memory. On disk, a default installation only takes around 200 MB.  The smaller footprint translates into a more responsive user experience given any hardware.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Straightforward operating system design:&#039;&#039;&#039; With a clear layout and easy to understand names  (Classes, Libs, Fonts, Prefs, Storage, etc.),  you can easily understand what everything in AmigaOS is and what it does for you.  Nothing is hidden from the user and the user is not restricted by AmigaOS.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;User configurable graphic interface:&#039;&#039;&#039;  Using the provided &amp;quot;preferences editors,&amp;quot; the user can dramatically reconfigure how AmigaOS looks, sounds, runs and responds to every user whim.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;File recognition based on file content:&#039;&#039;&#039;  You can name a file &#039;&#039;&#039;whatever you want&#039;&#039;&#039;, even without an extension. Examples: &amp;quot;my file&amp;quot; or &amp;quot;picture of Jay in Santa Clara&amp;quot;.  There is no need to add an extension to explain what the file is, like &amp;quot;.txt&amp;quot; or &amp;quot;.jpg&amp;quot;. AmigaOS really examines the &#039;&#039;&#039;file content&#039;&#039;&#039; to recognise what type of file it is.  &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Logical assignments:&#039;&#039;&#039; Easily set and use logical names names for directories located anywhere on your system.  For example, &amp;quot;Auto:&amp;quot; can point to your directory &amp;quot;car show pictures&amp;quot; buried on your media drive.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Ram disk concept:&#039;&#039;&#039;  On AmigaOS there is a special disk called the &#039;&#039;&#039;Ram disk&#039;&#039;&#039; which represents a part of your computer memory. This area is not fixed. It automatically grows whenever you store files in it.  For example, it&#039;s a great place to unpack files to install from there, greatly speeding up the installation.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Command line and graphical user interfaces:&#039;&#039;&#039;  Both the the graphical user interface (GUI) and command line interface (where you type commands into a window with the keyboard)  can be used to manage AmigaOS, its programs and files.   Both interfaces are intergated with each other so you can easily use command lines from the GUI or open graphical elements from a command line.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;ARexx Ports:&#039;&#039;&#039;  Throughout AmigaOS and third party programs, &amp;quot;ARexx&amp;quot; message ports let one application talk with others so that apps work together to serve the user.  AmigaOS also provides the lightweight ARexx and modern Python programming languages that can control AmigaOS and applications with ARexx ports.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Resident Commands:&#039;&#039;&#039; Commands can be made resident, i.e., they are kept in memory so that they can be reused with no loading time.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Restart only the operating system:&#039;&#039;&#039; if you feel the need to restart the system, you can do so by restarting &#039;&#039;&#039;only AmigaOS&#039;&#039;&#039; and not the whole computer.&lt;br /&gt;
&lt;br /&gt;
=== AmigaOS platform targets ===&lt;br /&gt;
&lt;br /&gt;
While the original versions of AmigaOS ran on computers of the eighties using Motorola 68k series CPU chips, the current AmigaOS runs on computers using PowerPC processor chips [http://www.amigaos.net/content/72/supported-hardware hardware]. These can be older Amiga computers (also called &amp;quot;Classic Amigas&amp;quot;) with PPC &amp;quot;accelerator cards&amp;quot; or new generation Amiga PPC computers. &lt;br /&gt;
&lt;br /&gt;
In this guide, we will concentrate on the current AmigaOS running on the supported hardware:&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOne A1222]] model by [http://www.a-eon.com A-EON Technology Ltd]&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOne X5000]] model by [http://www.a-eon.com A-EON Technology Ltd]&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOne X1000]] model by [http://www.a-eon.com A-EON Technology Ltd]&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOne 500]], Sam460, Sam440ep and Sam440ep-Flex models by [http://www.acube-systems.com ACube Systems]&lt;br /&gt;
&lt;br /&gt;
* [[Pegasos II]] model by [http://www.bplan-gmbh.de bplan GmbH]&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOne XE and MicroA1-C]] models by [http://en.wikipedia.org/wiki/Eyetech Eyetech Group Ltd].&lt;br /&gt;
&lt;br /&gt;
* [[Classic Amiga]] 4000(T), 3000(T) and 1200 models by Commodore Business Machines (when equipped with PowerPC accelerator cards).&lt;br /&gt;
&lt;br /&gt;
== How does AmigaOS work? - Concepts ==&lt;br /&gt;
&lt;br /&gt;
In this page we will discuss  [[UserDoc:How AmigaOS Works|how AmigaOS works]]:&lt;br /&gt;
&lt;br /&gt;
* [[UserDoc:How_AmigaOS_Works#The_most_important_components|The most important components]] (Exec, AmigaDOS, Intuition...)&lt;br /&gt;
* [[UserDoc:How_AmigaOS_Works#How_is_my_data_stored.3F|how files and data are stored]]&lt;br /&gt;
* [[UserDoc:How_AmigaOS_Works#All_AmigaOS_components|all AmigaOS components are described]]&lt;br /&gt;
* [[UserDoc:How_AmigaOS_Works#AmigaOS_boot_procedure|how AmigaOS is booted on your Amiga computer]]&lt;br /&gt;
* [[Workbench/Prefs|AmigaOS settings programs]]&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== How to use AmigaOS? ==&lt;br /&gt;
&lt;br /&gt;
AmigaOS is a collection of components that oversee the computer hardware &amp;amp; data and provide the user with easy, understandable tools to manage and use them.&lt;br /&gt;
&lt;br /&gt;
In the following [[UserDoc:Introduction to AmigaOS|Introduction to AmigaOS pages]] we will discuss the basic concepts:&lt;br /&gt;
&lt;br /&gt;
* how to use AmigaOS&lt;br /&gt;
* what the AmigaOS graphic user interface is composed of &lt;br /&gt;
* what interfaces AmigaOS provides, including the [[UserDoc:Workbench|Workbench]], the [[UserDoc:Shell|Shell]] or scripting languages.&lt;br /&gt;
&lt;br /&gt;
From the introduction page, you can continue with more detailed pages on the [[UserDoc:Workbench|Workbench]] and the [[AmigaDOS manual]] .   Now let&#039;s start with this [[UserDoc:Introduction to AmigaOS|Introduction to AmigaOS]].&lt;br /&gt;
&lt;br /&gt;
== Manuals ==&lt;br /&gt;
&lt;br /&gt;
[[UserDoc:AmigaOS File Systems|AmigaOS File Systems]] - AmigaOS File Systems&lt;br /&gt;
&lt;br /&gt;
[[AmigaOS Manual]] - AmigaOS Manual&lt;br /&gt;
&lt;br /&gt;
[[Bars &amp;amp; Pipes Professional]] - MIDI Sequencer&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Command_Reference&amp;diff=9272</id>
		<title>AmigaOS Manual: AmigaDOS Command Reference</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Command_Reference&amp;diff=9272"/>
		<updated>2017-12-28T20:26:52Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed a few typos and other problems reported by OldFart.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The commands in this chapter are executed from the Shell window. They are described in alphabetic order; however, some commands reserved for system use appear together at the end of the chapter.&lt;br /&gt;
&lt;br /&gt;
= Command Documentation =&lt;br /&gt;
&lt;br /&gt;
Each command documented in this manual is shown with the format, arguments, options, symbols, and abbreviations required for proper use.&lt;br /&gt;
&lt;br /&gt;
This chapter and Chapter 7 provide command specifications for the AmigaDOS commands and the Workbench programs accessible through the Shell using the following standard outline:&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: All the arguments and options accepted by a command. The special characters that indicate the particular type of argument are described on page 6-6.&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: An optional on-line reminder of the command&#039;s format that is embedded in the program&#039;s code. Entering a command followed by a space and a question mark (for example, DIR ?) displays the template. A complete description of the template notation is found on page 6-8.&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: The directory where the command is normally stored.&lt;br /&gt;
&lt;br /&gt;
; Examples&lt;br /&gt;
: A sample use of the command. Examples are displayed in the courier typeface to distinguish them from normal text. The 1&amp;gt; represents the Shell prompt; do not type it as part of the example command. Lines in the example not prefaced by 1&amp;gt; represent the output of a command. Command names and keywords are shown in all upper case letters and file and directory names usually have the first letter in upper case; however, they do not need to be entered that way. Press Return to execute the command line.&lt;br /&gt;
&lt;br /&gt;
Separate commands and arguments with spaces. Use punctuation only when required in the syntax of specific commands.&lt;br /&gt;
&lt;br /&gt;
== Format ==&lt;br /&gt;
&lt;br /&gt;
The following lists the characters that indicate the type of argument shown in format listings. Do not use these characters as part of the command.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &amp;lt; &amp;gt; || Angle brackets indicate where additional information, such as a file name, must be included. This argument is required if it is not surrounded by square brackets. (For example, [&amp;lt;filename&amp;gt;]; see below.)&lt;br /&gt;
|-&lt;br /&gt;
| [ ] || Square brackets enclose optional arguments and keywords. Although not required, these arguments and keywords are accepted by the command.&lt;br /&gt;
|-&lt;br /&gt;
| { } || Braces enclose items that can be given once or repeated any number of times. For example, {&amp;lt;args&amp;gt;} indicates that several items can be given for this argument.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; || Vertical bars separate lists of options from which you can choose only one. For example, &amp;lt;nowiki&amp;gt;[OPT R|S|RS]&amp;lt;/nowiki&amp;gt; indicates a choice of the R option, the S option, or both options.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;n&amp;gt; || A numeric value is expected by the argument.&lt;br /&gt;
|-&lt;br /&gt;
| KEYWORD || Italics indicate that the argument&#039;s keyword is required if you include that argument.&lt;br /&gt;
|-&lt;br /&gt;
| ... || An ellipsis (...) after a string argument indicates that the string must be the final argument on the command line. Including a comment is not allowed. The remainder of the command line is taken as the desired string. Quotation marks are not needed around the string, even if it contains spaces. If you enter quotation marks, they are part of the string. If you specify the keyword, you can put leading and trailing spaces in the string.&lt;br /&gt;
|-&lt;br /&gt;
| command line indentation || On command lines that are long enough to wrap to the next line, this manual shows the wrapped lines as indented for documentation purposes only. In practice, the wrapped lines align with the first character of the Shell prompt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The format for the COPY command illustrates the use of these conventions:&lt;br /&gt;
&lt;br /&gt;
 COPY [FROM] {&amp;lt;name | pattern&amp;gt;} [TO]&amp;lt;name | pattern&amp;gt;[ALL]&lt;br /&gt;
    [QUIET] [BUF | BUFFER=&amp;lt;n&amp;gt;] [CLONE] [DATES] [NOPRO]&lt;br /&gt;
    [COM] [NOREQ]&lt;br /&gt;
&lt;br /&gt;
The [FROM] keyword is optional. If it is not specified, the command reads the file name ir pattern to copy by ist position on the command line.&lt;br /&gt;
&lt;br /&gt;
The {&amp;lt;name | pattern&amp;gt;} argument must be provided. You must substitute either a file name or pattern. The braces indicate that more than one name or pattern can be given.&lt;br /&gt;
&lt;br /&gt;
The [TO] keyword is optional. If it is not specified, the command reads the file name or device to copy to by its position on the command line.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;name | pattern&amp;gt; argument must be provided. You can specify only one destination.&lt;br /&gt;
&lt;br /&gt;
The [ALL], [QUIET], [CLONE], [DATES], [NOPRO], [COM], and [NOREQ] arguments are optional.&lt;br /&gt;
&lt;br /&gt;
The [BUF | BUFFER=&amp;lt;n&amp;gt;] argument is optional. If given, the keyword is required, but you can use either BUF or BUFFER with the numerical argument. For example, both BUF=5 and BUFFER=5 are acceptable. The numerical argument can also be entered without the equals sign; spaces are optional.&lt;br /&gt;
&lt;br /&gt;
== Template ==&lt;br /&gt;
&lt;br /&gt;
The Template is built into the system to serve as an on-line reminder of a command&#039;s syntax and to let you run the command from the Template line by providing a prompt at which you enter the command&#039;s arguments.&lt;br /&gt;
&lt;br /&gt;
Display the Template by entering a question mark (?) after a command. The Shell assumes that you wish to run the command and it expects you to enter the command&#039;s arguments after the colon following the display. For example:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; TYPE ?&lt;br /&gt;
 FROM/A/M,TO/K,OPT/K,HEX/S,NUMBER/S:&lt;br /&gt;
&lt;br /&gt;
Pressing Return executes the command if it does not require any arguments to run properly. Entering the arguments and their respective keywords and then pressing Return also executes the command. If a command requires arguments and you do not supply them or if you enter anything other than the required arguments, pressing Return results in a non-fatal error message. Remember that you do not need to enter the entire format for a command at this prompt, just the required arguments.&lt;br /&gt;
&lt;br /&gt;
The Templates are listed with the arguments separated by commas, followed by a slash (/), and a capital letter indicating the type of argument. These slash/letter combinations are displayed to remind you of the command&#039;s particular requirements and are not entered as part of the command. The following table explains the notation:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Template Notation !! Format Equivalent !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| argument/A || &amp;lt;name&amp;gt; || The argument is always required.&lt;br /&gt;
|-&lt;br /&gt;
| option/K || KEYWORD || The option&#039;s keyword is required if the argument is given.&lt;br /&gt;
|-&lt;br /&gt;
| option/S || [KEYWORD] || The option works as a switch. The name of the option must be entered to specify it. Most options are switches.&lt;br /&gt;
|-&lt;br /&gt;
| value/N || &amp;lt;n&amp;gt; || The argument is numeric.&lt;br /&gt;
|-&lt;br /&gt;
| argument/M || {&amp;lt;name&amp;gt;} || Multiple items are accepted for this argument. Although there is no limit to the number of possible arguments, they must be provided before the next argument or option.&lt;br /&gt;
|-&lt;br /&gt;
| string/F || argument... || The string must be the final argument on the command line; the remainder of the command line is taken as the desired string.&lt;br /&gt;
|-&lt;br /&gt;
| = || KYWD &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; KEYWORD || Two different forms of the keyword are equivalent and either are accepted. The equals sign is not entered as part of the command.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Template for the COPY command illustrates the use of arguments:&lt;br /&gt;
&lt;br /&gt;
 FROM/M,TO/A,ALL/S,QUIET/S,BUF=BUFFER/K/N,&lt;br /&gt;
 CLONE/S,DATES/S,NOPRO/S,COM/S,NOREQ/S&lt;br /&gt;
&lt;br /&gt;
FROM/M indicates that the argument is required and more than one argument is acceptable.&lt;br /&gt;
&lt;br /&gt;
TO/A indicates that the argument is required.&lt;br /&gt;
&lt;br /&gt;
ALL/S, QUIET/S, CLONE/S, DATES/S, NOPRO/S, COM/S, and NOREQ/S indicate that the keywords act as switches. If the keyword is present in the line, the option is used.&lt;br /&gt;
&lt;br /&gt;
BUF=BUFFER/K/N indicates that the BUF or BUFFER keyword (/K) is required to specify this numerical (/N) argument. Both BUF and BUFFER are acceptable keywords (=).&lt;br /&gt;
&lt;br /&gt;
Keywords and their arguments can be linked with an equals sign (=) to ensure correct assignments in complex cases. For example, BUF=20.&lt;br /&gt;
&lt;br /&gt;
= Command Listing =&lt;br /&gt;
&lt;br /&gt;
== ADDBUFFERS ==&lt;br /&gt;
&lt;br /&gt;
Instructs the file system to add or display cache buffers for a drive.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ADDBUFFERS &amp;lt;drive&amp;gt; [&amp;lt;n&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRIVE/A,BUFFERS/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
ADDBUFFERS adds &amp;lt;n&amp;gt; buffers to the list of buffers available for &amp;lt;drive&amp;gt;. Although adding buffers speeds disk access, each additional buffer reduces free memory by approximately 512 bytes. The default buffer allocation is 5 for sloppy drives and 30 for hard disk partitions.&lt;br /&gt;
&lt;br /&gt;
The amount of extra available memory dictates the number of buffers you can add. There is no fixed upper limit; however, adding too many buffers reduces overall system performance by taking RAM away from other system functions. Specifying a negative number subtracts that many buffers from the current allocation. The minimum number of buffers is one; however, using only one is not recommended.&lt;br /&gt;
&lt;br /&gt;
Twenty buffers are recommended for a floppy drive in a 512 KB system. Use the default value recommended by the HDToolBox program for hard disks. (Display this value by selecting the Advanced Options gadget on the Partitioning screen.)&lt;br /&gt;
&lt;br /&gt;
If only the &amp;lt;drive&amp;gt; argument is specified, ADDBUFFERS displays the number of buffers currently allocated for that drive.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; ADDBUFFERS DF0:&lt;br /&gt;
 DF0: has 5 buffers&lt;br /&gt;
&lt;br /&gt;
A further example of ADDBUFFERS appears in Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== ADDNETINTERFACE ==&lt;br /&gt;
&lt;br /&gt;
Makes network interfaces known to the protocol stack.&lt;br /&gt;
&lt;br /&gt;
== ADDNETROUTE ==&lt;br /&gt;
&lt;br /&gt;
Adds message routing paths.&lt;br /&gt;
&lt;br /&gt;
== ALIAS ==&lt;br /&gt;
&lt;br /&gt;
Sets or displays command aliases.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ALIAS [&amp;lt;name&amp;gt;] [&amp;lt;string...&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,STRING/F&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ALIAS creates aliases, or alternative names, for AmigaDOS commands. ALIAS can be used to abbreviate frequently used commands or replace standard command names with different names.&lt;br /&gt;
&lt;br /&gt;
When AmigaDOS encounters &amp;lt;name&amp;gt;, it replaces it with the defined &amp;lt;string&amp;gt;, integrate the result with the rest of the command line, and attempts to interpret and execute the resulting line as an AmigaDOS command &amp;lt;Name&amp;gt; is the alias for the command and &amp;lt;string&amp;gt; is the command to be substituted for the alias.&lt;br /&gt;
&lt;br /&gt;
An alias must be entered at the beginning of the command line. You can enter arguments after the alias, but you cannot create an alias to represent a series of command arguments. For example, in the following command line:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; NEWSHELL WINDOW=CON:0/250/640/150/2SHELL/CLOSE&lt;br /&gt;
&lt;br /&gt;
the WINDOW argument cannot be replaced with an alias.&lt;br /&gt;
&lt;br /&gt;
You can substitute a file name or other instruction within an alias by placing square brackets ([ ]) with nothing between them in the &amp;lt;string&amp;gt;. Any argument entered after the alias is inserted at the brackets.&lt;br /&gt;
&lt;br /&gt;
ALIAS &amp;lt;name&amp;gt; displays the &amp;lt;string&amp;gt; for that alias. Entering ALIAS alone lists all current aliases.&lt;br /&gt;
&lt;br /&gt;
Aliases are local to the Shell in which they are defined. If you create another Shell with the NEWSHELL command, it shares the same aliases as its parent Shell. However, if you create another Shell with the Execute Command menu item, it des not recognize aliases created in your original Shell. A global lais that is recognized by all Shells can be crated by inserting the alias in the Shell-startup file.&lt;br /&gt;
&lt;br /&gt;
To remove an ALIAS, use the UNALIAS command.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; ALIAS d1 DIR DF1:&lt;br /&gt;
&lt;br /&gt;
Entering d1 displays a directory of the contents of the disk in DF1:; as if you entered DIR DF1:.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ALIAS hex TYPE [ ] HEX&lt;br /&gt;
&lt;br /&gt;
creates an alias called HEX that displays the contents of a specified file in hexadecimal format. The empty brackets indicate where the file name is inserted in this example. Entering:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; hex Myfile&lt;br /&gt;
&lt;br /&gt;
displays the contents of Myfile in hexadecimal format.&lt;br /&gt;
&lt;br /&gt;
See also: UNALIAS. Further examples of using ALIAS appear in Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== ARP ==&lt;br /&gt;
&lt;br /&gt;
Address resolution display control.&lt;br /&gt;
&lt;br /&gt;
== ASK ==&lt;br /&gt;
&lt;br /&gt;
Gets yes or no user input during script file execution.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ASK &amp;lt;prompt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PROMPT/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ASK is used in scripts to write the string specified by &amp;lt;prompt&amp;gt; to the current window and then wait for keyboard input. Valid keyboard responses are Y (yes), N (no), and Return (no). Selecting Y sets the condition flag to 5 (WARN). Selecting N or pressing Return sets the condition flag to 0. Check the response using an IF statement.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;prompt&amp;gt; contains spaces, it must be enclosed in quotation marks.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
Assume a script contained the following commands:&lt;br /&gt;
&lt;br /&gt;
 ASK Continue?&lt;br /&gt;
 IF WARN&lt;br /&gt;
    ECHO Yes&lt;br /&gt;
 ELSE&lt;br /&gt;
    ECHO No&lt;br /&gt;
 ENDIF&lt;br /&gt;
&lt;br /&gt;
At the ASK command, Continue? Is displayed on the screen. If Y is pressed, Yes is displayed on the screen. If N or a Return alone is pressed, No is displayed.&lt;br /&gt;
&lt;br /&gt;
See also: IF, ELSE, ENDIF, REQUESTCHOICE, WARN&lt;br /&gt;
&lt;br /&gt;
== ASSIGN ==&lt;br /&gt;
&lt;br /&gt;
Controls assignment of logical device names to files or directories.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ASSIGN [&amp;lt;name&amp;gt;:] [{&amp;lt;target&amp;gt;}] [LIST] [EXISTS] [DISMOUNT] [DEFER] [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,TARGET/M,LIST/S,EXISTS/;,DISMOUNT/S,DEFER/S,PATH/S,ADD/S,REMOVE/S,VOLS/S,DIRS/S,DEVICES/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
ASSIGN allows references to files or directories with short, convenient logical device names, rather than their usual names or complete paths. The ASSIGN command can create assignments, remove assignments, or list some or all current assignments.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;name&amp;gt; and {&amp;lt;target&amp;gt;} arguments are given, ASSIGN assigns the given logical name to the specified target. Each time the assigned logical device name is referred to, AmigaDOS accesses the specified target. If the &amp;lt;name&amp;gt; given is already assigned to a file or directory, the new target replaces the previous one. A colon must be included after the &amp;lt;name&amp;gt; argument.&lt;br /&gt;
&lt;br /&gt;
If only the &amp;lt;name&amp;gt; argument is given, any existing ASSIGN of a file or directory to that logical device name is cancelled.&lt;br /&gt;
&lt;br /&gt;
You can assign several logical device names to the same target by using multiple ASSIGN commands.&lt;br /&gt;
&lt;br /&gt;
You can assign one logical device name to several targets by specifying each file or directory after the &amp;lt;name&amp;gt; argument or by using several ASSIGN commands with the ADD option. Specifying the ADD option does not replace any existing target assigned to &amp;lt;name&amp;gt;. This target is added to the ASSIGN list and the system searches for all the targets when &amp;lt;name&amp;gt; is encountered. If the first target is not available, ASSIGN uses the next target added.&lt;br /&gt;
&lt;br /&gt;
The REMOVE option deletes a target name from the ASSIGN list.&lt;br /&gt;
&lt;br /&gt;
If no arguments are given with ASSIGN or if the LIST keyword is used, a list of all current assignments is displayed. If the VOLS, DIRS, or DEVICES switch is specified, ASSIGN limits the display to volumes, directories, or devices.&lt;br /&gt;
&lt;br /&gt;
When the EXISTS keyword is entered with a logical device name, AmigaDOS searches the ASSIGN list for that name and displays the volume and directory assigned to that device. If the device name is not found, the condition flag is set to 5 (WARN).&lt;br /&gt;
&lt;br /&gt;
When the {&amp;lt;target&amp;gt;} argument is given, AmigaDOS immediately looks for that file or directory. If the ASSIGN commands are part of the User-startup, the targets must be present on a mounted disk during the boot procedure. If an assigned target cannot be found, a requester asks for the volume containing it. However, using the DEFER and PATH options make the system wait until the target is needed before searching for it.&lt;br /&gt;
&lt;br /&gt;
{{Note|The assigned name does not have to retain the name of the file or directory and it does not have to be in upper case. For example, the name CLIPS: or Clips: can be assigned to the Ram Disk:Clipboards directory.}}&lt;br /&gt;
&lt;br /&gt;
The DEFER option creates a late-binding ASSIGN. This ASSIGN takes effect when the assigned object is first referenced, rather than when the assignment is made. When the DEFER option is used, the disk containing the assigned target is not needed until the object is called. The assignment then remains valid until explicitly changed.&lt;br /&gt;
&lt;br /&gt;
If you ASSIGN FONTS: to DF0:Fonts with the DEFER option, the system associates FONTS: with the disk that is in DF0: when FONTS: is referred to. For example, if you have a Workbench disk in DF0: at the time the FONTS: directory is needed, the system associates FONTS: with that particular Workbench disk. If you later remove that Workbench disk and insert another disk containing a Fonts directory, the system specifically requests the original Workbench disk the next time FONTS: is needed.&lt;br /&gt;
&lt;br /&gt;
The PATH option creates a non-binding ASSIGN. A non-binding ASSIGN acts like a DEFERed ASSIGN, except that is re-evaluated each time the assigned name is referenced. For example, if you assign FONTS: to DF0:Fonts with the PATH option, any disk in DF0: is searched when FONTS: is referenced. As long as the disk contains a Fonts directory, it satisfies the ASSIGN. You cannot assign multiple directories with the PATH option.&lt;br /&gt;
&lt;br /&gt;
Floppy disk only system users can find that using the PATH option eliminates the need to reinsert the original Workbench disk used to boot the system. As long as the drive you assigned with the PATH option contains a disk with the assigned directory name, the system uses that disk.&lt;br /&gt;
&lt;br /&gt;
The DISMOUNT option disconnects a volume or device from the list of mounted devices. You must provide the device name in the argument. DISMOUNT removes the name form the list, but does not free resources. You cannot cancel a DISMOUNT without rebooting. DISMOUNT is meant for use by software developers only and can cause a software failure if not used carefully.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN FONTS: MyFonts:Fontdir&lt;br /&gt;
&lt;br /&gt;
assigns the FONTS: directory to Fontdir on MyFonts:&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN LIST&lt;br /&gt;
 Volumes:&lt;br /&gt;
 Ram Disk [Mounted]&lt;br /&gt;
 Workbench [Mounted]&lt;br /&gt;
 MyFonts [Mounted]&lt;br /&gt;
 &lt;br /&gt;
 Directories:&lt;br /&gt;
 LOCALE Workbench:Locale&lt;br /&gt;
 KEYMAPS Workbench:Devs/Keymaps&lt;br /&gt;
 PRINTERS Workbench:Devs/Printers&lt;br /&gt;
 REXX Workbench:S&lt;br /&gt;
 CLIPS Ram Disk:Clipboards&lt;br /&gt;
 ENV Ram Disk:Env&lt;br /&gt;
 T Ram Disk:T&lt;br /&gt;
 ENVARC Workbench:Prefs/Env-Archive&lt;br /&gt;
 SYS Workbench:&lt;br /&gt;
 C Workbench:C&lt;br /&gt;
 S Workbench:S&lt;br /&gt;
 L Workbench:L&lt;br /&gt;
 FONTS MyFonts:Fontdir&lt;br /&gt;
 DEVS Workbench:Devs&lt;br /&gt;
 LIBS Workbench:Libs&lt;br /&gt;
 + Workbench:Classes&lt;br /&gt;
 &lt;br /&gt;
 Devices:&lt;br /&gt;
 PIPE AUX RAM CON&lt;br /&gt;
 RAW PAR SER PRT DF0&lt;br /&gt;
&lt;br /&gt;
Shows a typical list of all current assignments. The plus sign indicates any additional directories with the same assignment.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN FONTS: EXISTS&lt;br /&gt;
 FONTS: MyFonts:FontDir&lt;br /&gt;
&lt;br /&gt;
is an inquiry into the assignment of FONTS:. AmigaDOS responds by showing that FONTS: is assigned to the FontDir directory of the MyFonts volume. The return code is set to 0 if it exists or to 5 if it does not.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN LIBS: SYS:Libs BigAssem:Libs ADD&lt;br /&gt;
&lt;br /&gt;
is a multiple-directory assignment that creates a search path containing two Libs directories. Specifying ADD keeps the standard SYS:Classes assignment from being removed. These directories are searched in sequence each time LIBS: is invoked.&lt;br /&gt;
&lt;br /&gt;
; Example 5:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN DEVS:&lt;br /&gt;
&lt;br /&gt;
removes the DEVS: assignment from the system.&lt;br /&gt;
&lt;br /&gt;
; Example 6:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN WorkDisk: DF0: DEFER&lt;br /&gt;
 1&amp;gt; ASSIGN WorkDisk: EXISTS&lt;br /&gt;
 WorkDisk &amp;lt;DF0:&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sets up a late-binding assignment of the logical device WorkDisk:. Until the first time you refer to the name WorkDisk:, you do not need to insert it in DF0: ASSIGN shows DF0: enclosed in angle brackets to indicate that it is DEFERred. After the first reference to WorkDisk:, the volume name of the disk that was in DF0: replaces &amp;lt;DF0:&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
; Example 7:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN C: DF0:C PATH&lt;br /&gt;
 1&amp;gt; ASSIGN C: EXISTS&lt;br /&gt;
 C [Df0: C]&lt;br /&gt;
&lt;br /&gt;
references the C directory fo the disk that is in DF0: when a command is searched for. ASSIGN shows DF0:C in square brackets to indicate that it is a non-binding ASSIGN.&lt;br /&gt;
&lt;br /&gt;
; Example 8:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN LIBS: Zcad:Libs ADD&lt;br /&gt;
&lt;br /&gt;
adds Zcad:Libs to the list of directories assigned as LIBS:.&lt;br /&gt;
&lt;br /&gt;
; Example 9:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ASSIGN LIBS: Zcad:Libs REMOVE&lt;br /&gt;
&lt;br /&gt;
removes Zcad:Libs from the list of directories assigned as LIBS:.&lt;br /&gt;
&lt;br /&gt;
For more examples using ASSIGN, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== AVAIL ==&lt;br /&gt;
&lt;br /&gt;
Reports the amount of Chip and Fast memory available.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: AVAIL [VHIP | FAST | TOTAL] [FLUSH]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: CHIPS/S,FAST/S,TOTAL/S,FLUSH/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
AVAIL gives a summary of the system RAM, both Chip and Fast. For each memory type, AVAIL reports the total amount of memory, how much is available, how much is currently in use, and the largest contiguous memory block not yet allocated.&lt;br /&gt;
&lt;br /&gt;
Unless you want a complete summary, use the CHIP, FAST, and/or TOTAL options to have AVAIL display only the number of free bytes of Chip, Fast, or Total RAM available.&lt;br /&gt;
&lt;br /&gt;
The FLUSH option frees memory by removing all unused libraries, devices, fonts, catalogs.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; AVAIL&lt;br /&gt;
 Type Available In-Use Maximum Largest&lt;br /&gt;
 chip 233592 282272 515864 76792&lt;br /&gt;
 fast 341384 182896 524280 197360&lt;br /&gt;
 total 574976 465168 1040144 197360&lt;br /&gt;
&lt;br /&gt;
A complete summary of system RAM is displayed.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; AVAIL CHIP&lt;br /&gt;
 233592&lt;br /&gt;
&lt;br /&gt;
The number of free bytes of Chip RAM is displayed.&lt;br /&gt;
&lt;br /&gt;
See Chapter 8 for more examples using AVAIL.&lt;br /&gt;
&lt;br /&gt;
== BREAK ==&lt;br /&gt;
&lt;br /&gt;
Sets attention flags in the specified process.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: BREAK &amp;lt;process&amp;gt; [ALL | C | D | E | F]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PROCESS/A/N,ALL/S,C/S,D/S,E/S,F/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
BREAK sets the specified attention flags in the &amp;lt;process&amp;gt; indicated. Use the STATUS command to display the current process numbers. C sets the Ctrl+C flag, D sets the Ctrl+D flag, and so on, ALL sets all the flags from Ctrl+C to Ctrl+F. By default, only the Ctrl+C flag is set.&lt;br /&gt;
&lt;br /&gt;
BREAK acts the same as selecting the relevant process by clicking in its window and pressing the appropriate Ctrl+key combinations.&lt;br /&gt;
&lt;br /&gt;
Ctrl+C is the default for sending a BREAK signal to halt a process. A process that has been aborted this way displays ***Break in the Shell window. Ctrl+D halts execution of a script file. The STATUS command displays the current process numbers. Ctrl+E is undefined.&lt;br /&gt;
&lt;br /&gt;
Ctrl+F is used by programs that open windows to activate their window and bring it to the front of all windows. Not all programs respond to Ctrl+F.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; BREAK 7&lt;br /&gt;
&lt;br /&gt;
sets the Ctrl+C attention flag of process 7. This is the same as selecting process 7 and pressing Ctrl+C.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; BREAK 5 D&lt;br /&gt;
&lt;br /&gt;
sets the Ctrl+D attention flag of process 5.&lt;br /&gt;
&lt;br /&gt;
See also: STATUS&lt;br /&gt;
&lt;br /&gt;
== BUILDMAPTABLE ==&lt;br /&gt;
&lt;br /&gt;
Creates a binary mapping table to Unicode for diskfont.library from ASCII mapping table.&lt;br /&gt;
&lt;br /&gt;
== CD ==&lt;br /&gt;
&lt;br /&gt;
Sets or displays the current directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: CD [&amp;lt;dir | pattern&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DIR&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
CD with no arguments displays the name of the current directory. When a valid directory name is given, CD makes the named directory the current directory.&lt;br /&gt;
&lt;br /&gt;
You must specify a complete path to the directory since CD does not search through the disk for it. If CD cannot find the specified directory in the current directory or in the given path, a Can&#039;t find &amp;lt;directory&amp;gt; message is displayed.&lt;br /&gt;
&lt;br /&gt;
To move up a level in the filing hierarchy to the parent directory of the current directory, enter CD followed by a space and a single slash (/). You can move to another directory in the parent at the same time by including its name after the slash. If the current directory is a root directory, CD / has no effect. Use multiple slashes with no spaces between them to refer to additional higher levels.&lt;br /&gt;
&lt;br /&gt;
To move directly to the root directory of the current device, use CD followed by a space and a colon; for example, CD :&lt;br /&gt;
&lt;br /&gt;
AmigaDOS supports an implied CD so that the CD command itself can often be left out. Enter the directory name, path, colon, or slashes at the prompt.&lt;br /&gt;
&lt;br /&gt;
CD also supports pattern matching. When a directory matching the specified pattern is found, it becomes the current directory. If more than one directory matches the given pattern, an error message is displayed. You cannot use pattern matching with implied CD. For more information an pattern matching, see Chapter 3.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; CD DF1:Work&lt;br /&gt;
&lt;br /&gt;
sets the current directory to the Work directory on the disk in drive DF1:.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; CD SYS:Com/Basic&lt;br /&gt;
&lt;br /&gt;
makes the subdirectory Basic in the Com directory the current directory.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; //&lt;br /&gt;
&lt;br /&gt;
using the implied CD, moves up two levels in the directory structure.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; CD SYS:Li#?&lt;br /&gt;
&lt;br /&gt;
uses the #? pattern to match with the LIBS: directory.&lt;br /&gt;
&lt;br /&gt;
For more examples using the CD command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== CHANGETASKPRI ==&lt;br /&gt;
&lt;br /&gt;
Changes the priority of a currently running process.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: CHANGETASKPRI &amp;lt;priority&amp;gt; [PROCESS &amp;lt;process number&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PRI=PRIORITY/A/N,PROCESS/K/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
CHANGETASKPRI changes the priority of the specified Shell process. If no process is specified, the current Shell process is assumed. Any shell process started from &amp;lt;process number&amp;gt; inherits its priority.&lt;br /&gt;
&lt;br /&gt;
Use the STATUS command to display the current process numbers.&lt;br /&gt;
&lt;br /&gt;
The range of acceptable values for &amp;lt;priority&amp;gt; is the integers from -128 to 127, with higher values yielding a higher priority (a greater proportion of CPU time is allocated). However, do not enter values above +10 to avoid disrupting important system tasks.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; CHANGETASKPRI 4 Process 2&lt;br /&gt;
&lt;br /&gt;
The priority of Process 2 is changed to 4. Any shell process started from this Shell also has a priority of 4. They have priority over any other user tasks created without using CHANGETASKPRI (those tasks have a priority of 0).&lt;br /&gt;
&lt;br /&gt;
See also: STATUS. For another example for using CHANGETASKPRI, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== CHARSETCONVERT ==&lt;br /&gt;
&lt;br /&gt;
Converts a text file from one charset into another.&lt;br /&gt;
&lt;br /&gt;
== CLIP ==&lt;br /&gt;
&lt;br /&gt;
Reads or writes any clipboard unit.&lt;br /&gt;
&lt;br /&gt;
== CONFIGURENETINTERFACE ==&lt;br /&gt;
&lt;br /&gt;
Configure network interface parameters.&lt;br /&gt;
&lt;br /&gt;
== COPY ==&lt;br /&gt;
&lt;br /&gt;
Copies files or directories.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: COPY [FROM] {&amp;lt;name | pattern&amp;gt;} [TO] &amp;lt;name&amp;gt; [ALL] [quiet] [BUF | BUFFER=&amp;lt;n&amp;gt;] [CLONE] [DATES] [NOPRO] [COM] [NOREQ]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/M,TO/A,ALL/S,QUIET/S,BUF=BUFFER/K/N,CLONE/S,DATES/S,NOPRO/S,COM/S,NOREQ/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
COPY copies the file or directory specified with the FROM argument to the file or directory specified by the TO argument. You can copy several items at once by giving more than one name/pattern in the FROM argument; they should be separated by spaces. If the FROM argument is a pattern or consists of multiple names, the TO argument must be a directory.&lt;br /&gt;
&lt;br /&gt;
If a TO file name already exists, COPY overwrites the TO file with the FROM file. You can use a pair of double quotation marks (&amp;quot;&amp;quot;) to refer to the current directory. When used as the FROM argument, &amp;quot;&amp;quot; copies all the files in the current directory. Do not put any spaces between the double quotation marks.&lt;br /&gt;
&lt;br /&gt;
If the FROM argument is a directory, only the directory&#039;s files are copied; its subdirectories are not copied. Use the ALL option to copy the complete directory, including its files, subdirectories, and the subdirectories&#039; files. It is possible to create a directory as you copy if you are copying more than one file. To give the new directory a name, specify the directory name as the last component in the TO argument&#039;s path. This can be any name, including the same name as the original if it is a different path.&lt;br /&gt;
&lt;br /&gt;
COPY prints to the screen the name of each file as it is copied. This can be overridden by the QUIET option.&lt;br /&gt;
&lt;br /&gt;
The BUF= option is used to set the number of 512-byte buffers used during the copy. (Default is 128 buffers, 64 KB of RAM.) Limit the number of buffers when copying to RAM:. BUF=0 uses a buffer the same size as the file to be copied.&lt;br /&gt;
&lt;br /&gt;
By default, COPY gives a TO file the timestamp of when the copy was made, rather than that of the original file. Also by default, comments attached to the original FROM file are not copied and the protection bits of the FROM file are copied to the TO file. You can override these defaults using the following:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CLONE || The timestamp, comments, and protection bits of the FROM file are copied to the TO file.&lt;br /&gt;
|-&lt;br /&gt;
| DATES || The timestamp of the FROM file is copied to the TO file.&lt;br /&gt;
|-&lt;br /&gt;
| COM || Any comment attached to the FROM file is copied to the TO file.&lt;br /&gt;
|-&lt;br /&gt;
| NOPRO || The protection bits of the FROM file are not copied to the TO file. The TO file is given standard protection bits or r, w, e, and d.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
COPY displays a requester if the COPY cannot continue. When the NOREQ option is given, all requesters are suppressed. Use this in scripts to prevent a COPY failure from stopping the script to wait for a response. With the NOREQ option, the COPY command is aborted and the script continues.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; COPY File1 TO :Work/File2&lt;br /&gt;
&lt;br /&gt;
copies File1 in the current directory to the Work directory in the root of the current device, renaming it File2.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; COPY Chapter#? TO DF1:Backup&lt;br /&gt;
&lt;br /&gt;
copies all the files whose names start with Chapter in the current directory to the Backup directory on the disk in DF1:. The Backup directory is created if it does not already exist.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; COPY Work:Test TO &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
copies the files in the Test directory on Work to the current directory; subdirectories in Test are not copied.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; COPY Work:Test TO DF0:Test ALL&lt;br /&gt;
&lt;br /&gt;
copies all the files and any subdirectories of the Test directory on Work to the Test directory on DF0:. If a Test directory does not already exist on DF0:, COPY creates one.&lt;br /&gt;
&lt;br /&gt;
; Example 5:&lt;br /&gt;
 1&amp;gt; COPY DF0: TO DF1: ALL QUIET&lt;br /&gt;
&lt;br /&gt;
copies all files and directories on the disk in DF0: to DF1:, without displaying on the screen any file/directory names as they are copied. (For disks less than half full, this can be faster than DiskCopy.)&lt;br /&gt;
&lt;br /&gt;
For more examples using COPY, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== COUNTLINES ==&lt;br /&gt;
&lt;br /&gt;
Counts how many lines a file is made of.&lt;br /&gt;
&lt;br /&gt;
== CPU ==&lt;br /&gt;
&lt;br /&gt;
Adjusts various options of the microprocessor installed in your Amiga. The command also shows the processor and options that are currently enabled.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: CPU [CACHE | NONCACHE] [BURST | NOBURST] [DATACAHCE | NODATACACHE] [DATABURST | NODATABURST] [INSTCACHE | NOINSTCACHE] [INSTBURST | NOINSTBURST] [FASTROM | NOFASTROM] [TRAP | NOTRAP] [COPYBACK | NOCOPYBACK] [EXTERNALCACHE | NOEXTERNALCACHE] [NOMMUTEST] [CHECK 68010 | 68020 | 68030 | 68040 | 68881 | 68882 | 68851 | MMU | FPU]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: CACHES/S,BURST/S,NOCACHE/S,NOBURST/&amp;quot;,DATACACHE/S,NODATACHE/S,DATABURST/S,NODATABURST/S,INSTCACHE/S,NOINSTCACHE/S,INSTBURST/S,NOINSTBURST/S,COPYBACK/S,NOCOPYBACK/S,EXTERNALCACHE/S,NOEXTERNALCACHE/S,FASTROM/S,NOFASTROM/S,TRAP/S,NOTRAP/S,NOMMUTEST/S,CHECK/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
Many options only work with certain members of the 680x0 processor family. The 68020 has a special type of memory known as instruction cache. When instruction cache is used, instructions are executed more quickly. The 68030 and 68040 have two types of cache memory: instruction and data.&lt;br /&gt;
&lt;br /&gt;
If mutually exclusive options are specified, the safest option is used. Availability of the following options depends on the type of microprocessor present.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CACHE || Turns on all caches.&lt;br /&gt;
|-&lt;br /&gt;
| NOCACHE || Turns off all caches.&lt;br /&gt;
|-&lt;br /&gt;
| BURST || Turns on burst mode for both data and instructions.&lt;br /&gt;
|-&lt;br /&gt;
| NOBURST || Turns off burst mode for data and instructions.&lt;br /&gt;
|-&lt;br /&gt;
| DATACACHE || Turns on data cache.&lt;br /&gt;
|-&lt;br /&gt;
| NODATACACHE || Turns off data cache.&lt;br /&gt;
|-&lt;br /&gt;
| DATABURST || Turns on burst mode for data.&lt;br /&gt;
|-&lt;br /&gt;
| NODATABURST || Turns off burst mode for data.&lt;br /&gt;
|-&lt;br /&gt;
| INSTCACHE || Turns on instruction cache.&lt;br /&gt;
|-&lt;br /&gt;
| NOINSTCACHE || Turns off instruction cache.&lt;br /&gt;
|-&lt;br /&gt;
| INSTBURST || Turns on burst mode for instructions.&lt;br /&gt;
|-&lt;br /&gt;
| NOINSTBURST || Turns off burst mode for instructions.&lt;br /&gt;
|-&lt;br /&gt;
| FASTROM || With a processor having a supported MMU, copies the system ROM into 32-bit RAM, making access to operating system functions significantly faster. CPU then write-protects the RAM area so that the data cannot be changed.&lt;br /&gt;
|-&lt;br /&gt;
| NOFASTROM || Turns off FASTROM.&lt;br /&gt;
|-&lt;br /&gt;
| TRAP || This option is for developers only.&lt;br /&gt;
|-&lt;br /&gt;
| NOTRAP || This option is for developers only.&lt;br /&gt;
|-&lt;br /&gt;
| COPYBACK || Turns on 68040 copyback cache.&lt;br /&gt;
|-&lt;br /&gt;
| NOCOPYBACK || Turns off 68040 copyback cache.&lt;br /&gt;
|-&lt;br /&gt;
| EXTERNALCACHE || Turns on external cache.&lt;br /&gt;
|-&lt;br /&gt;
| NOEXTERNALCACHE || Turns off external cache.&lt;br /&gt;
|-&lt;br /&gt;
| NOMMUTEST || Allows the MMU settings to be changed without checking to see if an MMU is currently in use.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The CHECK option, when given with a keyword (68010, 68020, 68030, 68040, 68881, 68882, or 68851, MMU, FPU) checks for the presence of the processor indicated by the keyword.&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
 1&amp;gt; CPU&lt;br /&gt;
 System: 68030 68881 (INST: Cache Burst) (DATA: Cache NoBurst)&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; CPU NODATACACHE FASTROM&lt;br /&gt;
 System: 68030 68881 FastRom (INST: Cache Burst)&lt;br /&gt;
 (DATA: NoCache NoBurst)&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; CPU NOBURST DATACACHE NOINSTCACHE&lt;br /&gt;
 System: 68030 68881 (INST: NoCache NoBurst) (DATA: Cache NoBurst)&lt;br /&gt;
&lt;br /&gt;
== CUT ==&lt;br /&gt;
&lt;br /&gt;
Cuts some characters or words from a string.&lt;br /&gt;
&lt;br /&gt;
== DATE ==&lt;br /&gt;
&lt;br /&gt;
Displays or sets the system date and/or time.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: DATE [&amp;lt;day&amp;gt;] [&amp;lt;date&amp;gt;] [&amp;lt;time&amp;gt;] [TO | VER &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DAY,DATE,TIME,TO=VER/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
DATE with no argument displays the currently set system time and date, including the day of the week. Time is displayed using a 24-hour clock.&lt;br /&gt;
&lt;br /&gt;
DATE &amp;lt;date&amp;gt; sets only the date. The format for entry and display of &amp;lt;date&amp;gt; is DD-MMM-YY (day-month-year). The hyphens between the arguments are required. A leading zero in the date is not necessary. The number or the first three letters of the month (in English) must be used, as well as the last two digits of the year.&lt;br /&gt;
&lt;br /&gt;
If the date is already set, you can reset it by specifying a day name. You can also use tomorrow or yesterday as the &amp;lt;day&amp;gt; argument. You cannot specify a day name to change the date to more than seven days into the future.&lt;br /&gt;
&lt;br /&gt;
DATE &amp;lt;time&amp;gt; sets the time. The format for entry and display of &amp;lt;time&amp;gt; is HH:MM:SS (hours:minutes:seconds). Seconds is optional.&lt;br /&gt;
&lt;br /&gt;
If your Amiga does not have a battery backed-up hardware clock and you do not set the date, when the system boots it sets the date to the date of the most recently created file on the boot disk.&lt;br /&gt;
&lt;br /&gt;
If you specify the TO or VER option, followed by a file name, the output of the DATE command is sent to that file, overwriting any existing contents.&lt;br /&gt;
&lt;br /&gt;
Adjustments made with DATE only change the software clock and do not survive powering off the system. To set the battery backed-up hardware clock from the Shell, you must set the date and use SETCLOCK SAVE.&lt;br /&gt;
&lt;br /&gt;
Although DATE accepts and displays the date and time in a single format, programs such as Clock display the date and time according to your Locale country setting.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; DATE&lt;br /&gt;
 6-Sep-92&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; DATE 6-sep-92&lt;br /&gt;
&lt;br /&gt;
sets the date to September 6, 1992. The time is not reset.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; DATE tomorrow&lt;br /&gt;
&lt;br /&gt;
resets the date to one day ahead.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; DATE TO Fred&lt;br /&gt;
&lt;br /&gt;
sends the current date to the file Fred.&lt;br /&gt;
&lt;br /&gt;
; Example 5:&lt;br /&gt;
 1&amp;gt; DATE 23:00&lt;br /&gt;
&lt;br /&gt;
sets the current time to 11:00 p.m.&lt;br /&gt;
&lt;br /&gt;
; Example 6:&lt;br /&gt;
 1&amp;gt; DATE 1-jan-02&lt;br /&gt;
&lt;br /&gt;
sets the date to January 1st, 2002. The earliest date you can set is January 1, 1978.&lt;br /&gt;
&lt;br /&gt;
== DELETE ==&lt;br /&gt;
&lt;br /&gt;
Deletes files or directories.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: DELETE {&amp;lt;name | pattern&amp;gt;} [ALL] [QUIET] [FORCE]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/M/A,ALL/S,QUIET/S,FORCE/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
DELETE attempts to erase the specified items. You can delete multiple items at the same time by listing them individually or by using a wildcard to delete a specific set of files matching a pattern. The pattern can specify directory levels, as well as names. To abort a multiple-item DELETE, press Ctrl+C. A multiple-item DELETE aborts if and when it finds something that cannot be removed; for example, a file is delete-protected or in use. A pattern matching DELETE removes everything it can and lists the items that it did not delete, if any.&lt;br /&gt;
&lt;br /&gt;
{{Note|AmigaDOS does not request confirmation of deletions. Do not use pattern matching to delete things if you are not familiar with the procedure; deleted items cannot be recovered, unless you have an up-to-date backup of the items deleted.}}&lt;br /&gt;
&lt;br /&gt;
An error message warns you that you cannot delete directories that still contain files. Override this using the ALL option. DELETE ALL deletes the named directory, its subdirectories, and all files.&lt;br /&gt;
&lt;br /&gt;
File names are displayed on the screen as they are deleted. To suppress the screen output, use the QUIET option.&lt;br /&gt;
&lt;br /&gt;
If the d (deletable) protection bit of a file or directory has been cleared, that item cannot be deleted unless the FORCE option is used.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; DELETE Old-file&lt;br /&gt;
&lt;br /&gt;
deletes the file named Old-file in the current directory.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; DELETE Work/Prog1 Work/Prog2 Work&lt;br /&gt;
&lt;br /&gt;
deletes the files Prog1 and Prog2 in the Work directory and then deletes the Work directory if it contains no other files.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; DELETE T#?/#?(1|2)&lt;br /&gt;
&lt;br /&gt;
deletes all the files that end in 1 or 2 in directories that start with T.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; DELETE DF1:#? ALL FORCE&lt;br /&gt;
&lt;br /&gt;
deletes all the files on DF1:, even those set as not deletable.&lt;br /&gt;
&lt;br /&gt;
See also: PROTECT. For more examples using DELETE, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== DELETENETROUTE ==&lt;br /&gt;
&lt;br /&gt;
Deletes a message routing path currently in use.&lt;br /&gt;
&lt;br /&gt;
== DIR ==&lt;br /&gt;
&lt;br /&gt;
Displays a sorted list of the files in a directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: DIR [&amp;lt;dir | pattern&amp;gt;] [OPT A | I | AI | D | F] [ALL] [DIRS] [FILES] [INTER]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DIR,OPT/K,ALL/S,DIRS/S,FILES/S,INTER/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
DIR displays the file and directory names contained in the specified directory or the current directory. Directories are listed first, followed by an alphabetical list of the files in two columns. Pressing Ctrl+C aborts a directory listing.&lt;br /&gt;
&lt;br /&gt;
The options are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ALL || Displays all subdirectories and their files.&lt;br /&gt;
|-&lt;br /&gt;
| DIRS || Displays only directories.&lt;br /&gt;
|-&lt;br /&gt;
| FILES || Displays only files.&lt;br /&gt;
|-&lt;br /&gt;
| INTER || Enters an interactive listing mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The ALL, DIRS, FILES, and INTER keywords supersede the OPT A, D, F, and I options, respectively. The older keywords are retained for compatibility with earlier versions of AmigaDOS. Do not use OPT with the full keywords - ALL, DIRS, FILES, or INTER.&lt;br /&gt;
&lt;br /&gt;
Interactive listing mode stops after each name to display a question mark at which you can enter commands. The acceptable responses are shown below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Press Return || Displays the next name on the list.&lt;br /&gt;
|-&lt;br /&gt;
| E || Enters a directory; the files in that directory are displayed.&lt;br /&gt;
|-&lt;br /&gt;
| B || Goes back one directory level.&lt;br /&gt;
|-&lt;br /&gt;
| DEL or DELETE || Deletes a file or empty directory. DEL does not refer to the Del key; enter the letters D, E, and L.&lt;br /&gt;
|-&lt;br /&gt;
| T || Types the contents of a file.&lt;br /&gt;
|-&lt;br /&gt;
| C or COMMAND || Allows you to enter additional AmigaDOS commands.&lt;br /&gt;
|-&lt;br /&gt;
| Q || Quits interactive editing.&lt;br /&gt;
|-&lt;br /&gt;
| ? || Displays a list of the available interactive-mode commands.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The COMMAND option allows almost any AmigaDOS command to be executed during the interactive directory list. To issue a command, enter C (or COMMAND) at the question mark prompt. DIR asks you for the command. Enter the desired command, then press Return. The command is executed and DIR continues. You can also combine the C and the command on one line by putting the command in quotation marks following the C.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
&lt;br /&gt;
 ? C &amp;quot;type prefs.info hex&amp;quot;&lt;br /&gt;
&lt;br /&gt;
is equivalent to pressing Q to exit interactive listing mode and return to a regular Shell prompt, then entering:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; TYPE Prefs.info HEX&lt;br /&gt;
&lt;br /&gt;
to display the Prefs.info file on the screen in hexadecimal format.&lt;br /&gt;
&lt;br /&gt;
Formatting a disk from the DIR interactive mode is not recommended since the format takes place immediately, without any confirmation requesters appearing. Do not start another interactive DIR from interactive mode since it results in garbled output.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; DIR Workbench:&lt;br /&gt;
&lt;br /&gt;
displays a list of the directories and files on the Workbench disk.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; DIR MyDisk:#?.memo&lt;br /&gt;
&lt;br /&gt;
displays all the directories and files on MyDisk that end in .memo.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; DIR Extras: ALL&lt;br /&gt;
&lt;br /&gt;
displays the complete contents of the Extras drawer: all directories, all subdirectories, and all files, including those in the subdirectories.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; DIR Workbench: DIRS&lt;br /&gt;
&lt;br /&gt;
displays only the directories on Workbench.&lt;br /&gt;
&lt;br /&gt;
; Example 5:&lt;br /&gt;
 1&amp;gt; DIR Workbench: INTER&lt;br /&gt;
&lt;br /&gt;
begins an interactive list of the contents of the Workbench disk.&lt;br /&gt;
&lt;br /&gt;
For more examples using DIR, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== DISKCHANGE ==&lt;br /&gt;
&lt;br /&gt;
Informs the Amiga that you have changed a disk in a disk drive.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: DISKCHANGE &amp;lt;device&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRIVE/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
You must use the DISKCHANGE command to inform the system when you change disks or cartridges in 5.25 inch floppy disk drives or removable media drives without automatic diskchange hardware.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
If a requester asks you to insert a new disk into your 5.25 inch drive, known as DF2:, you must insert the disk and then enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; DISKCHANGE DF2:&lt;br /&gt;
&lt;br /&gt;
AmigaDOS then recognizes the new disk and you can proceed.&lt;br /&gt;
&lt;br /&gt;
== DISMOUNT ==&lt;br /&gt;
&lt;br /&gt;
Shuts down a file system device and all its associated volumes.&lt;br /&gt;
&lt;br /&gt;
== ECHO ==&lt;br /&gt;
&lt;br /&gt;
Displays a string.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ECHO [&amp;lt;string&amp;gt;] [NOLINE] [FIRST &amp;lt;n&amp;gt;] [LEN &amp;lt;n&amp;gt;] [TO &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: /M,LINE/S,FIRST/K/N,LEN/K/N,TO/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ECHO writes the specified string to the current output window or device. By default the string is sent to the screen, but if you use the TO option, you can send the string to any specified device or file.&lt;br /&gt;
&lt;br /&gt;
When the NOLINE option is specified, ECHO does not automatically move the cursor to the next line after printing the string.&lt;br /&gt;
&lt;br /&gt;
The FIRST and LEN options allow the echoing of a substring. FIRST &amp;lt;n&amp;gt; indicate the character position from which to begin the echo; LEN &amp;lt;n&amp;gt; indicates the number of characters of the substring to echo, beginning with the FIRST character. If the FIRST option is omitted and only the LEN keyword is given, the substring printed consists of the rightmost &amp;lt;n&amp;gt; characters of the main string. For example, if your string is 20 characters long and you specify LEN 4, the 17th, 18th, 19th and 20th characters of the string are echoed.&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ECHO &amp;quot;hello out there!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
hello out there!&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ECHO &amp;quot;hello out there!&amp;quot; NOLINE FIRST 0 LEN 5 hello1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For further examples using the ECHO command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== ED ==&lt;br /&gt;
&lt;br /&gt;
Edits text files (a screen editor).&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ED [FROM] &amp;lt;filename&amp;gt; [Size &amp;lt;n&amp;gt;] [WITH &amp;lt;filename&amp;gt;] [WINDOW &amp;lt;window specification&amp;gt;] [TABS &amp;lt;n&amp;gt;] [WIDTH | COLS &amp;lt;n&amp;gt;] [HEIGHT | ROWS &amp;lt;n&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A,SIZE/N,WITH/K,WINDOW/K,TABS/N,WIDTH=COLS/N,HEIGHT=ROWS/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
See [[AmigaOS_Manual:_AmigaDOS_Using_the_Editors#ED|ED]] for more information. See Chapter 8 for an example using ED.&lt;br /&gt;
&lt;br /&gt;
== EDIT ==&lt;br /&gt;
&lt;br /&gt;
Edits text files by processing the source file sequentially (a line editor).&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: EDIT [FROM] &amp;lt;filename&amp;gt; [[TO &amp;lt;filename&amp;gt;] [WITH &amp;lt;filename&amp;gt;] [VER &amp;lt;filename&amp;gt;] [OPT P &amp;lt;lines&amp;gt; | W &amp;lt;chars&amp;gt; | P&amp;lt;lines&amp;gt;W&amp;lt;chars&amp;gt;] [WIDTH &amp;lt;chars&amp;gt;] [PREVIOUS &amp;lt;lines&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A,TO,WITH/K,VER/K,OPT/K,WIDTH/N,PREVIOUS/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
See [[AmigaOS_Manual:_AmigaDOS_Using_the_Editors#EDIT|EDIT]] for more information.&lt;br /&gt;
&lt;br /&gt;
== ELSE ==&lt;br /&gt;
&lt;br /&gt;
Specifies an alternative for an IF statement in a script file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ELSE&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ELSE must be used in conjunction with the IF command. ELSE is used in an IF block of a script to specify an alternative action if the IF condition is not true. If the IF condition is not true, execution of the script jumps from the IF line to the line after ELSE; all intervening commands are skipped. If the IF condition is true, the commands immediately following the IF statement are executed up to the ELSE. Then, execution skips to the ENDIF statement that concludes the IF block.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
Assume a script, called Display, contains the following block:&lt;br /&gt;
&lt;br /&gt;
 IF exists picfile&lt;br /&gt;
    MultiView picfile&lt;br /&gt;
 ELSE&lt;br /&gt;
    ECHO &amp;quot;picfile is not in this directory&amp;quot;&lt;br /&gt;
 ENDIF&lt;br /&gt;
&lt;br /&gt;
If picfile can be found in the current directory, the MultiView program is executed and picfile is displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
If picfile cannot be found in the current directory, the script skips to the ECHO command. The following message is displayed in the Shell window:&lt;br /&gt;
&lt;br /&gt;
 picfile is not in this directory&lt;br /&gt;
&lt;br /&gt;
See also: IF, ENDIF, EXECUTE&lt;br /&gt;
&lt;br /&gt;
== ENDCLI ==&lt;br /&gt;
&lt;br /&gt;
Ends a Shell process.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ENDCLI&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ENDCLI ends a Shell process.&lt;br /&gt;
&lt;br /&gt;
See also: ENDSHELL&lt;br /&gt;
&lt;br /&gt;
== ENDIF ==&lt;br /&gt;
&lt;br /&gt;
Terminates an IF block in a script file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ENDIF&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ENDIF must be used when an IF commands is used. ENDIF is used in scripts at the end of an IF block. If the IF condition is not true or if the true-condition commands are executed and an ELSE is encountered, the execution of the script skips to the next ENDIF command. Every IF statement must be terminated by an ENDIF.&lt;br /&gt;
&lt;br /&gt;
The ENDIF applies to the most recent IF or ELSE command.&lt;br /&gt;
&lt;br /&gt;
See also: IF, ELSE. For examples using the ENDIF command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== ENDSHELL ==&lt;br /&gt;
&lt;br /&gt;
Ends a Shell process.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ENDSHELL&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ENDSHELL ends a Shell process and closes the Shell window.&lt;br /&gt;
&lt;br /&gt;
The Shell process can also be ended by ENDCLI, by clicking on the close gadget, or by pressing CTRL+\.&lt;br /&gt;
&lt;br /&gt;
Use ENDSHELL only when the Workbench or another Shell is running. If you quit the Workbench and you close your only Shell, you cannot communicate with the Amiga without rebooting.&lt;br /&gt;
&lt;br /&gt;
The Shell window cannot close if any process that were launched from the Shell and not detached are still running. Even though the window stays open, the Shell does not accept new input. You must terminate those processes before the window closes. For example, if you opened an editor from the Shell, the Shell window does not close until you exit the editor.&lt;br /&gt;
&lt;br /&gt;
For examples using the ENDSHELL command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== ENDSKIP ==&lt;br /&gt;
&lt;br /&gt;
Terminates a SKIP block in a script file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ENDSKIP&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
ENDSKIP is used in scripts to terminate the execution of a SKIP block. A SKIP block allows you to jump over intervening commands if a certain condition is met. When an ENDSKIP is encountered, execution of the script resumes at the line following the ENDSKIP. The condition flag is set to 5 (WARN).&lt;br /&gt;
&lt;br /&gt;
See also: SKIP&lt;br /&gt;
&lt;br /&gt;
== EVAL ==&lt;br /&gt;
&lt;br /&gt;
Evaluates integer or Boolean expressions.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: EVAL &amp;lt;value1&amp;gt; {[&amp;lt;operation&amp;gt;] [&amp;lt;value2&amp;gt;]} [TO &amp;lt;file&amp;gt;] [LFORMAT=&amp;lt;string&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: VALUE1/A,OP,VALUE2/M,TO/K,LFORMAT/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
EVAL is used to evaluate and print the answer of an integer expression. The fractional portion of input values and final results, if any, is truncated (cut off). If a non-integer is given as an input value, evaluation stops at the decimal point.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Value1&amp;gt; and &amp;lt;value2&amp;gt; can be decimal (the default), hexadecimal, or octal numbers. Hexadecimal numbers are indicated by either a leading Ox or #x. Octal numbers are indicated by either a leading 0 or a leading #. Alphabetical characters are indicated by a leading single quotation mark (`) and are evaluated as their ASCII equivalent.&lt;br /&gt;
&lt;br /&gt;
The LFORMAT keyword specifies the formatting string used to print the answer. You can use %X (hexadecimal), %O (octal), %N (decimal), or %C (character). The %X and %O options require a number of digits using the LFORMAT keyword, you can specify to print a new line by including *N in your string.&lt;br /&gt;
&lt;br /&gt;
The supported operations and their corresponding symbols are shown in the following table.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| addition || +&lt;br /&gt;
|-&lt;br /&gt;
| subtraction || -&lt;br /&gt;
|-&lt;br /&gt;
| multiplication || *&lt;br /&gt;
|-&lt;br /&gt;
| division || /&lt;br /&gt;
|-&lt;br /&gt;
| modulo || mod, M, m, or %&lt;br /&gt;
|-&lt;br /&gt;
| bitwise AND || &amp;amp;&lt;br /&gt;
|-&lt;br /&gt;
| bitwise OR || &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| bitwise NOT || ~&lt;br /&gt;
|-&lt;br /&gt;
| left shift || Ish, L, or l&lt;br /&gt;
|-&lt;br /&gt;
| right shift || rsh, R, or r&lt;br /&gt;
|-&lt;br /&gt;
| negation || -&lt;br /&gt;
|-&lt;br /&gt;
| exclusive OR || xor, X, or x&lt;br /&gt;
|-&lt;br /&gt;
| bitwise equivalence || eqv, E, or e&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
EVAL can be used in scripts as a counter for loops. In that case, use the TO option to send the output of EVAL to a file.&lt;br /&gt;
&lt;br /&gt;
Parentheses can be used in the expressions.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; EVAL 64 / 8 + 2&lt;br /&gt;
 10&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; EVAL 0x5f / 010 LFORMAT=&amp;quot;The answer is %X4*N&amp;quot;&lt;br /&gt;
 The answer is 000B&lt;br /&gt;
 1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This divides hexadecimal 5f (95) by octal 10 (8), yielding 000B, the integer portion of the decimal answer 11.875. (The 1&amp;gt; prompt appears immediately after the 000B if *N is not specified in the LFORMAT string.)&lt;br /&gt;
&lt;br /&gt;
For more examples using the EVAL command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== EXECUTE ==&lt;br /&gt;
&lt;br /&gt;
Executes a script with optional argument substitution.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: EXECUTE &amp;lt;script&amp;gt; [{&amp;lt;arguments&amp;gt;}]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
EXECUTE is used to run scripts of AmigaDOS commands. The lines in the script are executed as if they had been entered at a Shell prompt. If the s protection bit of a file is set and the file is in the search path, enter only the file name; the EXECUTE command is not needed.&lt;br /&gt;
&lt;br /&gt;
You can use parameter substitution in scripts by including special keywords in the script. When these keywords are used, you can pass variables to the script by including the variable in the EXECUTE command line. Before the script is executed, AmigaDOS checks the parameter names in the script against any arguments given on the command line. If any match, AmigaDOS substitutes the values specified on the command line for the parameter name in the script. You can also specify default values for AmigaDOS to use if no variables are given. If you have not specified a variable and there is no default specified in the script, then the value of the parameter is empty (no substitution is made).&lt;br /&gt;
&lt;br /&gt;
The allowable keywords for parameter substitution are explained in Chapter 5. Each keyword command line must be prefaced with a dot character (.).&lt;br /&gt;
&lt;br /&gt;
See also: IF, SKIP, FAILAT, LAB, ECHO, RUN, QUIT. For examples using the EXECUTE command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== FAILAT ==&lt;br /&gt;
&lt;br /&gt;
Instructs a command sequence not to fail unless a given error condition is returned.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: FAILAT [&amp;lt;n&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: RCLIM/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
Commands indicate that they have failed by setting a nonzero return code. The return code, normally 5, 10, or 20, indicates the severity of the error. A return code greater than or equal to a certain limit, the fail limit, terminates a sequence of non-interactive commands (commands specified after RUN or in a script).&lt;br /&gt;
&lt;br /&gt;
Use the FAILAT command to alter the fail limit RCLIM (Return Code Limit) from its initial value of 10. If you increase the limit, you indicate that certain classes of error should not be regarded as fatal and that execution of subsequent commands can proceed after the error. The argument must be a positive number. The fail limit is reset to the initial value of 10 on exit from the command sequence.&lt;br /&gt;
&lt;br /&gt;
If the argument is omitted, the current fail limit is displayed.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
Assume a script contains the following lines:&lt;br /&gt;
&lt;br /&gt;
 COPY DF0:MyFile to RAM:&lt;br /&gt;
 ECHO &amp;quot;MyFile being copied.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If MyFile cannot be found, the scripts is aborted and the following message appears in the Shell window:&lt;br /&gt;
&lt;br /&gt;
 COPY: object not found&lt;br /&gt;
 COPY failed returncode 20:&lt;br /&gt;
&lt;br /&gt;
However, if you changed the return code limit to higher than 20, the script continues even if the COPY command fails. For example, if you changed the script to read:&lt;br /&gt;
&lt;br /&gt;
 FAILAT 21&lt;br /&gt;
 COPY DF0:MyFile to RAM:&lt;br /&gt;
 ECHO &amp;quot;MyFile being copied.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Even if MyFile cannot be found, the script continues. The following message appears in the Shell window:&lt;br /&gt;
&lt;br /&gt;
 COPY: object not found&lt;br /&gt;
 MyFile being copied.&lt;br /&gt;
&lt;br /&gt;
See also: ECHO, EXECUTE.&lt;br /&gt;
&lt;br /&gt;
== FAULT ==&lt;br /&gt;
&lt;br /&gt;
Prints the messages for the specified error numbers.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: FAULT {&amp;lt;n&amp;gt;}&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: /N/M&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
FAULT prints the messages corresponding to the error numbers supplied. As many error numbers, separated by spaces, as you want can be specified to print at the same time.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
If you receive the error message:&lt;br /&gt;
&lt;br /&gt;
 Error when opening DF1:TestFile 205&lt;br /&gt;
&lt;br /&gt;
and need more information, enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; FAULT 205&lt;br /&gt;
 FAULT 205: object not found&lt;br /&gt;
&lt;br /&gt;
This tells you that the error occurred because TestFile could not be found on DF1:.&lt;br /&gt;
&lt;br /&gt;
A complete list of error messages appears in Appendix A.&lt;br /&gt;
&lt;br /&gt;
== FILENOTE ==&lt;br /&gt;
&lt;br /&gt;
Attaches a comment to a file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: FILENOTE [FILE] &amp;lt;file | pattern&amp;gt; [COMMENT &amp;lt;comment&amp;gt;] [ALL] [QUIET]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/A,COMMENT,ALL/S,QUIET/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
FILENOTE attaches an optional comment of up to 79 characters to the specified file or to all files matching the given pattern.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;comment&amp;gt; includes spaces, it must be enclosed in double quotation marks. To include double quotation marks in a filenote, each literal quotation mark must be immediately preceded by an asterisk (*) and the entire comment must be enclosed in quotation marks, regardless of whether the commend contains any spaces.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;comment&amp;gt; argument is omitted, any existing filenote is deleted from the named file.&lt;br /&gt;
&lt;br /&gt;
Creating a comment with FILENOTE is the same as entering a comment into the Comment gadget of an icon&#039;s Information window. Changes made with FILENOTE are reflected in the Information window, and vice versa.&lt;br /&gt;
&lt;br /&gt;
Use the LIST command to view comments made with FILENOTE. If a file has comments, LIST displays them below the file name.&lt;br /&gt;
&lt;br /&gt;
When a existing file is copied to (specified as the TO argument of a COPY command), it is overwritten, but its original comment is retained. Any comment attached to a FROM file is not copied unless the CLONE or COM option of COPY is specified.&lt;br /&gt;
&lt;br /&gt;
If the ALL option is given, FILENOTE adds the &amp;lt;comment&amp;gt; to all the files and subdirectories matching the pattern entered. If the QUIET options is given, screen output is suppressed.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; FILENOTE Sonata &amp;quot;allegro non troppo&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attaches the filenote allegro non troppo to the Sonata file.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; FILENOTE Toccata &amp;quot;*&amp;quot;presto*&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attaches the filenote &amp;quot;presto&amp;quot; to the Toccata file.&lt;br /&gt;
&lt;br /&gt;
== FILESIZE ==&lt;br /&gt;
&lt;br /&gt;
Collects information on the size of files stored on a disk.&lt;br /&gt;
&lt;br /&gt;
== FTP ==&lt;br /&gt;
&lt;br /&gt;
ARPANET file transfer program.&lt;br /&gt;
&lt;br /&gt;
== GET ==&lt;br /&gt;
&lt;br /&gt;
Gets the value of a local variable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: GET &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
GET is used to retrieve and display the value of a local environment variable. The value is displayed in the current window.&lt;br /&gt;
&lt;br /&gt;
Local environment variables are only recognized by the Shell in which they are created or by any Shells created from a NEWSHELL command executed in the original Shell. If you open an additional Shell by opening the Shell icon or by using the Execute Command menu item, previously created local environment variables are not available.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; GET editor&lt;br /&gt;
 Extras:Tools/MEmacs&lt;br /&gt;
&lt;br /&gt;
See also: SET&lt;br /&gt;
&lt;br /&gt;
== GETENV ==&lt;br /&gt;
&lt;br /&gt;
Gets the value of a global variable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: GETENV &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
GETENV is used to retrieve and display the value of a global environment variable. The value is displayed in the current window. Global variables are stored in ENV: and are recognized by all Shells.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; GETENV editor&lt;br /&gt;
 Extras:Tools/MEmacs&lt;br /&gt;
&lt;br /&gt;
See also: SETENV&lt;br /&gt;
&lt;br /&gt;
== GETNETSTATUS ==&lt;br /&gt;
&lt;br /&gt;
Queries whether the network is operational.&lt;br /&gt;
&lt;br /&gt;
== GROUP ==&lt;br /&gt;
&lt;br /&gt;
Changes the access rights of a file or directory.&lt;br /&gt;
&lt;br /&gt;
== HELP ==&lt;br /&gt;
&lt;br /&gt;
Text based help command.&lt;br /&gt;
&lt;br /&gt;
== HI ==&lt;br /&gt;
&lt;br /&gt;
Halts all ARexx programs.&lt;br /&gt;
&lt;br /&gt;
== HISTORY ==&lt;br /&gt;
&lt;br /&gt;
Display, recall, or store the command line history.&lt;br /&gt;
&lt;br /&gt;
== ICONX ==&lt;br /&gt;
&lt;br /&gt;
{{Note| ICONX is used only as a default tool in a project icon and cannot be used as a Shell command.}}&lt;br /&gt;
&lt;br /&gt;
Allows execution of a script file of AmigaDOS commands from an icon.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ICONX&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
To use ICONX, create or copy a project icon for the script. Open the icon&#039;s Information window and change the Default Tool of the icon to C:ICONX and select Save to store the changed .info file. The script can then be executed by double-clicking on the icon.&lt;br /&gt;
&lt;br /&gt;
When the icon is opened, ICONX changes the current directory to the directory containing the project icon before executing the script. A console window can be opened on the Workbench screen if the script produces output.&lt;br /&gt;
&lt;br /&gt;
Several Tool Types can be specified in the script icon. The WINDOW Tool Type provides an alternate window specification for the input/output window. By default, the window&#039;s specification is:&lt;br /&gt;
&lt;br /&gt;
 WINDOW=CON:0/50//80/IconX/AUTO/WAIT/CLOSE&lt;br /&gt;
&lt;br /&gt;
The AUTO option opens a window only if there is output created by the script. If a window opens, the WAIT option forces it to remain open after the script terminates until you specifically close it. The CLOSE option gives the window a close gadget.&lt;br /&gt;
&lt;br /&gt;
The WAIT Tool Type (not to be confused with the WAIT option of the WINDOW Tool Type) specifies the number of seconds the input/output window should remain open after the script terminates. If you use this option the default input/output window cannot be closed before the WAIT period has expired. There is also a DELAY Tool Type that works in very similar way, except that its parameter is in 1/50th of a second, instead of in seconds.&lt;br /&gt;
&lt;br /&gt;
The STACK Tool Type specifies the number of bytes to use for stack during script execution. If this Tool Type is not provided, the default 4096 bytes is used.&lt;br /&gt;
&lt;br /&gt;
Finally, the USERSHELL Tool Type runs the script file using the current Use Shell instead of the normal ROM Shell. You must specify USERSHELL=YES. User Shells are third party shells that you can purchase and install in your system to replace the standard Amiga Shell environment that comes with the operating system.&lt;br /&gt;
&lt;br /&gt;
Extended selection passes files that have icons to the script. Their file names appear to the script as keywords. To use this facility, the .KEY keyword must appear at the start of the script. In this case, the AmigaDOS EXECUTE command is used to execute the script file.&lt;br /&gt;
&lt;br /&gt;
See also: EXECUTE. For examples using the ICONX command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== IF ==&lt;br /&gt;
&lt;br /&gt;
Evaluates conditional operations in script files.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: IF [NOT] [WARN | ERROR | FAIL] [&amp;lt;string&amp;gt; EQ| GT | GE &amp;lt;string&amp;gt;] [VAL] [EXISTS &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NOT/S,WARN/S,ERROR/S,FAIL/S,EQ/K,GT/K,GE/K,VAL/S,EXISTS/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
In a script file, IF, when its conditional is true, carries out all the subsequent commands until an ENDIF or ELSE command is found. IF must be used in conjunction with ENDIF, however, ELSE is optional. When the conditional is not true, execution skips directly to the ENDIF or to an ELSE. The conditions and commands in IF and ELSE blocks can span more than one line before their corresponding ENDIFs.&lt;br /&gt;
&lt;br /&gt;
Nested Ifs jump to the matching ENDIF.&lt;br /&gt;
&lt;br /&gt;
The additional keywords are as follows:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NOT || Reverses the interpretation of the result.&lt;br /&gt;
|-&lt;br /&gt;
| WARN || True if previous return code is greater than or equal to 5.&lt;br /&gt;
|-&lt;br /&gt;
| ERROR || True if previous return codes is greater than or equal to 10; only available if FAILAT is set to greater than 10.&lt;br /&gt;
|-&lt;br /&gt;
| FAIL || True if previous return code is greater than or equal to 20; only available if FAILAT is set to greater than 20.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;a&amp;gt; GT &amp;lt;b&amp;gt; || True if the test of a is greater than the text of b (disregarding case). Use NOT GT for less than.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;a&amp;gt; GE &amp;lt;b&amp;gt; || True if the text of a is greater than or equal to the text of b (disregarding case). Use NOT GE for less than or equal to.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;a&amp;gt; EQ &amp;lt;b&amp;gt; || True if the text of a and b is identical (disregarding case).&lt;br /&gt;
|-&lt;br /&gt;
| VAL || Specifies a numeric comparison.&lt;br /&gt;
|-&lt;br /&gt;
| EXISTS &amp;lt;file&amp;gt; || True if the file exists.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If more than one of the three condition-flag keywords (WARN, ERROR, FAIL) are given, the one with the lowest value is used.&lt;br /&gt;
&lt;br /&gt;
You can use local or global variables with IF by prefacing the variable name with a $ character.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 IF EXISTS Work/Prog&lt;br /&gt;
    TYPE Work/Prog HEX&lt;br /&gt;
 ELSE&lt;br /&gt;
    ECHO &amp;quot;It&#039;s not here&amp;quot;&lt;br /&gt;
 ENDIF&lt;br /&gt;
&lt;br /&gt;
AmigaDOS displays the file Work/Prog if it exists in the current directory. Otherwise, AmigaDOS displays the message It&#039;s not here and continues after the ENDIF.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 IF ERROR&lt;br /&gt;
    SKIP errlab&lt;br /&gt;
 ENDIF&lt;br /&gt;
 ECHO &amp;quot;No error&amp;quot;&lt;br /&gt;
 LAB errlab&lt;br /&gt;
&lt;br /&gt;
If the previous command produces a return code greater than or equal to 10, AmigaDOS skips over the ECHO command to the errlab label.&lt;br /&gt;
&lt;br /&gt;
See also: EXECUTE, FAILAT, LAB, QUIET, SKIP. For more examples using the IF command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== INFO ==&lt;br /&gt;
&lt;br /&gt;
Gives information about mounted devices.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: INFO [&amp;lt;device&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DEVICE&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
INFO displays a line of information about each mounted storage device, including floppy disk drive and hard disk partitions. Listed are the unit name, maximum size of the disk, the used and free space in blocks, the percentage of the disk that is full, the number of soft disk errors that have occurred, the status of the disk, and the name of the disk.&lt;br /&gt;
&lt;br /&gt;
With the &amp;lt;device&amp;gt; argument, INFO provides information on the specified device or volume only.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt;INFO&lt;br /&gt;
 &lt;br /&gt;
 Unit Size Used Free Full Errs Status Name&lt;br /&gt;
 DF0: 879K 1738 20 98% 0 Read Only Workbench&lt;br /&gt;
 DF1: 879K 418 1140 24% 0 Read/Write Text-6&lt;br /&gt;
 &lt;br /&gt;
 Volumes available:&lt;br /&gt;
 Workbench [Mounted]&lt;br /&gt;
 Text-6 [Mounted]&lt;br /&gt;
&lt;br /&gt;
== INSTALL ==&lt;br /&gt;
&lt;br /&gt;
Writes or inspects a boot blocks on a formatted floppy disk or PCMCIA card, specifying whether it should be bootable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: INSTALL [DRIVE] &amp;lt;DF0: | DF1: | DF2: | DF3: | CC0:&amp;gt; [NOBOOT] [CHECK] [FFS]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRIVE/A,NOBOOT/S,CHECK/S,FFS/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
INSTALL clears a floppy disk&#039;s or PCMCIA memory card&#039;s boot block area and writes a valid boot onto it. INSTALL does not affect any files or directories on the disk or card. The necessary files and directories must still be present on a device to boot from it successfully.&lt;br /&gt;
&lt;br /&gt;
The NOBOOT option removes the boot block from an AmigaDOS disk or card, making it not bootable.&lt;br /&gt;
&lt;br /&gt;
The CHECK option checks for valid boot code. It reports whether a disk or card is bootable and whether standard Amiga boot code is present on the media. This is useful in detecting some viruses.&lt;br /&gt;
&lt;br /&gt;
The FFS switch is ignored. It remains part of the template to ensure compatibility with earlier scripts and programs.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; INSTALL DF0: CHECK&lt;br /&gt;
 No bootblock installed&lt;br /&gt;
&lt;br /&gt;
indicates that there is a non-bootable floppy in DF0:.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; INSTALL DF0:&lt;br /&gt;
&lt;br /&gt;
makes the disk in drive DF0: a bootable disk.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; INSTALL DF0: CHECK&lt;br /&gt;
 Appears to be FFS bootblock&lt;br /&gt;
&lt;br /&gt;
indicates that there is a bootable FFS floppy in DF0:.&lt;br /&gt;
&lt;br /&gt;
== IPF ==&lt;br /&gt;
&lt;br /&gt;
Alters packet filtering lists for IP packet input and output.&lt;br /&gt;
&lt;br /&gt;
== IPSTAT ==&lt;br /&gt;
&lt;br /&gt;
Reports on packet filter statistics and filter list.&lt;br /&gt;
&lt;br /&gt;
== IPMON ==&lt;br /&gt;
&lt;br /&gt;
Monitors /dev/ipl for logged packets.&lt;br /&gt;
&lt;br /&gt;
== IPNAT ==&lt;br /&gt;
&lt;br /&gt;
Defines NAT (Network Address Translation) rules.&lt;br /&gt;
&lt;br /&gt;
== JOIN ==&lt;br /&gt;
&lt;br /&gt;
Concatenates two or more files into a new file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: JOIN [FILE] &amp;lt;file | pattern&amp;gt;} AS | TO &amp;lt;filename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/M/A,AS=TO/K/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
JOIN copies all the listed files, in the order given, to one new file. This destination file cannot have the same name as any of the source files. You must supply a destination file name. The original files remain unchanged. Any number of files can be JOINed in one operation.&lt;br /&gt;
&lt;br /&gt;
TO can be used as a synonym for AS.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; JOIN Part1 Part2 Part3 AS Textfile&lt;br /&gt;
&lt;br /&gt;
For another example using JOIN, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== LAB ==&lt;br /&gt;
&lt;br /&gt;
Specified a label in a script file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: LAB [&amp;lt;string&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
LAB is used in script to define a label that is searched for by the SKIP command. The label &amp;lt;string&amp;gt; can be of any length, but must be alphanumeric. No symbols are allowed. If the &amp;lt;string&amp;gt; contains spaces, it must be enclosed in quotation marks.&lt;br /&gt;
&lt;br /&gt;
See also: SKIP, IF, EXECUTE. For more examples using LAB, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== LIST ==&lt;br /&gt;
&lt;br /&gt;
Lists specified information about directories and files.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: LIST [{&amp;lt;dir | pattern | filename&amp;gt;}] [P | PAT &amp;lt;pattern&amp;gt;] [KEYS] [DATES] [NODATES] [TO &amp;lt;name&amp;gt;] [DUB &amp;lt;string&amp;gt;] [SINCE &amp;lt;date&amp;gt;] [UPTO &amp;lt;date&amp;gt;] [QUICK] [BLOCK] [NOHEAD] [FILES] [DIRS] [LFORMAT &amp;lt;string&amp;gt;] [ALL]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DIR/M,P=PAT/K,KEYS/S,DATES/S,NODATES/S,TO/K,SUB/K,SINCE/K,UPTO/K,QUICK/S,BLOCK/S,NOHEAD/S,FILES/S,DIRS/S,LFORMAT/K,ALL/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
LIST displays information about the contents of the current directory. If you specify a &amp;lt;dir&amp;gt;, &amp;lt;pattern&amp;gt;, or &amp;lt;filename&amp;gt; argument, LIST displays information about the specified directory, all directories or files that match the pattern, or the specified file, respectively. The PAT argument lets you specify an additional pattern to match.&lt;br /&gt;
&lt;br /&gt;
Unless other options are specified, LIST displays the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| name || The name of the file or directory.&lt;br /&gt;
|-&lt;br /&gt;
| size || The size of the file in bytes. If there is nothing in this file, the field reads &amp;quot;empty&amp;quot;. For directories, this entry reads &amp;quot;Dir&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| protection || The protection bits that are set for this file are shown as letters. The clear (unset) bits are shown as hyphens. Most files show the default protection bits, ----rwed for readable/writable/executable/deletable. See the PROTECT command for more on protection bits.&lt;br /&gt;
|-&lt;br /&gt;
| date and time || The date and time the file was created or last changed.&lt;br /&gt;
|-&lt;br /&gt;
| comment || The comment, if any, placed on the file using the FILENOTE command. It is preceded by a colon (:).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
LIST uses the following options to change the way the output is displayed:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| KEYS || Displays the block number of each file header or directory.&lt;br /&gt;
|-&lt;br /&gt;
| DATES || Displays dates. (For example, DD-MMM-YY is the USA default).&lt;br /&gt;
|-&lt;br /&gt;
| NODATES || Does not display date and time information.&lt;br /&gt;
|-&lt;br /&gt;
| TO &amp;lt;name&amp;gt; || Specifies an output file or device for LIST; by default, LIST outputs to the current window.&lt;br /&gt;
|-&lt;br /&gt;
| SUB &amp;lt;string&amp;gt; || Lists only files containing the substring &amp;lt;string&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| SINCE &amp;lt;date&amp;gt; || Lists only files timestamped on or after the specified date.&lt;br /&gt;
|-&lt;br /&gt;
| UPTO &amp;lt;date&amp;gt; || Lists only files timestamped on or before the specified date.&lt;br /&gt;
|-&lt;br /&gt;
| QUICK || Lists only the names of files and directories.&lt;br /&gt;
|-&lt;br /&gt;
| BLOCK || Displays file sizes in 512-byte blocks, rather than bytes.&lt;br /&gt;
|-&lt;br /&gt;
| NOHEAD || Suppresses printing of the header and summary information.&lt;br /&gt;
|-&lt;br /&gt;
| FILES || Lists files only (no directories).&lt;br /&gt;
|-&lt;br /&gt;
| DIRS || Lists directories only (no files).&lt;br /&gt;
|-&lt;br /&gt;
| LFORMAT || Defines a string to specially format LIST output.&lt;br /&gt;
|-&lt;br /&gt;
| ALL || Lists the contents of all directories and subdirectories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The LFORMAT option modifies the output of LIST and can be used as a quick method of generating script files. When using LFORMAT, specify an output format string; this string is output for each file or directory normally listed. It can contain any text you specify, plus the usual LIST output information. When LFORMAT is specified, the QUICK and NOHEAD options are automatically selected. To save the output, you must redirect it to a file by using the &amp;gt; operator or specifying a TO file. (For examples using the LIST LFORMAT option, see Chapter 8.)&lt;br /&gt;
&lt;br /&gt;
The available substitution operators are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| %A || Prints file attributes (protection bits).&lt;br /&gt;
|-&lt;br /&gt;
| %B || Prints size of file in blocks.&lt;br /&gt;
|-&lt;br /&gt;
| %C || Prints any comments attached to the file.&lt;br /&gt;
|-&lt;br /&gt;
| %D || Prints the date associated with the file.&lt;br /&gt;
|-&lt;br /&gt;
| %E || Prints just the file extension.&lt;br /&gt;
|-&lt;br /&gt;
| %K || Prints the file key block number.&lt;br /&gt;
|-&lt;br /&gt;
| %L || Prints the length of the file in bytes.&lt;br /&gt;
|-&lt;br /&gt;
| %M || Prints the file name only, omitting any extension.&lt;br /&gt;
|-&lt;br /&gt;
| %N || Prints the name of the file.&lt;br /&gt;
|-&lt;br /&gt;
| %P || Prints the file parent path.&lt;br /&gt;
|-&lt;br /&gt;
| %S || Superseded by %N and %P; still functional.&lt;br /&gt;
|-&lt;br /&gt;
| %T || Prints the time associated with the file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You can put a length specifier and/or a justification specifier between the percent sign (%) and the field specifier. To specify left justification, place a minus sign (-) before the length specifier. Otherwise, the information displayed is right justified.&lt;br /&gt;
&lt;br /&gt;
The default output of the LIST command uses the following specification:&lt;br /&gt;
&lt;br /&gt;
 %-24 %7L %A %D %T&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 &amp;gt; LIST Dirs&lt;br /&gt;
&lt;br /&gt;
 Prefs Dir ----rwed 27-Jun-93 11:43:59&lt;br /&gt;
 T Dir ----rwed 16-Jul-93 11:37:43&lt;br /&gt;
 Trashcan Dir ----rwed 21-Jun-93 17:54:20&lt;br /&gt;
&lt;br /&gt;
Only the directories in the current directory, in this case SYS:, are listed. (A shortened version of the typical output is shown above.)&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; LIST LI#? TO RAM:Libs.file&lt;br /&gt;
&lt;br /&gt;
LIST searches for any directories or files that start with LI. The output of LIST is sent to Libs.file in RAM:.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; LIST DF0:Documents UPTO 09-Oct-90&lt;br /&gt;
&lt;br /&gt;
Only the files or directories in the Documents directory of DF0: that have not been changed since October 9, 1990 are listed.&lt;br /&gt;
&lt;br /&gt;
For further examples using the LIST command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== LOADMONDRVS ==&lt;br /&gt;
&lt;br /&gt;
Starts monitor drivers.&lt;br /&gt;
&lt;br /&gt;
== LOADRESOURCE ==&lt;br /&gt;
&lt;br /&gt;
Preloads resources into memory to avoid excessive disk swaps.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: LOADRESOURCE {&amp;lt;name&amp;gt;} [LOCK | UNLOCK]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME/M,LOCK/S,UNLOCK/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
LOADRESOURCE reduces the need for excessive disk swaps on floppy-only systems by preloading the following of resources into memory:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Libraries || Specify the path name to the library.&lt;br /&gt;
|-&lt;br /&gt;
| Devices || Specify the path name to the device; you cannot LOCK devices into memory.&lt;br /&gt;
|-&lt;br /&gt;
| Fonts || Specify the path name to the exact font file to be loaded.&lt;br /&gt;
|-&lt;br /&gt;
| Catalogs || Specify a path name as LOCALE:Catalogs/&amp;lt;language&amp;gt;/Sys/&amp;lt;catalog&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The {&amp;lt;name&amp;gt;} option specifies the paths of the resources to load. The LOCK option tells the command to lock resources, such as libraries, fonts, and catalogs, into memory. This prevents the system from flushing the resource from RAM if memory is low. Although you can preload devices into memory using LOADRESOURCE, you cannot force them to stay in memory using the LOCK option. The UNLOCK option tells the command to unlock the resource from memory, allowing it to be flushed from RAM.&lt;br /&gt;
&lt;br /&gt;
Entering LOADRESOURCE with no options lists all the LOCKed resources in RAM.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 LOADRESOURCE LIBS:asl.library&lt;br /&gt;
&lt;br /&gt;
loads asl.library into memory. The system can flush this library from RAM the next time it runs low on memory, unless the LOCK option is included in the command line.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 LOADRESOURCE FONTS:topaz/11&lt;br /&gt;
&lt;br /&gt;
loads the Topaz 11 font into memory.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 LOADRESOURCE LOCALE:Catalogs/English/Sys/monitors.catalog&lt;br /&gt;
&lt;br /&gt;
is a valid path name.&lt;br /&gt;
&lt;br /&gt;
== LOADWB ==&lt;br /&gt;
&lt;br /&gt;
Starts Workbench.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: LOADWB [-DEBUG] [DELAY] [CLEANUP] [NEWPATH]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: -DEBUG/S,DELAY/S,CLEANUP/S,NEWPATH/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
LOADWB starts the Workbench. Normally, this is in the Startup-sequence file that starts Workbench when booting. If you close the Workbench, LOADWB can restart it from a Shell.&lt;br /&gt;
&lt;br /&gt;
The -DEBUG option makes a special developer menu, Debug, available in the Workbench menu bar. If the DELAY option is specified, LOADWB waits three seconds before executing to allow disk activity time to stop. The CLEANUP option automatically performs a cleanup of the initial disk window.&lt;br /&gt;
&lt;br /&gt;
Workbench snapshots the current paths in effect when the LOADWB command is executed. It uses these paths for each Shell started from Workbench. NEWPATH allows you to specify a new path that is snapshot from the current Shell.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
&lt;br /&gt;
If you quit the Workbench and are working through a Shell, enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; LOADWB&lt;br /&gt;
&lt;br /&gt;
to return the Workbench. Entering LOADWB when the Workbench is already loaded has no effect.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; PATH DF2:bin ADD&lt;br /&gt;
 1&amp;gt; LOADWB NEWPATH&lt;br /&gt;
&lt;br /&gt;
loads the Workbench. Any Shells started from the icon have the same path as the Shell used to run the LOADWB NEWPATH command.&lt;br /&gt;
&lt;br /&gt;
== LOCK ==&lt;br /&gt;
&lt;br /&gt;
Sets the write-protect status of a device.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: LOCK &amp;lt;drive&amp;gt; [ON | OFF] [&amp;lt;passkey&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRIVE/A,ON/S,OFF/S,PASSKEY&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
LOCK sets or unsets the write-protect status of a device or partition. The LOCK remains on until the system is rebooted or until the LOCK is turned off with the LOCK OFF command.&lt;br /&gt;
&lt;br /&gt;
An optional passkey can be specified. If the passkey is used to lock a hard disk partition, the same passkey must be specified to unlock the partition. The passkey can be any number of characters long.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; LOCK Work: ON SecretCode&lt;br /&gt;
&lt;br /&gt;
The Work partition is locked. You can read the contents of Work with commands such as DIR, LIST, or MORE but you cannot alter the contents of the partition. If you try to edit the contents of a file on Work, a requester indicates that Work is write-protected. For example, if you try to create a new directory by entering the following:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; MAKEDIR WORK:Test&lt;br /&gt;
&lt;br /&gt;
the following message appears:&lt;br /&gt;
&lt;br /&gt;
 Can&#039;t create directory Work:Test&lt;br /&gt;
 Disk is write-protected&lt;br /&gt;
&lt;br /&gt;
To unlock the partition, enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; LOCK Work: OFF SecretCode&lt;br /&gt;
&lt;br /&gt;
Locking a device is only good for the duration of the current session. Resetting or turning off the Amiga cancels the lock.&lt;br /&gt;
&lt;br /&gt;
== LOGVIEWER ==&lt;br /&gt;
&lt;br /&gt;
Captures any debug or notification messages sent by bsdsocket.library or its clients.&lt;br /&gt;
&lt;br /&gt;
== MAGTAPE ==&lt;br /&gt;
&lt;br /&gt;
Retensions, rewinds, or skips forward SCSI (Small Computer System Interface) tapes.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: MAGTAPE [DEVICE &amp;lt;device name&amp;gt;] [UNIT &amp;lt;n&amp;gt;] [RET | RETENSION] [REW | REWIND] [SKIP &amp;lt;n&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DEVICE/K,UNIT/N/K,RET=RETENSION/S,REW=REWIND/S,SKIP/N/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
By default, MAGTAPE uses SCSI device unit 4. To change the default, you must use both the DEVICE and UNIT keywords.&lt;br /&gt;
&lt;br /&gt;
The RET | RETENSION option runs the tape to the end and rewinds it. The REW | REWIND option rewinds the tape. The SKIP &amp;lt;n&amp;gt; option skips &amp;lt;n&amp;gt; files on the tape.&lt;br /&gt;
&lt;br /&gt;
MAGTAPE tests to see if the unit is ready before sending the command. If your tape is not on-line, repeat the command.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; MAGTAPE DEVICE second_scsi.device UNIT 0 REW &lt;br /&gt;
&lt;br /&gt;
== MAKEDIR ==&lt;br /&gt;
&lt;br /&gt;
Creates a new directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: MAKEDIR {&amp;lt;name}&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME/M&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
MAKEDIR creates new, empty directories with the names you specify. The command works within only one directory level at a time, so any directories on the given paths must already exist. The command fails of a directory or a file of the same name already exists in the directory in which you attempt to create a new one.&lt;br /&gt;
&lt;br /&gt;
MAKEDIR does not create a drawer icon for the new directory.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; MAKEDIR Tests&lt;br /&gt;
&lt;br /&gt;
creates a directory called Tests in the current directory.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; MAKEDIR DF1:Xyz&lt;br /&gt;
&lt;br /&gt;
creates a directory Xyz in the root directory of the disk in DF1:.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; CD DF0:&lt;br /&gt;
 1&amp;gt; MAKEDIR Documents Payables Orders&lt;br /&gt;
&lt;br /&gt;
creates three directories on the disk in DF0:: Documents, Payables, and Orders.&lt;br /&gt;
&lt;br /&gt;
For more examples using MAKEDIR, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== MAKELINK ==&lt;br /&gt;
&lt;br /&gt;
Creates a link between files.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: MAKELINK [FROM] &amp;lt;file&amp;gt; [TO] &amp;lt;file&amp;gt; [HARD] [FORCE]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A,TO/A,HARD/S,FORCE/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
MAKELINK creates a FROM file, known as a link, that is a pointer to another file, the TO file, on the disk. When an application or command falls the FROM file, the TO file is used. By default, MAKELINK supports hard links: the FROM file and TO file must be on the same volume.&lt;br /&gt;
&lt;br /&gt;
Normally, MAKELINK does not support directory links. To create a directory link, you must use the FORCE option. If MAKELINK detects that you are creating a circular link, such as a link to a parent directory, a Link loop not allowed message is issued.&lt;br /&gt;
&lt;br /&gt;
== MD5SUM ==&lt;br /&gt;
&lt;br /&gt;
Calculates and compares checksums of files.&lt;br /&gt;
&lt;br /&gt;
== MOUNT ==&lt;br /&gt;
&lt;br /&gt;
Makes a device connected to the system available.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: MOUNT {device} [FROM &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DEVICE/M,FROM/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
MOUNT reads a device&#039;s configuration parameters from a file. It then uses the parameter information to mount the devices or make them available to the system. Multiple devices can be mounted with a single command. The {device} argument specifies the names of the devices to be mounted.&lt;br /&gt;
&lt;br /&gt;
MOUNT can process either DOSDrivers mount files or a traditional multiple-entry MountList file, depending on which of the following three ways the arguments are specified:&lt;br /&gt;
&lt;br /&gt;
# Given a device name, MOUNT tries to find a mount file of that name in DEVS:DOSDrivers, then in SYS:Storage/DOSDrivers, and finally as an entry in DEVS:MountList. This method is best if you have only one configuration for that device on your system.&lt;br /&gt;
# Given a path, MOUNT looks for a mount file in that location. Wildcards may be used to mount multiple devices; as in MOUNT DEVS:DOSDrivers/~(#?.info). Use this method when you have mount files stored somewhere other than the DOSDrivers drawers or if you have several mount file to process at once.&lt;br /&gt;
# Given the FROM keyword and a path, MOUNT specifies the location of a MountList file to process. Use this method if you have a MountList stored somewhere other than DEVS: or if you have several MountLists.&lt;br /&gt;
&lt;br /&gt;
{{Note|A mount file&#039;s icon Tool Types, if any, override parameters of the same name in the mount file itself.}}&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; MOUNT PIPE:&lt;br /&gt;
&lt;br /&gt;
This looks for the mount file DEVS:DOSDrivers/PIPE and processes it if found. If DEVS:DOSDrivers/PIPE does not exist, MOUNT looks for SYS:Storage/DOSDrivers/PIPE. If this also fails, then MOUNT looks for a PIPE: entry in DEVS:MountList.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; MOUNT Work:Devices/PIPE&lt;br /&gt;
&lt;br /&gt;
This looks for a PIPE mount file in Work:Devices.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; MOUNT PIPE: FROM SYS:Mydevs/MountList&lt;br /&gt;
&lt;br /&gt;
This scans for a PIPE entry in SYS:Mydevs/MountList.&lt;br /&gt;
&lt;br /&gt;
See Appendix B for further information on MountLists.&lt;br /&gt;
&lt;br /&gt;
== MOUNTINFO ==&lt;br /&gt;
&lt;br /&gt;
Creates mount files for file systems.&lt;br /&gt;
&lt;br /&gt;
== MOVE ==&lt;br /&gt;
&lt;br /&gt;
Moves files or directories.&lt;br /&gt;
&lt;br /&gt;
== NETLOGVIEWER ==&lt;br /&gt;
&lt;br /&gt;
Captures any debug or notification messages sent by bsdsocket.library or its clients.&lt;br /&gt;
&lt;br /&gt;
== NETSHUTDOWN ==&lt;br /&gt;
&lt;br /&gt;
Shuts down the network in an orderly fashion.&lt;br /&gt;
&lt;br /&gt;
== NEWCLI ==&lt;br /&gt;
&lt;br /&gt;
Opens a new Shell window.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: NEWCLI [&amp;lt;window specification&amp;gt;] [FROM &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: WINDOW,FROM&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
NEWCLI starts a new Shell process. It is the same as using the NEWSHELL command.&lt;br /&gt;
&lt;br /&gt;
== NEWSHELL ==&lt;br /&gt;
&lt;br /&gt;
Opens a new Shell window.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: NEWSHELL [&amp;lt;window specification&amp;gt;] [FROM &amp;lt;filename&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: WINDOW,FROM&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
The new Shell window becomes the currently-selected window and process. The new window has the same current directory, prompt string, path, local environment variables, and stack size as the one from which it is invoked. However, each Shell window is independent, allowing separate input, output, and program execution.&lt;br /&gt;
&lt;br /&gt;
The window can be sized, dragged, zoomed, and depth-adjusted like most other Amiga windows.&lt;br /&gt;
&lt;br /&gt;
To create a custom window, you can include the &amp;lt;window specification&amp;gt; argument. Specify the initial dimensions, location, and title of the window with this &amp;lt;window specification&amp;gt; syntax:&lt;br /&gt;
&lt;br /&gt;
 CON:x/y/width/height/title/options&lt;br /&gt;
&lt;br /&gt;
where:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| x || Is the number of pixels from the left edge of the screen to the left border of the Shell window. Use a value (//) to specify the minimum possible pixels.&lt;br /&gt;
|-&lt;br /&gt;
| y || Is the number of pixels from the top of the screen to the top of the Shell window. Use no value (//) to specify the minimum possible pixels.&lt;br /&gt;
|-&lt;br /&gt;
| width || Is the width of the Shell window, in pixels. Use no value (//) to specify the full width of the screen.&lt;br /&gt;
|-&lt;br /&gt;
| height || Is the height of the Shell window, in pixels. Use no value (//) to specify minimum possible height.&lt;br /&gt;
|-&lt;br /&gt;
| title || Is the text that appears in the Shell window title bar.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Use slashes to separate the parameters and options. If any spaces appear in the specification argument, the entire argument must be enclosed in double quotation marks (&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The allowable options are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| AUTO || The window automatically appears when the program needs input or produces output. With the Shell window, it opens for input immediately. The window can only be closed with the ENDSHELL command. Selecting the Shell&#039;s close gadget closes the window, but it re-opens immediately since it is expecting input.&lt;br /&gt;
|-&lt;br /&gt;
| ALT || The window appears in the specified size and position when the zoom gadget is clicked. The four parameters must be separated with slashes (for example, ALT30/30/200/200).&lt;br /&gt;
|-&lt;br /&gt;
| BACKDROP || The window appears on the backdrop, behind all the Workbench windows. This Shell window cannot be brought to the front of the screen; you have to resize the Workbench windows to see it.&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE || The window has all the standard gadgets, including a close gadget. This is the default for Shell windows, but you must specify it to get a standard Shell if you use the WINDOW argument.&lt;br /&gt;
|-&lt;br /&gt;
| INACTIVE || The window opens, but is not made the active window.&lt;br /&gt;
|-&lt;br /&gt;
| NOBORDER || The window opens without any left or bottom window border. Only the zoom, depth, and sizing gadgets are available.&lt;br /&gt;
|-&lt;br /&gt;
| NOCLOSE || The window does not have a close gadget. If you open a console normally, there is no close gadget. If you open a console using the AUTO option, there is automatically a close gadget on the window.&lt;br /&gt;
|-&lt;br /&gt;
| NODEPTH || The window has no window depth gadget.&lt;br /&gt;
|-&lt;br /&gt;
| NODRAG || The window cannot be dragged. It has zoom, depth and sizing gadgets, but no close gadget.&lt;br /&gt;
|-&lt;br /&gt;
| NOSIZE || The window only has a depth gadget.&lt;br /&gt;
|-&lt;br /&gt;
| SCREEN || The window opens on a public screen. The screen must already exist. You must specify the name of the screen after the SCREEN keyword.&lt;br /&gt;
|-&lt;br /&gt;
| SIMPLE || If you enlarge the window, the text expands to fill the newly available space, allowing you to see text that had been scrolled out of the window. This is the default for standard Shells.&lt;br /&gt;
|-&lt;br /&gt;
| SMART || If you enlarge the window, the text does not expand to fill the newly available space. This saves memory.&lt;br /&gt;
|-&lt;br /&gt;
| WAIT || The window can only be closed by selecting the close gadget or entering Ctrl+\. If WAIT is the only option, there is no close gadget.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
NEWSHELL uses the default startup file S:Shell-startup, unless a FROM file name is specified. S:Shell-startup is a standard AmigaDOS script file. For example, you can have several different Shell-startup files, each having different command aliases. You can call such customized Shell environments with FROM.&lt;br /&gt;
&lt;br /&gt;
The NEWCLI command has the same effect as NEWSHELL; it invokes a new Shell process.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; NEWSHELL&lt;br /&gt;
&lt;br /&gt;
opens a new Shell window with the default window specification.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; NEWSHELL &amp;quot;CON://640/200/My Shell/CLOSE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
A window starting in the upper left corner of the screen and measuring 640 pixels wide and 200 pixels high opens. The window is titled My Shell and it has a close gadget. The entire argument is enclosed in quotation marks because the title contains a space. If you add the command to your User-startup file, a Shell window opens automatically when your Amiga is booted.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; NEWSHELL FROM S:Programming.startup&lt;br /&gt;
&lt;br /&gt;
opens a new Shell, but instead of executing the Shell-startup file, the Programming.startup file is executed. You can have aliases and prompt commands in the Programming.startup file that are used only when you are programming.&lt;br /&gt;
&lt;br /&gt;
For more examples using NEWSHELL, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== NVGETVAR ==&lt;br /&gt;
&lt;br /&gt;
Prints the value of all or a named UBoot environment variable.&lt;br /&gt;
&lt;br /&gt;
== OWNER ==&lt;br /&gt;
&lt;br /&gt;
Changes the ownership of a file or directory.&lt;br /&gt;
&lt;br /&gt;
== PATH ==&lt;br /&gt;
&lt;br /&gt;
Controls the directory list that the Shell searches to find commands.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: PATH [{&amp;lt;dir&amp;gt;}] [ADD] [SHOW] [RESET] [REMOVE] [QUIET]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PATH/M,ADD/S,SHOW/S,RESET/S,REMOVE/S,QUIET/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
PATH lets you see, add to, or change the search path the AmigaDOS follows when looking for a command or program to execute. When a directory is in the search path, you do not need to specify the complete path to any command within that directory. Entering the name alone makes AmigaDOS look through the directories in the search path until it finds the file.&lt;br /&gt;
&lt;br /&gt;
{{Note|The search path is only relevant when AmigaDOS is searching for a command or program to execute. Full path specifications are always necessary in arguments for commands such as COPY and DELETE.}}&lt;br /&gt;
&lt;br /&gt;
Enter the PATH command alone or with the SHOW option to display directory names in the current search path. Normally, when PATH is displaying the directory names, a requester appears if a volume that is part of the search path cannot be found. For example, if you add a floppy disk to the search path and then remove that disk from the disk drive, a requester asks you to insert the disk.&lt;br /&gt;
&lt;br /&gt;
If you specify the QUIET option, PATH does not display requesters for volumes that are not currently mounted. If PATH encounters an unmounted volume, it displays the message device (or volume) is not mounted . The names of any directories on that volume included in the PATH are not displayed.&lt;br /&gt;
&lt;br /&gt;
The ADD option specifies directory to be added to the current PATH. You can add any number of directories with one PATH ADD command (the ADD keyword is optional); names of the directories must be separated by at least one space. When you issue the PATH command, AmigaDOS searches for each of the ADDed directories.&lt;br /&gt;
&lt;br /&gt;
To replace the existing search path with a new one, use PATH RESET followed by the names of the new directories. The existing search path, except for the current directory and C:, is erased and the new one is substituted.&lt;br /&gt;
&lt;br /&gt;
The REMOVE option eliminates the named directory from the search path.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; PATH EXTRAS:Tools ADD&lt;br /&gt;
&lt;br /&gt;
adds the Tools directory in the Extras drawer to the search path of the Shell. If the EXTRAS: is not in a disk drive, a requester asks you to insert it in any drive.&lt;br /&gt;
&lt;br /&gt;
If you remove EXTRAS: from the drive and enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; PATH&lt;br /&gt;
&lt;br /&gt;
a list of directories in the search path is displayed. A requester asks you to insert EXTRAS:. If you enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; PATH QUIET&lt;br /&gt;
&lt;br /&gt;
the list of directories in the search path is displayed. However, when the path comes to Extras:Tools, the error message appears in the list.&lt;br /&gt;
&lt;br /&gt;
See also: ASSIGN. For more examples using PATH, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== PATHPART ==&lt;br /&gt;
&lt;br /&gt;
Splits and assembles directory and file names.&lt;br /&gt;
&lt;br /&gt;
== PING ==&lt;br /&gt;
&lt;br /&gt;
Sends ICMP ECHO_REQUEST packets to network hosts.&lt;br /&gt;
&lt;br /&gt;
== PIPE ==&lt;br /&gt;
&lt;br /&gt;
Connects input and output streams of Shell commands.&lt;br /&gt;
&lt;br /&gt;
== POPCD ==&lt;br /&gt;
&lt;br /&gt;
Returns the directory last recently saved with the PUSHCD command.&lt;br /&gt;
&lt;br /&gt;
== PROMPT ==&lt;br /&gt;
&lt;br /&gt;
Changes the prompt string of the current Shell.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: PROMPT [&amp;lt;prompt&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PROMPT&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
PROMPT allows you to customize the prompt string, the text printed by the Shell at the beginning of a command line. The prompt string can contain any characters, including escape sequences.&lt;br /&gt;
&lt;br /&gt;
This manual shows the prompt string as 1&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The default prompt string is:&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;%N.%S&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
which displays the Shell number, a period, the current directory, a right angle-bracket, and a space. Entering PROMPT without a string argument resets the prompt to this default.&lt;br /&gt;
&lt;br /&gt;
The substitutions available for the &amp;lt;prompt&amp;gt; string are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| %N || Displays the process number for the Shell.&lt;br /&gt;
|-&lt;br /&gt;
| %S || Displays the current directory.&lt;br /&gt;
|-&lt;br /&gt;
| %R || Displays the return code for the last operation.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A space is not automatically added to the end of the string. If you want a space between the prompt and typed-in text, place it in the string, and enclose the string in double quotation marks,&lt;br /&gt;
&lt;br /&gt;
You can embed commands in the prompt string by enclosing the command in back apostrophes (`).&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; PROMPT %N&lt;br /&gt;
 1&lt;br /&gt;
&lt;br /&gt;
Only the Shell number is shown. The &amp;gt; is removed from the prompt.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; PROMPT &amp;quot;%N.%S.%R&amp;gt;&amp;quot;&lt;br /&gt;
 1.Work:Snim.0&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Shell number, current directory, and return code of the previous command are shown. A space is included after the &amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For more examples using the PROMPT command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== PROTECT ==&lt;br /&gt;
&lt;br /&gt;
Changes the protection bits of a file or directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: PROTECT [FILE] &amp;lt;file | pattern&amp;gt; [FLAGS][+ | -] [&amp;lt;flags&amp;gt;] [ADD | SUB] [ALL] [QUIET]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/A,FLAGS,ADD/S,SUB/S,ALL/S,QUIET/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
All files and directories have a series of protection bits (attributes) stored with them that control their properties. These bits can be altered to indicate the type of file and the operations permitted. PROTECT is used to set or clear the protection bits. For directories, only the d bit is significant.&lt;br /&gt;
&lt;br /&gt;
The protection bits are represented by letters:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| s || The file is a script.&lt;br /&gt;
|-&lt;br /&gt;
| p || The file is a pure command and can be made resident.&lt;br /&gt;
|-&lt;br /&gt;
| a || The file has been archived.&lt;br /&gt;
|-&lt;br /&gt;
| r || The file can be read.&lt;br /&gt;
|-&lt;br /&gt;
| w || The file can be written to (altered).&lt;br /&gt;
|-&lt;br /&gt;
| e || The file is executable (a program).&lt;br /&gt;
|-&lt;br /&gt;
| d || The file or directory can be deleted. (Files within a delete-protected directory can still be deleted.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Use the LIST command to see the protection bits associated with a file. The protection field is displayed with set (on) bits shown by their letters and clear (off) bits shown by hyphens. For example, a file that is readable, writable, and deletable has ----rw-d in the protection field.&lt;br /&gt;
&lt;br /&gt;
To specify the entire protection field at the same time, enter the letters of the bits you want set as the FLAGS argument without any other keywords. The named bits are set and all the others are cleared.&lt;br /&gt;
&lt;br /&gt;
The symbols + and - (or the equivalent keywords ADD and SUB) are used to control specific bits without affecting the state of unspecified bits. Follow + or - with the letters of the bits to set or clear, respectively, and only those bits are changed. There is no space after the symbol or between the letters. The order of the letters does not matter. ADD and SUB work similarly, but there must be a space between the keyword and the letters. You cannot both set and clear bits in the same command.&lt;br /&gt;
&lt;br /&gt;
The ALL options adds or removes the specified protection bits from all the files and subdirectories matching the pattern entered. The QUIET option suppresses the screen output.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; PROTECT DF0:Memo +rw&lt;br /&gt;
&lt;br /&gt;
sets only the protection bits r (readable) and w (writable) of the file Memo on DF0:. No other protection bits are changed.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; PROTECT L:#? e SUB&lt;br /&gt;
&lt;br /&gt;
clears the e (executable) protection bit from all the files in the L: directory.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; PROTECT Work:Paint rwed&lt;br /&gt;
&lt;br /&gt;
The protection status of Paint becomes &amp;quot;----rwed&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== PUSHCD ==&lt;br /&gt;
&lt;br /&gt;
Saves the current directory on a stack and optionally changes it.&lt;br /&gt;
&lt;br /&gt;
== QUIT ==&lt;br /&gt;
&lt;br /&gt;
Exits from a script file with a specified return code.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: QUIT [&amp;lt;return code&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: RC/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
QUIT stops the execution of the script at the specified return code. The default return code is zero. We recommend you use the standard return code values of 5, 10, and 20.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
 ASK &amp;quot;Do you want to stop now?&amp;quot;&lt;br /&gt;
 IF WARN&lt;br /&gt;
    QUIT 5&lt;br /&gt;
 ENDIF&lt;br /&gt;
 ECHO &amp;quot;OK&amp;quot;&lt;br /&gt;
 ECHO &amp;quot;The script is continuing.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you press Y at the prompt, the script is aborted, since WARN is equal to a return code of 5. If you press N or press Return:&lt;br /&gt;
&lt;br /&gt;
 OK&lt;br /&gt;
 The script is continuing.&lt;br /&gt;
&lt;br /&gt;
Is displayed in the Shell window.&lt;br /&gt;
&lt;br /&gt;
== REBOOT ==&lt;br /&gt;
&lt;br /&gt;
Reboots your Amiga.&lt;br /&gt;
&lt;br /&gt;
== RECORDER ==&lt;br /&gt;
&lt;br /&gt;
Captures console output and stores it in a file.&lt;br /&gt;
&lt;br /&gt;
== RELABEL ==&lt;br /&gt;
&lt;br /&gt;
Changes the volume name of the disk in the given drive to the specified name.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: RELABEL [DRIVE] &amp;lt;drive&amp;gt; [NAME] &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRIVE/A,NAME/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
Volume names are set when disks are initially formatted. RELABEL allows you to change a disk&#039;s volume name to any name specified.&lt;br /&gt;
&lt;br /&gt;
On floppy-only systems with one drive, be sure to specify the disks by volume name instead of drive name.&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
 1&amp;gt; RELABEL Workbench: MyDisk&lt;br /&gt;
&lt;br /&gt;
changes the name of the Workbench disk to MyDisk. No colon is necessary after the second name.&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; RELABEL DF2: DataDisk&lt;br /&gt;
&lt;br /&gt;
changes the name of the disk in DF2: to DataDisk.&lt;br /&gt;
&lt;br /&gt;
== REMRAD ==&lt;br /&gt;
&lt;br /&gt;
Removes the recoverable RAM disk.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: REMRAD [&amp;lt;device&amp;gt;] [FORCE]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DEVICE,FORCE/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
REMRAD allows you to remove the recoverable RAM disk (usually mounted as RAD:) from memory without powering off the system. If you have mounted more than one recoverable RAM disk, use the DEVICE specification.&lt;br /&gt;
&lt;br /&gt;
REMRAD instructs RAD: to delete all of its files and become inactive. However, the RAD: RAM_0 disk icon does not disappear. The next time the Amiga is rebooted. RAD: is removed from memory completely and the icon is no longer displayed.&lt;br /&gt;
&lt;br /&gt;
If the device is in use when the REMRAD command is given, the operation aborts with a device in use message. To remove it if it is in use, you must use the FORCE option.&lt;br /&gt;
&lt;br /&gt;
== RENAME ==&lt;br /&gt;
&lt;br /&gt;
Changes the name of or moves a file or directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: RENAME [FROM] {&amp;lt;name} [TO | AS] &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A/M,TO=AS/A,QUIET/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
RENAME renames the FROM file or directory with the specified TO name. The FROM and TO files or directories must be on the same volume. If the name refers to a directory, RENAME changes the directory name without changing the names of the files or subdirectories in that directory. When there are multiple items in the FROM argument, the TO argument must be a directory.&lt;br /&gt;
&lt;br /&gt;
If you rename a directory or if you use RENAME to give a file another directory name, AmigaDOS changes the position of that directory or file in the filing system hierarchy. This effectively moves the items.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; RENAME Work/Ex1 AS :Test/Ex2&lt;br /&gt;
&lt;br /&gt;
renames the file Ex1 as Ex2 and moves it from the Work directory to the Test directory. The Test directory must exist in the root directory for this command to work.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; RENAME 3.doc 5.doc a.doc TO Docs&lt;br /&gt;
&lt;br /&gt;
moves the 3.doc, 5.doc, and a.doc files to the Docs directory. The Docs directory must already exist.&lt;br /&gt;
&lt;br /&gt;
== REQUESTCHOICE ==&lt;br /&gt;
&lt;br /&gt;
Allows AmigaDOS and ARexx scripts to use custom requesters.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: REQUESTCHOICE &amp;gt;TITLE&amp;gt; &amp;lt;body&amp;gt; {&amp;lt;gadgets&amp;gt;} [PUBSCREEN &amp;lt;public screen name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: TITLE/A,BODY/A,GADGETS/A/M,PUBSCREEN/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;title&amp;gt; argument specifies the title of the requester.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;body&amp;gt; argument specifies the text of the requester. Linefeeds can be embedded using *N.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;gadgets&amp;gt; argument specifies the text for the different gadgets. The gadget labels are separated with spaces.&lt;br /&gt;
&lt;br /&gt;
The number of the selected gadget is printed as a result to the console. For evaluation in a script file, you can redirect this output into an environment variable. If the requester cannot be opened, the command generates a return code of 20.&lt;br /&gt;
&lt;br /&gt;
The PUBSCREEN argument allows the requester to open its window on a public screen.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; RequestChoice &amp;gt;ENV:rcnum &amp;quot;New Title&amp;quot; &amp;quot;This is my requester*nSelect a gadget&amp;quot; &amp;quot;OK&amp;quot; &amp;quot;Maybe&amp;quot; &amp;quot;Cancel&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig6-1.png|center|frame|Sample RequestChoice Requester]]&lt;br /&gt;
&lt;br /&gt;
ENV:rcnum contains 0, 1, or 2 after a gadget is selected. The script can use this value to control its later execution.&lt;br /&gt;
&lt;br /&gt;
== REQUESTFILE ==&lt;br /&gt;
&lt;br /&gt;
Allows AmigaDOS and ARexx scripts to use a file requester.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: REQUESTFILE [Drawer &amp;lt;drawer name&amp;gt;] [FILE &amp;lt;file&amp;gt;] [PATTERN &amp;lt;pattern&amp;gt;] [TITLE &amp;lt;title&amp;gt;] [POSITIVE &amp;lt;text&amp;gt;] [NEGATIVE &amp;lt;text&amp;gt;] [ACCEPTPATTERN &amp;lt;pattern&amp;gt;] [REJECTPATTERN &amp;lt;pattern&amp;gt;] [SAVEMODE] [MULTISELECT] [DRAWERSONLY] [NOICONS] [PUBSCREEN &amp;lt;public screen name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: DRAWER,FILE/K,PATTERN/K,TITLE/K,POSITIVE/K,NEGATIVE/K,ACCEPTPATTERN/K,REJECTPATTERN/K,SAVEMODE/S,MULTISELECT/S,DRAWERSONLY/S,NOICONS/S,PUBSCREEN/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
When entered with no arguments, a file requester with OK, Volumes, Parent, and Cancel buttons is created. Ist Drawer and File gadgets are empty and it displays the contents of the current directory.&lt;br /&gt;
&lt;br /&gt;
The DRAWER argument specifies the initial contents of the Drawer gadget.&lt;br /&gt;
&lt;br /&gt;
The FILE option specifies the initial contents of the File gadget.&lt;br /&gt;
&lt;br /&gt;
The PATTERN option allows the use of a standard AmigaDOS pattern. It includes a Pattern gadget in the requester and specifies the initial contents of the gadget. If this option is not provided, the file requester does not have any Pattern gadget.&lt;br /&gt;
&lt;br /&gt;
The TITLE option specifies the title of the requester.&lt;br /&gt;
&lt;br /&gt;
The POSITIVE option specifies the text to appear in the positive (left) choice in the file requester.&lt;br /&gt;
&lt;br /&gt;
The NEGATIVE option specifies the text to appear in the negative (right) choice in the file requester.&lt;br /&gt;
&lt;br /&gt;
The ACCEPTPATTERN option specifies a standard AmigaDOS pattern. Only files matching this pattern are displayed in the file requester.&lt;br /&gt;
&lt;br /&gt;
The REJECTPATTERN option specifies a standard AmigaDOS pattern. Files matching this pattern are not displayed in the file requester.&lt;br /&gt;
&lt;br /&gt;
If SAVEMODE is specified, the requester is used for writing files to disk. If MULTISELECT is specified, the requester allows multiple files to be selected at once. If DRAWERSONLY is specified, the requester does not have a File gadget. This effectively turns the file requester into a directory requester. If NOICONS is specified, the requester does not display icons (.info files).&lt;br /&gt;
&lt;br /&gt;
The selected files are returned on the command line, enclosed in double quotation marks and separated with spaces. The command generates a return code of 0 if you select a file or 5 if you cancel the requester.&lt;br /&gt;
&lt;br /&gt;
The PUBSCREEN argument allows the requester to open its window on a public screen.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; REQUESTFILE DRAWER Devs: TITLE &amp;quot;My Req&amp;quot; NOICONS&lt;br /&gt;
&lt;br /&gt;
[[File:DosFig6-2.png|center|frame|Sample RequestFile Requester]]&lt;br /&gt;
&lt;br /&gt;
== REQUESTSTRING ==&lt;br /&gt;
&lt;br /&gt;
Allows AmigaDOS and ARexx scripts to use custom string requesters.&lt;br /&gt;
&lt;br /&gt;
== RESIDENT ==&lt;br /&gt;
&lt;br /&gt;
Displays and modifies the list of resident commands.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: RESIDENT [&amp;lt;resident name&amp;gt;] [&amp;lt;filename&amp;gt;] [REMOVE] [ADD] [REPLACE] [PURE | FORCE] [SYSTEM]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,FILE,REMOVE/S,ADD/S,REPLACE/S,PURE=FORCE/S,SYSTEM/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
RESIDENT loads a command into memory and adds it to the resident list maintained by the Shell. This allows the command to be executed without reloading it from disk each time. If RESIDENT is invoked with no options, it lists the programs on the resident list.&lt;br /&gt;
&lt;br /&gt;
To be made resident, a command should be pure, meaning that it is both re-entrant and re-executable. A re-entrant command can properly support independent use by two or more programs at the same time. A re-executable command dies not have to be reloaded to be executed again. Commands that have these characteristics are called pure and have the p (pure) protection bit set.&lt;br /&gt;
&lt;br /&gt;
The following commands cannot be made resident: BINDDRIVERS, CONCLIP, IPREFS, LOADRESOURCE, LOADWB, and SETPATCH.&lt;br /&gt;
&lt;br /&gt;
LIST the C: directory to check for the presence of the p protection bit to determine which commands are pure.&lt;br /&gt;
&lt;br /&gt;
Many of the commands in the C: directory, as well as the MORE command in Utilities, are pure commands and can be made resident. If a command does not have its pure bit set, it probably cannot be made resident safely. (Setting the pure bit does not make a command or program pure.)&lt;br /&gt;
&lt;br /&gt;
The REPLACE option is the default option and does not need to be explicitly stated. If no &amp;lt;resident name&amp;gt; is specified (for example, only a file name is specified), RESIDENT uses the file name portion as the name on the resident list. The full path to the file must be used.&lt;br /&gt;
&lt;br /&gt;
If a &amp;lt;resident name&amp;gt; is specified and RESIDENT finds a program with that name already on the list, it attempts to replace the command. That &amp;lt;resident name&amp;gt; must be used to reference the resident version of the command. The replacement succeeds only if the already-resident command is not in use.&lt;br /&gt;
&lt;br /&gt;
To override REPLACEment and make several versions of a command resident simultaneously, use the ADD option, giving a different &amp;lt;resident name&amp;gt; for each version loaded.&lt;br /&gt;
&lt;br /&gt;
If the System option is specified, the command is added to the system portion of the resident list and becomes available as a system component. Any commands added to the resident list with the SYSTEM option cannot be removed. To list these files on the RESIDENT list, you must specify the SYSTEM option.&lt;br /&gt;
&lt;br /&gt;
The PURE option forces RESIDENT to load commands that are not marked as pure and use them to test the pureness of other commands and programs. Use the PURE option with caution. Be sure the programs that you make RESIDENT meet the criteria to be resident or be careful to use the command in only one process at a time.&lt;br /&gt;
&lt;br /&gt;
The availability of internal commands can also be controlled with RESIDENT. To deactivate an Internal command (for example, if an application has its own command of the same name), use RESIDENT &amp;lt;command&amp;gt; REMOVE. The command can be reactivated with the REPLACE option.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; RESIDENT C:COPY&lt;br /&gt;
&lt;br /&gt;
makes the COPY command resident (replaces any previous version).&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; RESIDENT Copy2 DF1:C/COPY ADD&lt;br /&gt;
&lt;br /&gt;
adds another version of COPY to the resident list, under the name Copy2.&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; RESIDENT Xdir DF1:C/Xdir PURE&lt;br /&gt;
&lt;br /&gt;
makes an experimental, non-pure version of the DIR command resident.&lt;br /&gt;
&lt;br /&gt;
; Example 4:&lt;br /&gt;
 1&amp;gt; RESIDENT CD REMOVE&lt;br /&gt;
&lt;br /&gt;
makes the Internal CD command unavailable.&lt;br /&gt;
&lt;br /&gt;
; Example 5:&lt;br /&gt;
 1&amp;gt; RESIDENT CD REPLACE&lt;br /&gt;
&lt;br /&gt;
restores the CD command to the system.&lt;br /&gt;
&lt;br /&gt;
See also: PROTECT, LIST.&lt;br /&gt;
&lt;br /&gt;
== ROADSHOWCONTROL ==&lt;br /&gt;
&lt;br /&gt;
Displays and changes the internal configuration options of the TCP/IP stack.&lt;br /&gt;
&lt;br /&gt;
== RUN ==&lt;br /&gt;
&lt;br /&gt;
Executes commands as background processes.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: RUN &amp;lt;command...&amp;gt; [{+ &amp;lt;command&amp;gt;}]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: COMMAND/F&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
RUN is used to start background processes. A background process does not open its own window for input or output and does not take over the parent Shell.&lt;br /&gt;
&lt;br /&gt;
RUN attempts to execute the &amp;lt;command&amp;gt; and any arguments entered on the command line. You can RUN multiple command lines by separating them with plus signs (+). If you press Return after a plus sign, RUN interprets the next line as a continuation of the same command line.&lt;br /&gt;
&lt;br /&gt;
To make it possible to close the Shell window in which the process was started, redirect the output of RUN with RUN &amp;gt;NIL: &amp;lt;command&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
A new background Shell has the same search path and command stack size as the Shell from which RUN is given.&lt;br /&gt;
&lt;br /&gt;
You can RUN commands stored to the resident list. Resident commands are checked before commands in the command path. A Shell started with RUN NEWSHELL uses the default startup file, S:Shell-startup.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; RUN COPY Text TO PRT:+&lt;br /&gt;
 DELETE Text +&lt;br /&gt;
 ECHO &amp;quot;Printing finished&amp;quot;&lt;br /&gt;
&lt;br /&gt;
prints the Text file by copying it to the printer device, deletes it, then displays the given message. Plus signs string together the command lines, causing each command to be run after the previous command finishes.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; RUN EXECUTE Comseq&lt;br /&gt;
&lt;br /&gt;
executes, in the background, all the commands in the script file Comseq.&lt;br /&gt;
&lt;br /&gt;
For more examples using the RUN command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== RX ==&lt;br /&gt;
&lt;br /&gt;
Launches an ARexx program.&lt;br /&gt;
&lt;br /&gt;
== RXC ==&lt;br /&gt;
&lt;br /&gt;
Closes the ARexx resident process.&lt;br /&gt;
&lt;br /&gt;
== RXLIB ==&lt;br /&gt;
&lt;br /&gt;
Manages and lists ARexx function libraries and hosts.&lt;br /&gt;
&lt;br /&gt;
== RXSET ==&lt;br /&gt;
&lt;br /&gt;
Manages or lists the Clip List.&lt;br /&gt;
&lt;br /&gt;
== SEARCH ==&lt;br /&gt;
&lt;br /&gt;
Looks for the specified text string in the files of the specified directories.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SEARCH [FROM] {&amp;lt;name | pattern&amp;gt;} [SEARCH] &amp;lt;string | pattern&amp;gt; [ALL] [NONUM] [QUIET] [QUICK] [FILE] [PATTERN]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/M,SEARCH/A,ALL/S,NONUM/S,QUIET/S,QUICK/S,FILE/S,PATTERN/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SEARCH looks through all the files in the FROM directory for the string given in the SEARCH string. (The FROM and SEARCH keywords are optional.) If the ALL switch is given, SEARCH also looks through all the subdirectories of the FROM directory. SEARCH displays the name of the file being searched and any line that contains the text sought. You must place quotation marks around any search text containing a space. The search is not case-sensitive.&lt;br /&gt;
&lt;br /&gt;
The options are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NONUM || Line numbers are not printed with the strings.&lt;br /&gt;
|-&lt;br /&gt;
| QUIET || Searches quietly; file names being searched are not displayed.&lt;br /&gt;
|-&lt;br /&gt;
| QUICK || Use a more compact output format.&lt;br /&gt;
|-&lt;br /&gt;
| FILE || Looks for a file by the specified name, rather than for a string in the file.&lt;br /&gt;
|-&lt;br /&gt;
| PATTERN || Uses pattern matching to search for the string.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
SEARCH leaves a 0 in the condition flag if the object is found, and a 5 (WARN) otherwise. To abandon the search of the current file and continue to the next file, if any, press Ctrl+D. SEARCH is aborted when Ctrl+C is pressed.&lt;br /&gt;
&lt;br /&gt;
; Examples&lt;br /&gt;
 1&amp;gt; SEARCH DEVS: DOSDrivers globvec&lt;br /&gt;
 DOSDrivers (dir)&lt;br /&gt;
 PIPE..&lt;br /&gt;
 6 GlobVec =-1&lt;br /&gt;
 PIPE.info&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; SEARCH utilities #?.info FILE&lt;br /&gt;
 Workbench:Utilities/Clock.info&lt;br /&gt;
 Workbench:Utilities/MultiView.info&lt;br /&gt;
&lt;br /&gt;
== SET ==&lt;br /&gt;
&lt;br /&gt;
Sets a local vaiable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SET [&amp;lt;name&amp;gt;] [&amp;lt;string...&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,STRING/F&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
SET with no arguments lists the current local variables.&lt;br /&gt;
&lt;br /&gt;
SET with &amp;lt;name&amp;gt; and &amp;lt;string&amp;gt; arguments creates a new environment variable. The first word after SET is taken as the &amp;lt;name&amp;gt;. Everything else on the command line is taken as the &amp;lt;string&amp;gt; argument. Quotation marks are not required.&lt;br /&gt;
&lt;br /&gt;
An environment variable created with SET is local to the Shell in which it was created. If you create a new Shell with the NEWSHELL command, that Shell also recognizes any variables created in its parent Shell. However, if you create a new Shell with the Execute Command Workbench menu item or by opening the Shell icon, variables created with SET are not recognized in the new Shells.&lt;br /&gt;
&lt;br /&gt;
You can call environment variables in a script or on a command line by placing a dollar sign ($) in front of the variable name.&lt;br /&gt;
&lt;br /&gt;
To remove a local variable definition, use the UNSET command.&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
 1&amp;gt; SET Origin This process launched from icon&lt;br /&gt;
&lt;br /&gt;
creates the local variable Origin that sores a reminder that a Shell was invoked from an icon rather than a NEWSHELL.&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ECHO $Origin&lt;br /&gt;
 This process launched from icon&lt;br /&gt;
&lt;br /&gt;
See also: GET, UNSET&lt;br /&gt;
&lt;br /&gt;
== SETCLOCK ==&lt;br /&gt;
&lt;br /&gt;
Sets or reads the battery backed-up hardware clock.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETCLOCK LOAD | SAVE | RESET&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: LOAD/S,SAVE/S,RESET/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SETCLOCK SAVE sets the date and time of the battery backed-up hardware clock (if your system has one) from the current system time, which is set with the Time editor or with the DATE command. SETCLOCK SAVE is typically used after a DATE command.&lt;br /&gt;
&lt;br /&gt;
SETCLOCK LOAD sets the current system time from the battery backed-up clock. This is done automatically during the boot process.&lt;br /&gt;
&lt;br /&gt;
The RESET option resets the clock completely. Use this option if the clock is accidentally turned off or LOAD and SAVE do not appear to work correctly.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; DATE 22-Jan-93 07:15:25&lt;br /&gt;
 1&amp;gt; SETCLOCK SAVE&lt;br /&gt;
&lt;br /&gt;
saves the date, January 22, 1993, and the time, 7:15 a.m., to the battery backed-up hardware clock. When the system is booted, the system clock is set with the time saved in the hardware clock.&lt;br /&gt;
&lt;br /&gt;
Some Amiga models do not have battery backed-up clocks unless an expansion unit has been installed.&lt;br /&gt;
&lt;br /&gt;
See also: DATE&lt;br /&gt;
&lt;br /&gt;
== SETDATE ==&lt;br /&gt;
&lt;br /&gt;
Change the timestamp of a file or directory.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETDATE &amp;lt;file | pattern&amp;gt; [&amp;lt;weekday&amp;gt;] [&amp;lt;date&amp;gt;] [&amp;lt;time&amp;gt;] [ALL]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/A,WEEKDAY,DATE,TIME,ALL/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SETDATE changes the timestamp, the date and time of the creation or last change, of a file or directory. SETDATE &amp;lt;file&amp;gt; changes the date/time of the file to the current system date/time. SETDATE ALL changes the date and time of all the files and subdirectories matching the pattern entered.&lt;br /&gt;
&lt;br /&gt;
The system clocks are not affected by SETDATE.&lt;br /&gt;
&lt;br /&gt;
You can use output from the DATE command as input to SETDATE.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; SETDATE TestFile&lt;br /&gt;
&lt;br /&gt;
changes the date and time associated with TestFile to the current date and time.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; SETDATE TestFile 01-04-91 13:45:32&lt;br /&gt;
&lt;br /&gt;
Changes the date and time associated with TestFile to April 1, 1991, 1:45 p.m.&lt;br /&gt;
&lt;br /&gt;
See also: DATE&lt;br /&gt;
&lt;br /&gt;
== SETENV ==&lt;br /&gt;
&lt;br /&gt;
Sets a global variable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETENV [&amp;lt;name&amp;gt;] [&amp;lt;string...&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,STRING/F&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
SETENV with no arguments lists the current global variables.&lt;br /&gt;
&lt;br /&gt;
SETENV with &amp;lt;name&amp;gt; and &amp;lt;string&amp;gt; arguments creates a new global environment variable. The first word after SETENV is taken as the &amp;lt;name&amp;gt;. Everything else on the command line is taken as the &amp;lt;string&amp;gt; argument. Quotation marks are not required.&lt;br /&gt;
&lt;br /&gt;
Global variables are stored in the ENV: directory and are available to all processes. However, if a local variable (defined by SET) and a global variable share the same name, the local variable is used.&lt;br /&gt;
&lt;br /&gt;
Environment variables are called by scripts or other commands by including a dollar sign ($) in front of the variable name.&lt;br /&gt;
&lt;br /&gt;
To remove a global variable definition, use the UNSETENV command.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; SETENV Editor Extras:Tools/MEmacs&lt;br /&gt;
&lt;br /&gt;
creates the environment variable Editor That can be used with the MORE utility. This specifies the editor as MEmacs, located in the Tools drawer of EXTRAS:. The variable Editor is available in any Shell.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; SETENV Editor C:ED&lt;br /&gt;
&lt;br /&gt;
same as above, only the editor specified is ED.&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; ECHO $Editor&lt;br /&gt;
 C:ED&lt;br /&gt;
&lt;br /&gt;
See also: GETENV, UNSETENV&lt;br /&gt;
&lt;br /&gt;
== SETFONT ==&lt;br /&gt;
&lt;br /&gt;
Changes the font of the current Shell.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETFONT &amp;lt;font&amp;gt; &amp;lt;size&amp;gt; [SCALE] [PROP] [ITALIC] [BOLD] [UNDERLINE]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME/A,SIZE/N/A,SCALE/S,PROP/S,ITALIC/S,BOLD/S,UNDERLINE/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SETFONT lets you change the font used in a particular Shell window, overriding the System Default Text setting specified in the Font editor. SETFONT is only effective in the window in which it is invoked.&lt;br /&gt;
&lt;br /&gt;
You must specify both a font name and a size when using the SETFONT command. The other options are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| SCALE || Enables bitmap font scaling.&lt;br /&gt;
|-&lt;br /&gt;
| PROP || Allows proportional fonts.&lt;br /&gt;
|-&lt;br /&gt;
| ITALIC || The font is italic.&lt;br /&gt;
|-&lt;br /&gt;
| BOLD || The font is boldface.&lt;br /&gt;
|-&lt;br /&gt;
| UNDERLINE || The font is underlined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Invoking SETFONT clears the Shell window of its current contents and displays a new prompt, in the new font, at the top of the window. Using proportional fonts in a Shell window is not recommended because the variable character spacing prevents columns of information from lining up and makes editing the command line difficult.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; SETFONT topaz 11 BOLD UNDERLINE&lt;br /&gt;
&lt;br /&gt;
The Shell window clears and the new prompt is in 11 point Topaz, underlined and boldface.&lt;br /&gt;
&lt;br /&gt;
== SETFONTCHARSET ==&lt;br /&gt;
&lt;br /&gt;
Adds charset tag and version string to a FontContentsHeader file.&lt;br /&gt;
&lt;br /&gt;
== SETKEYBOARD ==&lt;br /&gt;
&lt;br /&gt;
Sets the keymap for the Shell.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETKEYBOARD &amp;lt;keymap name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: KEYMAP/A&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SETKEYBOARD specifies the keymap used by the current Shell. The available files are listed below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Keymap !! Keyboard&lt;br /&gt;
|-&lt;br /&gt;
| cdn || Canadian Français&lt;br /&gt;
|-&lt;br /&gt;
| ch1 || Suisse&lt;br /&gt;
|-&lt;br /&gt;
| ch2 || Schweiz&lt;br /&gt;
|-&lt;br /&gt;
| d || Deutsch&lt;br /&gt;
|-&lt;br /&gt;
| dk || Dansk&lt;br /&gt;
|-&lt;br /&gt;
| e || Español&lt;br /&gt;
|-&lt;br /&gt;
| f || Français&lt;br /&gt;
|-&lt;br /&gt;
| gb || British&lt;br /&gt;
|-&lt;br /&gt;
| i || Italiana&lt;br /&gt;
|-&lt;br /&gt;
| n || Norsk&lt;br /&gt;
|-&lt;br /&gt;
| po || Português&lt;br /&gt;
|-&lt;br /&gt;
| s || Svenskt&lt;br /&gt;
|-&lt;br /&gt;
| usa || American&lt;br /&gt;
|-&lt;br /&gt;
| usa2 || Dvorak&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To specify the same permanent keymap, use the Preferences Input editor to save your choice.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
&lt;br /&gt;
To change to a French Canadian keymap, enter:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; SETKEYBOARD cdn&lt;br /&gt;
&lt;br /&gt;
The keymap file must be in the KEYMAPS: directory for SETKEYBOARD to find it.&lt;br /&gt;
&lt;br /&gt;
== SHOWNETSTATUS ==&lt;br /&gt;
&lt;br /&gt;
Displays various information about the status of the network configuration.&lt;br /&gt;
&lt;br /&gt;
== SKIP ==&lt;br /&gt;
&lt;br /&gt;
Skips to a label when executing script files.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SKIP [&amp;lt;label&amp;gt;] [BACK]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: LABEL,BACK/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
SKIP is used in scripts to allow you to skip ahead in the script to a &amp;lt;label&amp;gt; defined by a LAB statement. If no &amp;lt;label&amp;gt; is specified, SKIP jumps to the next LAB statement.&lt;br /&gt;
&lt;br /&gt;
SKIP always searches forward from the current line of the file. However, when the BACK option is used, SKIP starts searching for the label from the beginning of the file. This allows SKIPs to points prior to the SKIP command.&lt;br /&gt;
&lt;br /&gt;
You can only SKIP as far back as the last EXECUTE statement. If there are no EXECUTE statements in a script, you SKIP back to the beginning of the file.&lt;br /&gt;
&lt;br /&gt;
If SKIP does not find the label specified, the command sequence terminates and the message Label &amp;lt;label&amp;gt; not found by Skip is displayed.&lt;br /&gt;
&lt;br /&gt;
; Example: &lt;br /&gt;
Assume you have the following script, called CheckFile:&lt;br /&gt;
&lt;br /&gt;
 .KEY name&lt;br /&gt;
 IF exists &amp;lt;name&amp;gt;&lt;br /&gt;
    SKIP message&lt;br /&gt;
 ELSE&lt;br /&gt;
    ECHO &amp;quot;&amp;lt;name&amp;gt; is not in this directory.&amp;quot;&lt;br /&gt;
    QUIT&lt;br /&gt;
 ENDIF&lt;br /&gt;
 LAB message&lt;br /&gt;
 ECHO &amp;quot;The &amp;lt;name&amp;gt; file exists.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
You can run the script by entering:&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; EXECUTE CheckFile Document&lt;br /&gt;
&lt;br /&gt;
If the Document file exists in the current directory, the execution of the script SKIPs ahead to the LAB command. The message:&lt;br /&gt;
&lt;br /&gt;
 The Document file exists.&lt;br /&gt;
&lt;br /&gt;
Is displayed in the Shell window.&lt;br /&gt;
&lt;br /&gt;
If the Document file is not in the current directory, the execution of the script jumps to the line after the ELSE statement, displaying the message:&lt;br /&gt;
&lt;br /&gt;
 Document is not in this directory.&lt;br /&gt;
&lt;br /&gt;
See also: EXECUTE, LAB. For more examples using the SKIP command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== SORT ==&lt;br /&gt;
&lt;br /&gt;
Alphabetically sorts the lines of a file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SORT [FROM] &amp;lt;file | pattern&amp;gt; [TO] &amp;lt;filename&amp;gt; [COLSTART &amp;lt;n&amp;gt;] [CASE] [NUMERIC]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A,TO/A,COLSTART/K,CASE/S,NUMERIC/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SORT sorts the FROM file alphabetically, line-by-line, sending the sorted results to the TO file. SORT assumes the file is a normal text file in which lines are separated by line feeds. SORT normally disregards case. If the CASE switch is given, upper-cased items are output first.&lt;br /&gt;
&lt;br /&gt;
The COLSTART keyword specifies the character column at which the comparison begins. SORT starts comparing the lines from that point, wrapping around to the beginning of the line if the compared lines match to the end.&lt;br /&gt;
&lt;br /&gt;
When the NUMERIC option is specified, the lines are interpreted as numbers from the first column reading to the right, stopping at the first non-numeric character. Lines not beginning with numbers are treated as 0. The lines are output in numerical order. CASE is ignored when NUMERIC is specified.&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; SORT DF0:Glossary TO DF0:Glossary.alpha&lt;br /&gt;
&lt;br /&gt;
sorts the lines in the Glossary file, arranges them alphabetically, and outputs them to a next file called Glossary.alpha. The case of the words is disregarded.&lt;br /&gt;
&lt;br /&gt;
For more examples using the SORT command, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== STACK ==&lt;br /&gt;
&lt;br /&gt;
Displays or sets the stack size within the current Shell.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: STACK [[SIZE] &amp;lt;stack size&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: SIZE/N&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
A Shell uses a certain amount of stack, a special area in memory allocated for it. Each Shell has a specific stack size. If a program causes a system failure, changing the Shell&#039;s stack size may solve the problem. Commands performing operations consisting of multiple levels can require additional stack space.&lt;br /&gt;
&lt;br /&gt;
Stack sizes typically range from 4096 to 40000 bytes. If the stack size is too small, a system failure can occur. If the stack size is too large, it can use too much memory.&lt;br /&gt;
&lt;br /&gt;
{{Note|A software failure message is displayed if you run out of stack space. Increase the stack space for the Shell that caused the error.}}&lt;br /&gt;
&lt;br /&gt;
Entering the STACK command with no arguments displays the current stack size.&lt;br /&gt;
&lt;br /&gt;
== STATUS ==&lt;br /&gt;
&lt;br /&gt;
Lists information about Shell processes.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: STATUS [&amp;lt;process&amp;gt;] [FULL] [TCB] [CLI | ALL] [COM | COMMAND &amp;lt;command&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: PROCESS/N,FULL/S,TCB/S,CLI=ALL/S,COM=COMMAND/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
STATUS without any arguments lists the numbers of the current Shell processes and the program or command running in each. The &amp;lt;process&amp;gt; argument specifies a process number, limiting STATUS to giving information about that process only.&lt;br /&gt;
&lt;br /&gt;
For information on the stack size, global vector size, priority, and the current command for each process, use the FULL keyword. The TCB keyword is similar, omitting the command information. The CLI=ALL keyword gives only the command information.&lt;br /&gt;
&lt;br /&gt;
STATUS searches for a command when you use the COMMAND option. STATUS scans the Shell list, looking for the specified &amp;lt;command&amp;gt;. If the command is found, the Shell&#039;s process number is output, and the condition flag is set to 0. Otherwise, the flag is set to 5 (WARN).&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; STATUS 1&lt;br /&gt;
 Process 1: Loaded as command: status&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; STATUS 1 FULL&lt;br /&gt;
 Process 1: stk 4000, gv 150, pri 0 Loaded as command: status&lt;br /&gt;
&lt;br /&gt;
; Example 3:&lt;br /&gt;
 1&amp;gt; STATUS &amp;gt;RAM:Xyz COMMAND=COPY&lt;br /&gt;
 1&amp;gt; BREAK &amp;lt;RAM:Xyz &amp;gt;NIL: ?&lt;br /&gt;
&lt;br /&gt;
sends a break to the process executing COPY.&lt;br /&gt;
&lt;br /&gt;
== SWAPCD ==&lt;br /&gt;
&lt;br /&gt;
Interchanges the current directory and a stacked directory.&lt;br /&gt;
&lt;br /&gt;
== TCC ==&lt;br /&gt;
&lt;br /&gt;
Closes the ARexx tracing console window.&lt;br /&gt;
&lt;br /&gt;
== TCO ==&lt;br /&gt;
&lt;br /&gt;
Opens the ARexx tracing console window.&lt;br /&gt;
&lt;br /&gt;
== TCP-HANDLER ==&lt;br /&gt;
&lt;br /&gt;
Access network resources through AmigaDOS.&lt;br /&gt;
&lt;br /&gt;
== TCPDUMP ==&lt;br /&gt;
&lt;br /&gt;
Dumps traffic on a network.&lt;br /&gt;
&lt;br /&gt;
== TE ==&lt;br /&gt;
&lt;br /&gt;
Clears the ARexx global tracing flag.&lt;br /&gt;
&lt;br /&gt;
== TEE ==&lt;br /&gt;
&lt;br /&gt;
Sends data from the standard input to the standard output and prints it to the console.&lt;br /&gt;
&lt;br /&gt;
== TRACEROUTE ==&lt;br /&gt;
&lt;br /&gt;
Prints the network route packets.&lt;br /&gt;
&lt;br /&gt;
== TS ==&lt;br /&gt;
&lt;br /&gt;
Starts ARexx&#039;s interactive tracing.&lt;br /&gt;
&lt;br /&gt;
== TYPE ==&lt;br /&gt;
&lt;br /&gt;
Displays the contents of a file.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: TYPE {&amp;lt;file | pattern&amp;gt;} [TO &amp;lt;name&amp;gt;] [OPT H | N] [HEX | NUMBER]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FROM/A/M,TO/K,OPT/K,HEX/S,NUMBER/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
TYPE outputs the contents of the named file to the current window if no destination is given or to a specified output file. The TO keyword types information to a specified file. If more than one file name is specified, the file names are typed in sequence.&lt;br /&gt;
&lt;br /&gt;
The OPT H and OPT N options are also available by the HEX and NUMBER keywords, respectively. However, the two options are mutually exclusive. The HEX option types the file as columns of hexadecimal numbers, with an ASCII character interpretation column. The NUMBER option numbers the lines as they are output.&lt;br /&gt;
&lt;br /&gt;
To pause output, press the Space bar. To resume output, press Backspace, Return, or Ctrl+X. To stop output, press Ctrl+C (***Break is displayed).&lt;br /&gt;
&lt;br /&gt;
; Example:&lt;br /&gt;
 1&amp;gt; TYPE S:Startup-sequence&lt;br /&gt;
&lt;br /&gt;
The contents of the Startup-sequence file in the S: directory are displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
For more examples using TYPE, see Chapter 8.&lt;br /&gt;
&lt;br /&gt;
== UNALIAS ==&lt;br /&gt;
&lt;br /&gt;
Removes an alias.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: UNALIAS [&amp;lt;name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
UNALIAS removes the named alias from the alias list. With no arguments, UNALIAS lists the current aliases.&lt;br /&gt;
&lt;br /&gt;
See also: ALIAS&lt;br /&gt;
&lt;br /&gt;
== UNSET ==&lt;br /&gt;
&lt;br /&gt;
Removes a local variable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: UNSET [&amp;lt;name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
UNSET removes the named local variable from the variable list for the current process. With no arguments, UNSET lists the current variables.&lt;br /&gt;
&lt;br /&gt;
See also: SET&lt;br /&gt;
&lt;br /&gt;
== UNSETENV ==&lt;br /&gt;
&lt;br /&gt;
Removes a global variable.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: UNSETENV [&amp;lt;name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
UNSETENV removes the named global variable from the current variable list. With no arguments, UNSETENV lists the current variables.&lt;br /&gt;
&lt;br /&gt;
See also: SETENV&lt;br /&gt;
&lt;br /&gt;
== UPTIME ==&lt;br /&gt;
&lt;br /&gt;
Tells how long the system has been running.&lt;br /&gt;
&lt;br /&gt;
== VERSION ==&lt;br /&gt;
&lt;br /&gt;
Finds software version and revision numbers.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: VERSION [&amp;lt;library | device | file&amp;gt;] [&amp;lt;version #&amp;gt;] [&amp;lt;revision #&amp;gt;] [&amp;lt;unit #&amp;gt;] [FILE] [INTERNAL] [RES] [FULL]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: NAME,VERSION/N,REVISION/N,UNIT/N,FILE/S,INTERNAL/S,FULL/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
VERSION finds the version and revision number of a library, device, command, or Workbench disk. VERSION can also test for a specific version/revision and set the condition flags if the version/revision is greater.&lt;br /&gt;
&lt;br /&gt;
VERSION with no &amp;lt;library | device | file&amp;gt; argument prints the Kickstart version number and the Workbench version number and sets the two corresponding environment variables. If a name is specified, VERSION attempts to open the library, device, drive, or file and read the version information. Specify a device name, such as DF0: or DH0:, to get the version of the file system used by a drive.&lt;br /&gt;
&lt;br /&gt;
When a &amp;lt;version #&amp;gt; or a &amp;lt;revision #&amp;gt; is specified, VERSION sets the condition flag to 0 if the version and revision number of the Kickstart, library, or device driver is greater than or equal to the specified values. Otherwise, the flag is set to 5 (WARN). If a revision number is not specified, no comparison on the revision number is performed.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;unit #&amp;gt; option is obsolete and is retained for compatibility with older programs.&lt;br /&gt;
&lt;br /&gt;
The FILE option forces VERSION to ignore libraries or device drivers currently loaded. This allows you to get the version number of a .library or .device file on disk when a library or device of that name is already in memory or available in LIBS:. The RES option gets the version of Resident commands. Built-in Shell commands have the same version string as the Shell. INTERNAL is also obsolete and retained for compatibility. The FULL option prints out the complete version of the string, including the date.&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
 1&amp;gt; VERSION&lt;br /&gt;
 Kickstart 40.70 Workbench 44.1&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; VERSION Alpha:Libs/xyz.library FILE FULL&lt;br /&gt;
 xyz.library 1.13 (05/24/93)&lt;br /&gt;
&lt;br /&gt;
== WAIT ==&lt;br /&gt;
&lt;br /&gt;
Waits for the specified time.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: WAIT [&amp;lt;n&amp;gt;] [SEC | SECS |MIN | MINS] [UNTIL &amp;lt;time&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: /N,SEC=SECS/S,MIN=MINS/S,UNTIL/K&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
WAIT is used in command sequences or after RUN to wait for a certain period of time or until a specific time. The default waiting period is one second.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;n&amp;gt; argument specifies the number of seconds or minutes to wait. These options are mutually exclusive; you can only enter seconds or minutes.&lt;br /&gt;
&lt;br /&gt;
Use the keyword UNTIL to wait until a particular time of the day, given in the format HH:MM.&lt;br /&gt;
&lt;br /&gt;
; Example 1:&lt;br /&gt;
 1&amp;gt; WAIT 10 MINS&lt;br /&gt;
&lt;br /&gt;
waits ten minutes.&lt;br /&gt;
&lt;br /&gt;
; Example 2:&lt;br /&gt;
 1&amp;gt; WAIT UNTIL 21:15&lt;br /&gt;
&lt;br /&gt;
waits until 9:15 p.m.&lt;br /&gt;
&lt;br /&gt;
== WAITFORPORT ==&lt;br /&gt;
&lt;br /&gt;
Waits for public message port to appear.&lt;br /&gt;
&lt;br /&gt;
== WBINFO ==&lt;br /&gt;
&lt;br /&gt;
Opens Workbench information windows from a Shell.&lt;br /&gt;
&lt;br /&gt;
== WBRUN ==&lt;br /&gt;
&lt;br /&gt;
Runs programs from Shell as if they were executed from the Workbench.&lt;br /&gt;
&lt;br /&gt;
== WBSTARTUPCTRL ==&lt;br /&gt;
&lt;br /&gt;
Manipulates the startup configuration of Workbench.&lt;br /&gt;
&lt;br /&gt;
== WHICH ==&lt;br /&gt;
&lt;br /&gt;
Searches the command path for a particular item.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: WHICH &amp;lt;command&amp;gt; [NORES] [RES] [ALL]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILE/A,NORES/S,RES/S,ALL/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
WHICH lets you find a specific command, program, or directory by entering its name. If the named item is in the search path, WHICH displays the complete path to that item. WHICH lists resident commands as RESIDENT and internal commands as INTERNAL.&lt;br /&gt;
&lt;br /&gt;
Normally, WHICH searches the resident list, the current directory, the command paths, and the C: directory. If the item is not found, WHICH sets the condition flag to 5 (WARN), but does not print any error message.&lt;br /&gt;
&lt;br /&gt;
If the NORES option is specified, the resident list is not searched. If the RES option is specified, only the resident list is searched.&lt;br /&gt;
&lt;br /&gt;
The ALL switch continues the search through the full search path, finding and listing all locations of a command or program. It can, however, lead to multiple listings of the same command if that command is reached by more than one route (such as C: and the current directory).&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
 1&amp;gt; WHICH avail&lt;br /&gt;
 C:Avail&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; WHICH C:&lt;br /&gt;
 Workbench:C&lt;br /&gt;
&lt;br /&gt;
 1&amp;gt; WHICH alias&lt;br /&gt;
 INTERNAL alias&lt;br /&gt;
&lt;br /&gt;
== WHY ==&lt;br /&gt;
&lt;br /&gt;
Prints an error message explaining why the previous command failed.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: WHY&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: Internal&lt;br /&gt;
&lt;br /&gt;
When a command fails, the screen displays a brief message. This message typically includes the name of the file, if that was the problem, but provides no details. If the reason for a failure is not evident, enter WHY for a more complete explanation.&lt;br /&gt;
&lt;br /&gt;
= System Commands =&lt;br /&gt;
&lt;br /&gt;
System commands are required for normal system operation. They are used by the standard Startup-sequence or called automatically by the system for applications. The user does not typically invoke these commands.&lt;br /&gt;
&lt;br /&gt;
== ADDDATATYPES ==&lt;br /&gt;
&lt;br /&gt;
Builds a list of data types that datatypes.library can understand.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: ADDDATATYPES [FILES] {filenames} [QUIET] [REFRESH]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: FILES/M,QUIET/S,REFRESH/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
Data type descriptors are stored in DEVS:DataTypes. These descriptors allow programs such as MultiView to interpret different data file types. ADDDATATYPES can also be called by application installation scripts to add their own data types to the list.&lt;br /&gt;
&lt;br /&gt;
The FILES argument specifies the names of the data type descriptors to add to the existing list of data type descriptors.&lt;br /&gt;
&lt;br /&gt;
Specifying the QUIET option suppresses error and output messages.&lt;br /&gt;
&lt;br /&gt;
Specifying the REFRESH option scans the DEVS:DataTypes directory for new or changed data type descriptors.&lt;br /&gt;
&lt;br /&gt;
== BINDDRIVERS ==&lt;br /&gt;
&lt;br /&gt;
Binds device drivers to hardware.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: BINDDRIVERS&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
BINDDRIVERS loads and runs device drivers for add-on hardware. These devices are automatically configured by the expansion library if their device drivers are in the SYS:Expansion directory.&lt;br /&gt;
&lt;br /&gt;
The BINDDRIVERS command must appear in the Startup-sequence file to configure the hardware when the system is booted.&lt;br /&gt;
&lt;br /&gt;
== CONCLIP ==&lt;br /&gt;
&lt;br /&gt;
Moves data between console windows and the Clipboard.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: CONCLIP [CLIPUNIT | UNIT &amp;lt;unit number&amp;gt;] [OFF]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: CLIPUNIT=UNIT/N,OFF/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
CONCLIP is called from the standard Startup-sequence. It keeps track of the information that has been cut to the Clipboard.&lt;br /&gt;
&lt;br /&gt;
The CLIPUNIT option allows you to specify the clipboard device unit number to use. Specify any unit from 0 to 255. The default number is 0. We recommend that this option be used only by advanced users or programmers who wish to use different units for different data, such as one for text and another for graphics. Run the command from the Shell, specifying the new unit number. The next time you copy and paste, that Clipboard unit is used.&lt;br /&gt;
&lt;br /&gt;
Using the OFF option with Shell, MEmacs, and ED causes these commands to stop interacting with the system Clipboard during cutting and pasting operations. We recommend that you do not use this option.&lt;br /&gt;
&lt;br /&gt;
== IPREFS ==&lt;br /&gt;
&lt;br /&gt;
Communicates Preferences information stored in the individual editor files to the operating system.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: IPREFS&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: (none)&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
IPREFS reads the individual system Preferences files and passes the information to the system. IPREFS is generally run in the Startup-sequence after the Preferences files are copied to ENV:. Each time a user selects Save or Use from within an editor, IPREFS is notified and passes the information to the system. If necessary, IPREFS resets Workbench to implement those changes. If any Shell, project, or tool windows are open, IPREFS displays a requester asking you to close them.&lt;br /&gt;
&lt;br /&gt;
== SETPATCH ==&lt;br /&gt;
&lt;br /&gt;
Makes ROM patches in system software.&lt;br /&gt;
&lt;br /&gt;
; Format&lt;br /&gt;
: SETPATCH [QUIET] [NOCACHE] [REVERSE]&lt;br /&gt;
&lt;br /&gt;
; Template&lt;br /&gt;
: QUIET/S,NOCACHE/S,REVERSE/S&lt;br /&gt;
&lt;br /&gt;
; Location&lt;br /&gt;
: C:&lt;br /&gt;
&lt;br /&gt;
SETPATCH installs temporary modifications to the operating system. It must be run at the beginning of the Startup-sequence file. Updated versions of SETPATCH are made available when necessary as AmigaDOS development continues.&lt;br /&gt;
&lt;br /&gt;
If QUIET is specified, no output is sent to the screen.&lt;br /&gt;
&lt;br /&gt;
NOCACHE prevents data caching from being activated on some 68030 and 68040 systems.&lt;br /&gt;
&lt;br /&gt;
REVERSE stores patches in reverse order. This option is useful for CDTV developers only.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_DOS_-_The_Data_Administrator&amp;diff=9271</id>
		<title>Programming AmigaOS 4: DOS - The Data Administrator</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_DOS_-_The_Data_Administrator&amp;diff=9271"/>
		<updated>2017-12-20T08:21:01Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed a few typos, updated info on filesystems.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;This article was adapted from Amiga Future magazine&#039;s series on developing for AmigaOS.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The file names that have become long thanks to AmigaOS 4 can make some of the old software/data administration sweat. We would like to show you this time what has changed in DOS.&lt;br /&gt;
&lt;br /&gt;
The interface name for the dos.library is &amp;quot;IDOS&amp;quot; and its type is &amp;quot;struct DOSIFace *&amp;quot;. Unlike other interface names all letters are in upper case and not only the first one. However, you don&#039;t need to open the interface itself. That&#039;s the job of the startup code. Only if you program libraries/devices or BOOPSI gadgets and data types must you take care of it yourself. So much about the pre-conditions.&lt;br /&gt;
&lt;br /&gt;
== Long names ==&lt;br /&gt;
&lt;br /&gt;
Thanks to more modern filesystems (see [[UserDoc:AmigaOS_File_Systems|this table]] for an overview), file- and directory names are no longer limited to 30 characters in length. Also, absolute path statements may now be longer than the original maximum of 254 characters (a limit of BSTR). When in the past only names with up to 30 characters were allowed, you could create many directory depths before you hit the limit. Now this is possible very quickly, at least in theory. You should keep this in mind when you create local buffers in order to fill them, for example by using IDOS-&amp;gt;NameFromLock(). This function would cause the error code ERROR_LINE_TOO_LONG (from dos/errors.h) if the buffer is too small.&lt;br /&gt;
&lt;br /&gt;
Old file managers such as FileMaster or MaxonTools may have problems dealing with longer file names, which are then displayed incomplete; you may also face problems when manipulating such files. Modern file management tools like Filer, Workbench Explorer or DirMeUp are already prepared for long filenames.&lt;br /&gt;
&lt;br /&gt;
The developer of the dos.library suggests a buffer length of at least 1024 characters. This length will probably never be reached in reality. Who on Earth would enter a path statement in the Shell window that stretches for 10 lines (80 column mode)?&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_dos-prefs_eng.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
== Normal file operations ==&lt;br /&gt;
&lt;br /&gt;
Nothing has changed regarding the normal file operations apart from the possibility to use long names. Nevertheless we would like to broach this subject, as misunderstandings keep happening in reality. Initially AmigaDOS offered only &amp;quot;LowLevel&amp;quot; file operations such as IDOS-&amp;gt;Read() and IDOS-&amp;gt;Write(). With Kickstart 2.0 the buffered file operations were also added. You can recognise them on the &amp;quot;F&amp;quot; in front: IDOS-&amp;gt;FRead(), IDOS-&amp;gt;FWrite() etc. Be careful not to switch between direct reading/writing and buffered reading/writing. DOS creates here an IDOS-&amp;gt;FFlush(), if buffered data must be saved, but the whole thing is not really nice. The speed is also affected when reading as well as when buffering content that has already been read as it must always be discarded and positioned back into the file. An IDOS-&amp;gt;Seek() is a very &amp;quot;expensive&amp;quot; file operation and therefore ideally entirely avoided, at least backward. In practice it is therefore better to simply read over data that is not needed instead of skipping it with &amp;quot;seek&amp;quot;! In the first examples &amp;quot;CopyFile.c&amp;quot; different DOS commands are used to read and write files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* CopyFile.c&lt;br /&gt;
 *&lt;br /&gt;
 *  gcc -o CopyFile CopyFile.c&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/rdargs.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  int res = RETURN_ERROR;&lt;br /&gt;
  struct RDArgs *readargs;&lt;br /&gt;
  int32          rargs[3];&lt;br /&gt;
&lt;br /&gt;
  IUtility-&amp;gt;ClearMem(rargs, sizeof(rargs));&lt;br /&gt;
&lt;br /&gt;
  if((readargs = IDOS-&amp;gt;ReadArgs(&amp;quot;FROM/A,TO/A&amp;quot;, rargs, NULL)))&lt;br /&gt;
  {&lt;br /&gt;
    uint8 *fromfile = (UBYTE *) (rargs[0]);&lt;br /&gt;
    uint8 *tofile   = (UBYTE *) (rargs[1]);&lt;br /&gt;
    BPTR fhin, fhout;&lt;br /&gt;
    uint8 buffer[1024];&lt;br /&gt;
    int32 readbyte;&lt;br /&gt;
&lt;br /&gt;
    if((fhin = IDOS-&amp;gt;Open(fromfile, MODE_OLDFILE)))&lt;br /&gt;
    {&lt;br /&gt;
      if((fhout = IDOS-&amp;gt;Open(tofile, MODE_NEWFILE)))&lt;br /&gt;
      {&lt;br /&gt;
        res = RETURN_OK;&lt;br /&gt;
        while((readbyte = IDOS-&amp;gt;Read(fhin, buffer, sizeof(buffer))))&lt;br /&gt;
        {&lt;br /&gt;
          if(IDOS-&amp;gt;Write(fhout, buffer, readbyte) != readbyte)&lt;br /&gt;
          {&lt;br /&gt;
            IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), tofile);&lt;br /&gt;
            res = RETURN_FAIL;&lt;br /&gt;
            break;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        IDOS-&amp;gt;Close(fhout);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), tofile);&lt;br /&gt;
      IDOS-&amp;gt;Close(fhin);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), fromfile);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
&lt;br /&gt;
  if(res == RETURN_OK) IDOS-&amp;gt;Printf(&amp;quot;Copy successful\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_CopyFile.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
== Create directories ==&lt;br /&gt;
&lt;br /&gt;
So far it has been quite arduous to create whole hierarchies of directories and sub-directories. Backup programs or the like are the usual candidates here. Now it is possible to create a complete path with a single command. In reference to IDOS-&amp;gt;CreateDir() the new function is called IDOS-&amp;gt;CreateDirTree(). The relative or absolute path entry is expected. You should keep in mind here that the last directory name must be entered without the concluding slash &amp;quot;/&amp;quot;. This is possible in the shell-command &amp;quot;MakeDir&amp;quot;, because the command makes sure that it is removed. If parts of the path already exist, there will be no error. Only if a directory of the path can&#039;t be created, the function will return with an error code. A possible error source is, for instance, a file with the name of the drawer that is to be created. A FileLock is returned to the last directory, which is cleared with IDOS-&amp;gt;UnLock() by the program. As usual, we&#039;ve created a short sample program &amp;quot;CreateDir.c&amp;quot;. IDOS-&amp;gt;ReadArgs() is also used for parsing the command line in the source code and IDOS-&amp;gt;PrintFault() for error reports.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* CreateDir.c&lt;br /&gt;
 *&lt;br /&gt;
 *  gcc -o CreateDir CreateDir.c&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/rdargs.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  int res = RETURN_ERROR;&lt;br /&gt;
  struct RDArgs *readargs;&lt;br /&gt;
  int32          rargs[2];&lt;br /&gt;
  BPTR           fh;&lt;br /&gt;
&lt;br /&gt;
  IUtility-&amp;gt;ClearMem(rargs, sizeof(rargs));&lt;br /&gt;
&lt;br /&gt;
  if((readargs = IDOS-&amp;gt;ReadArgs(&amp;quot;DRAWER/A/M&amp;quot;, rargs, NULL)))&lt;br /&gt;
  {&lt;br /&gt;
    if(rargs[0])&lt;br /&gt;
    {&lt;br /&gt;
      /* run through the list of arguments, */&lt;br /&gt;
      CONST_STRPTR *drawer = (CONST_STRPTR *) rargs[0];&lt;br /&gt;
      while(*drawer)&lt;br /&gt;
      {&lt;br /&gt;
        /* create directory with full path when not exists */&lt;br /&gt;
        if((fh = IDOS-&amp;gt;CreateDirTree(*drawer)))&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Path &amp;lt;%s&amp;gt; created successfully\n&amp;quot;,*drawer);&lt;br /&gt;
          IDOS-&amp;gt;UnLock(fh);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), *drawer);&lt;br /&gt;
&lt;br /&gt;
        drawer++;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),NULL);&lt;br /&gt;
&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_createdir.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
&lt;br /&gt;
So far there has been no direct possibility to get from the second-based timestamp (as used by ITimer-&amp;gt;GetSysTime()) to the AmigaDOS timestamp (based on days/minutes/ticks). Both new functions in charge of this task are IDOS-&amp;gt;DateStampToSeconds() and IDOS-&amp;gt;SecondsToDateStamp(). The result is a uint32-value with the seconds or a TimeStamp structure with the Amiga-values. However, you must keep in mind that the ANSI-C Timer functions can&#039;t be mixed with the AmigaDOS-Timer functions! ANSI-C has been delivering the seconds since 1.1.1970, while DOS uses the 1.1.1978 as its basis. So it becomes clear that if you mix them you will get the wrong results. We&#039;ve created a practical example under &amp;quot;CurrentTime.c&amp;quot;, which also uses the timer.device, but we&#039;ll get back to it in another part of this workshop. You should also keep in mind that the &amp;quot;struct timerequest&amp;quot; has been renamed in &amp;quot;TimeRequest&amp;quot; as &amp;quot;devices/timer.h&amp;quot; so there won&#039;t be any collisions/mixups with the structure with the same name in the C-libraries. If you want to use the old names (e.g. when porting), you can turn them on per Define __USE_OLD_TIMEVAL__.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* CurrentTime.c&lt;br /&gt;
 *&lt;br /&gt;
 *  gcc -o CurrentTime CurrentTime.c&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/datetime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint32 GetSysTime()&lt;br /&gt;
{&lt;br /&gt;
  uint32 res = 0;&lt;br /&gt;
  struct TimeRequest *timerio;&lt;br /&gt;
  struct MsgPort     *timerport;&lt;br /&gt;
&lt;br /&gt;
  if((timerport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    if((timerio = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
                           ASOIOR_ReplyPort, timerport,&lt;br /&gt;
                           ASOIOR_Size, sizeof(struct TimeRequest), TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      if(IExec-&amp;gt;OpenDevice(&amp;quot;timer.device&amp;quot;, UNIT_MICROHZ, (struct IORequest *)timerio, 0) == 0)&lt;br /&gt;
      {&lt;br /&gt;
        /* send synchron command to device */&lt;br /&gt;
        timerio-&amp;gt;Request.io_Command = TR_GETSYSTIME;&lt;br /&gt;
        IExec-&amp;gt;DoIO((struct IORequest *) timerio);&lt;br /&gt;
&lt;br /&gt;
        /* we will retunr only seconds, microseconds are ignored */&lt;br /&gt;
        res = timerio-&amp;gt;Time.Seconds;&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;CloseDevice((struct IORequest *) timerio);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;Error: cannot open timer.device\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, timerio);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Erro: cannot create IORequest\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, timerport);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Error: cannot create MsgPort\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  return( res );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  const ULONG act = GetSysTime();&lt;br /&gt;
  struct DateStamp ds = {0};&lt;br /&gt;
&lt;br /&gt;
  IDOS-&amp;gt;SecondsToDateStamp(act, &amp;amp;ds);&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;%ld seconds have elapsed since 1.1.1978\n&amp;quot;, act);&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;or %ld days, %ld minutes and %ld ticks\n&amp;quot;, ds.ds_Days, ds.ds_Minute, ds.ds_Tick);&lt;br /&gt;
&lt;br /&gt;
  TEXT daystr[LEN_DATSTRING], timestr[LEN_DATSTRING];&lt;br /&gt;
  struct DateTime dt = { ds.ds_Days, ds.ds_Minute, ds.ds_Tick, FORMAT_DEF, 0, NULL, daystr, timestr };&lt;br /&gt;
  IDOS-&amp;gt;DateToStr(&amp;amp;dt);&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Today is %s at %s o&#039;clock\n&amp;quot;, daystr, timestr);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_currenttime.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
The function to calculate the second timestamp is new: IDOS-&amp;gt;AddDates() and IDOS-&amp;gt;SubtractDates(). The first one, adds two dates and returns the result in the first argument. In the second one an Argument1 ? Argument2 is calculated and the result is stored again in Argument1. Both functions return DOSTRUE if valid parameters are used.&lt;br /&gt;
&lt;br /&gt;
== Notification ==&lt;br /&gt;
&lt;br /&gt;
By using notifications you can get informed about changes in the files or directories. Interestingly enough you can monitor files that still don&#039;t exist. But as soon as you create them, you will get a change notification. You can select between Signal or Message Type, but the latter is recommended. Then you will get the message type &#039;struct NotifyMessage&#039;. With nm_NReq.nr_Name you can determine the name of the monitored file/directory. The short example &amp;quot;SignalNotification.c&amp;quot; shows you how the function IDOS-&amp;gt;StartNotify() is used in practice.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* SignalNotification.c&lt;br /&gt;
 *&lt;br /&gt;
 *  gcc -o SignalNotification SignalNotification.c&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosasl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/notify.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/rdargs.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  struct RDArgs *readargs;&lt;br /&gt;
  int32          rargs[2];&lt;br /&gt;
  uint32         signr;&lt;br /&gt;
&lt;br /&gt;
  IUtility-&amp;gt;ClearMem(rargs, sizeof(rargs));&lt;br /&gt;
&lt;br /&gt;
  if((readargs = IDOS-&amp;gt;ReadArgs(&amp;quot;FILENAME/A&amp;quot;, rargs, NULL)))&lt;br /&gt;
  {&lt;br /&gt;
    STRPTR filename = (STRPTR) (rargs[0]);&lt;br /&gt;
&lt;br /&gt;
    if((signr = IExec-&amp;gt;AllocSignal(-1)) != -1)&lt;br /&gt;
    {&lt;br /&gt;
      struct NotifyRequest notifyrequest = {0};&lt;br /&gt;
&lt;br /&gt;
      /* Send a signal to our task when file/directory are changed */&lt;br /&gt;
      /* NRF_NOTIFY_INITIAL = send immediately a signal when file/directory are exist */&lt;br /&gt;
      notifyrequest.nr_Name = filename;&lt;br /&gt;
      notifyrequest.nr_Flags = NRF_SEND_SIGNAL | NRF_NOTIFY_INITIAL;&lt;br /&gt;
      notifyrequest.nr_stuff.nr_Signal.nr_Task = (struct Task *) IExec-&amp;gt;FindTask(NULL);&lt;br /&gt;
      notifyrequest.nr_stuff.nr_Signal.nr_SignalNum = signr;&lt;br /&gt;
&lt;br /&gt;
      if((IDOS-&amp;gt;StartNotify(&amp;amp;notifyrequest)) == DOSTRUE)&lt;br /&gt;
      {&lt;br /&gt;
        for(;;)&lt;br /&gt;
        {&lt;br /&gt;
          const ULONG signal = IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; signr | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
          if(signal &amp;amp; (1L &amp;lt;&amp;lt; signr))&lt;br /&gt;
          {&lt;br /&gt;
            /* we have notification */&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Notification from &#039;%s&#039; !\n&amp;quot;,notifyrequest.nr_FullName);&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
          if(signal &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
          {&lt;br /&gt;
            /* CTRL-C = quit program */&lt;br /&gt;
            IDOS-&amp;gt;EndNotify(&amp;amp;notifyrequest);&lt;br /&gt;
            IDOS-&amp;gt;PrintFault(ERROR_BREAK,NULL);&lt;br /&gt;
            break;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;PrintFault(ERROR_NOT_IMPLEMENTED,NULL);&lt;br /&gt;
&lt;br /&gt;
      IExec-&amp;gt;FreeSignal(signr);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Error: there is no signal available\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;FreeArgs(readargs);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(), NULL);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_signalnotification.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
That&#039;s for the theory that was applicable to the old FastFileSystem (FFS). But some issues arise with AmigaOS 4 and the new FastFileSystem2. But let me make a digression and write about the file system. Let&#039;s assume that we have a dir1, then a dir2 and in it a file3 in the RAMDisk. If the file3 is changed or something new is created in the dir2, the file3 (i.e. the newly created one) will get the current timestamp. Furthermore, the superordinate directory dir2 will also get the current timestamp. The novelty with AmigaOS 4 (and in theory also the correct thing) is that this process goes all the way to the root directory. So the dir1 gets the current timestamp too. The advantage is obvious; you must only take a look at the root directory of the data storage medium to realise when and in what directory something has been change. Whether this has happened one level down or five levels down is irrelevant at this point. The user can click himself through the directories to get to the changed content. Also backup programs with the rule &amp;quot;everything since &#039;.&#039;&amp;quot; can work more expeditiously and must not search all directories.&lt;br /&gt;
&lt;br /&gt;
The whole things becomes problematic in practice with the notifications. Let&#039;s suppose, we&#039;re monitoring the dir1 and change the file3. Before AmigaOS 4 we wouldn&#039;t have got a notification about the change. Since AmigaOS 4, however, we get the change notification, although strictly speaking nothing has changed in the monitored directory. At the moment is not known how this dilemma will be solved. The developers presume that it&#039;s better if one notification too many occurs than one notification too few. But the user might be confused, if, for example, the list in the file viewer keeps being re-configured without seeing a change afterwards.&lt;br /&gt;
&lt;br /&gt;
== Virtual drives ==&lt;br /&gt;
&lt;br /&gt;
Various virtual drives have been integrated with AmigaOS 4. This way you can address a CDROM-Image (ISO file) over ICD0: as if it were a burned CD. Using a comfortable GUI (DiskImageGUI) you can &#039;create&#039; and delete the images. The whole thing works also with ADF files that are addressed as IDF0:. Also ENV: is no longer re-copied in the RAM-Disk, but administered with its own handler and synchronised with ENVARC: on the hard disk. Another drive is URL: with two different options. You can basically start a web-browser with it and display an Internet site (HTTP). Alternatively the displayed file can also lie locally on the hard disk (FILE). The second option is opening the mail program. You can configure the whole thing by using the &amp;quot;URLOpen&amp;quot; Prefs-program.&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_url-prefs_eng.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  BPTR handle = IDOS-&amp;gt;Open(&amp;quot;URL:http://www.amigafuture.de&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
  BPTR handle = IDOS-&amp;gt;Open(&amp;quot;URL:file=Work:docs/demo.html&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
  BPTR handle = IDOS-&amp;gt;Open(&amp;quot;URL:mailto.name@amigafuture.de&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
&lt;br /&gt;
  if (handle) IDOS-&amp;gt;Close(handle);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check whether the URL device is registered you can use the following Block Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  APTR oldwin = IDOS-&amp;gt;SetProcWindow((APTR)-1); &lt;br /&gt;
  handle = IDOS-&amp;gt;Open(&amp;quot;URL:NIL:&amp;quot;,MODE_OLDFILE);&lt;br /&gt;
  IDOS-&amp;gt;SetProcWindow(oldwin);&lt;br /&gt;
 &lt;br /&gt;
  if(handle)&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Close(handle);&lt;br /&gt;
    /* URL-Device can be used */&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsolete ==&lt;br /&gt;
&lt;br /&gt;
With AmigaOS 4 functions from the dos.library have been dropped out or are reported as &amp;quot;DEPRECATED&amp;quot; when compiling. Some of them are, for example, Examine() or Seek(). The reason for this is that these functions can handle files with a maximum of 2 GByte size (because of the data type int). Completely new functions have been created here as supplement. Let&#039;s take a brief look at them. With Examine() you can request information about a file or a directory. It has been replaced with ExamineObject(). Its counterpart is FreeDosObject(DOS_EXAMINEDATA) that you use to deallocate the object. The structure ExamineData contains comparable values like the old FileInfoBlock, but values about the file size are available as 64 bit Integer. In order to go through directories using ExAll/ExNext/ExAllEnd you can now use the ExamineDir() function that gives you the next entry with each request. You need an administration block created with ObtainDirContext() which helps you define the result data and which you can deallocate with ReleaseDirContext(). Instead, with Seek() you position with ChangeFilePosition() in the file and with GetFilePosition() you request the current position. To change the file size you need ChangeFileSize() instead of SetFileSize(). Keep in mind that the function of the physical data blocks must be set up on the hard disk and hence it can take a while in very large areas until the function returns. The SetOwnerInfo() function for file rights supplements the so far used SetOwner() function if you need it at all. With Execute() you have several options to replace it. System() or RunCommand() are the candidates to use. System() is thanks to its tag list far easier to handle. By using CreateProc() you can create new processes; this function must be replaced with CreateNewProc() or you can resort directly to System(). &amp;quot;NewDosFunctions.c&amp;quot; shows an example with the new functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* NewDosFunctions.c&lt;br /&gt;
 *&lt;br /&gt;
 *  gcc -o NewDosFunctions NewDosFunctions.c&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
 &lt;br /&gt;
  BPTR lock;&lt;br /&gt;
  BPTR fh;&lt;br /&gt;
  struct ExamineData *ed;&lt;br /&gt;
  struct ExamineData *edata;&lt;br /&gt;
  APTR context;&lt;br /&gt;
&lt;br /&gt;
  if((lock = IDOS-&amp;gt;Lock(&amp;quot;&amp;quot;,SHARED_LOCK)))&lt;br /&gt;
  {&lt;br /&gt;
    if((ed = IDOS-&amp;gt;ExamineObjectTags(EX_FileLockInput, lock, TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Directory &amp;lt;%s&amp;gt; ...\n&amp;quot;, ed-&amp;gt;Name);&lt;br /&gt;
&lt;br /&gt;
      if((context = IDOS-&amp;gt;ObtainDirContextTags(EX_FileLockInput,lock,&lt;br /&gt;
                            EX_DoCurrentDir,TRUE,&lt;br /&gt;
                            EX_DataFields,EXF_ALL,&lt;br /&gt;
                            TAG_END)))&lt;br /&gt;
      {&lt;br /&gt;
        while((edata = IDOS-&amp;gt;ExamineDir(context)))&lt;br /&gt;
        {&lt;br /&gt;
          if( EXD_IS_LINK(edata) )&lt;br /&gt;
          {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;%s [link]\n&amp;quot;,edata-&amp;gt;Name);&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;   File is linked to &amp;lt;%s&amp;gt;\n&amp;quot;, edata-&amp;gt;Link);&lt;br /&gt;
          }&lt;br /&gt;
          else if ( EXD_IS_DIRECTORY(edata) )&lt;br /&gt;
          {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;%s [directory]\n&amp;quot;, edata-&amp;gt;Name);&lt;br /&gt;
          }&lt;br /&gt;
          else if( EXD_IS_FILE(edata) )&lt;br /&gt;
          {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;%s [file]\n&amp;quot;, edata-&amp;gt;Name);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if(IDOS-&amp;gt;IoErr() != ERROR_NO_MORE_ENTRIES)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Directory scan error\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t create context\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;ReleaseDirContext(context);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t examine object\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;FreeDosObject(DOS_EXAMINEDATA, ed);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;UnLock(lock);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t lock current dir\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  if((fh = IDOS-&amp;gt;Open(&amp;quot;datei&amp;quot;, MODE_NEWFILE)))&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;ChangeFilePosition(fh, 100, OFFSET_BEGINNING);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;ChangeFileSize(fh, 1024, OFFSET_BEGINNING);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Close(fh);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open file &amp;lt;datei&amp;gt;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:AF105_grab_newdosfunctions.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
The description of the old functions has been stored in &amp;quot;dos.obsolete.doc&amp;quot;. You can still compile old sources with these functions without any problems. At some point in the future they certainly won&#039;t be used any more; at the latest then you must adjust the code. Above all, when creating new sources or revising existing sources you should switch to the new functions.&lt;br /&gt;
&lt;br /&gt;
== Forbidden ==&lt;br /&gt;
&lt;br /&gt;
We would also like to mention the Exit() functions that can lead to a compiling error, as they no longer exist. You can simply replace them with exit() for the Runtime library.&lt;br /&gt;
&lt;br /&gt;
You must relocate formatted file outputs via VFWritef() to VFPrintf(). The greater difference lies in the format string, as it was coded in the old function as a BCPL string and is therefore not directly compatible with the RawDoFmt() placeholders.&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
&lt;br /&gt;
New (selected) functions in the dos.library:&lt;br /&gt;
 int32 AddDates(struct DateStamp *to, CONST struct DateStamp *from) : Adds dates&lt;br /&gt;
 BPTR CreateDirTree(STRPTR path) : creates old directory trees&lt;br /&gt;
 uint32 DateStampToSeconds(struct DateStamp *ds) : Converts a DateStamp structure into seconds&lt;br /&gt;
 int32 DevNameFromLock(BPTR fh, STRPTR buffer, int32 buffersize) : Gives the device name (not the data carrier name !) to the assigned lock&lt;br /&gt;
 int32 DosControl(struct TagItem *taglist) : Setting and reading DOS internals&lt;br /&gt;
 BPTR ErrorOutput(void) : gives the error output stream (stderr)&lt;br /&gt;
 int32 FixDateStamp(struct DateStamp *ds) : if necessary, it corrects the datestamp entries&lt;br /&gt;
 BPTR GetCurrentDir(VOID) : gives a lock for the current directory&lt;br /&gt;
 int32 GetSegListInfo(BPTR seglist, struct TagItem *taglist) : gives information about the loaded program segment&lt;br /&gt;
 int32 HexToLong(STRPTR buffer, uint32 *val) : Transforms a hexadecimal number into a long value&lt;br /&gt;
 int32 PutErrStr(STRPTR txt) : Text output in the error output stream&lt;br /&gt;
 struct DateStamp * SecondsToDateStamp(uint32 sec, struct DateStamp *ds) : Converts seconds into a datestamp structure&lt;br /&gt;
 BPTR SelectErrorOutput(BPTR fh) : Changes the error output stream&lt;br /&gt;
 APTR SetProcWindow(APTR ) : sets the pr_WindowPtr&lt;br /&gt;
 int32 SubtractDates(struct DateStamp *ds1, CONST struct DateStamp *ds2) : Subtracts dates&lt;br /&gt;
 int32 WaitForData(BPTR stream, int32 data_direction, int32 timeout) : waits until the stream is ready to send/read (especially Pipes)&lt;br /&gt;
&lt;br /&gt;
What has become deprecated:&lt;br /&gt;
 LONG Seek(BPTR file, LONG position, LONG offset)&lt;br /&gt;
 -&amp;gt; ChangeFilePosition()&lt;br /&gt;
 LONG SetFileSize(BPTR fh, LONG pos, LONG mode)&lt;br /&gt;
 -&amp;gt;ChangeFileSize()&lt;br /&gt;
 LONG SetOwner(CONST_STRPTR name, ULONG owner_info)&lt;br /&gt;
 -&amp;gt; SetOwnerInfo()&lt;br /&gt;
 LONG Examine(BPTR lock, struct FileInfoBlock *fib)&lt;br /&gt;
 -&amp;gt; ExamineObject()&lt;br /&gt;
 LONG ExamineFH(BPTR fh, struct FileInfoBlock *fib)&lt;br /&gt;
 -&amp;gt; ExamineObject()&lt;br /&gt;
 LONG ExAll(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control)&lt;br /&gt;
 -&amp;gt; ExamineDir()&lt;br /&gt;
 LONG ExNext(BPTR lock, struct FileInfoBlock *fib)&lt;br /&gt;
 -&amp;gt; ExamineDir()&lt;br /&gt;
 VOID ExAllEnd(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control)&lt;br /&gt;
 -&amp;gt; ExamineDir()&lt;br /&gt;
 struct MsgPort *CreateProc(CONST_STRPTR name, LONG pri, BPTR segList, LONG stackSize)&lt;br /&gt;
 -&amp;gt; CreateNewProc()&lt;br /&gt;
 struct MsgPort *DeviceProc(CONST_STRPTR name)&lt;br /&gt;
 -&amp;gt; GetDeviceProc()&lt;br /&gt;
 LONG ReadLink(struct MsgPort *port, BPTR lock, CONST_STRPTR path, STRPTR buffer, ULONG size)&lt;br /&gt;
 -&amp;gt; ExamineObject()&lt;br /&gt;
 LONG SetVBuf(BPTR fh, STRPTR buff, LONG type, LONG size);&lt;br /&gt;
 -&amp;gt; SetFileHandleAttr&lt;br /&gt;
 LONG Execute(CONST_STRPTR string, BPTR file, BPTR file2);&lt;br /&gt;
 -&amp;gt; RunCommand()&lt;br /&gt;
&lt;br /&gt;
What has become obsolete:&lt;br /&gt;
 VOID Exit(LONG returnCode);&lt;br /&gt;
 -&amp;gt; exit()&lt;br /&gt;
 VOID VFWritef(BPTR fh, CONST_STRPTR format, LONG *argarray)&lt;br /&gt;
 -&amp;gt; VFPrintf()&lt;br /&gt;
 VOID FWritef(BPTR fh, CONST_STRPTR format, ...)&lt;br /&gt;
 -&amp;gt; VFPrintf()&lt;br /&gt;
 BPTR InternalLoadSeg(BPTR fh, BPTR table, const LONG *funcarray)&lt;br /&gt;
 -&amp;gt; LoadHunk()&lt;br /&gt;
 LONG InternalUnLoadSeg(BPTR seglist, VOID (*freefunc )())&lt;br /&gt;
 -&amp;gt; UnLoadHunk()&lt;br /&gt;
 BPTR LoadSeg(CONST_STRPTR name, BPTR hunktab, BPTR stream)&lt;br /&gt;
 -&amp;gt; RunCommand&lt;br /&gt;
&lt;br /&gt;
== Authors ==&lt;br /&gt;
&lt;br /&gt;
Written by Michael Christoph and Aleksandra Schmidt-Pendarovska&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright (c) 2013 Michael Christoph&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9270</id>
		<title>UserDoc:AmigaOS File Systems</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9270"/>
		<updated>2017-12-20T07:37:01Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ AmigaOS File System Table&lt;br /&gt;
!&lt;br /&gt;
! FastFileSystem&amp;lt;br/&amp;gt;(FFS)&lt;br /&gt;
! FastFileSystem 2&amp;lt;br/&amp;gt;(FFS2)&lt;br /&gt;
! SmartFileSystem&amp;lt;br/&amp;gt;(SFS)&lt;br /&gt;
! SmartFileSystem 2&amp;lt;br/&amp;gt;(SFS2)&lt;br /&gt;
! JXFS&lt;br /&gt;
! [[AmigaDOS_Vector-Port#New_Generation_File_System|New Generation File System]]&amp;lt;br/&amp;gt;(NGFS)&lt;br /&gt;
|-&lt;br /&gt;
| Bootable?&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| No&lt;br /&gt;
| Yes, on AmigaOne X-1000 and later machines using amigaboot&lt;br /&gt;
|-&lt;br /&gt;
| DosType&lt;br /&gt;
| DOS\03 0x444f5303&lt;br /&gt;
| DOS\07 0x444f5307&lt;br /&gt;
| SFS\00 0x53465300&lt;br /&gt;
| SFS\02 0x53465302&lt;br /&gt;
| JXF\04 0x4A584604&lt;br /&gt;
| NGF\00 0x4E474600&lt;br /&gt;
|-&lt;br /&gt;
| Max. Filename Length&lt;br /&gt;
| 30 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 255 characters (fewer if the filename is UTF-8 encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Max. Partition Size&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 128 GB&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
| 100 EiB&lt;br /&gt;
|-&lt;br /&gt;
| Max. File Size&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
| 2^64 - 1 Bytes&lt;br /&gt;
|-&lt;br /&gt;
| Supported Block Size&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| use 512&lt;br /&gt;
| use 512&lt;br /&gt;
| 512&lt;br /&gt;
| 256 to 65536 (auto selected)&lt;br /&gt;
|-&lt;br /&gt;
| Comments&lt;br /&gt;
| Legacy file system, not recommended for AmigaOS 4.x due to lack of long filename support. Can be used for bootloader partition in AmigaOS 4.x only setup.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, recovery tools available, not as fast as SFS, requires validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, some recovery tools available. Fast, does not require validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x. Fast, does not require validation if disk error. No recovery tools.&lt;br /&gt;
| Fastest but not backwards compatible with some 68K legacy software. No recovery tools. Discontinued starting with 4.1 Final Edition.&lt;br /&gt;
| [[AmigaDOS_Vector-Port|Vector-port]]-based file system, full DOS compatibility. Journalling, full check/repair facility provided. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* SFS\00 and SFS\02 support other block sizes but only 512 bytes should be used since it is slower with larger blocks. JXFS only supports 512 block size.&lt;br /&gt;
* Recovery tools are located in Media Toolbox in the SYS:System drawer. Always have a backup!&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9269</id>
		<title>UserDoc:AmigaOS File Systems</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9269"/>
		<updated>2017-12-11T21:39:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added information about NGFS.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ AmigaOS File System Table&lt;br /&gt;
!&lt;br /&gt;
! FastFileSystem&amp;lt;br/&amp;gt;(FFS)&lt;br /&gt;
! FastFileSystem 2&amp;lt;br/&amp;gt;(FFS2)&lt;br /&gt;
! SmartFileSystem&amp;lt;br/&amp;gt;(SFS)&lt;br /&gt;
! SmartFileSystem 2&amp;lt;br/&amp;gt;(SFS2)&lt;br /&gt;
! JXFS&lt;br /&gt;
! [[AmigaDOS_Vector-Port#New_Generation_File_System|NGFS]]&lt;br /&gt;
|-&lt;br /&gt;
| Bootable?&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| No&lt;br /&gt;
| Yes, on AmigaOne X-1000 and later machines using amigaboot&lt;br /&gt;
|-&lt;br /&gt;
| DosType&lt;br /&gt;
| DOS\03 0x444f5303&lt;br /&gt;
| DOS\07 0x444f5307&lt;br /&gt;
| SFS\00 0x53465300&lt;br /&gt;
| SFS\02 0x53465302&lt;br /&gt;
| JXF\04 0x4A584604&lt;br /&gt;
| NGF\00 0x4E474600&lt;br /&gt;
|-&lt;br /&gt;
| Max. Filename Length&lt;br /&gt;
| 30 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 255 characters (fewer if the filename is UTF-8 encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Max. Partition Size&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 128 GB&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
| 100 EiB&lt;br /&gt;
|-&lt;br /&gt;
| Max. File Size&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
| 2^64 - 1 Bytes&lt;br /&gt;
|-&lt;br /&gt;
| Supported Block Size&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| use 512&lt;br /&gt;
| use 512&lt;br /&gt;
| 512&lt;br /&gt;
| 256 to 65536 (auto selected)&lt;br /&gt;
|-&lt;br /&gt;
| Comments&lt;br /&gt;
| Legacy file system, not recommended for AmigaOS 4.x due to lack of long filename support. Can be used for bootloader partition in AmigaOS 4.x only setup.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, recovery tools available, not as fast as SFS, requires validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, some recovery tools available. Fast, does not require validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x. Fast, does not require validation if disk error. No recovery tools.&lt;br /&gt;
| Fastest but not backwards compatible with some 68K legacy software. No recovery tools. Discontinued starting with 4.1 Final Edition.&lt;br /&gt;
| [[AmigaDOS_Vector-Port|Vector-port]]-based file system, full DOS compatibility. Journalling, full check/repair facility provided. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* SFS\00 and SFS\02 support other block sizes but only 512 bytes should be used since it is slower with larger blocks. JXFS only supports 512 block size.&lt;br /&gt;
* Recovery tools are located in Media Toolbox in the SYS:System drawer. Always have a backup!&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9268</id>
		<title>UserDoc:AmigaOS File Systems</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UserDoc:AmigaOS_File_Systems&amp;diff=9268"/>
		<updated>2017-12-11T12:58:27Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed spelling.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;SFS\00 and SFS\02 support other block sizes but only 512 bytes should be used since it is slower with larger blocks. JXFS only supports 512 block size. Recovery tools are located in Media Toolbox in the SYS:System drawer. Always have a backup.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ AmigaOS File System Table&lt;br /&gt;
!&lt;br /&gt;
! FastFileSystem&amp;lt;br/&amp;gt;(FFS)&lt;br /&gt;
! FastFileSystem 2&amp;lt;br/&amp;gt;(FFS2)&lt;br /&gt;
! SmartFileSystem&amp;lt;br/&amp;gt;(SFS)&lt;br /&gt;
! SmartFileSystem 2&amp;lt;br/&amp;gt;(SFS2)&lt;br /&gt;
! JXFS&lt;br /&gt;
|-&lt;br /&gt;
| Bootable?&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| Yes&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| DosType&lt;br /&gt;
| DOS\03 0x444f5303&lt;br /&gt;
| DOS\07 0x444f5307&lt;br /&gt;
| SFS\00 0x53465300&lt;br /&gt;
| SFS\02 0x53465302&lt;br /&gt;
| JXF\04 0x4A584604&lt;br /&gt;
|-&lt;br /&gt;
| Max. Filename Length&lt;br /&gt;
| 30 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
| 107 characters&lt;br /&gt;
|-&lt;br /&gt;
| Max. Partition Size&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 2 TB to 128 TB&lt;br /&gt;
| 128 GB&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
|-&lt;br /&gt;
| Max. File Size&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 4 GB - 2 Bytes&lt;br /&gt;
| 1 TB&lt;br /&gt;
| 256 TB&lt;br /&gt;
|-&lt;br /&gt;
| Supported Block Size&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| 512 to 32768&lt;br /&gt;
| only 512&lt;br /&gt;
| only 512&lt;br /&gt;
| only 512&lt;br /&gt;
|-&lt;br /&gt;
| Comments&lt;br /&gt;
| Legacy file system, not recommended for AmigaOS 4.x due to lack of long filename support. Can be used for bootloader partition in AmigaOS 4.x only setup.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, recovery tools available, not as fast as SFS, requires validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x, some recovery tools available. Fast, does not require validation if disk error.&lt;br /&gt;
| Current file system, bootable, can be used with AmigaOS 4.x. Fast, does not require validation if disk error. No recovery tools.&lt;br /&gt;
| Fastest but not backwards compatible with some 68K legacy software. No recovery tools. Discontinued starting with 4.1 Final Edition.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BattClock_Resource&amp;diff=9267</id>
		<title>BattClock Resource</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BattClock_Resource&amp;diff=9267"/>
		<updated>2017-12-06T12:03:47Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Polished the page, added OS4-updated example code by Doug Stastny, and removed the WIP banner.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The battery-backed clock (BattClock) keeps Amiga time while the system is powered off. The time from the BattClock is loaded into the Amiga system clock as part of the boot sequence.&lt;br /&gt;
&lt;br /&gt;
The BattClock Resource component provides access to the BattClock. Three [[#Function reference|functions]] allow you to read the BattClock value, reset it and set it to a value you desire.&lt;br /&gt;
&lt;br /&gt;
The [[Utility Library]] contains time functions which convert the number of seconds since 12:00 AM, January 1, 1978 to a date and time we can understand, and vice versa. You will find these functions useful when dealing with the BattClock. The example program below uses the Amiga2Date() utility function to convert the value returned by ReadBattClock(). See the [[Utility Library]] page for a discussion of the scope of the library, and the [[Autodocs:Main|autodocs]] for a listing of its functions.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=&#039;&#039;So, you want to be a Time Lord?&#039;&#039;|This resource will allow you to set the BattClock to any value you desire. Keep in mind that this time will endure a reboot and could adversely affect your system!}}&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/date.h&amp;gt;&lt;br /&gt;
#include &amp;lt;resources/battclock.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/battclock.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    STRPTR ampm;&lt;br /&gt;
    uint32 AmigaTime;&lt;br /&gt;
    struct ClockData MyClock;&lt;br /&gt;
    CONST CONST_STRPTR Days[] = {&amp;quot;Sunday&amp;quot;, &amp;quot;Monday&amp;quot;, &amp;quot;Tuesday&amp;quot;, &amp;quot;Wednesday&amp;quot;, &amp;quot;Thursday&amp;quot;, &amp;quot;Friday&amp;quot;, &amp;quot;Saturday&amp;quot;};&lt;br /&gt;
    CONST CONST_STRPTR Months[] = {&amp;quot;January&amp;quot;, &amp;quot;February&amp;quot;, &amp;quot;March&amp;quot;, &amp;quot;April&amp;quot;, &amp;quot;May&amp;quot;, &amp;quot;June&amp;quot;, &amp;quot;July&amp;quot;, &amp;quot;August&amp;quot;, &lt;br /&gt;
    &amp;quot;September&amp;quot;, &amp;quot;October&amp;quot;, &amp;quot;November&amp;quot;, &amp;quot;December&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    struct Library *BattClockBase = IExec-&amp;gt;OpenResource(BATTCLOCKNAME);&lt;br /&gt;
    struct BattClockIFace *IBattClock = (struct BattClockIFace *) IExec-&amp;gt;GetInterface(BattClockBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IBattClock != NULL) {&lt;br /&gt;
        /* Get number of seconds till now */&lt;br /&gt;
        AmigaTime = IBattClock-&amp;gt;ReadBattClock();&lt;br /&gt;
 &lt;br /&gt;
        /* Convert to a ClockData structure */&lt;br /&gt;
        IUtility-&amp;gt;Amiga2Date(AmigaTime, &amp;amp;MyClock);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\nRobin, tell everyone the BatDate and BatTime&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print the Date */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\n\nOkay Batman, the BatDate is &amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s, %s %ld, %ld&amp;quot;, Days[MyClock.wday], Months[MyClock.month-1], MyClock.mday, MyClock.year);&lt;br /&gt;
&lt;br /&gt;
        /* Convert military time to normal time and set AM/PM */&lt;br /&gt;
        if (MyClock.hour &amp;lt; 12) {&lt;br /&gt;
            ampm = &amp;quot;AM&amp;quot;;        /* hour less than 12, must be morning */&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            ampm = &amp;quot;PM&amp;quot;;         /* hour greater than 12,must be night */&lt;br /&gt;
            MyClock.hour -= 12;  /* subtract the extra 12 of military */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        if (MyClock.hour == 0) {&lt;br /&gt;
                MyClock.hour = 12;   /* don&#039;t forget the 12s */&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Print the time */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;\n             the BatTime is &amp;quot;);&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%02ld:%02ld:%02ld %s\n\n&amp;quot;, MyClock.hour, MyClock.min, MyClock.sec, ampm);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*) IBattClock);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
       IDOS-&amp;gt;Printf(&amp;quot;Error: Unable to open the %s\n&amp;quot;, BATTCLOCKNAME);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the BattClock Resource can be found in the respective include files and in the BattClock Resource and Utility Library [[Autodocs:Main|autodocs]].&lt;br /&gt;
&lt;br /&gt;
== Function reference ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ReadBattClock() || Read the time from the BattClock. Returns the time as the number of seconds since 12:00 AM, January 1, 1978.&lt;br /&gt;
|-&lt;br /&gt;
| ResetBattClock() || Reset the BattClock to 12:00 AM, January 1, 1978.&lt;br /&gt;
|-&lt;br /&gt;
| WriteBattClock() || Set the BattClock to the number of seconds you pass it relative to 12:00 AM, January 1, 1978.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Wiki_Author_Credits&amp;diff=9250</id>
		<title>Wiki Author Credits</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Wiki_Author_Credits&amp;diff=9250"/>
		<updated>2017-12-03T11:53:01Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added Douglas Stastny to the list&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The following is a list of known contributors to this wiki:&lt;br /&gt;
&lt;br /&gt;
* Adam Levin&lt;br /&gt;
* Alexandre Balaban&lt;br /&gt;
* [http://amigaak.org.nz/ Amiga Auckland]&lt;br /&gt;
* Andy Finkel&lt;br /&gt;
* Bart Whitebook&lt;br /&gt;
* Bill Borsari&lt;br /&gt;
* Bill Koester&lt;br /&gt;
* Bob Burns&lt;br /&gt;
* Bob Pariseau&lt;br /&gt;
* Bruce Barrett&lt;br /&gt;
* Bryce Nesbitt&lt;br /&gt;
* Carl Sassenrath&lt;br /&gt;
* Carolyn Scheppner&lt;br /&gt;
* Chris Green&lt;br /&gt;
* Chris Ludwig&lt;br /&gt;
* Chris Raymond&lt;br /&gt;
* Dale Luck&lt;br /&gt;
* Dan Baker&lt;br /&gt;
* [http://gtmooya.blogspot.com/ Daniel Hutchinson]&lt;br /&gt;
* Daniel Jedlicka&lt;br /&gt;
* Darius Taghavy&lt;br /&gt;
* Darren Greenwald&lt;br /&gt;
* Dave Berezowski&lt;br /&gt;
* Dave Lucas&lt;br /&gt;
* David Junod&lt;br /&gt;
* David Miller&lt;br /&gt;
* Don Cox&lt;br /&gt;
* Douglas Stastny&lt;br /&gt;
* Eric Cotton&lt;br /&gt;
* Ewout Walraven&lt;br /&gt;
* Frank Bunton, for his [http://aminet.net/package/docs/help/adosbegin AmigaDOS guide]&lt;br /&gt;
* Hans-Joerg Frieden&lt;br /&gt;
* James Jacobs&lt;br /&gt;
* [http://www.janneperaaho.arkku.net/ Janne Peräaho]&lt;br /&gt;
* Jerry Hartzler&lt;br /&gt;
* Jez San&lt;br /&gt;
* Jim Mackraz&lt;br /&gt;
* Joe Katz&lt;br /&gt;
* John Orr&lt;br /&gt;
* John Wiederhirn&lt;br /&gt;
* Karl Churchill&lt;br /&gt;
* Ken Farinsky&lt;br /&gt;
* Kevin Klop&lt;br /&gt;
* Larry Hildenbrand&lt;br /&gt;
* Leo Schwab&lt;br /&gt;
* [https://sites.google.com/site/takeaprogrammertolunch/ Lyle Hazelwood]&lt;br /&gt;
* Mark Barton&lt;br /&gt;
* Mark Ricci&lt;br /&gt;
* [http://wandel.ca/ Markus Wandel]&lt;br /&gt;
* Martin Taillefer&lt;br /&gt;
* Michael Sinz&lt;br /&gt;
* Nancy Rains&lt;br /&gt;
* Neil Kafferkey&lt;br /&gt;
* Neil Katin&lt;br /&gt;
* Olaf Barthel&lt;br /&gt;
* Paul Higginbottom&lt;br /&gt;
* Paul Sadlik&lt;br /&gt;
* Peter Cherna&lt;br /&gt;
* Philippe Ferrucci&lt;br /&gt;
* R.J. Mical&lt;br /&gt;
* Randell Jesup&lt;br /&gt;
* Ray Brand&lt;br /&gt;
* Rob Cranley&lt;br /&gt;
* Rob Peck&lt;br /&gt;
* Rob Wyesham&lt;br /&gt;
* Roman Kargin&lt;br /&gt;
* Sam Dicker&lt;br /&gt;
* Simon Archer&lt;br /&gt;
* Spencer Shanson&lt;br /&gt;
* Stan Shepard&lt;br /&gt;
* Steve Beats&lt;br /&gt;
* [http://www.solie.ca Steven Solie]&lt;br /&gt;
* Stuart Ferguson&lt;br /&gt;
* Susan Deyl&lt;br /&gt;
* Thomas Frieden&lt;br /&gt;
* Tom Pohorsky&lt;br /&gt;
* Tom Rokicki&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Window_Display_Preservation&amp;diff=9249</id>
		<title>Window Display Preservation</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Window_Display_Preservation&amp;diff=9249"/>
		<updated>2017-12-03T11:50:19Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: SuperBitMap Window Example: code rewritten for OS4 by Doug Stastny&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Preserving the Window Display =&lt;br /&gt;
&lt;br /&gt;
The layers library is what allows the display and manipulation of multiple overlapping rectangles, or &#039;&#039;layers&#039;&#039;. Intuition uses the layers library to manage its windows, by associating a layer to each window.&lt;br /&gt;
&lt;br /&gt;
Each window is a virtual display. When rendering, the application does not have to worry about the current size or position of its window, and what other windows might be partly or fully obscuring its window. The window&#039;s RastPort is the handle to the its virtual display space. Intuition and graphics library rendering calls will recognize that this RastPort belongs to a layer, and act accordingly.&lt;br /&gt;
&lt;br /&gt;
As windows are moved, resized, rearranged, opened, or closed, the on-screen representation changes. When part of a window which was visible now needs to appear in a new location, the layers library will move that imagery without involving the application. However, when part of a window that was previously obscured is revealed, or when a window is made larger, the imagery for the newly-visible part of the window needs to be redrawn. Intuition, through layers, offers three choices for how this is managed, trading off speed, memory usage, and application complexity.&lt;br /&gt;
&lt;br /&gt;
* The most basic type of window is called &#039;&#039;Simple Refresh&#039;&#039;. When any graphics operation takes place in this kind of window, the visible parts are updated, but rendering to the obscured parts is discarded. When the window arrangement changes to reveal a previously obscured part of such a window, the application must refresh that area.&lt;br /&gt;
&lt;br /&gt;
* Alternately, a window may be made &#039;&#039;Smart Refresh&#039;&#039;, which means that when rendering occurs, the system will not only update the visible parts of the window, but it will maintain the obscured parts as well, by using off-screen buffers. This means that when an obscured part of the window is revealed, the system will restore the imagery that belongs there. The application needs only to refresh parts of the window that appear when the window is made bigger. Smart Refresh windows use more memory than Simple Refresh windows (for the storage of obscured areas), but they are faster.&lt;br /&gt;
&lt;br /&gt;
* The third kind of window is called &#039;&#039;SuperBitMap&#039;&#039;. In such a window, the system can refresh the window even when it is sized bigger. For this to work, the application must store a complete bitmap for the window&#039;s maximum size. Such a window is more work to manage, and uses yet more memory. SuperBitMap windows are used less often than the other two types.&lt;br /&gt;
&lt;br /&gt;
Intuition helps your application manage window refresh. First, Intuition will take care of redrawing the window border and any system and application gadgets in the window. Your application never has to worry about that. Second, Intuition will notify your application when it needs to refresh its window (by sending the IDCMP_REFRESHWINDOW event). Third, Intuition provides functions that restrict your rendering to the newly-revealed (damaged) areas only, which speeds up your refresh rendering and makes it look cleaner.&lt;br /&gt;
&lt;br /&gt;
The Intuition, layers, and graphics libraries work together to make rendering into and managing windows easy. You obtain your windows through Intuition, which uses the Layers library to manage the overlapping, resizing, and re-positioning of the window layers. The layers library is responsible for identifying the areas of each window that are visible, obscured but preserved off-screen, or obscured and not preserved. The rendering functions in the graphics library and Intuition library know how to render into the multiple areas that layers library establishes.&lt;br /&gt;
&lt;br /&gt;
Note that you may not directly manipulate layers on an Intuition screen. You cannot create your own layers on an Intuition screen, nor can you use the layers movement, sizing, or arrangement functions on Intuition windows. Use the corresponding Intuition calls instead. Some other Layers library calls (such as the locking calls) are sometimes used on Intuition screens and windows.&lt;br /&gt;
&lt;br /&gt;
= Damage Regions =&lt;br /&gt;
&lt;br /&gt;
The layers library and Intuition maintain a &#039;&#039;damage region&#039;&#039; for each window, which is the part of the window whose imagery is in need of repair, or refreshing. Several things can add areas of the window to the damage region:&lt;br /&gt;
&lt;br /&gt;
* Revealing an obscured part of a Simple Refresh window adds that area to the damage region&lt;br /&gt;
* Sizing a Simple or Smart Refresh window bigger along either axis adds the new area to the damage region&lt;br /&gt;
* Resizing a Simple or Smart Refresh window (smaller or bigger) adds the old and new border areas, and the areas occupied by certain gadgets (those whose position or size depend on window size) to the damage region.&lt;br /&gt;
&lt;br /&gt;
= Refreshing Intuition Windows =&lt;br /&gt;
&lt;br /&gt;
When the user or an application performs an Intuition operation which causes damage to a window, Intuition notifies that window&#039;s application. It does this by sending a message of the class IDCMP_REFRESHWINDOW to that window&#039;s IDCMP.&lt;br /&gt;
&lt;br /&gt;
In response to this message, your application should update the damaged areas. Rendering proceeds faster and looks cleaner if it is restricted to the damaged areas only. The BeginRefresh()/EndRefresh() pair achieve that. The application should call BeginRefresh() for the window, and then do its rendering. Any rendering that would have gone into undamaged areas of the window is automatically discarded; only the area in need of repair is affected. Finally, the application should call EndRefresh(), which removes the restriction on rendering, and informs the system that the damage region has been dealt with. Even if your application intends to do no rendering, it must at least call BeginRefresh()/EndRefresh(), to inform the system that the damage region is no longer needed. If your application never needs to render in response to a refresh event, it can avoid having to call BeginRefresh()/EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.&lt;br /&gt;
&lt;br /&gt;
Note that by the time that your application receives notification that refresh is needed, Intuition will have already refreshed your window&#039;s border and all gadgets in the window, as needed. Thus, it is unnecessary to use any of the gadget-refreshing functions in response to an IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Avoid RefreshGList() or RefreshGadgets(), or you risk deadlocking the computer. Avoid calls that may lock the LayerInfo or get complicated in Intuition, since BeginRefresh() leaves the window&#039;s layer or layers locked. Avoid AutoRequest() and EasyRequest(), and therefore all direct or indirect disk related DOS calls. See the [[Intuition_Gadgets|Intuition Gadgets]] section for more information on gadget restrictions with BeginRefresh() and EndRefresh().&lt;br /&gt;
&lt;br /&gt;
== Simple Refresh ==&lt;br /&gt;
&lt;br /&gt;
For a Simple Refresh window, only those pixels actually on-screen are maintained by the system. When part of a Simple Refresh window is obscured, the imagery that was there is lost. As well, any rendering into obscured portions of such a window is discarded.&lt;br /&gt;
&lt;br /&gt;
When part of the window is newly revealed (either because the window was just made larger, or because that part used to be obscured by another window), the application must refresh any rendering it wishes to appear into that part. The application will learn that refresh is needed because Intuition sends an IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
== Smart Refresh ==&lt;br /&gt;
&lt;br /&gt;
If a window is of the Smart Refresh type, then the system will not only preserve those pixels which are actually on-screen, but it will save all obscured pixels that are within the current window&#039;s size. The system will refresh those parts of the window revealed by changes in the overlapping with other windows on the screen, without involving the application. However, any part of the window revealed through the sizing of the window must be redrawn by the application. Again, Intuition will notify the application through the IDCMP_REFRESHWINDOW event.&lt;br /&gt;
&lt;br /&gt;
Because the obscured areas are kept in off-screen buffers, Smart Refresh windows are refreshed faster than Simple Refresh windows are, and often without involving the application. Of course, for the same reason, they use more display memory.&lt;br /&gt;
&lt;br /&gt;
== SuperBitMap Refresh ==&lt;br /&gt;
&lt;br /&gt;
The SuperBitMap refresh type allows the application to provide and maintain bitmap memory for graphics in the window. The bitmap can be any size as long as the window sizing limits respect the maximum size of the bitmap.&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows have their own memory for maintaining all obscured parts of the window up to the size of the defined bitmap, including those parts outside of the current window. Intuition will update all parts of the window that are revealed through changes in sizing and changes in window overlapping. The application never needs to redraw portions of the window that were revealed by sizing or positioning windows in the screen.&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows require the application to allocate a bitmap for use as off-screen memory, instead of using Intuition managed buffers. This bitmap must be as large as, or larger than, the inner window&#039;s maximum dimensions (that is, the window&#039;s outside dimensions less the border sizes).&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows are almost always WFLG_GIMMEZEROZERO, which renders the borders and system gadgets in a separate bitmap. If the application wishes to create a SuperBitMap window that is not GimmeZeroZero, it must make the window borderless with no system gadgets, so that no border imagery is rendered by Intuition into the application&#039;s bitmap.&lt;br /&gt;
&lt;br /&gt;
= Intuition Refresh Events =&lt;br /&gt;
&lt;br /&gt;
When using a Simple Refresh or a Smart Refresh windows, the program may receive refresh events, informing it to update the display. See the above discussion for information on when refresh events are sent.&lt;br /&gt;
&lt;br /&gt;
A message of the class IDCMP_REFRESHWINDOW arrives at the IDCMP, informing the program of the need to update the display. The program must take some action when it receives a refresh event, even if it is just the acceptable minimum action described below.&lt;br /&gt;
&lt;br /&gt;
On receiving a refresh event, BeginRefresh() must be called, then the program should redraw its display, and, finally, call EndRefresh(). The minimum required action is to call the BeginRefresh()/EndRefresh() pair. This allows Intuition and the Layers library keep things sorted and organized.&lt;br /&gt;
&lt;br /&gt;
= Optimized Window Refreshing =&lt;br /&gt;
&lt;br /&gt;
Bracketing the display updating in the BeginRefresh()/EndRefresh() pair automatically restricts all rendering to the &amp;quot;damaged&amp;quot; areas.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID BeginRefresh( struct Window *window );&lt;br /&gt;
VOID EndRefresh  ( struct Window *window, LONG complete );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions makes sure that refreshing is done in the most efficient way, only redrawing those portions of the window that really need to be redrawn. The rest of the rendering commands are discarded.&lt;br /&gt;
&lt;br /&gt;
Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Calls to RefreshGadgets() are not permitted. Avoid calls that may lock the LayerInfo, or get complicated in Intuition, since BeginRefresh() leaves the window&#039;s layer or layers locked. Avoid AutoRequest(), and therefore all direct or indirect disk related DOS calls. See [[Intuition_Gadgets|Intuition Gadgets]] for more information on gadget restrictions with BeginRefresh()/EndRefresh().&lt;br /&gt;
&lt;br /&gt;
Certain applications do not need to receive refresh events, and can avoid having to call BeginRefresh() and EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.&lt;br /&gt;
&lt;br /&gt;
The EndRefresh() function takes a boolean value as an argument (complete in the prototype above). This value determines whether refreshing is completely finished. When set to FALSE, further refreshing may be performed between subsequent BeginRefresh()/ EndRefresh() pairs. Set the boolean to TRUE for the last call to EndRefresh().&lt;br /&gt;
&lt;br /&gt;
It is critical that applications performing multiple BeginRefresh() and EndRefresh() pairs using EndRefresh(win,FALSE) hold layers locked through the entire process. The layer lock may only be released after the final call to EndRefresh(win,TRUE). See [[Layers_Library|Layers Library]] for more details.&lt;br /&gt;
&lt;br /&gt;
The procedures outlined in this section take care of refreshing what is inside the window. Another function named RefreshWindowFrame() refreshes window borders, including the title region and gadgets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID RefreshWindowFrame( struct Window *window );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Applications can use this function to update window borders after overwriting them with graphics.&lt;br /&gt;
&lt;br /&gt;
= Setting up a SuperBitMap Window =&lt;br /&gt;
&lt;br /&gt;
SuperBitMap windows are created by setting the WFLG_SUPER_BITMAP flag, or by specifying the WA_SuperBitMap tag in the OpenWindowTagList() call. A pointer to an allocated and initialized BitMap structure must be provided.&lt;br /&gt;
&lt;br /&gt;
A SuperBitMap window requires the application to allocate and initialize its own bitmap. This entails allocating a BitMap structure, initializing the structure and allocating memory for the bit planes.&lt;br /&gt;
&lt;br /&gt;
Allocate a BitMap structure with the Exec AllocMem() function. Then use the graphics function InitBitMap() to initialize the BitMap structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
VOID InitBitMap( struct BitMap *bitMap, LONG depth, LONG width, LONG height );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
InitBitMap() fills in fields in the BitMap structure describing how a linear memory area is organized as a series of one or more rectangular bit-planes.&lt;br /&gt;
&lt;br /&gt;
Once you have allocated and initialized the BitMap structure, use the graphics library function AllocRaster() to allocate the memory space for all the bit planes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PLANEPTR AllocRaster( ULONG width, ULONG height );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example listed in the next section shows how to allocate a BitMap structure, initialize it with InitBitMap() and use AllocRaster() function to set up memory for the bitplanes.&lt;br /&gt;
&lt;br /&gt;
= Graphics and Layers Functions for SuperBitMap Windows =&lt;br /&gt;
&lt;br /&gt;
The portion of the bitmap showing within a SuperBitMap window is controlled by the application. Initially, the window shows the bitmap starting from its origin (0,0) and clipped to fit within the window layer. The visible portion of the bitmap can be scrolled around within the window using the layers library ScrollLayer() function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
VOID ScrollLayer(LONG unused, struct Layer *layer, LONG dx, LONG dy)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pass this function a pointer to the window&#039;s layer in layer and the scroll offsets in dx and dy. (A pointer to the window&#039;s layer can be obtained from Window.RPort-&amp;amp;gt;Layer.)&lt;br /&gt;
&lt;br /&gt;
When rendering operations are performed in a SuperBitMap window, any rendering that falls outside window boundaries is done in the application&#039;s bitmap. Rendering that falls within window bounds is done in the screen&#039;s bitmap. Before performing an operation such as a save on the application bitmap, the graphics library function SyncSBitMap() should be called:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
VOID SyncSBitMap(struct Layer *layer)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pass this function a pointer to the window&#039;s layer. SyncSBitMap() copies the window contents to the corresponding part of the application bitmap, bringing it up to date. (If no rendering operations have been performed this call is not necessary.)&lt;br /&gt;
&lt;br /&gt;
Similarly, after making any changes to the application bitmap such as loading a new one, the window&#039;s layer should be locked and the CopySBitMap() function should be called.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
VOID CopySBitMap(struct Layer *)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function copies the new information in the appropriate area of the underlying bitmap to the window&#039;s layer.&lt;br /&gt;
&lt;br /&gt;
For more information about bitmaps and layers, see the [[Graphics_Primitives|Graphics Primitives]] and [[Layers_Library|Layers Library]]. Also see the &amp;amp;lt;graphics/clip.h&amp;amp;gt;, &amp;amp;lt;graphics/gfx.h&amp;amp;gt;, &amp;amp;lt;graphics/layers.h &amp;amp;gt; files in the SDK.&lt;br /&gt;
&lt;br /&gt;
== SuperBitMap Window Example ==&lt;br /&gt;
&lt;br /&gt;
This example shows how to implement a superbitmap, and uses a host of Intuition facilities. Further reading of other Intuition, BOOPSI and graphics chapters may be required for a complete understanding of this example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 **  lines.c -- Window SuperBitMap example.&lt;br /&gt;
 **&lt;br /&gt;
 **  gcc -o lines lines.c&lt;br /&gt;
 ** &lt;br /&gt;
 **/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/scroller.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/layout.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define WIDTH_SUPER (800)&lt;br /&gt;
#define HEIGHT_SUPER (600)&lt;br /&gt;
#define MINWIDTH (150)&lt;br /&gt;
#define MINHEIGHT (150)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LAYERXOFFSET(x) (x-&amp;gt;RPort-&amp;gt;Layer-&amp;gt;Scroll_X)&lt;br /&gt;
#define LAYERYOFFSET(x) (x-&amp;gt;RPort-&amp;gt;Layer-&amp;gt;Scroll_Y)&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
    WID_MAIN = 0,&lt;br /&gt;
    WID_LAST&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
    OID_MAIN = 0,&lt;br /&gt;
    OID_VPROP,&lt;br /&gt;
    OID_HPROP,&lt;br /&gt;
    OID_LAST&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct LayersIFace *ILayers;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
static struct Window *windows[WID_LAST];&lt;br /&gt;
static Object *objects[OID_LAST];&lt;br /&gt;
static struct Hook idcmphook;&lt;br /&gt;
&lt;br /&gt;
/* Prototypes for our functions */&lt;br /&gt;
&lt;br /&gt;
void initBorderProps(struct Screen *screen);&lt;br /&gt;
void doNewSize();&lt;br /&gt;
void doDrawStuff();&lt;br /&gt;
void doMsgLoop();&lt;br /&gt;
void superWindow(struct Screen *screen);&lt;br /&gt;
void slideBitMap(struct Window *win, int16 dX, int16 dY);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct Screen *screen;&lt;br /&gt;
&lt;br /&gt;
    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    IIntuition = (struct IntuitionIFace *) IExec-&amp;gt;GetInterface(IntuitionBase,&lt;br /&gt;
        &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace *) IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
    ILayers = (struct LayersIFace *) IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL) {&lt;br /&gt;
        if ((screen = IIntuition-&amp;gt;LockPubScreen(NULL)) != NULL) {&lt;br /&gt;
            superWindow(screen);&lt;br /&gt;
            IIntuition-&amp;gt;UnlockPubScreen(NULL, screen);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) ILayers);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) IGraphics);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *) IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void IDCMPHook(struct Hook *hook, Object *object, struct IntuiMessage *msg)&lt;br /&gt;
{&lt;br /&gt;
    if (msg-&amp;gt;Class == IDCMP_IDCMPUPDATE) {&lt;br /&gt;
        int16 dX = 0;&lt;br /&gt;
        int16 dY = 0;&lt;br /&gt;
        int32 pos;&lt;br /&gt;
&lt;br /&gt;
        struct Window *win = windows[WID_MAIN];&lt;br /&gt;
        if (win != NULL) {&lt;br /&gt;
            uint32 gadgetid = IUtility-&amp;gt;GetTagData(GA_ID, 0, (struct TagItem *) msg-&amp;gt;IAddress);&lt;br /&gt;
            if (IIntuition-&amp;gt;GetAttr(SCROLLER_Top, objects[gadgetid], (uint32 *) &amp;amp;pos)) {&lt;br /&gt;
                switch (gadgetid) {&lt;br /&gt;
                case OID_VPROP:&lt;br /&gt;
                    dY = pos - LAYERYOFFSET(win);&lt;br /&gt;
                    break;&lt;br /&gt;
                case OID_HPROP:&lt;br /&gt;
                    dX = pos - LAYERXOFFSET(win);&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                if (dX || dY) {&lt;br /&gt;
                    slideBitMap(win, dX, dY);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void superWindow(struct Screen *myscreen)&lt;br /&gt;
{&lt;br /&gt;
    struct BitMap *bigBitMap = IGraphics-&amp;gt;AllocBitMapTags(WIDTH_SUPER, HEIGHT_SUPER, myscreen-&amp;gt;BitMap.Depth,&lt;br /&gt;
            BMATags_Friend, myscreen-&amp;gt;BitMap, BMATags_Displayable, TRUE, BMATags_Clear, 0, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (bigBitMap != NULL) {&lt;br /&gt;
        struct MsgPort *AppPort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_DONE);&lt;br /&gt;
&lt;br /&gt;
        if ( AppPort != NULL )&lt;br /&gt;
        {&lt;br /&gt;
            idcmphook.h_Entry = (uint32 (*)())IDCMPHook;&lt;br /&gt;
            idcmphook.h_SubEntry = NULL;&lt;br /&gt;
&lt;br /&gt;
            if ((objects[OID_MAIN] = IIntuition-&amp;gt;NewObject(NULL, &amp;quot;window.class&amp;quot;,&lt;br /&gt;
                WA_ScreenTitle, &amp;quot;SuperBitmap&amp;quot;,&lt;br /&gt;
                WA_Title, &amp;quot;Lines&amp;quot;,&lt;br /&gt;
                WA_Activate, TRUE,&lt;br /&gt;
                WA_DepthGadget, TRUE,&lt;br /&gt;
                WA_DragBar, TRUE,&lt;br /&gt;
                WA_CloseGadget, TRUE,&lt;br /&gt;
                WA_SizeGadget, TRUE,&lt;br /&gt;
                WA_Top, 10,&lt;br /&gt;
                WA_Left, 10,&lt;br /&gt;
                WA_MinWidth, MINWIDTH,&lt;br /&gt;
                WA_MinHeight, MINHEIGHT,&lt;br /&gt;
                WA_Width, MINWIDTH,&lt;br /&gt;
                WA_Height, MINHEIGHT,&lt;br /&gt;
                WA_IDCMP, IDCMP_NEWSIZE | IDCMP_IDCMPUPDATE,&lt;br /&gt;
                WA_PubScreen, myscreen, WA_SuperBitMap, bigBitMap,&lt;br /&gt;
                WA_NoCareRefresh, TRUE,&lt;br /&gt;
                WA_GimmeZeroZero, TRUE,     &lt;br /&gt;
                WINDOW_IDCMPHook, &amp;amp;idcmphook,&lt;br /&gt;
                WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE,&lt;br /&gt;
                WINDOW_HorizProp, 1,&lt;br /&gt;
                WINDOW_VertProp, 1,&lt;br /&gt;
                WINDOW_AppPort, AppPort, TAG_DONE)) != NULL) {&lt;br /&gt;
                //  Open the window.&lt;br /&gt;
                if (windows[WID_MAIN] = (struct Window *) IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_OPEN)) {   &lt;br /&gt;
                    /* adjust props to represent portion visible */&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();&lt;br /&gt;
                    /* process the window, return on IDCMP_CLOSEWINDOW */&lt;br /&gt;
                    doMsgLoop();&lt;br /&gt;
                }&lt;br /&gt;
                IIntuition-&amp;gt;DisposeObject(objects[OID_MAIN]);&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, AppPort);&lt;br /&gt;
        }   &lt;br /&gt;
        IGraphics-&amp;gt;FreeBitMap(bigBitMap);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void slideBitMap(struct Window *win, int16 Dx, int16 Dy)&lt;br /&gt;
{&lt;br /&gt;
    ILayers-&amp;gt;ScrollLayer(0, win-&amp;gt;RPort-&amp;gt;Layer, Dx, Dy);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Update the prop gadgets and bitmap positioning when the size changes.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void doNewSize()&lt;br /&gt;
{&lt;br /&gt;
    struct Window *win = windows[WID_MAIN];&lt;br /&gt;
    if (win != NULL) {&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_HorizObject, objects[OID_MAIN], (uint32 *) &amp;amp;objects[OID_HPROP]);&lt;br /&gt;
        if (objects[OID_HPROP]) {       &lt;br /&gt;
            if (win-&amp;gt;GZZWidth &amp;gt;= WIDTH_SUPER)&lt;br /&gt;
                slideBitMap(win, -LAYERXOFFSET(win), 0);&lt;br /&gt;
            IIntuition-&amp;gt;RefreshSetGadgetAttrs((APTR) objects[OID_HPROP], win, NULL,&lt;br /&gt;
                GA_ID, OID_HPROP,&lt;br /&gt;
                SCROLLER_Top, LAYERXOFFSET(win),&lt;br /&gt;
                SCROLLER_Total, WIDTH_SUPER,&lt;br /&gt;
                SCROLLER_Visible, win-&amp;gt;GZZWidth,&lt;br /&gt;
                ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
                TAG_DONE); &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_VertObject, objects[OID_MAIN], (uint32 *) &amp;amp;objects[OID_VPROP]);&lt;br /&gt;
        if (objects[OID_VPROP]) {&lt;br /&gt;
            if (win-&amp;gt;GZZHeight &amp;gt;= HEIGHT_SUPER)&lt;br /&gt;
                slideBitMap(win, 0, -LAYERYOFFSET(win));&lt;br /&gt;
&lt;br /&gt;
            IIntuition-&amp;gt;RefreshSetGadgetAttrs((APTR) objects[OID_VPROP], win, NULL,&lt;br /&gt;
                GA_ID, OID_VPROP,&lt;br /&gt;
                SCROLLER_Top, LAYERYOFFSET(win),&lt;br /&gt;
                SCROLLER_Total, HEIGHT_SUPER,&lt;br /&gt;
                SCROLLER_Visible, win-&amp;gt;GZZHeight,&lt;br /&gt;
                ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
                TAG_DONE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int16 RangeRand(uint32 maxValue)&lt;br /&gt;
{&lt;br /&gt;
    static struct RandomState range = {0xFFFF, 0};&lt;br /&gt;
    return IUtility-&amp;gt;Random(&amp;amp;range) % maxValue;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void doDrawStuff()&lt;br /&gt;
{&lt;br /&gt;
    struct Window *win = windows[WID_MAIN];&lt;br /&gt;
    if (win != NULL) {&lt;br /&gt;
        /* clear the bitplanes */&lt;br /&gt;
        IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 0);&lt;br /&gt;
        IGraphics-&amp;gt;SetDrMd(win-&amp;gt;RPort, JAM1);&lt;br /&gt;
        int16 x1, y1, x2, y2;&lt;br /&gt;
        int16 pen, ncolors, deltx, delty;&lt;br /&gt;
        ncolors = 1 &amp;lt;&amp;lt; win-&amp;gt;WScreen-&amp;gt;BitMap.Depth;&lt;br /&gt;
        deltx = RangeRand(6) + 2;&lt;br /&gt;
        delty = RangeRand(6) + 2;&lt;br /&gt;
        pen = RangeRand(ncolors - 1) + 1;&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(win-&amp;gt;RPort, pen);&lt;br /&gt;
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;&lt;br /&gt;
            x1 &amp;lt; WIDTH_SUPER; x1 += deltx, x2 -= deltx) {&lt;br /&gt;
            IGraphics-&amp;gt;Move(win-&amp;gt;RPort, x1, y1);&lt;br /&gt;
            IGraphics-&amp;gt;Draw(win-&amp;gt;RPort, x2, y2);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        pen = RangeRand(ncolors - 1) + 1;&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(win-&amp;gt;RPort, pen);&lt;br /&gt;
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;&lt;br /&gt;
             y1 &amp;lt; HEIGHT_SUPER; y1 += delty, y2 -= delty) {&lt;br /&gt;
            IGraphics-&amp;gt;Move(win-&amp;gt;RPort, x1, y1);&lt;br /&gt;
            IGraphics-&amp;gt;Draw(win-&amp;gt;RPort, x2, y2);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void doMsgLoop()&lt;br /&gt;
{                   &lt;br /&gt;
    uint32 signal = 0;&lt;br /&gt;
    BOOL done = FALSE;&lt;br /&gt;
    &lt;br /&gt;
    // Obtain the window wait signal mask.&lt;br /&gt;
    IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, objects[OID_MAIN], &amp;amp;signal);&lt;br /&gt;
&lt;br /&gt;
    // Input Event Loop&lt;br /&gt;
    while (!done) {&lt;br /&gt;
        uint32 wait = IExec-&amp;gt;Wait(signal | SIGBREAKF_CTRL_C);&lt;br /&gt;
        if ( wait &amp;amp; SIGBREAKF_CTRL_C ) {&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( wait &amp;amp; signal ) {&lt;br /&gt;
            uint32 result = WMHI_LASTMSG;&lt;br /&gt;
            int16 code = 0;&lt;br /&gt;
            while ((result = IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_HANDLEINPUT, &amp;amp;code)) != WMHI_LASTMSG) {&lt;br /&gt;
                switch (result &amp;amp; WMHI_CLASSMASK) {&lt;br /&gt;
                case WMHI_NEWSIZE:&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_CLOSEWINDOW:&lt;br /&gt;
                    windows[WID_MAIN] = NULL;&lt;br /&gt;
                    done = TRUE;&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_ICONIFY:&lt;br /&gt;
                    IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_ICONIFY);&lt;br /&gt;
                    windows[WID_MAIN] = NULL;&lt;br /&gt;
                    break;&lt;br /&gt;
                case WMHI_UNICONIFY:&lt;br /&gt;
                    windows[WID_MAIN] = (struct Window *) IIntuition-&amp;gt;IDoMethod(objects[OID_MAIN], WM_OPEN);&lt;br /&gt;
                    doNewSize();&lt;br /&gt;
                    doDrawStuff();     &lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9248</id>
		<title>Graphics Regions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9248"/>
		<updated>2017-11-10T10:45:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added links to library pages; fixed a few minor things&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Regions ==&lt;br /&gt;
&lt;br /&gt;
Regions allow the application to install clipping rectangles into layers. A clipping rectangle is a rectangular area into which the graphics routines will draw. All drawing that would fall outside of that rectangular area is clipped (not rendered).&lt;br /&gt;
&lt;br /&gt;
User clipping regions are linked lists of clipping rectangles created by an application program through the [[Graphics Library]] routines described below. By combining together various clipping rectangles, any arbitrary clipping shape can be created. Once the region is set up, you use the [[Layers Library]] call InstallClipRegion() to make the clipping region active in a layer.&lt;br /&gt;
&lt;br /&gt;
Regions are safe to use with layers created by Intuition (i.e. windows).&lt;br /&gt;
&lt;br /&gt;
The application can selectively update a custom-shaped part of a layer without disturbing any of the other layers that might be present.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Never modify the DamageList of a layer directly|text=Use the routine InstallClipRegion() to add clipping to the layer. The regions installed by InstallClipRegion() are independent of the layer&#039;s DamageList and use of user clipping regions will not interfere with optimized window refreshing.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do not modify a region after it has been added|text=After a region has been added with InstallClipRegion(), the program may not modify it until the region has been removed with another call to InstallClipRegion().}}&lt;br /&gt;
&lt;br /&gt;
=== Creating and Deleting Regions ===&lt;br /&gt;
&lt;br /&gt;
You allocate a Region data structure with the NewRegion() call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *NewRegion( VOID );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The NewRegion() function allocates and initializes a Region structure that has no drawable areas defined in it. If the application draws through a new region, nothing will be drawn as the region is empty. The application must add rectangles to the region before any graphics will appear.&lt;br /&gt;
&lt;br /&gt;
Use DisposeRegion() to free the Region structure when you are done with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DisposeRegion( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DisposeRegion() returns all memory associated with a region to the system and deallocates all rectangles that have been linked to it.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t forget to free your rectangles|text=All of the functions that add rectangles to the region make copies of the rectangles. If the program allocates a rectangle, then adds it to a region, it still must deallocate the rectangle. The call to DisposeRegion() will not deallocate rectangles &#039;&#039;explicitly allocated&#039;&#039; by the application.}}&lt;br /&gt;
&lt;br /&gt;
=== Installing Regions ===&lt;br /&gt;
&lt;br /&gt;
Use the function InstallClipRegion() to install the region.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *InstallClipRegion( struct Layer *layer, struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This installs a transparent clipping region to a layer. All subsequent graphics calls will be clipped to this region. The region must be removed with a second call to InstallClipRegion() before removing the layer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** Sample installation and removal of a clipping region&lt;br /&gt;
*/&lt;br /&gt;
register struct Region     *new_region ;&lt;br /&gt;
register struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* If the application owns the layer and has not installed a region,&lt;br /&gt;
** old_region will return NULL here.&lt;br /&gt;
*/&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
/* draw into the layer or window */&lt;br /&gt;
&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    {&lt;br /&gt;
    /* throw the used region away.  This region could be saved and&lt;br /&gt;
    ** used again later, if desired by the application.&lt;br /&gt;
    */&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A warning about InstallClipRegion()|text=The program must not call InstallClipRegion() inside of a Begin/EndRefresh() or Begin/EndUpdate() pair. The following code segment shows how to modify the user clipping region when using these calls. See the Autodoc for BeginRefresh() for more details.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region     *new_region ;&lt;br /&gt;
struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* you have to have already setup the new_region and old_region */&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list and the new_region */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
/* put back the old region */&lt;br /&gt;
new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, TRUE);               /* remove the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the new_region only into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);&lt;br /&gt;
&lt;br /&gt;
/* finally get rid of the new region, old_region still installed */&lt;br /&gt;
if (NULL != (new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Changing a Region ===&lt;br /&gt;
&lt;br /&gt;
Regions may be modified by performing logical operations with rectangles, or with other regions.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Reuse your rectangles|text=In all of the rectangle and region routines the clipping rectangle is copied into the region. This means that a single clipping rectangle (Rectangle structure) may be used many times by simply changing the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; values. The application need &#039;&#039;not&#039;&#039; create a new instance of the Rectangle structure for each rectangle added to a region.}}&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
extern struct Region *RowRegion;  /* created elsewhere */&lt;br /&gt;
&lt;br /&gt;
int16 ktr;&lt;br /&gt;
struct Rectangle rect;&lt;br /&gt;
&lt;br /&gt;
for (ktr = 1; ktr &amp;lt; 6; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    rect.MinX = 50;&lt;br /&gt;
    rect.MaxX = 315;&lt;br /&gt;
    rect.MinY = (ktr * 10) - 5;&lt;br /&gt;
    rect.MaxY = (ktr * 10);&lt;br /&gt;
&lt;br /&gt;
    if (!IGraphics-&amp;gt;OrRectRegion(RowRegion, &amp;amp;rect))&lt;br /&gt;
        clean_exit(RETURN_WARN);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and Regions ====&lt;br /&gt;
&lt;br /&gt;
There are four [[Graphics Library]] functions for changing a region through logical operations with a rectangle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL OrRectRegion   ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
VOID AndRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL XorRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL ClearRectRegion( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OrRectRegion() modifies a region structure by &#039;&#039;&#039;OR&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. When the application draws through this region (assuming that the region was originally empty), only the pixels within the clipping rectangle will be affected. If the region already has drawable areas, they will still exist, this rectangle is added to the drawable area.&lt;br /&gt;
&lt;br /&gt;
AndRectRegion() modifies the region structure by &#039;&#039;&#039;AND&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. Only those pixels that were already drawable and within the rectangle will remain drawable, any that are outside of it will be clipped in future.&lt;br /&gt;
&lt;br /&gt;
XorRectRegion() applies the rectangle to the region in an &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; mode. Within the given rectangle, any areas that were drawable become clipped, any areas that were clipped become drawable. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
ClearRectRegion() clears the rectangle from the region. Within the given rectangle, any areas that were drawable become clipped. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
==== Regions and Regions ====&lt;br /&gt;
&lt;br /&gt;
As with rectangles and regions, there are four functions for combining regions with regions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL AndRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL OrRegionRegion ( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL XorRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
VOID ClearRegion    ( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AndRegionRegion() performs a logical &#039;&#039;&#039;AND&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever the regions drawable areas overlap. That is, where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
OrRegionRegion() performs a logical &#039;&#039;&#039;OR&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
XorRegionRegion() performs a logical &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region but not both. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region. But where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will &#039;&#039;not&#039;&#039; be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
ClearRegion() puts the region back to the same state it was in when the region was created with NewRegion(), that is, no areas are drawable.&lt;br /&gt;
&lt;br /&gt;
=== Regions Example ===&lt;br /&gt;
&lt;br /&gt;
The following example shows the use of the [[Layers Library]] call InstallClipRegion(), as well as simple use of the [[Graphics Library]] regions functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* clipping.c */&lt;br /&gt;
&lt;br /&gt;
/* Force use of new variable names to help prevent errors */&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuitionbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MY_WIN_WIDTH  (300)&lt;br /&gt;
#define MY_WIN_HEIGHT (100)&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct LayersIFace *ILayers;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** unclipWindow()&lt;br /&gt;
**&lt;br /&gt;
** Used to remove a clipping region installed by clipWindow() or&lt;br /&gt;
** clipWindowToBorders(), disposing of the installed region and&lt;br /&gt;
** reinstalling the region removed.&lt;br /&gt;
*/&lt;br /&gt;
void unclipWindow(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region     *old_region;&lt;br /&gt;
&lt;br /&gt;
/* Remove any old region by installing a NULL region,&lt;br /&gt;
** then dispose of the old region if one was installed.&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, NULL)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** clipWindow()&lt;br /&gt;
** Clip a window to a specified rectangle (given by upper left and&lt;br /&gt;
** lower right corner.)  the removed region is returned so that it&lt;br /&gt;
** may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindow(struct Window *win,&lt;br /&gt;
    LONG minX, LONG minY, LONG maxX, LONG maxY)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *new_region;&lt;br /&gt;
struct Rectangle  my_rectangle;&lt;br /&gt;
&lt;br /&gt;
/* set up the limits for the clip */&lt;br /&gt;
my_rectangle.MinX = minX;&lt;br /&gt;
my_rectangle.MinY = minY;&lt;br /&gt;
my_rectangle.MaxX = maxX;&lt;br /&gt;
my_rectangle.MaxY = maxY;&lt;br /&gt;
&lt;br /&gt;
/* get a new region and OR in the limits. */&lt;br /&gt;
if (NULL != (new_region = IGraphics-&amp;gt;NewRegion()))&lt;br /&gt;
    {&lt;br /&gt;
    if (FALSE == IGraphics-&amp;gt;OrRectRegion(new_region, &amp;amp;my_rectangle))&lt;br /&gt;
        {&lt;br /&gt;
        IGraphics-&amp;gt;DisposeRegion(new_region);&lt;br /&gt;
        new_region = NULL;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Install the new region, and return any existing region.&lt;br /&gt;
** If the above allocation and region processing failed, then&lt;br /&gt;
** new_region will be NULL and no clip region will be installed.&lt;br /&gt;
*/&lt;br /&gt;
return(ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** clipWindowToBorders()&lt;br /&gt;
** clip a window to its borders.&lt;br /&gt;
** The removed region is returned so that it may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindowToBorders(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
return(clipWindow(win, win-&amp;gt;BorderLeft, win-&amp;gt;BorderTop,&lt;br /&gt;
    win-&amp;gt;Width - win-&amp;gt;BorderRight - 1, win-&amp;gt;Height - win-&amp;gt;BorderBottom - 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Wait for the user to select the close gadget.&lt;br /&gt;
*/&lt;br /&gt;
VOID wait_for_close(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
BOOL done;&lt;br /&gt;
&lt;br /&gt;
done = FALSE;&lt;br /&gt;
&lt;br /&gt;
while (FALSE == done)&lt;br /&gt;
    {&lt;br /&gt;
    /* we only have one signal bit, so we do not have to check which&lt;br /&gt;
    ** bit broke the Wait().&lt;br /&gt;
    */&lt;br /&gt;
    IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
    while ( (FALSE == done) &amp;amp;&amp;amp;&lt;br /&gt;
            (NULL != (msg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort))))&lt;br /&gt;
        {&lt;br /&gt;
        /* use a switch statement if looking for multiple event types */&lt;br /&gt;
        if (msg-&amp;gt;Class == IDCMP_CLOSEWINDOW)&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Simple routine to blast all bits in a window with color three to show&lt;br /&gt;
** where the window is clipped.  After a delay, flush back to color zero&lt;br /&gt;
** and refresh the window borders.&lt;br /&gt;
*/&lt;br /&gt;
VOID draw_in_window(struct Window *win, UBYTE *message)&lt;br /&gt;
{&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;%s...&amp;quot;, message);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 3);&lt;br /&gt;
IDOS-&amp;gt;Delay(200);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 0);&lt;br /&gt;
IIntuition-&amp;gt;RefreshWindowFrame(win);&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;done\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Show drawing into an unclipped window, a window clipped to the&lt;br /&gt;
** borders and a window clipped to a random rectangle.  It is possible&lt;br /&gt;
** to clip more complex shapes by AND&#039;ing, OR&#039;ing and exclusive-OR&#039;ing&lt;br /&gt;
** regions and rectangles to build a user clip region.&lt;br /&gt;
**&lt;br /&gt;
** This example assumes that old regions are not going to be re-used,&lt;br /&gt;
** so it simply throws them away.&lt;br /&gt;
*/&lt;br /&gt;
VOID clip_test(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *old_region;&lt;br /&gt;
&lt;br /&gt;
draw_in_window(win,&amp;quot;Window with no clipping&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* if the application has never installed a user clip region,&lt;br /&gt;
** then old_region will be NULL here.  Otherwise, delete the&lt;br /&gt;
** old region (you could save it and re-install it later...)&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindowToBorders(win)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped to window borders&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
&lt;br /&gt;
/* here we know old_region will be NULL, as that is what we&lt;br /&gt;
** installed with unclipWindow()...&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindow(win,20,20,100,50)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped from (20,20) to (100,50)&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
&lt;br /&gt;
wait_for_close(win);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Open and close resources, call the test routine when ready.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct Window *win;&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
  IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
  ILayers = (struct LayersIFace*)IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (NULL != (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                             WA_Width,       MY_WIN_WIDTH,&lt;br /&gt;
                             WA_Height,      MY_WIN_HEIGHT,&lt;br /&gt;
                             WA_IDCMP,       IDCMP_CLOSEWINDOW,&lt;br /&gt;
                             WA_CloseGadget, TRUE,&lt;br /&gt;
                             WA_DragBar,     TRUE,&lt;br /&gt;
                             WA_Activate,    TRUE,&lt;br /&gt;
                             TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      clip_test(win);&lt;br /&gt;
&lt;br /&gt;
      IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)ILayers);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the [[Graphics Library]] functions related to regions. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Routine&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| LockLayerRom()&lt;br /&gt;
| Same as LockLayer(), from the [[Layers Library]].&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerRom()&lt;br /&gt;
| Release LockLayerRom() lock.&lt;br /&gt;
|-&lt;br /&gt;
| AttemptLockLayerRom()&lt;br /&gt;
| Lock layer only if it is immediately available.&lt;br /&gt;
|-&lt;br /&gt;
| NewRegion()&lt;br /&gt;
| Create a new, empty region.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeRegion()&lt;br /&gt;
| Dispose of an existing region and its rectangles.&lt;br /&gt;
|-&lt;br /&gt;
| AndRectRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| OrRectRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| XorRectRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRectRegion()&lt;br /&gt;
| Clear a rectangular portion of a region.&lt;br /&gt;
|-&lt;br /&gt;
| AndRegionRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| OrRegionRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| XorRegionRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRegion()&lt;br /&gt;
| Clear a region.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9247</id>
		<title>Graphics Regions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9247"/>
		<updated>2017-11-10T10:23:43Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Regions Example: a few more corrections and updates&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Regions ==&lt;br /&gt;
&lt;br /&gt;
Regions allow the application to install clipping rectangles into layers. A clipping rectangle is a rectangular area into which the graphics routines will draw. All drawing that would fall outside of that rectangular area is clipped (not rendered).&lt;br /&gt;
&lt;br /&gt;
User clipping regions are linked lists of clipping rectangles created by an application program through the graphics library routines described below. By combining together various clipping rectangles, any arbitrary clipping shape can be created. Once the region is set up, you use the layers library call InstallClipRegion() to make the clipping region active in a layer.&lt;br /&gt;
&lt;br /&gt;
Regions are safe to use with layers created by Intuition (i.e., windows).&lt;br /&gt;
&lt;br /&gt;
The application can selectively update a custom-shaped part of a layer without disturbing any of the other layers that might be present.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Never Modify the DamageList of a Layer Directly|text=Use the routine InstallClipRegion() to add clipping to the layer. The regions installed by InstallClipRegion() are independent of the layer&#039;s DamageList and use of user clipping regions will not interfere with optimized window refreshing.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do Not Modify A Region After It Has Been Added|text=After a region has been added with InstallClipRegion(), the program may not modify it until the region has been removed with another call to InstallClipRegion().}}&lt;br /&gt;
&lt;br /&gt;
=== Creating and Deleting Regions ===&lt;br /&gt;
&lt;br /&gt;
You allocate a Region data structure with the NewRegion() call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *NewRegion( VOID );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The NewRegion() function allocates and initializes a Region structure that has no drawable areas defined in it. If the application draws through a new region, nothing will be drawn as the region is empty. The application must add rectangles to the region before any graphics will appear.&lt;br /&gt;
&lt;br /&gt;
Use DisposeRegion() to free the Region structure when you are done with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DisposeRegion( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DisposeRegion() returns all memory associated with a region to the system and deallocates all rectangles that have been linked to it.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t Forget to Free Your Rectangles|text=All of the functions that add rectangles to the region make copies of the rectangles. If the program allocates a rectangle, then adds it to a region, it still must deallocate the rectangle. The call to DisposeRegion() will not deallocate rectangles &#039;&#039;explicitly allocated&#039;&#039; by the application.}}&lt;br /&gt;
&lt;br /&gt;
=== Installing Regions ===&lt;br /&gt;
&lt;br /&gt;
Use the function InstallClipRegion() to install the region.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *InstallClipRegion( struct Layer *layer, struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This installs a transparent clipping region to a layer. All subsequent graphics calls will be clipped to this region. The region must be removed with a second call to InstallClipRegion() before removing the layer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** Sample installation and removal of a clipping region&lt;br /&gt;
*/&lt;br /&gt;
register struct Region     *new_region ;&lt;br /&gt;
register struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* If the application owns the layer and has not installed a region,&lt;br /&gt;
** old_region will return NULL here.&lt;br /&gt;
*/&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
/* draw into the layer or window */&lt;br /&gt;
&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    {&lt;br /&gt;
    /* throw the used region away.  This region could be saved and&lt;br /&gt;
    ** used again later, if desired by the application.&lt;br /&gt;
    */&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Warning About InstallClipRegion()|text=The program must not call InstallClipRegion() inside of a Begin/EndRefresh() or Begin/EndUpdate() pair. The following code segment shows how to modify the user clipping region when using these calls. See the Autodoc for BeginRefresh() for more details.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region     *new_region ;&lt;br /&gt;
struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* you have to have already setup the new_region and old_region */&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list and the new_region */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
/* put back the old region */&lt;br /&gt;
new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, TRUE);               /* remove the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the new_region only into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);&lt;br /&gt;
&lt;br /&gt;
/* finally get rid of the new region, old_region still installed */&lt;br /&gt;
if (NULL != (new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Changing a Region ===&lt;br /&gt;
&lt;br /&gt;
Regions may be modified by performing logical operations with rectangles, or with other regions.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Reuse Your Rectangles|text=In all of the rectangle and region routines the clipping rectangle is copied into the region. This means that a single clipping rectangle (Rectangle structure) may be used many times by simply changing the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; values. The application need &#039;&#039;not&#039;&#039; create a new instance of the Rectangle structure for each rectangle added to a region.}}&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
extern struct Region *RowRegion;  /* created elsewhere */&lt;br /&gt;
&lt;br /&gt;
int16 ktr;&lt;br /&gt;
struct Rectangle rect;&lt;br /&gt;
&lt;br /&gt;
for (ktr = 1; ktr &amp;lt; 6; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    rect.MinX = 50;&lt;br /&gt;
    rect.MaxX = 315;&lt;br /&gt;
    rect.MinY = (ktr * 10) - 5;&lt;br /&gt;
    rect.MaxY = (ktr * 10);&lt;br /&gt;
&lt;br /&gt;
    if (!ILayers-&amp;gt;OrRectRegion(RowRegion, &amp;amp;rect))&lt;br /&gt;
        clean_exit(RETURN_WARN);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and Regions ====&lt;br /&gt;
&lt;br /&gt;
There are four functions for changing a region through logical operations with a rectangle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL OrRectRegion   ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
VOID AndRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL XorRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL ClearRectRegion( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OrRectRegion() modifies a region structure by &#039;&#039;&#039;OR&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. When the application draws through this region (assuming that the region was originally empty), only the pixels within the clipping rectangle will be affected. If the region already has drawable areas, they will still exist, this rectangle is added to the drawable area.&lt;br /&gt;
&lt;br /&gt;
AndRectRegion() modifies the region structure by &#039;&#039;&#039;AND&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. Only those pixels that were already drawable and within the rectangle will remain drawable, any that are outside of it will be clipped in future.&lt;br /&gt;
&lt;br /&gt;
XorRectRegion() applies the rectangle to the region in an &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; mode. Within the given rectangle, any areas that were drawable become clipped, any areas that were clipped become drawable. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
ClearRectRegion() clears the rectangle from the region. Within the given rectangle, any areas that were drawable become clipped. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
==== Regions and Regions ====&lt;br /&gt;
&lt;br /&gt;
As with rectangles and regions, there are four layers library functions for combining regions with regions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL AndRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL OrRegionRegion ( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL XorRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
VOID ClearRegion    ( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AndRegionRegion() performs a logical &#039;&#039;&#039;AND&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever the regions drawable areas overlap. That is, where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
OrRegionRegion() performs a logical &#039;&#039;&#039;OR&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
XorRegionRegion() performs a logical &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region but not both. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region. But where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will &#039;&#039;not&#039;&#039; be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
ClearRegion() puts the region back to the same state it was in when the region was created with NewRegion(), that is, no areas are drawable.&lt;br /&gt;
&lt;br /&gt;
=== Regions Example ===&lt;br /&gt;
&lt;br /&gt;
The following example shows the use of the [[Layers Library]] call InstallClipRegion(), as well as simple use of the [[Graphics Library]] regions functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* clipping.c */&lt;br /&gt;
&lt;br /&gt;
/* Force use of new variable names to help prevent errors */&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuitionbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MY_WIN_WIDTH  (300)&lt;br /&gt;
#define MY_WIN_HEIGHT (100)&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct LayersIFace *ILayers;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** unclipWindow()&lt;br /&gt;
**&lt;br /&gt;
** Used to remove a clipping region installed by clipWindow() or&lt;br /&gt;
** clipWindowToBorders(), disposing of the installed region and&lt;br /&gt;
** reinstalling the region removed.&lt;br /&gt;
*/&lt;br /&gt;
void unclipWindow(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region     *old_region;&lt;br /&gt;
&lt;br /&gt;
/* Remove any old region by installing a NULL region,&lt;br /&gt;
** then dispose of the old region if one was installed.&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, NULL)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** clipWindow()&lt;br /&gt;
** Clip a window to a specified rectangle (given by upper left and&lt;br /&gt;
** lower right corner.)  the removed region is returned so that it&lt;br /&gt;
** may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindow(struct Window *win,&lt;br /&gt;
    LONG minX, LONG minY, LONG maxX, LONG maxY)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *new_region;&lt;br /&gt;
struct Rectangle  my_rectangle;&lt;br /&gt;
&lt;br /&gt;
/* set up the limits for the clip */&lt;br /&gt;
my_rectangle.MinX = minX;&lt;br /&gt;
my_rectangle.MinY = minY;&lt;br /&gt;
my_rectangle.MaxX = maxX;&lt;br /&gt;
my_rectangle.MaxY = maxY;&lt;br /&gt;
&lt;br /&gt;
/* get a new region and OR in the limits. */&lt;br /&gt;
if (NULL != (new_region = IGraphics-&amp;gt;NewRegion()))&lt;br /&gt;
    {&lt;br /&gt;
    if (FALSE == IGraphics-&amp;gt;OrRectRegion(new_region, &amp;amp;my_rectangle))&lt;br /&gt;
        {&lt;br /&gt;
        IGraphics-&amp;gt;DisposeRegion(new_region);&lt;br /&gt;
        new_region = NULL;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* Install the new region, and return any existing region.&lt;br /&gt;
** If the above allocation and region processing failed, then&lt;br /&gt;
** new_region will be NULL and no clip region will be installed.&lt;br /&gt;
*/&lt;br /&gt;
return(ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** clipWindowToBorders()&lt;br /&gt;
** clip a window to its borders.&lt;br /&gt;
** The removed region is returned so that it may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindowToBorders(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
return(clipWindow(win, win-&amp;gt;BorderLeft, win-&amp;gt;BorderTop,&lt;br /&gt;
    win-&amp;gt;Width - win-&amp;gt;BorderRight - 1, win-&amp;gt;Height - win-&amp;gt;BorderBottom - 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Wait for the user to select the close gadget.&lt;br /&gt;
*/&lt;br /&gt;
VOID wait_for_close(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
BOOL done;&lt;br /&gt;
&lt;br /&gt;
done = FALSE;&lt;br /&gt;
&lt;br /&gt;
while (FALSE == done)&lt;br /&gt;
    {&lt;br /&gt;
    /* we only have one signal bit, so we do not have to check which&lt;br /&gt;
    ** bit broke the Wait().&lt;br /&gt;
    */&lt;br /&gt;
    IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
&lt;br /&gt;
    while ( (FALSE == done) &amp;amp;&amp;amp;&lt;br /&gt;
            (NULL != (msg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort))))&lt;br /&gt;
        {&lt;br /&gt;
        /* use a switch statement if looking for multiple event types */&lt;br /&gt;
        if (msg-&amp;gt;Class == IDCMP_CLOSEWINDOW)&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Simple routine to blast all bits in a window with color three to show&lt;br /&gt;
** where the window is clipped.  After a delay, flush back to color zero&lt;br /&gt;
** and refresh the window borders.&lt;br /&gt;
*/&lt;br /&gt;
VOID draw_in_window(struct Window *win, UBYTE *message)&lt;br /&gt;
{&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;%s...&amp;quot;, message);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 3);&lt;br /&gt;
IDOS-&amp;gt;Delay(200);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 0);&lt;br /&gt;
IIntuition-&amp;gt;RefreshWindowFrame(win);&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;done\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Show drawing into an unclipped window, a window clipped to the&lt;br /&gt;
** borders and a window clipped to a random rectangle.  It is possible&lt;br /&gt;
** to clip more complex shapes by AND&#039;ing, OR&#039;ing and exclusive-OR&#039;ing&lt;br /&gt;
** regions and rectangles to build a user clip region.&lt;br /&gt;
**&lt;br /&gt;
** This example assumes that old regions are not going to be re-used,&lt;br /&gt;
** so it simply throws them away.&lt;br /&gt;
*/&lt;br /&gt;
VOID clip_test(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *old_region;&lt;br /&gt;
&lt;br /&gt;
draw_in_window(win,&amp;quot;Window with no clipping&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* if the application has never installed a user clip region,&lt;br /&gt;
** then old_region will be NULL here.  Otherwise, delete the&lt;br /&gt;
** old region (you could save it and re-install it later...)&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindowToBorders(win)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped to window borders&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
&lt;br /&gt;
/* here we know old_region will be NULL, as that is what we&lt;br /&gt;
** installed with unclipWindow()...&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindow(win,20,20,100,50)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped from (20,20) to (100,50)&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
&lt;br /&gt;
wait_for_close(win);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
** Open and close resources, call the test routine when ready.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct Window *win;&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
  IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
  ILayers = (struct LayersIFace*)IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (NULL != (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                             WA_Width,       MY_WIN_WIDTH,&lt;br /&gt;
                             WA_Height,      MY_WIN_HEIGHT,&lt;br /&gt;
                             WA_IDCMP,       IDCMP_CLOSEWINDOW,&lt;br /&gt;
                             WA_CloseGadget, TRUE,&lt;br /&gt;
                             WA_DragBar,     TRUE,&lt;br /&gt;
                             WA_Activate,    TRUE,&lt;br /&gt;
                             TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      clip_test(win);&lt;br /&gt;
&lt;br /&gt;
      IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)ILayers);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the graphics library functions related to regions. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Routine&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| LockLayerRom()&lt;br /&gt;
| Same as LockLayer(), from layers library.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerRom()&lt;br /&gt;
| Release LockLayerRom() lock.&lt;br /&gt;
|-&lt;br /&gt;
| AttemptLockLayerRom()&lt;br /&gt;
| Lock layer only if it is immediately available.&lt;br /&gt;
|-&lt;br /&gt;
| NewRegion()&lt;br /&gt;
| Create a new, empty region.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeRegion()&lt;br /&gt;
| Dispose of an existing region and its rectangles.&lt;br /&gt;
|-&lt;br /&gt;
| AndRectRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| OrRectRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| XorRectRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRectRegion()&lt;br /&gt;
| Clear a rectangular portion of a region.&lt;br /&gt;
|-&lt;br /&gt;
| AndRegionRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| OrRegionRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| XorRegionRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRegion()&lt;br /&gt;
| Clear a region.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9246</id>
		<title>Graphics Regions</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Regions&amp;diff=9246"/>
		<updated>2017-11-10T10:02:40Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Regions Example: code corrections by Doug Stastny&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Regions ==&lt;br /&gt;
&lt;br /&gt;
Regions allow the application to install clipping rectangles into layers. A clipping rectangle is a rectangular area into which the graphics routines will draw. All drawing that would fall outside of that rectangular area is clipped (not rendered).&lt;br /&gt;
&lt;br /&gt;
User clipping regions are linked lists of clipping rectangles created by an application program through the graphics library routines described below. By combining together various clipping rectangles, any arbitrary clipping shape can be created. Once the region is set up, you use the layers library call InstallClipRegion() to make the clipping region active in a layer.&lt;br /&gt;
&lt;br /&gt;
Regions are safe to use with layers created by Intuition (i.e., windows).&lt;br /&gt;
&lt;br /&gt;
The application can selectively update a custom-shaped part of a layer without disturbing any of the other layers that might be present.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Never Modify the DamageList of a Layer Directly|text=Use the routine InstallClipRegion() to add clipping to the layer. The regions installed by InstallClipRegion() are independent of the layer&#039;s DamageList and use of user clipping regions will not interfere with optimized window refreshing.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do Not Modify A Region After It Has Been Added|text=After a region has been added with InstallClipRegion(), the program may not modify it until the region has been removed with another call to InstallClipRegion().}}&lt;br /&gt;
&lt;br /&gt;
=== Creating and Deleting Regions ===&lt;br /&gt;
&lt;br /&gt;
You allocate a Region data structure with the NewRegion() call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *NewRegion( VOID );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The NewRegion() function allocates and initializes a Region structure that has no drawable areas defined in it. If the application draws through a new region, nothing will be drawn as the region is empty. The application must add rectangles to the region before any graphics will appear.&lt;br /&gt;
&lt;br /&gt;
Use DisposeRegion() to free the Region structure when you are done with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DisposeRegion( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DisposeRegion() returns all memory associated with a region to the system and deallocates all rectangles that have been linked to it.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Don&#039;t Forget to Free Your Rectangles|text=All of the functions that add rectangles to the region make copies of the rectangles. If the program allocates a rectangle, then adds it to a region, it still must deallocate the rectangle. The call to DisposeRegion() will not deallocate rectangles &#039;&#039;explicitly allocated&#039;&#039; by the application.}}&lt;br /&gt;
&lt;br /&gt;
=== Installing Regions ===&lt;br /&gt;
&lt;br /&gt;
Use the function InstallClipRegion() to install the region.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region *InstallClipRegion( struct Layer *layer, struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This installs a transparent clipping region to a layer. All subsequent graphics calls will be clipped to this region. The region must be removed with a second call to InstallClipRegion() before removing the layer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** Sample installation and removal of a clipping region&lt;br /&gt;
*/&lt;br /&gt;
register struct Region     *new_region ;&lt;br /&gt;
register struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* If the application owns the layer and has not installed a region,&lt;br /&gt;
** old_region will return NULL here.&lt;br /&gt;
*/&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
/* draw into the layer or window */&lt;br /&gt;
&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    {&lt;br /&gt;
    /* throw the used region away.  This region could be saved and&lt;br /&gt;
    ** used again later, if desired by the application.&lt;br /&gt;
    */&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Warning About InstallClipRegion()|text=The program must not call InstallClipRegion() inside of a Begin/EndRefresh() or Begin/EndUpdate() pair. The following code segment shows how to modify the user clipping region when using these calls. See the Autodoc for BeginRefresh() for more details.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Region     *new_region ;&lt;br /&gt;
struct Region     *old_region ;&lt;br /&gt;
&lt;br /&gt;
/* you have to have already setup the new_region and old_region */&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the damage list and the new_region */&lt;br /&gt;
/* into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);              /* keep the damage list */&lt;br /&gt;
&lt;br /&gt;
/* put back the old region */&lt;br /&gt;
new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, TRUE);               /* remove the damage list */&lt;br /&gt;
&lt;br /&gt;
old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region);&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;BeginRefresh(window);&lt;br /&gt;
/* draw through the new_region only into the layer or window */&lt;br /&gt;
IIntuition-&amp;gt;EndRefresh(window, FALSE);&lt;br /&gt;
&lt;br /&gt;
/* finally get rid of the new region, old_region still installed */&lt;br /&gt;
if (NULL != (new_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, old_region)))&lt;br /&gt;
    ILayers-&amp;gt;DisposeRegion(new_region) ;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Changing a Region ===&lt;br /&gt;
&lt;br /&gt;
Regions may be modified by performing logical operations with rectangles, or with other regions.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Reuse Your Rectangles|text=In all of the rectangle and region routines the clipping rectangle is copied into the region. This means that a single clipping rectangle (Rectangle structure) may be used many times by simply changing the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; values. The application need &#039;&#039;not&#039;&#039; create a new instance of the Rectangle structure for each rectangle added to a region.}}&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
extern struct Region *RowRegion;  /* created elsewhere */&lt;br /&gt;
&lt;br /&gt;
int16 ktr;&lt;br /&gt;
struct Rectangle rect;&lt;br /&gt;
&lt;br /&gt;
for (ktr = 1; ktr &amp;lt; 6; ktr++)&lt;br /&gt;
    {&lt;br /&gt;
    rect.MinX = 50;&lt;br /&gt;
    rect.MaxX = 315;&lt;br /&gt;
    rect.MinY = (ktr * 10) - 5;&lt;br /&gt;
    rect.MaxY = (ktr * 10);&lt;br /&gt;
&lt;br /&gt;
    if (!ILayers-&amp;gt;OrRectRegion(RowRegion, &amp;amp;rect))&lt;br /&gt;
        clean_exit(RETURN_WARN);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and Regions ====&lt;br /&gt;
&lt;br /&gt;
There are four functions for changing a region through logical operations with a rectangle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL OrRectRegion   ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
VOID AndRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL XorRectRegion  ( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
BOOL ClearRectRegion( struct Region *region, struct Rectangle *rectangle );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OrRectRegion() modifies a region structure by &#039;&#039;&#039;OR&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. When the application draws through this region (assuming that the region was originally empty), only the pixels within the clipping rectangle will be affected. If the region already has drawable areas, they will still exist, this rectangle is added to the drawable area.&lt;br /&gt;
&lt;br /&gt;
AndRectRegion() modifies the region structure by &#039;&#039;&#039;AND&#039;&#039;&#039;&#039;ing a clipping rectangle into the region. Only those pixels that were already drawable and within the rectangle will remain drawable, any that are outside of it will be clipped in future.&lt;br /&gt;
&lt;br /&gt;
XorRectRegion() applies the rectangle to the region in an &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; mode. Within the given rectangle, any areas that were drawable become clipped, any areas that were clipped become drawable. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
ClearRectRegion() clears the rectangle from the region. Within the given rectangle, any areas that were drawable become clipped. Areas outside of the rectangle are not affected.&lt;br /&gt;
&lt;br /&gt;
==== Regions and Regions ====&lt;br /&gt;
&lt;br /&gt;
As with rectangles and regions, there are four layers library functions for combining regions with regions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL AndRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL OrRegionRegion ( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
BOOL XorRegionRegion( struct Region *srcRegion, struct Region *destRegion );&lt;br /&gt;
VOID ClearRegion    ( struct Region *region );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AndRegionRegion() performs a logical &#039;&#039;&#039;AND&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever the regions drawable areas overlap. That is, where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
OrRegionRegion() performs a logical &#039;&#039;&#039;OR&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
XorRegionRegion() performs a logical &#039;&#039;&#039;exclusive-or&#039;&#039;&#039; operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region but not both. That is, where there are drawable areas in either region 1 &#039;&#039;&#039;OR&#039;&#039;&#039; region 2, there will be drawable areas left in the result region. But where there are drawable areas in both region 1 &#039;&#039;&#039;AND&#039;&#039;&#039; region 2, there will &#039;&#039;not&#039;&#039; be drawable areas left in the result region.&lt;br /&gt;
&lt;br /&gt;
ClearRegion() puts the region back to the same state it was in when the region was created with NewRegion(), that is, no areas are drawable.&lt;br /&gt;
&lt;br /&gt;
=== Regions Example ===&lt;br /&gt;
&lt;br /&gt;
The following example shows the use of the layers library call InstallClipRegion(), as well as simple use of the graphics library regions functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* clipping.c */&lt;br /&gt;
 &lt;br /&gt;
/* Force use of new variable names to help prevent errors */&lt;br /&gt;
#define INTUI_V36_NAMES_ONLY&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuitionbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layers.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
#define MY_WIN_WIDTH  (300)&lt;br /&gt;
#define MY_WIN_HEIGHT (100)&lt;br /&gt;
 &lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct LayersIFace *ILayers;&lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** unclipWindow()&lt;br /&gt;
**&lt;br /&gt;
** Used to remove a clipping region installed by clipWindow() or&lt;br /&gt;
** clipWindowToBorders(), disposing of the installed region and&lt;br /&gt;
** reinstalling the region removed.&lt;br /&gt;
*/&lt;br /&gt;
void unclipWindow(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region     *old_region;&lt;br /&gt;
 &lt;br /&gt;
/* Remove any old region by installing a NULL region,&lt;br /&gt;
** then dispose of the old region if one was installed.&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, NULL)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** clipWindow()&lt;br /&gt;
** Clip a window to a specified rectangle (given by upper left and&lt;br /&gt;
** lower right corner.)  the removed region is returned so that it&lt;br /&gt;
** may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindow(struct Window *win,&lt;br /&gt;
    LONG minX, LONG minY, LONG maxX, LONG maxY)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *new_region;&lt;br /&gt;
struct Rectangle  my_rectangle;&lt;br /&gt;
 &lt;br /&gt;
/* set up the limits for the clip */&lt;br /&gt;
my_rectangle.MinX = minX;&lt;br /&gt;
my_rectangle.MinY = minY;&lt;br /&gt;
my_rectangle.MaxX = maxX;&lt;br /&gt;
my_rectangle.MaxY = maxY;&lt;br /&gt;
 &lt;br /&gt;
/* get a new region and OR in the limits. */&lt;br /&gt;
if (NULL != (new_region = IGraphics-&amp;gt;NewRegion()))&lt;br /&gt;
    {&lt;br /&gt;
    if (FALSE == IGraphics-&amp;gt;OrRectRegion(new_region, &amp;amp;my_rectangle))&lt;br /&gt;
        {&lt;br /&gt;
        IGraphics-&amp;gt;DisposeRegion(new_region);&lt;br /&gt;
        new_region = NULL;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
/* Install the new region, and return any existing region.&lt;br /&gt;
** If the above allocation and region processing failed, then&lt;br /&gt;
** new_region will be NULL and no clip region will be installed.&lt;br /&gt;
*/&lt;br /&gt;
return(ILayers-&amp;gt;InstallClipRegion(win-&amp;gt;WLayer, new_region));&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** clipWindowToBorders()&lt;br /&gt;
** clip a window to its borders.&lt;br /&gt;
** The removed region is returned so that it may be re-installed later.&lt;br /&gt;
*/&lt;br /&gt;
struct Region *clipWindowToBorders(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
return(clipWindow(win, win-&amp;gt;BorderLeft, win-&amp;gt;BorderTop,&lt;br /&gt;
    win-&amp;gt;Width - win-&amp;gt;BorderRight - 1, win-&amp;gt;Height - win-&amp;gt;BorderBottom - 1));&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** Wait for the user to select the close gadget.&lt;br /&gt;
*/&lt;br /&gt;
VOID wait_for_close(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
SHORT done;&lt;br /&gt;
 &lt;br /&gt;
done = FALSE;&lt;br /&gt;
while (FALSE == done)&lt;br /&gt;
    {&lt;br /&gt;
    /* we only have one signal bit, so we do not have to check which&lt;br /&gt;
    ** bit broke the Wait().&lt;br /&gt;
    */&lt;br /&gt;
    IExec-&amp;gt;Wait(1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit);&lt;br /&gt;
 &lt;br /&gt;
    while ( (FALSE == done) &amp;amp;&amp;amp;&lt;br /&gt;
            (NULL != (msg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort))))&lt;br /&gt;
        {&lt;br /&gt;
        /* use a switch statement if looking for multiple event types */&lt;br /&gt;
        if (msg-&amp;gt;Class == IDCMP_CLOSEWINDOW)&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
 &lt;br /&gt;
        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** Simple routine to blast all bits in a window with color three to show&lt;br /&gt;
** where the window is clipped.  After a delay, flush back to color zero&lt;br /&gt;
** and refresh the window borders.&lt;br /&gt;
*/&lt;br /&gt;
VOID draw_in_window(struct Window *win, UBYTE *message)&lt;br /&gt;
{&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;%s...&amp;quot;, message);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 3);&lt;br /&gt;
IDOS-&amp;gt;Delay(200);&lt;br /&gt;
IGraphics-&amp;gt;SetRast(win-&amp;gt;RPort, 0);&lt;br /&gt;
IIntuition-&amp;gt;RefreshWindowFrame(win);&lt;br /&gt;
IDOS-&amp;gt;Printf(&amp;quot;done\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** Show drawing into an unclipped window, a window clipped to the&lt;br /&gt;
** borders and a window clipped to a random rectangle.  It is possible&lt;br /&gt;
** to clip more complex shapes by AND&#039;ing, OR&#039;ing and exclusive-OR&#039;ing&lt;br /&gt;
** regions and rectangles to build a user clip region.&lt;br /&gt;
**&lt;br /&gt;
** This example assumes that old regions are not going to be re-used,&lt;br /&gt;
** so it simply throws them away.&lt;br /&gt;
*/&lt;br /&gt;
VOID clip_test(struct Window *win)&lt;br /&gt;
{&lt;br /&gt;
struct Region    *old_region;&lt;br /&gt;
 &lt;br /&gt;
draw_in_window(win,&amp;quot;Window with no clipping&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
/* if the application has never installed a user clip region,&lt;br /&gt;
** then old_region will be NULL here.  Otherwise, delete the&lt;br /&gt;
** old region (you could save it and re-install it later...)&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindowToBorders(win)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped to window borders&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
 &lt;br /&gt;
/* here we know old_region will be NULL, as that is what we&lt;br /&gt;
** installed with unclipWindow()...&lt;br /&gt;
*/&lt;br /&gt;
if (NULL != (old_region = clipWindow(win,20,20,100,50)))&lt;br /&gt;
    IGraphics-&amp;gt;DisposeRegion(old_region);&lt;br /&gt;
draw_in_window(win,&amp;quot;Window clipped from (20,20) to (100,50)&amp;quot;);&lt;br /&gt;
unclipWindow(win);&lt;br /&gt;
 &lt;br /&gt;
wait_for_close(win);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
/*&lt;br /&gt;
** Open and close resources, call the test routine when ready.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct Window *win;&lt;br /&gt;
 &lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
  struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
  IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
  struct Library *LayersBase = IExec-&amp;gt;OpenLibrary(&amp;quot;layers.library&amp;quot;, 50);&lt;br /&gt;
  ILayers = (struct LayersIFace*)IExec-&amp;gt;GetInterface(LayersBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL &amp;amp;&amp;amp; ILayers != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (NULL != (win =IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                             WA_Width,       MY_WIN_WIDTH,&lt;br /&gt;
                             WA_Height,      MY_WIN_HEIGHT,&lt;br /&gt;
                             WA_IDCMP,       IDCMP_CLOSEWINDOW,&lt;br /&gt;
                             WA_CloseGadget, TRUE,&lt;br /&gt;
                             WA_DragBar,     TRUE,&lt;br /&gt;
                             WA_Activate,    TRUE,&lt;br /&gt;
                             TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      clip_test(win);&lt;br /&gt;
 &lt;br /&gt;
      IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)ILayers);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(LayersBase);&lt;br /&gt;
 &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(GfxBase);&lt;br /&gt;
 &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
 &lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the graphics library functions related to regions. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Routine&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| LockLayerRom()&lt;br /&gt;
| Same as LockLayer(), from layers library.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockLayerRom()&lt;br /&gt;
| Release LockLayerRom() lock.&lt;br /&gt;
|-&lt;br /&gt;
| AttemptLockLayerRom()&lt;br /&gt;
| Lock layer only if it is immediately available.&lt;br /&gt;
|-&lt;br /&gt;
| NewRegion()&lt;br /&gt;
| Create a new, empty region.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeRegion()&lt;br /&gt;
| Dispose of an existing region and its rectangles.&lt;br /&gt;
|-&lt;br /&gt;
| AndRectRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| OrRectRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| XorRectRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; a rectangle into a region.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRectRegion()&lt;br /&gt;
| Clear a rectangular portion of a region.&lt;br /&gt;
|-&lt;br /&gt;
| AndRegionRegion()&lt;br /&gt;
| &#039;&#039;And&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| OrRegionRegion()&lt;br /&gt;
| &#039;&#039;Or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| XorRegionRegion()&lt;br /&gt;
| &#039;&#039;Exclusive-or&#039;&#039; two regions together.&lt;br /&gt;
|-&lt;br /&gt;
| ClearRegion()&lt;br /&gt;
| Clear a region.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=9245</id>
		<title>Application Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=9245"/>
		<updated>2017-10-25T13:52:28Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Message Handling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of &#039;&#039;application&#039;&#039; 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 &#039;&#039;background services&#039;&#039;), and genuine full-blown &#039;&#039;applications&#039;&#039; with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can turn into a “watchdog” and get notified when other applications register.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can easily create and manage lists of recently-used documents.&lt;br /&gt;
&lt;br /&gt;
* It can register as a &#039;&#039;unique application&#039;&#039;, preventing other instances of itself from running.&lt;br /&gt;
&lt;br /&gt;
* It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.&lt;br /&gt;
&lt;br /&gt;
* 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”.&lt;br /&gt;
&lt;br /&gt;
=== Frequently Asked Questions ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Should my programs register with the Application Library?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: After registering with the library, will my application become controlled by the OS or by other applications?&#039;&#039;&lt;br /&gt;
|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&#039;t want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming [[#Control Messages|control messages]].&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Doesn&#039;t the Application Library duplicate functionality already present in commodities?&#039;&#039;&lt;br /&gt;
|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]]&#039;s event stream. The Application Library&#039;s field and scope of operation is completely different (and much wider).&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Why is this a library? Wouldn&#039;t it be wiser to implement it as a class, like MUI does it?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Can a program be registered both as an application and as a commodity?&#039;&#039;&lt;br /&gt;
|A: Yes, that&#039;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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system?&#039;&#039;&lt;br /&gt;
|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&#039;t put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Minimum Application Library Support ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* Provide a short [[#Description|description]] for a better identification of the application.&lt;br /&gt;
* Tell the OS whether your application supports iconification, that is, hiding/showing its GUI. If iconification is supported:&lt;br /&gt;
** set the REGAPP_HasIconifyFeature registration tag to TRUE;&lt;br /&gt;
** make the application respond to the [[#Control Messages|APPLIBMT_Hide and APPLIBMT_Unhide]] control messages;&lt;br /&gt;
** update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies.&lt;br /&gt;
* Allow the application to be shut down externally in a safe and graceful manner in reaction to the [[#Control Messages|APPLIBMT_Quit]] message.&lt;br /&gt;
&lt;br /&gt;
A failure to implement this suggested minimum will mean that the application manager will be unable to control your application properly and consistently. This may cause confusion on the part of the users and degrade their AmigaOS experience.&lt;br /&gt;
&lt;br /&gt;
== Library Opening Chores ==&lt;br /&gt;
&lt;br /&gt;
{{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.}}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Library *ApplicationBase = NULL;&lt;br /&gt;
struct ApplicationIFace *IApplication = NULL;&lt;br /&gt;
struct PrefsObjectsIFace *IPrefsObjects = NULL;&lt;br /&gt;
&lt;br /&gt;
if ( (ApplicationBase = IExec-&amp;gt;OpenLibrary(&amp;quot;application.library&amp;quot;, 52)) )&lt;br /&gt;
{&lt;br /&gt;
   IApplication  = (struct ApplicationIFace *)  IExec-&amp;gt;GetInterface(ApplicationBase, &lt;br /&gt;
                                                                    &amp;quot;application&amp;quot;, 2, NULL);&lt;br /&gt;
   IPrefsObjects = (struct PrefsObjectsIFace *) IExec-&amp;gt;GetInterface(ApplicationBase,&lt;br /&gt;
                                                                    &amp;quot;prefsobjects&amp;quot;, 2, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if ( !ApplicationBase || !IApplication || !IPrefsObjects )&lt;br /&gt;
{&lt;br /&gt;
   /* handle library opening error */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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. &#039;&#039;&#039;Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2.&#039;&#039;&#039; This is due to certain changes in the Application Library API.&lt;br /&gt;
&lt;br /&gt;
When your application has run its course, don’t forget to clean up and close both the library and its interface(s):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IPrefsObjects);&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IApplication);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(ApplicationBase);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registering the Application ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
*hint* Avoid using the  _ underscore character in the registered name. These may cause &amp;quot;charsetconvert&amp;quot; errors when the system boots up later.&lt;br /&gt;
&lt;br /&gt;
=== Application Identifiers ===&lt;br /&gt;
&lt;br /&gt;
A successfully registered application receives a numeric identifier, which in this documentation will be referred to as &#039;&#039;appID&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* the application name;&lt;br /&gt;
* an instance count (if more than one instance of the same application is started);&lt;br /&gt;
* a URL identifier (for example, the domain name of the application’s homepage – optional).&lt;br /&gt;
&lt;br /&gt;
The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.&lt;br /&gt;
&lt;br /&gt;
=== Registration Functions ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (!appID)&lt;br /&gt;
{&lt;br /&gt;
   /* report registration error and quit */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    do whatever your program does&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
IApplication-&amp;gt;UnregisterApplication(appID, NULL);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.)&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available.&lt;br /&gt;
&lt;br /&gt;
== Application Attributes ==&lt;br /&gt;
&lt;br /&gt;
An application is described by a set of &#039;&#039;attributes&#039;&#039;. There are attributes that describe the application&#039;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 &amp;quot;static&amp;quot; application features in one place.&lt;br /&gt;
&lt;br /&gt;
=== Description ===&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The application description cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Uniqueness ===&lt;br /&gt;
A program can register as a &#039;&#039;unique application&#039;&#039;, 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.&lt;br /&gt;
&lt;br /&gt;
The following function call will register Audio Monster as a unique application:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_UniqueApplication, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Quite logically, uniqueness is an attribute that cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Feature Set / Control Scope ===&lt;br /&gt;
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):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasIconifyFeature&lt;br /&gt;
|The application supports iconification and uniconification.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasPrefsWindow&lt;br /&gt;
|The application has a dedicated Preferences (Settings) window.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanCreateNewDocs&lt;br /&gt;
|The application can create new documents (projects).&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanPrintDocs&lt;br /&gt;
|The application can print documents.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_PrintDoc message.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These four attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Receiving Notifications ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_AppNotifications&lt;br /&gt;
|Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_BlankerNotifications&lt;br /&gt;
|Receive notifications concerning blanker activity.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream.&lt;br /&gt;
&lt;br /&gt;
These two attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Changing Application Attributes ===&lt;br /&gt;
&lt;br /&gt;
Certain attributes describe &amp;quot;dynamic&amp;quot; application properties and, as such, can be set or changed after registration. Some of these are &#039;&#039;state attributes&#039;&#039; 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.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AllowsBlanker&lt;br /&gt;
|Same as REGAPP_AllowsBlanker above.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppOpenedDocument&lt;br /&gt;
|Adds a new entry to the application&#039;s Last Used Documents list.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppNotifications&amp;lt;br /&amp;gt;APPATTR_BlankerNotifications&lt;br /&gt;
|Same as the two corresponding [[#Receiving Notifications|registration tags]] above.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_CanCreateNewDocs&amp;lt;br /&amp;gt;APPATTR_CanPrintDocs&amp;lt;br /&amp;gt;APPATTR_HasIconifyFeature&amp;lt;br /&amp;gt;APPATTR_HasPrefsWindow&lt;br /&gt;
|Same as the four corresponding [[#Feature Set / Control Scope|registration tags]] above.&lt;br /&gt;
|Prefer setting these properties at registration time.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_ClearLastUsedDocs&lt;br /&gt;
|Clears the application&#039;s list of last used documents.&lt;br /&gt;
|Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.)&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_FlushPrefs&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_Hidden&lt;br /&gt;
|A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not.&lt;br /&gt;
|Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_IconType&lt;br /&gt;
|Changes the application icon type.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_MainPrefsDict&lt;br /&gt;
|Allows changing the application&#039;s Prefs dictionary.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_NeedsGameMode&lt;br /&gt;
|A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode.&lt;br /&gt;
|By the &amp;quot;game mode&amp;quot; we understand a mode in which an application doesn&#039;t want to be &amp;quot;disturbed&amp;quot; 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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_SavePrefs&lt;br /&gt;
|Same as REGAPP_SavePrefs above.&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;SetApplicationAttrs(appID,&lt;br /&gt;
            APPATTR_Hidden, TRUE,&lt;br /&gt;
            APPATTR_AllowsBlanker, TRUE,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finding Applications ==&lt;br /&gt;
&lt;br /&gt;
The Application Library maintains a public list of all registered applications. Certain special-purpose programs – &#039;&#039;application managers&#039;&#039; – 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?&lt;br /&gt;
&lt;br /&gt;
Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library&#039;s messaging system. To do this you need to know at least one of the following:&lt;br /&gt;
&lt;br /&gt;
* the application name, ie. the one under which it was registered via RegisterApplication();&lt;br /&gt;
* 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;&lt;br /&gt;
* the pathname pointing to the program file on disk, e.g. “Work:Utils/AudioMonster”.&lt;br /&gt;
&lt;br /&gt;
Based on this information, the respective piece of code that will find our application in the system might look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
/* if you only know the application name */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the application name identifier */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you specifically want to talk to the second running instance */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster_1.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the pathname to the program file */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_FileName, &amp;quot;Work:Utils/AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have obtained the appID you can start communicating with the respective application.&lt;br /&gt;
&lt;br /&gt;
== Messaging ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
{{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 &#039;&#039;AppMessages&#039;&#039;. 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).&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Data Structures ===&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
The following three structures are defined for carrying Application Library message data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg&lt;br /&gt;
{&lt;br /&gt;
	struct Message msg;&lt;br /&gt;
	uint32 senderAppID;  // the appID of the sender application&lt;br /&gt;
	uint32 type;         // identifies the type of message&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationOpenPrintDocMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR fileName;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationCustomMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR customMsg;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, structure &#039;&#039;ApplicationMsg&#039;&#039; contains a standard &#039;&#039;[[Exec_Messages_and_Ports#Messages|struct Message]]&#039;&#039;, the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: &#039;&#039;ApplicationOpenPrintDocMsg&#039;&#039; is used by certain [[#Control Messages|control messages]] that require a pointer to a filename; the &#039;&#039;ApplicationCustomMsg&#039;&#039; structure is used for [[#Custom Messages|custom messages]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Control Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Message name&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Quit&lt;br /&gt;
|The application is asked to shut down itself and quit.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ForceQuit&lt;br /&gt;
|Same as before but this time the application shall quit immediately, without asking for saving documents etc.&lt;br /&gt;
|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&#039;t implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Hide&lt;br /&gt;
|The application shall hide its interface and iconify on the Workbench screen.&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Unhide&lt;br /&gt;
|The application shall come back from the iconified (hidden) state.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ToFront&lt;br /&gt;
|The application window shall come to front.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenPrefs&lt;br /&gt;
|The application shall open its preferences window.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ReloadPrefs&lt;br /&gt;
|The application shall reload its preferences.&lt;br /&gt;
|Whatever this command is useful for.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_NewBlankDoc&lt;br /&gt;
|The application shall open a new, blank document or project.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenDoc&lt;br /&gt;
|The application shall try to open a specific document or project.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_PrintDoc&lt;br /&gt;
|The application shall try to print a specific document.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Custom Messages ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The pointer to the message text string is contained in a special [[#Data Structures|data structure]], &#039;&#039;struct ApplicationCustomMsg&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Special Messages ===&lt;br /&gt;
&lt;br /&gt;
There are also some special messages that an application can receive from the library or another running application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Message name !! Description !! Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Message Handling ===&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;notification port&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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&#039;ll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void event_loop(uint32 appID)&lt;br /&gt;
{&lt;br /&gt;
 struct MsgPort              *notificationPort = NULL;&lt;br /&gt;
 struct ApplicationMsg       *appLibMsg = NULL;&lt;br /&gt;
 struct ApplicationCustomMsg *customMsg = NULL;&lt;br /&gt;
 uint32 appLibSignal = 0, sigGot = 0;&lt;br /&gt;
 BOOL   done = FALSE;&lt;br /&gt;
&lt;br /&gt;
 /* Obtain pointer to Application Library&#039;s notification port and set the signal bit. */&lt;br /&gt;
 IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;amp;notificationPort, TAG_END);&lt;br /&gt;
 appLibSignal = (1L &amp;lt;&amp;lt; notificationPort-&amp;gt;mp_SigBit);&lt;br /&gt;
 &lt;br /&gt;
 /* Go into the event loop. */&lt;br /&gt;
 while (!done)&lt;br /&gt;
  {&lt;br /&gt;
   /* Wait for a signal to arrive. */&lt;br /&gt;
   sigGot = IExec-&amp;gt;Wait(appLibSignal);&lt;br /&gt;
&lt;br /&gt;
   /* Process all Application Library messages. */&lt;br /&gt;
   if ( sigGot &amp;amp; appLibSignal )&lt;br /&gt;
    {&lt;br /&gt;
     /* Obtain pointer to the message. */&lt;br /&gt;
     while ( (appLibMsg = (struct ApplicationMsg *) IExec-&amp;gt;GetMsg(notificationPort)) )&lt;br /&gt;
      {&lt;br /&gt;
       /* Identify the type of message. */&lt;br /&gt;
       switch (appLibMsg-&amp;gt;type)&lt;br /&gt;
        {&lt;br /&gt;
         case APPLIBMT_Quit:&lt;br /&gt;
         case APPLIBMT_ForceQuit:&lt;br /&gt;
           done = TRUE;&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_ToFront:&lt;br /&gt;
           /* Make the program window come to front. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Hide:&lt;br /&gt;
           /* Iconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Unhide:&lt;br /&gt;
           /* Uniconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*  Process the custom message as you like.&lt;br /&gt;
             Here we just use printf() to output the message text. */&lt;br /&gt;
         case APPLIBMT_CustomMsg:&lt;br /&gt;
           customMsg = (struct ApplicationCustomMsg *) appLibMsg;&lt;br /&gt;
           printf(&amp;quot;The message text is: %s\n&amp;quot;, customMsg-&amp;gt; customMsg);&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*&lt;br /&gt;
             Process any other Application Library messages.&lt;br /&gt;
          */&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
       /* Return the processed message to the sender so that it can be freed. */&lt;br /&gt;
       IExec-&amp;gt;ReplyMsg( (struct Message *) appLibMsg);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Also remember that the notifications are addressed to an abstract &#039;&#039;application&#039;&#039; – this makes them rather different from IDCMP messages, which are always sent to a particular &#039;&#039;window&#039;&#039; (Intuition doesn&#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Don&#039;t do this!!! */&lt;br /&gt;
case APPLIBMT_ToFront:&lt;br /&gt;
  IIntuition-&amp;gt;ScreenToFront(win-&amp;gt;WScreen);&lt;br /&gt;
  IIntuition-&amp;gt;WindowToFront(win);&lt;br /&gt;
  IIntuition-&amp;gt;ActivateWindow(win);&lt;br /&gt;
break;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
=== Sending Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
# Prepare the respective [[#Data Structures|data structure]]: specify the type of message and supply the sender&#039;s [[#Application Identifiers|identifier]] (appID).&lt;br /&gt;
# [[#Finding Applications|Find the receiver]] application.&lt;br /&gt;
# Send a message to it via the SendApplicationMsg() function.&lt;br /&gt;
&lt;br /&gt;
For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg appMsg;    // message data structure&lt;br /&gt;
uint32 audioMonsterID;           // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Step 1: Prepare the message data structure. */&lt;br /&gt;
appMsg.senderAppID = appID;      // identifier of the sender&lt;br /&gt;
appMsg.type = APPLIBMT_Quit;     // type of message&lt;br /&gt;
&lt;br /&gt;
/* Step 2: Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Step 3: Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 &amp;amp;appMsg,&lt;br /&gt;
                 APPLIBMT_Quit);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0.&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;struct ApplicationMsg&#039;&#039;. 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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationCustomMsg customMsg;      // custom message data structure&lt;br /&gt;
uint32 audioMonsterID;                      // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Prepare the message data structure. */&lt;br /&gt;
customMsg.almsg.senderAppID = appID;        // identifier of the sender&lt;br /&gt;
customMsg.almsg.type = APPLIBMT_CustomMsg;  // type of message&lt;br /&gt;
customMsg.customMsg = &amp;quot;Start playback&amp;quot;;     // message text&lt;br /&gt;
&lt;br /&gt;
/* Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 (struct ApplicationMsg *) &amp;amp;customMsg,&lt;br /&gt;
                 APPLIBMT_CustomMsg);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pop-up Notifications (Ringhio Messages) ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;Ringhio messages&#039;&#039; 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).&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;All right, ma!&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&#039;t mean “Yes” or “No”.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;URL:http://www.supercoders.com&amp;quot;,&lt;br /&gt;
               APPNOTIFY_CloseOnDC, TRUE,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pop-up Message Style Guide ===&lt;br /&gt;
&lt;br /&gt;
* Keep the message text short. Give the user chance to read the entire message before it disappears.&lt;br /&gt;
* 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).&lt;br /&gt;
* Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| FindApplication()&lt;br /&gt;
| Searches for a previously registered application.&lt;br /&gt;
|-&lt;br /&gt;
| FreeApplicationList()&lt;br /&gt;
| Frees the list of applications generated by GetApplicationList().&lt;br /&gt;
|-&lt;br /&gt;
| GetAppLibAttrs()&lt;br /&gt;
| Obtains global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationAttrs()&lt;br /&gt;
| Obtains attributes of a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationList()&lt;br /&gt;
| Obtains the list of all currently registered applications.&lt;br /&gt;
|-&lt;br /&gt;
| LockApplicationIcon()&lt;br /&gt;
| Attempts to lock an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| Notify()&lt;br /&gt;
| Displays a pop-up message box.&lt;br /&gt;
|-&lt;br /&gt;
| RegisterApplication()&lt;br /&gt;
| Registers an application.&lt;br /&gt;
|-&lt;br /&gt;
| SendApplicationMsg()&lt;br /&gt;
| Sends a message to a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| SetAppLibAttrs()&lt;br /&gt;
| Sets or changes global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| SetApplicationAttrs()&lt;br /&gt;
| Sets or changes application attributes.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockApplicationIcon()&lt;br /&gt;
| Unlocks an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| UnregisterApplication()&lt;br /&gt;
| Unregisters an application.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9241</id>
		<title>IFFParse Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=IFFParse_Library&amp;diff=9241"/>
		<updated>2017-10-15T19:14:44Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed a typo, added two missing library interface pointers.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== IFFParse Library ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;iffparse.library&#039;&#039; was created to help simplify the job of parsing IFF files. Unlike other IFF libraries, iffparse.library is not form-specific. This means that the job of interpreting the structure and contents of the IFF file is up to you. Previous IFF file parsers were either simple but not general, or general but not simple. IFFParse tries to be both simple &#039;&#039;and&#039;&#039; general.&lt;br /&gt;
&lt;br /&gt;
== The Structure of IFF Files ==&lt;br /&gt;
&lt;br /&gt;
Many people have a misconception that IFF means image files. This is not the case. IFF (short for Interchange File Format) is a method of portably storing structured information in machine-readable form. The actual information can be anything, but the manner in which it is stored is very specifically detailed. This specification is the IFF standard.&lt;br /&gt;
&lt;br /&gt;
The IFF standard was originally designed in 1985 by Electronic Arts in conjunction with a committee of developers. The full standard along with file descriptions and sample code is available at [[IFF_Standard|IFF Standard]].&lt;br /&gt;
&lt;br /&gt;
The goal of the IFF standard is to allow customers to move their own data between independently developed software products. The types of data objects to exchange are open-ended and currently include plain and formatted text, raster and structured graphics, fonts, music, sound effects, musical instrument descriptions, and animation. IFF addresses these needs by defining a standard for self-identifying file structures and rules for accessing these files.&lt;br /&gt;
&lt;br /&gt;
=== Chunks: The Building Blocks of IFF ===&lt;br /&gt;
&lt;br /&gt;
IFF files contain various types and amounts of data grouped in data &#039;&#039;chunks&#039;&#039;, each starting with a four-letter ASCII identifier (the chunk ID) followed by a 32-bit length count (the chunk size). The identifier and length count make it possible for IFF readers to skip over chunks that they don’t understand or don’t care about, and extract only the information they need. It may be helpful to think of these chunks as the building blocks of an IFF file.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-1.png|frame|center|The Chunk - The Building Block of IFF]]&lt;br /&gt;
&lt;br /&gt;
The ‘CKID’ (chunk ID) characters of a real chunk will be a four letter identifier such as ‘BODY’ or ‘CMAP’, specifying the type and format of data stored in the chunk. Most chunks contain a simple defined grouping of byte, word, and long variables similar to the contents of a C structure. Others such as sound sample and bitmap image body chunks, contain a stream of compressed data.&lt;br /&gt;
&lt;br /&gt;
Chunk writing is fairly straightforward with the one caveat that all chunks must be word-aligned. Therefore an odd-length chunk must be followed by a pad byte of zero (which is not counted in the size of the chunk). When chunks are nested, the enclosing chunk must state the total size of its composite contents (including any pad bytes).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About Chunk Length|text=Every IFF data chunk begins with a 4-character identifier field followed by a 32-bit size field (these 8 bytes are sometimes referred to as the chunk header). The size field is a count of the rest of the data bytes in the chunk, &#039;&#039;not&#039;&#039; including any pad byte. Hence, the total space occupied by a chunk is given by its size field (rounded to the nearest even number) + 8 bytes for the chunk header}}&lt;br /&gt;
&lt;br /&gt;
=== Composite Data Types ===&lt;br /&gt;
&lt;br /&gt;
Standard IFF files generally contain a variable number of chunks within one of the standard IFF composite data types (which may be thought of as chunks which contain other chunks). The IFF specification defines three basic composite data types, ‘FORM’, ‘CAT ’, and ‘LIST’. A special composite data type, the ‘PROP’, is found only inside a LIST.&lt;br /&gt;
&lt;br /&gt;
Usage of Composite IFF Types&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| FORM || A grouping of chunks which describe one set of data&lt;br /&gt;
|-&lt;br /&gt;
| LIST || A grouping of the same type of FORMs with shared properties in a PROP&lt;br /&gt;
|-&lt;br /&gt;
| PROP || May appear in a LIST to define chunks shared by several FORMs&lt;br /&gt;
|-&lt;br /&gt;
| CAT || A concatenation of related FORMs or LISTs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The special IFF composite data types, like simple chunks, start with a 4-character identifier (such as ‘FORM’), followed by a 32-bit length. But their data begins with a second 4-character identifier that tells you what type of data is in the composite. For example, a FORM ILBM contains chunks describing a bitmap image, and a FORM FTXT contains chunks describing formatted text.&lt;br /&gt;
&lt;br /&gt;
It may help to think of each composite data type as a box containing chunks, the IFF building blocks. The following figure shows a diagram of a typical composite.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-2.png|frame|center|The FORM – The Most Common IFF File Type]]&lt;br /&gt;
&lt;br /&gt;
The example FORM in the previous figure is outlined below showing the size of chunks within the FORM. Notice that the size of a composite &#039;&#039;includes&#039;&#039; its second 4-character identifier (shown below as ‘NAME’).&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 22     (then 22 bytes of data, so chunk header + chunk size is 30)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
{{Note|text=In the example above, indentation represents the nesting of the chunks within the FORM. Real FORMs and chunks would have different four-character identifiers rather than ‘NAME’ and ‘CKID’.}}&lt;br /&gt;
&lt;br /&gt;
Any odd-length chunks must have a pad byte of zero at the end of the chunk. As shown below, this pad byte is not counted in the size of the chunk but &#039;&#039;is&#039;&#039; counted in the size of any composite types (such as FORM) that contain an odd-length chunk. &#039;&#039;&#039;Warning:&#039;&#039;&#039; some IFF readers and writers do not deal with this properly.&lt;br /&gt;
&lt;br /&gt;
 . FORM 72 NAME  (72 is the size of the FORM starting with the “N” of NAME)&lt;br /&gt;
 . . CKID 21     (then 21 bytes of data, so chunk header + chunk size is 29)&lt;br /&gt;
 . . 0           (pad byte of zero, size 1)&lt;br /&gt;
 . . CKID 10     (then 10 bytes of data, so chunk header + chunk size is 18)&lt;br /&gt;
 . . CKID 12     (then 12 bytes of data, so chunk header + chunk size is 20)&lt;br /&gt;
&lt;br /&gt;
Most IFF files are of the composite type FORM. Generally, a FORM is a simple grouping of chunks that provide information about some data, and the data itself. Although some standard chunks have common usage within different FORMS, the identity and format of most chunks within a FORM are relative to the definition and specification of the FORM. For example, a CRNG chunk in a FORM ILBM may have a totally different format and contents than a chunk named CRNG in a different type of FORM.&lt;br /&gt;
&lt;br /&gt;
One of the most popular IFF FORMs that has been defined is the ILBM standard. This is how most Amiga bitmap imagery is stored. Since this is the most common IFF file, it is used frequently as an example.&lt;br /&gt;
&lt;br /&gt;
Here is the output of a program called “Sift.c” that displays a text description of the contents of an IFF file ([[#IFF Scanner Example|Sift.c]] is listed at the end of this article). The file shown below is a fairly simple ILBM.&lt;br /&gt;
&lt;br /&gt;
 . FORM 11068 ILBM&lt;br /&gt;
 . . BMHD 20&lt;br /&gt;
 . . CAMG 4&lt;br /&gt;
 . . CMAP 12&lt;br /&gt;
 . . BODY 10995&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Computing the Size of a FORM|text=The size of the FORM (11,068 bytes) is equal to the sum of the sizes stated in each chunk contained within the FORM, plus 8 bytes for the overhead of each chunk header (4 bytes for the 4-character chunk ID, and 4 bytes for the 32-bit chunk size), plus 4 bytes for the FORM’s own 4-character ID (‘ILBM’), plus 1 byte more for each pad byte that follow any odd-length chunks.}}&lt;br /&gt;
&lt;br /&gt;
== Parsing an IFF File ==&lt;br /&gt;
&lt;br /&gt;
Chunk reading requires a parser to scan each chunk and dispatch the proper access/conversion procedure. For a simple IFF file, such parsing may be relatively easy, consisting mainly reading in the data of desired chunks, and seeking over unwanted chunks (and the pad byte after odd-length chunks). Interpreting nested chunks is more complex, and requires a method for keeping track of the current context, i.e., the data which is still relevant at any particular depth into the nest. The original IFF specifications compare an IFF file to a C program. Such a metaphor can be useful in understanding the scope of of the chunks in an IFF file.&lt;br /&gt;
&lt;br /&gt;
The IFFParse library addresses general IFF parsing requirements by providing a run-time library which can extract the chunks you want from an IFF file, with the ability to pass control to you when it reaches a chunk that requires special processing such as decompression. IFFParse also understands complex nested IFF formats and will keep track of the context for you.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functions and Structures of IFFParse Library ===&lt;br /&gt;
&lt;br /&gt;
The structures and flags of the IFFParse library are defined in the include files &amp;amp;lt;libraries/iffparse.h&amp;amp;gt; and &amp;amp;lt;libraries/iffparse.i&amp;amp;gt;. IFF files are manipulated through a structure called an IFFHandle. Only some of the fields in the IFFHandle are publicly documented. The rest are managed internally by IFFParse. This handle is passed to all IFFParse functions, and contains the current parse state and position in the file. An IFFHandle is obtained by calling AllocIFF(), and freed through FreeIFF(). This is the only legal way to obtain and dispose of an IFFHandle.&lt;br /&gt;
&lt;br /&gt;
The public portion of if IFFHandle is defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Structure associated with an active IFF stream.&lt;br /&gt;
 * &amp;quot;iff_Stream&amp;quot; is a value used by the client&#039;s read/write/seek functions -&lt;br /&gt;
 * it will not be accessed by the library itself and can have any value&lt;br /&gt;
 * (could even be a pointer or a BPTR).&lt;br /&gt;
 */&lt;br /&gt;
struct IFFHandle {&lt;br /&gt;
        ULONG   iff_Stream;&lt;br /&gt;
        ULONG   iff_Flags;&lt;br /&gt;
        LONG    iff_Depth;      /*  Depth of context stack.  */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stream Management ==&lt;br /&gt;
&lt;br /&gt;
A stream is a linear array of bytes that may be accessed sequentially or randomly. DOS files are streams. IFFParse uses Hook structures (defined in &amp;amp;lt;utility/hooks.h&amp;amp;gt;) to implement a general stream management facility. This allows the IFFParse library to read, write, and seek any type of file handle or device by using an application-provided hook function to open, read, write, seek and close the stream.&lt;br /&gt;
&lt;br /&gt;
Built on top of this facility, IFFParse has two internal stream managers: one for unbuffered DOS files (AmigaDOS filehandles), and one for the Clipboard.&lt;br /&gt;
&lt;br /&gt;
=== Initialization ===&lt;br /&gt;
&lt;br /&gt;
As shown above, the IFFHandle structure contains the public field iff_Stream. This is a longword that must be initialized to contain something meaningful to the stream manager. IFFParse never looks at this field itself (at least not directly). Your iff_Stream may be an AmigaDOS filehandle, or an IFFParse ClipboardHandle, or a custom stream of any type if you provide your own custom stream handler (see the section on [[#Custom Stream Handlers|Custom Stream Handlers]]). You must initialize your IFFHandle structure’s iff_Stream to your stream, and then initialize the IFFHandle’s flags and stream hook.&lt;br /&gt;
&lt;br /&gt;
Three sample initializations are shown here. In the case of the internal DOS stream manager, iff_Stream is an AmigaDOS filehandle as returned by Open():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IDOS-&amp;gt;Open (&amp;quot;filename&amp;quot;, MODE_OLDFILE);&lt;br /&gt;
if(iff-&amp;gt;iff_Stream) IIFFParse-&amp;gt;InitIFFasDOS (iff);  /* use internal DOS stream manager */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case of the internal Clipboard stream manager, iff_Stream is a pointer to an IFFParse ClipboardHandle structure (the OpenClipboard() call used here is a function of iffparse.library, not the clipboard.device):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard (PRIMARY_CLIP);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFFasClip (iff); /* use internal Clipboard stream manager  */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using a custom handle such as an fopen() file handle, or a device other than the clipboard, you must provide your own flags and stream handler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) OpenMyCustomStream(&amp;quot;foo&amp;quot;);&lt;br /&gt;
IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IFFParse “knows” the seek capabilities of DOS and ClipboardHandle streams, so InitIFFasDOS() and InitIFFasClip() set the flags for you.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You May Change the Seek Bits in iff_Flags|text=IFFParse sets IFFF_FSEEK | IFFF_RSEEK for DOS files. This is not always true (e.g., pipes). If you know that a DOS file has different seek characteristics, your application may correct the seek bits in iff_Flags after calling InitIFFasDOS().}}&lt;br /&gt;
&lt;br /&gt;
When using InitIFF() to provide a custom handler, you must also provide flags to tell IFFParse the capabilities of your stream. The flags are:&lt;br /&gt;
&lt;br /&gt;
; IFFF_FSEEK&lt;br /&gt;
: This stream has forward-seek capability only.&lt;br /&gt;
&lt;br /&gt;
; IFFF_RSEEK&lt;br /&gt;
: This stream has random-seek capability. IFFF_RSEEK tends to imply IFFF_FSEEK, but it’s best to specify both.&lt;br /&gt;
&lt;br /&gt;
If neither flag is specified, you’re telling IFFParse that it can’t seek through the stream.&lt;br /&gt;
&lt;br /&gt;
After your stream is initialized, call OpenIFF():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you establish a read/write mode (by passing IFFF_READ or IFFF_WRITE), you remain in that mode until you call CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Termination ===&lt;br /&gt;
&lt;br /&gt;
Termination is simple. Just call CloseIFF(iff). This may be called at any time, and terminates IFFParse’s transaction with the stream. The stream itself is not closed. The IFFHandle may be reused; you may safely call OpenIFF() on it again. You are responsible for closing the streams you opened. A stream used in an IFFHandle must generally remain open until you CloseIFF().&lt;br /&gt;
&lt;br /&gt;
=== Custom Streams ===&lt;br /&gt;
&lt;br /&gt;
A custom stream handler can allow you (and iffparse.library) to use your compiler’s own file I/O functions such as fopen(), fread() and fwrite(), rather than the lower-level AmigaDOS equivalents Open(), Read(), and Write(). A custom stream handler could also be used to read or write IFF files from an Exec device or an unusual handler or file system.&lt;br /&gt;
&lt;br /&gt;
If you install your own stream handler function, iffparse.library will call your function whenever it needs to read, write, or seek on your file. Your stream handler function will then perform these stream actions for iffparse.library. See the “Custom Stream Handlers” section for more information on custom stream handlers.&lt;br /&gt;
&lt;br /&gt;
== Parsing and Writing IFF ==&lt;br /&gt;
&lt;br /&gt;
For information on parsing IFF see [[Parsing_IFF|Parsing IFF]].&lt;br /&gt;
&lt;br /&gt;
For information on writing IFF see [[Writing_IFF|Writing IFF]].&lt;br /&gt;
&lt;br /&gt;
== Context Functions ==&lt;br /&gt;
&lt;br /&gt;
Internally, IFFParse maintains IFF nesting and scoping context via a &#039;&#039;context stack&#039;&#039;. The PushChunk() and PopChunk() functions get their names from this basic idea of the iffparse.library. Direct access to this stack is not allowed. However, many functions are provided to assist in examining and manipulating the context stack.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Context Stack|text=It is probably easier to think of a stack of blocks on a table in front of you when reading this discussion.}}&lt;br /&gt;
&lt;br /&gt;
As the nesting level increases (as would happen when parsing a nested LIST or FORM), the depth of the context stack increases; new elements are added to the top. When these contexts expire, the ContextNodes are deleted and the stack shrinks.&lt;br /&gt;
&lt;br /&gt;
=== Context Nodes ===&lt;br /&gt;
&lt;br /&gt;
The current context is said to be the top element on the stack. Contextual information is stored in a structure called a ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode {&lt;br /&gt;
        struct MinNode  cn_Node;&lt;br /&gt;
        LONG            cn_ID;&lt;br /&gt;
        LONG            cn_Type;&lt;br /&gt;
        LONG            cn_Size;        /*  Size of this chunk             */&lt;br /&gt;
        LONG            cn_Scan;        /*  # of bytes read/written so far */&lt;br /&gt;
        /*  There are private fields hiding here.  */&lt;br /&gt;
        };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CurrentChunk() ====&lt;br /&gt;
&lt;br /&gt;
You can obtain a pointer to the current ContextNode through the function CurrentChunk():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
currentnode = IIFFParse-&amp;gt;CurrentChunk (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ContextNode tells you the type, ID, and size of the currently active chunk. If there is no currently active context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
==== ParentChunk() ====&lt;br /&gt;
&lt;br /&gt;
To find the parent of a context, you call ParentChunk() on the relevant ContextNode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
parentnode = IIFFParse-&amp;gt;ParentChunk(currentnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is no parent context, NULL is returned.&lt;br /&gt;
&lt;br /&gt;
=== The Default Context ===&lt;br /&gt;
&lt;br /&gt;
When you first obtain an IFFHandle through AllocIFF(), a hidden default context node is created. You cannot get direct access to this node through CurrentChunk() or ParentChunk(). However, using StoreLocalItem(), you can store information in this context.&lt;br /&gt;
&lt;br /&gt;
=== Context-Specific Data: LocalContextItems ===&lt;br /&gt;
&lt;br /&gt;
ContextNodes can contain application data specific to that context. These data objects are called LocalContextItems. LocalContextItems (LCIs) are a grey-box structure which contain a type, ID and identification field. LCIs are used to store context-sensitive data. The format of this data is application-defined. A ContextNode can contain as many LCIs as you want.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig33-3.png|frame|center|IFFParse Context Stack]]&lt;br /&gt;
&lt;br /&gt;
The previous figure shows the relationship between the IFFHandle, the ContextNodes, and the LCIs. The IFFHandle is the root of the tree, so to speak. ContextNodes “grow” out of the IFFHandle. In turn, LCIs &amp;quot;grow&amp;quot; out of the ContextNodes. What grows out of an LCI is client-defined.&lt;br /&gt;
&lt;br /&gt;
==== AllocLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
To create an LCI, you use the function AllocLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;AllocLocalItem (type, id, ident, datasize);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If successful, you will be returned a pointer to an LCI having the specified type, ID, and identification values; and with datasize bytes of buffer space for your application to use.&lt;br /&gt;
&lt;br /&gt;
==== LocalItemData() ====&lt;br /&gt;
&lt;br /&gt;
To get a pointer to an LCIs data buffer, you use LocalItemData():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
buf = IIFFParse-&amp;gt;LocalItemData (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may read and write the buffer to your heart’s content; it is yours. You should not, however, write beyond the end of the buffer. The size of the buffer is what you asked for when you called AllocLocalItem().&lt;br /&gt;
&lt;br /&gt;
=== Storing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Once you’ve created and initialized an LCI, you’ll want to attach it to a ContextNode. Though a ContextNode can have many LCIs, a given LCI can be linked to only one ContextNode. Once linked, an LCI cannot be removed from a ContextNode (this may change in the future). Storing an LCI in a ContextNode is done with the functions StoreLocalItem() and StoreItemInContext().&lt;br /&gt;
&lt;br /&gt;
==== StoreLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
The StoreLocalItem() function is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = StoreLocalItem (iff, lci, position);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The position argument determines where the LCI is stored. The possible values are IFFSLI_ROOT, IFFSLI_TOP, and IFFSLI_PROP.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_ROOT&lt;br /&gt;
: causes StoreLocalItem() to store your LCI in the default ContextNode.&lt;br /&gt;
&lt;br /&gt;
; IFFSLI_TOP&lt;br /&gt;
: gets your LCI stored in the top (current) ContextNode.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The LCI Ends When the Current Context Ends|text=When the current context expires, your LCI will be deleted by the parser.}}&lt;br /&gt;
&lt;br /&gt;
IFFSLI_PROP causes your LCI to be stored in the topmost context from which a property would apply. This is usually the topmost FORM or LIST chunk. For example, suppose you had a deeply nested ILBM FORM, and you wanted to store the BMHD property in its correct context such that, when the current FORM context expired, the BMHD property would be deleted. IFFSLI_PROP will cause StoreLocalItem() to locate the proper context for such scoping, and store the LCI there. See the section on &amp;quot;Finding the Prop Context&amp;quot; for additional information on the scope of properties.&lt;br /&gt;
&lt;br /&gt;
==== StoreItemInContext() ====&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() is used when you already have a pointer to the ContextNode to which you want to attach your LCI. It is called like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;StoreItemInContext (iff, lci, contextnode);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
StoreItemInContext() links your LCI into the specified ContextNode. Then it searches the ContextNode to see if there is another LCI with the same type, ID, and identification values. If so, the old one is deleted.&lt;br /&gt;
&lt;br /&gt;
==== FindLocalItem() ====&lt;br /&gt;
&lt;br /&gt;
After you’ve stored your LCI in a ContextNode, you will no doubt want to be able to find it again later. You do this with the function FindLocalItem(), which is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
lci = IIFFParse-&amp;gt;FindLocalItem (iff, type, id, ident);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() attempts to locate an LCI having the specified type, ID, and identification values. The search proceeds as follows (refer to the previous figure to understand this better).&lt;br /&gt;
&lt;br /&gt;
FindLocalItem() starts at the top (current) ContextNode and searches all LCIs in that context. If no matching LCIs are found, it proceeds down the context stack to the next ContextNode and searches all its LCIs. The process repeats until it finds the desired LCI (whereupon it returns a pointer to it), or reaches the end without finding anything (where it returns NULL).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Context Stack Position|text=LCIs higher in the stack will &amp;quot;override&amp;quot; lower LCIs with the same type, ID, and identification field. This is how property scoping is handled. As ContextNodes are popped off the context stack, all its LCIs are deleted as well. See the section on “Freeing LCIs” below for additional information on deleting LCIs.}}&lt;br /&gt;
&lt;br /&gt;
=== Some Interesting Internal Details ===&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This section details some internal implementation details of iffparse.library which may help you to understand it better. Use of the following information to do “clever” things in an application is forbidden and unsupportable. Don’t even think about it.}}&lt;br /&gt;
&lt;br /&gt;
It turns out that StoredProperties, CollectionItems, and entry and exit handlers are all implemented using LCIs. For example, when you call FindProp(), you are actually calling a front-end to FindLocalItem(). The mysterious identification value (which has heretofore never been discussed) is a value which permits you to differentiate between LCIs having the same type and ID.&lt;br /&gt;
&lt;br /&gt;
For instance, suppose you called PropChunk(), asking it to store an ILBM BMHD. PropChunk() will install an entry handler in the form of an LCI, having type equal to ‘ILBM’, ID equal to ‘BMHD’, and an identification value of IFFLCI_ENTRYHANDLER.&lt;br /&gt;
&lt;br /&gt;
When an ILBM BMHD is encountered, the entry handler is called, and it creates and stores another LCI having type equal to ‘ILBM’, ID equal to ‘BMHD’ and an identification value of IFFLCI_PROP.&lt;br /&gt;
&lt;br /&gt;
Thus, when you call FindProp(), it merely calls FindLocalItem() with your type and ID, and supplies IFFLCI_PROP for the identification value.&lt;br /&gt;
&lt;br /&gt;
Therefore, handlers, StoredProperties, CollectionItems and your own custom LCIs can never be confused with each other, since they all have unique identification values. Since they are all handled (and searched for) in the same way, they all “override” each other in a consistent way. Just as StoredProperties higher in the context stack will be found and returned before identical ones in lower contexts, so will chunk handlers be found and invoked before ones lower on the context stack (recall FindLocalItem()’s search procedure).&lt;br /&gt;
&lt;br /&gt;
This means you can temporarily override a chunk handler by installing an identical handler in a higher context. The handler will persist until the context in which it is stored expires, after which, the original one regains scope.&lt;br /&gt;
&lt;br /&gt;
== Error Handling ==&lt;br /&gt;
&lt;br /&gt;
If at any time during reading or writing you encounter an error, the IFFHandle is left in an undefined state. Upon detection of an error, you should perform an abort sequence and CloseIFF() the IFFHandle. Once CloseIFF() has been called, the IFFHandle is restored to normalcy and may be reused.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
&lt;br /&gt;
This section discusses customizing of IFFParse data handling. For applications with special needs, IFFParse supports both custom stream handlers and custom chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Custom Stream Handlers ===&lt;br /&gt;
&lt;br /&gt;
As discussed earlier, IFFParse contains built-in stream handlers for AmigaDOS file handles as returned by Open(), and for the clipboard.device. If you are using AmigaDOS filehandles or the clipboard.device, you need not supply a custom stream handler.&lt;br /&gt;
&lt;br /&gt;
If you wish to use your compiler’s own file I/O functions (such as fread() ) or need to read or write to an unusual handler or Exec device, you must provide a custom stream handler for your IFFHandle. Your custom stream handler will be called to perform all reads, writes, and seeks on your custom stream. The allows you to use compiler file I/O functions, Exec device commands, or any other method to perform the requested stream operations.&lt;br /&gt;
&lt;br /&gt;
If you are implementing your own custom stream handler, you will need to know the mechanics of hook call-backs, and how to interpret the parameters. An IFFParse custom stream handler is simply a function in your code that follows hook function conventions (Hook functions are also known as callback functions. See [[Utility_Library|Utility Library]] for more details).&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
To initialize your IFFHandle to point to your custom stream and stream handler, you must open your stream and place a pointer to the stream in your IFFHandle’s iff_Stream field. This pointer could be the return from fopen(), or an Exec device I/O request, or any other type of pointer which will provide your custom stream handler with a way to manage your stream. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
iff-&amp;gt;iff_Stream = (ULONG) fopen(&amp;quot;foo&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you must install your custom handler for this stream, and also tell IFFParse the seek capabilities of your stream. To install your custom stream handler, you must first set up a Hook structure (&amp;amp;lt;utility/hooks.h&amp;amp;gt;) to point to your stream handling function. Then use InitIFF() to install your stream handler and also tell IFFParse the seek capabilities of your stream. There is “some assembly required”.&lt;br /&gt;
&lt;br /&gt;
For an IFFParse custom stream hook the function prototype looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 mystreamhandler(struct Hook *hook, struct IFFHandle *iff, struct IFFStreamCmd *actionpkt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A return of 0 indicates success. A non-zero return indicates an error.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler&lt;br /&gt;
(&lt;br /&gt;
    struct Hook         *hook,&lt;br /&gt;
    struct IFFHandle    *iff,&lt;br /&gt;
    struct IFFStreamCmd *actionpkt&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
/*&lt;br /&gt;
 * Code to handle the stream commands - see end this section&lt;br /&gt;
 * for a complete example custom stream handler function.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Initialization of Hook structure for registerized handler function */&lt;br /&gt;
struct Hook mystreamhook&lt;br /&gt;
{&lt;br /&gt;
  { NULL },&lt;br /&gt;
  (HOOKFUNC) mystreamhandler,  /* h_Entry, registerized function entry */&lt;br /&gt;
  NULL,&lt;br /&gt;
  NULL&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Open custom stream and InitIFF to custom stream handler */&lt;br /&gt;
if ( iff-&amp;gt;iff_Stream = (ULONG) myopen(&amp;quot;foo&amp;quot;) )&lt;br /&gt;
        {&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &amp;amp;mystreamhook);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Stream Handler ====&lt;br /&gt;
&lt;br /&gt;
When the library calls your stream handler, you’ll be passed a pointer to the Hook structure (hook in the example used here), a pointer to the IFFHandle (iff), and a pointer to an IFFStreamCmd structure (actionpkt). Your stream pointer may be found in iff-&amp;amp;gt;iff_Stream where you previously stored it. The IFFStreamCmd (actionpkt) will describe the action that IFFParse needs you to perform on your stream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Custom stream handler is passed struct IFFStreamCmd *actionpkt */&lt;br /&gt;
struct IFFStreamCmd {&lt;br /&gt;
        LONG    sc_Command;     /*  Operation to be performed (IFFCMD_) */&lt;br /&gt;
        APTR    sc_Buf;         /*  Pointer to data buffer              */&lt;br /&gt;
        LONG    sc_NBytes;      /*  Number of bytes to be affected      */&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
/* Possible call-back command values.  (Using 0 as the value for IFFCMD_INIT&lt;br /&gt;
 * was, in retrospect, probably a bad idea.)&lt;br /&gt;
 */&lt;br /&gt;
#define IFFCMD_INIT     0       /*  Prepare the stream for a session    */&lt;br /&gt;
#define IFFCMD_CLEANUP  1       /*  Terminate stream session            */&lt;br /&gt;
#define IFFCMD_READ     2       /*  Read bytes from stream              */&lt;br /&gt;
#define IFFCMD_WRITE    3       /*  Write bytes to stream               */&lt;br /&gt;
#define IFFCMD_SEEK     4       /*  Seek on stream                      */&lt;br /&gt;
#define IFFCMD_ENTRY    5       /*  You just entered a new context      */&lt;br /&gt;
#define IFFCMD_EXIT     6       /*  You&#039;re about to leave a context     */&lt;br /&gt;
#define IFFCMD_PURGELCI 7       /*  Purge a LocalContextItem            */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your custom stream handler should perform the requested action on your custom stream, and then return 0 for success or an IFFParse error if an error occurred. The following code demonstrates a sample stream handler for a stream which was opened with a compiler’s fopen() buffered file I/O function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
static int32 mystreamhandler ( struct Hook *hook,&lt;br /&gt;
                              struct IFFHandle *iff,&lt;br /&gt;
                              struct IFFStreamCmd *actionpkt )&lt;br /&gt;
{&lt;br /&gt;
        FILE   *stream;&lt;br /&gt;
        int32  nbytes, error;&lt;br /&gt;
        uint8  *buf;&lt;br /&gt;
&lt;br /&gt;
        stream  = (FILE *) iff-&amp;gt;iff_Stream;     /* get your stream pointer */&lt;br /&gt;
        nbytes  = actionpkt-&amp;gt;sc_NBytes;         /* length for command */&lt;br /&gt;
        buf     = (uint8 *) actionpkt-&amp;gt;sc_Buf;  /* buffer for the command */&lt;br /&gt;
&lt;br /&gt;
        /* Now perform the action specified by the actionpkt-&amp;amp;gt;sc_Command */&lt;br /&gt;
&lt;br /&gt;
        switch (actionpkt-&amp;gt;sc_Command) {&lt;br /&gt;
        case IFFCMD_READ:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_READ means read sc_NBytes from the stream and place&lt;br /&gt;
                 * it in the memory pointed to by sc_Buf.  Be aware that&lt;br /&gt;
                 * sc_NBytes may be larger than can be contained in an int.&lt;br /&gt;
                 * This is important if you plan on recompiling this for&lt;br /&gt;
                 * 16-bit ints, since fread() takes int arguments.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_READ.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fread (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_WRITE:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_WRITE is analogous to IFFCMD_READ.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_WRITE.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fwrite (buf, 1, nbytes, stream) != nbytes);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_SEEK:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_SEEK asks that you performs a seek relative to the&lt;br /&gt;
                 * current position.  sc_NBytes is a signed number,&lt;br /&gt;
                 * indicating seek direction (positive for forward, negative&lt;br /&gt;
                 * for backward).  sc_Buf has no meaning here.&lt;br /&gt;
                 *&lt;br /&gt;
                 * Any error code returned will be remapped by IFFParse into&lt;br /&gt;
                 * IFFERR_SEEK.&lt;br /&gt;
                 */&lt;br /&gt;
                error = (fseek (stream, nbytes, 1) == -1);&lt;br /&gt;
                break;&lt;br /&gt;
&lt;br /&gt;
        case IFFCMD_INIT:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_INIT means to prepare your stream for reading.&lt;br /&gt;
                 * This is used for certain streams that can&#039;t be read&lt;br /&gt;
                 * immediately upon opening, and need further preparation.&lt;br /&gt;
                 * This operation is allowed to fail;  the error code placed&lt;br /&gt;
                 * in D0 will be returned directly to the client.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is the clipboard.  The&lt;br /&gt;
                 * clipboard.device requires you to set the io_ClipID and&lt;br /&gt;
                 * io_Offset to zero before starting a read.  You would&lt;br /&gt;
                 * perform such a sequence here.  (Stdio files need no such&lt;br /&gt;
                 * preparation, so we simply return zero for success.)&lt;br /&gt;
                 */&lt;br /&gt;
        case IFFCMD_CLEANUP:&lt;br /&gt;
                /*&lt;br /&gt;
                 * IFFCMD_CLEANUP means to terminate the transaction with&lt;br /&gt;
                 * the associated stream.  This is used for streams that&lt;br /&gt;
                 * can&#039;t simply be closed.  This operation is not allowed to&lt;br /&gt;
                 * fail;  any error returned will be ignored.&lt;br /&gt;
                 *&lt;br /&gt;
                 * An example of such a stream is (surprise!) the clipboard.&lt;br /&gt;
                 * It requires you to explicitly end reads by CMD_READing&lt;br /&gt;
                 * past the end of a clip, and end writes by sending a&lt;br /&gt;
                 * CMD_UPDATE.  You would perform such a sequence here.&lt;br /&gt;
                 * (Again, stdio needs no such sequence.)&lt;br /&gt;
                 */&lt;br /&gt;
                error = 0;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        return (error);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Chunk Handlers ===&lt;br /&gt;
&lt;br /&gt;
Like custom stream handlers, custom chunk handlers are implemented using Hook structures. See the previous section for details on how a handler function may be interfaced using a Hook structure.&lt;br /&gt;
&lt;br /&gt;
There are two types of chunk handlers: entry handlers and exit handlers. Entry handlers are invoked just after the parser enters the chunk; the stream will be positioned to read the first byte in the chunk. (If the chunk is a FORM, LIST, CAT, or PROP, the longword type will be read by the parser; the stream will be positioned to read the byte immediately following the type.) Exit handlers are invoked just before the parser leaves the chunk; the stream is not positioned predictably within the chunk.&lt;br /&gt;
&lt;br /&gt;
==== Installing a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
To install an entry handler, you call EntryHandler() in the following manner:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;EntryHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An exit handler is installed by saying:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = IIFFParse-&amp;gt;ExitHandler (iff, type, id, position, hookptr, object);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases, a handler is installed for chunks having the specified type and id. The position argument specifies in what context to install the handler, and is identical to the position argument used by StoreLocalItem(). The hookptr argument given above is a pointer to your Hook structure.&lt;br /&gt;
&lt;br /&gt;
==== Inside a Custom Chunk Handler ====&lt;br /&gt;
&lt;br /&gt;
The mechanics of receiving parameters through the Hook are covered in the “Custom Stream Handlers” section, and are not duplicated here. Refer to the EntryHandler() and ExitHandler() Autodocs for the contents of the registers upon entry.&lt;br /&gt;
&lt;br /&gt;
Once inside your handler, you can call nearly all functions in the iffparse.library, however, you should avoid calling ParseIFF() from within chunk handlers.&lt;br /&gt;
&lt;br /&gt;
Your handler runs in the same environment as whoever called ParseIFF(). The propagation sequence is:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mainline&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;ParseIFF()&#039;&#039;&#039; --&#039;&#039;calls&#039;&#039;--&amp;gt; &#039;&#039;&#039;your_handler()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Thus, your handler runs on your mainline’s stack, and can call any OS functions the mainline code can. (Your handler will have to set the global base pointer if your code uses base-relative addressing.)&lt;br /&gt;
&lt;br /&gt;
The return code will affect the parser in a number of ways:&lt;br /&gt;
&lt;br /&gt;
* If you return zero (a normal, uneventful return), ParseIFF() will continue normally. If you return the value IFFERR_RETURN2CLIENT, ParseIFF() will stop and return the value zero to the mainline code.&lt;br /&gt;
&lt;br /&gt;
* If you return any other value, ParseIFF() will stop and return that value to the mainline code. This is how you should return error conditions to the client code.&lt;br /&gt;
&lt;br /&gt;
==== The Object Parameter ====&lt;br /&gt;
&lt;br /&gt;
The object parameter supplied to EntryHandler() and ExitHandler() is a pointer which will be passed to your handler when invoked. This pointer can be anything; the parser doesn’t use it directly. As an example, you might pass the pointer to the IFFHandle. This way, when your handler is called, you’ll be able to easily perform operations on the IFFHandle within your handler. Such code might appear as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
error = EntryHandler (iff, ID_ILBM, ID_BMHD, IFFSLI_ROOT, hook, iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And your handler would be declared as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 MyHandler (struct Hook      *hook,&lt;br /&gt;
                 int32            *cmd,&lt;br /&gt;
                 struct IFFHandle *iff)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From within your handler, you could then call CurrentChunk(), ReadChunkBytes(), or nearly any other operation on the IFFHandle. Please refer to the EntryHandler() and ExitHandler() Autodocs for additional information on the use of chunk handlers.&lt;br /&gt;
&lt;br /&gt;
=== Finding the Prop Context ===&lt;br /&gt;
&lt;br /&gt;
Earlier it was mentioned that supplying a position value of IFFSLI_PROP to StoreLocalItem() would store it in the topmost property scope. FindPropContext() is the routine that finds that topmost context.&lt;br /&gt;
&lt;br /&gt;
Property chunks (such as the BMHD, CMAP, and others) have dominion over the FORM that contains them; they are said to be “in scope” and their definition persists until the FORM’s context ends. Thus, a property chunk has a scoping level equal to the FORM that contains it; when the FORM ends, the property dies with it.&lt;br /&gt;
&lt;br /&gt;
Consider a more complicated example. Suppose you have a LIST with a PROP in it. PROPs are the global variables of LISTs; thus a property chunk declared in a PROP will persist until the LIST’s context ends.&lt;br /&gt;
&lt;br /&gt;
This is what FindPropContext() is looking for; a context level in which a property chunk may be installed.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() starts at the parent of the current context (second from the top of the context stack) and starts searching downward, looking for the first FORM or LIST context it finds. If it finds one, it returns a pointer to that ContextNode. If it can’t find a suitable context level, it returns NULL.&lt;br /&gt;
&lt;br /&gt;
FindPropContext() is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ContextNode *cn = IFFParse-&amp;gt;FindPropContext (iff);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Freeing LCIs ===&lt;br /&gt;
&lt;br /&gt;
Ordinarily, the parser will automatically delete LCIs you have allocated and installed. However, you may have a case where simply FreeVec()ing your LCI is not enough; you may need to free some ancillary memory, or decrement a counter, or send a signal, or something. This is where SetLocalItemPurge() comes in. It is called as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IFFParse-&amp;gt;SetLocalItemPurge (lci, hookptr);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the parser is ready to delete your LCI, your purge handler code will be called through the Hook you supplied. You can then perform all your necessary operations. One of these operations should be to free the LCI itself. This is done with FreeLocalItem():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IIFFParse-&amp;gt;FreeLocalItem (lci);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This deallocates the memory used to store the LCI and the client buffer allocated with it. FreeLocalItem() is only called as part of a custom purge handler.&lt;br /&gt;
&lt;br /&gt;
As with custom chunk handlers, your purge handler executes in the same environment as the mainline code that called ParseIFF(). It is recommended that you keep purge handlers short and to the point; super clever stuff should be reserved for custom chunk handlers, or for the client’s mainline code. Custom purge handlers must &#039;&#039;always&#039;&#039; work; failures will be ignored.&lt;br /&gt;
&lt;br /&gt;
== IFF FORM Specifications ==&lt;br /&gt;
&lt;br /&gt;
The specifications for Amiga IFF formats are maintained by the AmigaOS Development Team. The latest specifications are published in the [[IFF_Standard|IFF Standard]]. Updates of the IFF Manual, selected new FORMs and changes to existing FORMs are documented on this wiki.&lt;br /&gt;
&lt;br /&gt;
Some of the most commonly used IFF FORMs are the four that were originally specified in the EA IFF-85 standard:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ILBM || Bitmap images and palettes&lt;br /&gt;
|-&lt;br /&gt;
| FTXT || Simple formatted text&lt;br /&gt;
|-&lt;br /&gt;
| SMUS || Simple musical scores&lt;br /&gt;
|-&lt;br /&gt;
| 8SVX || 8-bit sound samples&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Of these four, ILBM is the most commonly encountered FORM, and FTXT is becoming increasingly important since the &#039;&#039;conclip&#039;&#039; command passes clipped console text through the clipboard as FTXT. All data clipped to the clipboard must be in an IFF format.&lt;br /&gt;
&lt;br /&gt;
This section will provide a brief summary of the ILBM and FTXT FORMs and their most used common chunks. Please consult the EA-IFF specifications for additional information.&lt;br /&gt;
&lt;br /&gt;
=== FORM ILBM ===&lt;br /&gt;
&lt;br /&gt;
The IFF file format for graphic images on the Amiga is called FORM ILBM (InterLeaved BitMap). It follows a standard parsable IFF format.&lt;br /&gt;
&lt;br /&gt;
==== ILBM.BMHD BitMapHeader Chunk ====&lt;br /&gt;
&lt;br /&gt;
The most important chunk in a FORM ILBM is the BMHD (BitMapHeader) chunk which describes the size and compression of the image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef UBYTE Masking;  /* Choice of masking technique - Usually 0. */&lt;br /&gt;
#define mskNone                 0&lt;br /&gt;
#define mskHasMask              1&lt;br /&gt;
#define mskHasTransparentColor  2&lt;br /&gt;
#define mskLasso                3&lt;br /&gt;
&lt;br /&gt;
/* Compression algorithm applied to the rows of all source&lt;br /&gt;
 * and mask planes. &amp;amp;quot;cmpByteRun1&amp;amp;quot; is byte run encoding.&lt;br /&gt;
 * Do not compress across rows!  Compression is usually 1.&lt;br /&gt;
 */&lt;br /&gt;
typedef UBYTE Compression;&lt;br /&gt;
#define cmpNone                 0&lt;br /&gt;
#define cmpByteRun1             1&lt;br /&gt;
&lt;br /&gt;
/* The BitMapHeader structure expressed as a C structure */&lt;br /&gt;
typedef struct {&lt;br /&gt;
        UWORD w, h;             /* raster width &amp;amp;amp; height in pixels      */&lt;br /&gt;
        WORD  x, y;             /* pixel position for this image        */&lt;br /&gt;
        UBYTE nPlanes;          /* # bitplanes (without mask, if any)   */&lt;br /&gt;
        Masking     masking;    /* One of the values above.  Usually 0  */&lt;br /&gt;
        Compression compression;/* One of the values above.  Usually 1  */&lt;br /&gt;
        UBYTE reserved1;        /* reserved; ignore on read, write as 0 */&lt;br /&gt;
        UWORD transparentColor; /* transparent color number. Usually 0  */&lt;br /&gt;
        UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */&lt;br /&gt;
        WORD  pageWidth, pageHeight;    /* source &amp;amp;quot;page&amp;amp;quot; size in pixels */&lt;br /&gt;
} BitMapHeader;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Warning|text=This hex dump is shown only to help the reader understand how IFF chunks appear in a file. You cannot ever depend on any particular ILBM chunk being at any particular offset into the file. IFF files are composed, in their simplest form, of chunks within a FORM. Each chunk starts with a 4-letter chunkID, followed by a 32-bit length of the rest of the chunk. You must &#039;&#039;parse&#039;&#039; IFF files, skipping past unneeded or unknown chunks by seeking their length (+1 if odd length) to the next 4-letter chunk ID. In a real ILBM file, you are likely to encounter additional optional chunks. See the IFF Specification listed in [[IFF_Standard|IFF Standard]] for additional information on such chunks.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A0E0 424F4459    ............BODY&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...  etc.&lt;br /&gt;
&lt;br /&gt;
Interpretation:&lt;br /&gt;
&lt;br /&gt;
      &#039;F O R M&#039; length  &#039;I L B M&#039;&#039;B M H D&#039;&amp;amp;lt;-start of BitMapHeader chunk&lt;br /&gt;
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD&lt;br /&gt;
&lt;br /&gt;
       length  WideHigh XorgYorg PlMkCoRe &amp;amp;lt;- Planes Mask Compression Reserved&lt;br /&gt;
0010: 00000014 01400190 00000000 06000100    .....@..........&lt;br /&gt;
&lt;br /&gt;
      TranAspt PagwPagh &#039;C A M G&#039; length  &amp;amp;lt;- start of C-AMiGa View modes chunk&lt;br /&gt;
0020: 00000A0B 01400190 43414D47 00000004    .....@..CAMG....&lt;br /&gt;
&lt;br /&gt;
dir include:&lt;br /&gt;
      ViewMode &#039;C M A P&#039; length   R g b R &amp;amp;lt;- ViewMode 800=HAM | 4=LACE&lt;br /&gt;
0030: 00000804 434D4150 00000030 001000E0    ....CMAP...0....&lt;br /&gt;
&lt;br /&gt;
       g b R g  b R g b  R g b R  g b R g &amp;amp;lt;- Rgb&#039;s are for reg0 thru regN&lt;br /&gt;
0040: E0E00000 20000050 30303050 50500030    .... ..P000PPP.0&lt;br /&gt;
&lt;br /&gt;
       b R g b  R g b R  g b R g  b R g b&lt;br /&gt;
0050: 90805040 70707010 60E02060 E06080D0    ..P@ppp.`. `.`..&lt;br /&gt;
&lt;br /&gt;
       R g b R  g b R g  b R g b &#039;B O D Y&#039;&lt;br /&gt;
0060: A0A0A0A0 90E0C0C0 C0D0A000 424F4459    ............BODY&lt;br /&gt;
&lt;br /&gt;
       length   start of body data        &amp;amp;lt;- Compacted (Compression=1 above)&lt;br /&gt;
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...&lt;br /&gt;
0080: FFBFF800 0F7FF7FC FF04F85A 77AD5DFE    ...........Zw.].  etc.&lt;br /&gt;
&lt;br /&gt;
Simple CAMG ViewModes:  HIRES=0x8000  LACE=0x4  HAM=0x800  HALFBRITE=0x80&lt;br /&gt;
&lt;br /&gt;
( ILBMs may contain a LONGWORD ViewPort ModeID in CAMG )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Interpreting ILBMs ====&lt;br /&gt;
&lt;br /&gt;
ILBM is a fairly simple IFF FORM. All you really need to deal with to extract the image are the following chunks:&lt;br /&gt;
&lt;br /&gt;
Information about the size, depth, compaction method (see interpreted hex dump above).&lt;br /&gt;
&lt;br /&gt;
Optional Amiga ViewModes chunk. Most HAM and HALFBRITE ILBMs should have this chunk. If no CAMG chunk is present, and image is 6 planes deep, assume HAM and you’ll probably be right. Some Amiga ViewModes flags are HIRES=0x8000, LACE=0x4, HAM=0x800, HALFBRITE=0x80. 2.0 ILBM writers should write a full 32-bit mode ID in the CAMG. See the IFF Manual for more information on writing and interpreting 32-bit CAMG values.&lt;br /&gt;
&lt;br /&gt;
RGB values for color registers 0 to N. Previously, 4-bit RGB components each left justified in a byte. These should now be stored as a full 8-bit RGB values by duplicating 4-bit values in the high and low nibble of the byte.&lt;br /&gt;
&lt;br /&gt;
The pixel data, stored in an interleaved fashion as follows (each line individually compacted if BMHD Compression = 1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
plane 0 scan line 0&lt;br /&gt;
plane 1 scan line 0&lt;br /&gt;
plane 2 scan line 0&lt;br /&gt;
...&lt;br /&gt;
plane n scan line 0&lt;br /&gt;
plane 0 scan line 1&lt;br /&gt;
plane 1 scan line 1&lt;br /&gt;
etc.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also watch for AUTH Author chunks and (c) copyright chunks and preserve any copyright information if you rewrite the ILBM.&lt;br /&gt;
&lt;br /&gt;
==== ILBM BODY Compression ====&lt;br /&gt;
&lt;br /&gt;
The BODY contains pixel data for the image. Width, Height, and depth (Planes) is specified in the BMHD.&lt;br /&gt;
&lt;br /&gt;
If the BMHD Compression byte is 0, then the scan line data is not compressed. If Compression=1, then each scan line is individually compressed as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;while (not produced the desired number of bytes)&lt;br /&gt;
&lt;br /&gt;
    /* get a byte, call it N */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= 0 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= 127)&lt;br /&gt;
        /* copy the next N+1 bytes literally */&lt;br /&gt;
&lt;br /&gt;
    if (N &amp;amp;gt;= -127 &amp;amp;amp;&amp;amp;amp; N &amp;amp;lt;= -1)&lt;br /&gt;
        /* repeat the next byte N+1 times */&lt;br /&gt;
&lt;br /&gt;
    if (N == -128)&lt;br /&gt;
        /* skip it, presumably it&#039;s padding */&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Interpreting the Scan Line Data ====&lt;br /&gt;
&lt;br /&gt;
If the ILBM is not HAM or HALFBRITE, then after parsing and uncompacting if necessary, you will have N planes of pixel data. Color register used for each pixel is specified by looking at each pixel thru the planes. For instance, if you have 5 planes, and the bit for a particular pixel is set in planes 0 and 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PLANE     4 3 2 1 0&lt;br /&gt;
PIXEL     0 1 0 0 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then that pixel uses color register binary 01001 = 9.&lt;br /&gt;
&lt;br /&gt;
The RGB value for each color register is stored in the CMAP chunk of the ILBM, starting with register 0, with each register’s RGB value stored as one byte of R, one byte G, and one byte of B, with each component left justified in the byte. (i.e. Amiga R, G, and B components are each stored in the high nibble of a byte)&lt;br /&gt;
&lt;br /&gt;
But, if the picture is HAM or HALFBRITE, it is interpreted differently. Hopefully, if the picture is HAM or HALFBRITE, the package that saved it properly saved a CAMG chunk (look at a hex dump of your file with ASCII interpretation - you will see the chunks - they all start with a 4-ASCII-char chunk ID). If the picture is 6 planes deep and has no CAMG chunk, it is probably HAM. If you see a CAMG chunk, the ‘CAMG’ is followed by the 32-bit chunk length, and then the 32-bit Amiga view mode flags.&lt;br /&gt;
&lt;br /&gt;
HAM pictures will have the 0x800 bit set in CAMG chunk ViewModes. HALFBRITE pictures will have the 0x80 bit set. See the graphics library articles for more information on HAM and HALFBRITE modes.&lt;br /&gt;
&lt;br /&gt;
==== Other ILBM Notes ====&lt;br /&gt;
&lt;br /&gt;
Amiga ILBMs images must be stored as an even number of bytes in width. However, the ILBM BMHD field w (width) should describe the actual image width, not the rounded up width as stored.&lt;br /&gt;
&lt;br /&gt;
ILBMs created with Electronic Arts IBM or Amiga &#039;&#039;Deluxe Paint II&#039;&#039; packages are compatible (though you may have to use a ‘.lbm’ filename extension on an IBM). The ILBM graphic files may be transferred between the machines (or between the Amiga and IBM sides your Amiga if you have a CBM Bridgeboard card installed) and loaded into either package.&lt;br /&gt;
&lt;br /&gt;
=== FORM FTXT ===&lt;br /&gt;
&lt;br /&gt;
The FTXT (Formatted TeXT) form is the standard format for sharing text on the Amiga. The console device clip and paste functions, in conjunction with the startup-sequence &#039;&#039;conclip&#039;&#039; command, pass clipped console text through the clipboard as FTXT.&lt;br /&gt;
&lt;br /&gt;
By supporting reading and writing of clipboard device FTXT, your application will allow users to cut and paste text between your application, other applications, and Amiga Shell windows.&lt;br /&gt;
&lt;br /&gt;
The FTXT form is very simple. Generally, for clip and paste operations, you will only be concerned with the CHRS (character string) chunks of an FTXT. There may be one or more CHRS chunks, each of which contains a non-NULL-terminated string of ASCII characters whose length is equal to the length (StoredProperty.sp_Size) of the chunk.&lt;br /&gt;
&lt;br /&gt;
Be aware that if you CollectionChunk() the CHRS chunks of an FTXT, the list accumulated by IFFParse will be in reverse order from the chunk order in the file. See the ClipFTXT.c example at the end of this article for a simple example of reading (and optionally writing) FTXT to and from the clipboard. See also the [[Clipboard_Device|Clipboard Device]] and [[Console_Device|Console Device]] for more information on console clip and paste.&lt;br /&gt;
&lt;br /&gt;
== IFFParse Examples ==&lt;br /&gt;
&lt;br /&gt;
Two examples follow: “ClipFTXT.c” and “Sift.c”. These are simple examples that demonstrate the use of the IFFParse library. More complex examples for displaying and saving ILBMs may be found in the [[IFF_Standard|IFF Standard]] section.&lt;br /&gt;
&lt;br /&gt;
=== Clipboard FTXT Example ===&lt;br /&gt;
&lt;br /&gt;
ClipFTXT.c demonstrates reading (and optional writing) of clipboard device FTXT. This example may be used as the basis for supporting console pastes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * clipftxt.c:   Writes ASCII text to clipboard unit as FTXT&lt;br /&gt;
 *               (All clipboard data must be IFF)&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: clipftxt unitnumber&lt;br /&gt;
 *&lt;br /&gt;
 * To convert to an example of reading only, comment out #define WRITEREAD&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Causes example to write FTXT first, then read it back&lt;br /&gt;
 * Comment out to create a reader only&lt;br /&gt;
 */&lt;br /&gt;
#define WRITEREAD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: clipftxt unitnumber (use zero for primary unit)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various&lt;br /&gt;
 * IFF routines.  To get the index into this array, take your IFFERR code,&lt;br /&gt;
 * negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char    *errormsgs[] = {&lt;br /&gt;
        &amp;quot;End of file (not an error).&amp;quot;,&lt;br /&gt;
        &amp;quot;End of context (not an error).&amp;quot;,&lt;br /&gt;
        &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
        &amp;quot;Insufficient memory.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream read error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream seek error.&amp;quot;,&lt;br /&gt;
        &amp;quot;File is corrupt.&amp;quot;,&lt;br /&gt;
        &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Not an IFF file.&amp;quot;,&lt;br /&gt;
        &amp;quot;Required call-back hook missing.&amp;quot;,&lt;br /&gt;
        &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define RBUFSZ 512&lt;br /&gt;
&lt;br /&gt;
#define  ID_FTXT        MAKE_ID(&#039;F&#039;,&#039;T&#039;,&#039;X&#039;,&#039;T&#039;)&lt;br /&gt;
#define  ID_CHRS        MAKE_ID(&#039;C&#039;,&#039;H&#039;,&#039;R&#039;,&#039;S&#039;)&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse;&lt;br /&gt;
&lt;br /&gt;
UBYTE mytext[]=&amp;quot;This FTXT written to clipboard by clipftxt example.\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IFFHandle    *iff = NULL;&lt;br /&gt;
    struct ContextNode  *cn;&lt;br /&gt;
    long                error=0, unitnumber=0, rlen;&lt;br /&gt;
    int textlen;&lt;br /&gt;
    UBYTE readbuf[RBUFSZ];&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                return RETURN_WARN;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        unitnumber = atoi(argv[1]);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        &lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Allocate IFF_File structure.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF ()))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
         */&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) OpenClipboard (unitnumber)))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Opened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
        IIFFParse-&amp;gt;InitIFFasClip (iff);&lt;br /&gt;
&lt;br /&gt;
#ifdef WRITEREAD&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Start the IFF transaction.&lt;br /&gt;
         */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_WRITE))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for write failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Write our text to the clipboard as CHRS chunk in FORM FTXT&lt;br /&gt;
         *&lt;br /&gt;
         * First, write the FORM ID (FTXT)&lt;br /&gt;
         */&lt;br /&gt;
        if(!(error = IIFFParse-&amp;gt;PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                {&lt;br /&gt;
                /* Now the CHRS chunk ID followed by the chunk data&lt;br /&gt;
                 * We&#039;ll just write one CHRS chunk.&lt;br /&gt;
                 * You could write more chunks.&lt;br /&gt;
                 */&lt;br /&gt;
                if(!(error = IIFFParse-&amp;gt;PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN)))&lt;br /&gt;
                        {&lt;br /&gt;
                        /* Now the actual data (the text) */&lt;br /&gt;
                        textlen = strlen(mytext);&lt;br /&gt;
                        if(IIFFParse-&amp;gt;WriteChunkBytes(iff, mytext, textlen) != textlen)&lt;br /&gt;
                                {&lt;br /&gt;
                                IDOS-&amp;gt;Printf(&amp;quot;Error writing CHRS data.\n&amp;quot;);&lt;br /&gt;
                                error = IFFERR_WRITE;&lt;br /&gt;
                                }&lt;br /&gt;
                        }&lt;br /&gt;
                if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
                }&lt;br /&gt;
        if(!error) error = IIFFParse-&amp;gt;PopChunk(iff);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if(error)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;IFF write failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Wrote text to clipboard as FTXT\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Now let&#039;s close it, then read it back&lt;br /&gt;
         * First close the write handle, then close the clipboard&lt;br /&gt;
         */&lt;br /&gt;
        IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
        if (iff-&amp;gt;iff_Stream) CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
        if (!(iff-&amp;gt;iff_Stream = (ULONG) OpenClipboard (unitnumber)))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Reopen of Clipboard failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Reopened clipboard unit %ld\n&amp;quot;, unitnumber);&lt;br /&gt;
&lt;br /&gt;
#endif /* WRITEREAD */&lt;br /&gt;
&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF (iff, IFFF_READ))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF for read failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Tell iffparse we want to stop on FTXT CHRS chunks */&lt;br /&gt;
        if (error = IFFParse-&amp;gt;StopChunk(iff, ID_FTXT, ID_CHRS))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;StopChunk failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Find all of the FTXT CHRS chunks */&lt;br /&gt;
        while(1)&lt;br /&gt;
                {&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff,IFFPARSE_SCAN);&lt;br /&gt;
                if(error == IFFERR_EOC) continue;       /* enter next context */&lt;br /&gt;
                else if(error) break;&lt;br /&gt;
&lt;br /&gt;
                /* We only asked to stop at FTXT CHRS chunks&lt;br /&gt;
                 * If no error we&#039;ve hit a stop chunk&lt;br /&gt;
                 * Read the CHRS chunk data&lt;br /&gt;
                 */&lt;br /&gt;
                cn = IIFFParse-&amp;gt;CurrentChunk(iff);&lt;br /&gt;
&lt;br /&gt;
                if((cn)&amp;amp;&amp;amp;(cn-&amp;gt;cn_Type == ID_FTXT)&amp;amp;&amp;amp;(cn-&amp;gt;cn_ID == ID_CHRS))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;CHRS chunk contains:\n&amp;quot;);&lt;br /&gt;
                        while((rlen = IIFFParse-&amp;gt;ReadChunkBytes(iff,readbuf,RBUFSZ)) &amp;gt; 0)&lt;br /&gt;
                                {&lt;br /&gt;
                                IDOS-&amp;gt;Write(Output(),readbuf,rlen);&lt;br /&gt;
                                }&lt;br /&gt;
                        if(rlen &amp;lt; 0)    error = rlen;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        if((error)&amp;amp;&amp;amp;(error != IFFERR_EOF))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf (&amp;quot;IFF read failed, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff) {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Terminate the IFF transaction with the stream.  Free&lt;br /&gt;
                 * all associated structures.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF (iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the clipboard stream&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                                CloseClipboard ((struct ClipboardHandle *)&lt;br /&gt;
                                                iff-&amp;gt;iff_Stream);&lt;br /&gt;
                /*&lt;br /&gt;
                 * Free the IFF_File structure itself.&lt;br /&gt;
                 */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF (iff);&lt;br /&gt;
                }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary (IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IFF Scanner Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Sift.c&amp;quot; lists the type and size of every chunk in an IFF file and, and checks the IFF file for correct syntax. You should use &amp;quot;Sift&amp;quot; to check IFF files created by your programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 *&lt;br /&gt;
 * sift.c:       Takes any IFF file and tells you what&#039;s in it.  Verifies syntax and all that cool stuff.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage: sift -c          ; For clipboard scanning&lt;br /&gt;
 *    or  sift &amp;lt;file&amp;gt;      ; For DOS file scanning&lt;br /&gt;
 *&lt;br /&gt;
 * Reads the specified stream and prints an IFFCheck-like listing of the contents of the IFF file, if any.&lt;br /&gt;
 * Stream is a DOS file for &amp;lt;file&amp;gt; argument, or is the clipboard&#039;s primary clip for -c.&lt;br /&gt;
 * This program must be run from a CLI.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/iffparse.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MINARGS 2&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR usage = &amp;quot;Usage: sift IFFfilename (or -c for clipboard)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *);  /* prototype for our function */&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Text error messages for possible IFFERR_#? returns from various IFF routines.  To get the index into&lt;br /&gt;
 * this array, take your IFFERR code, negate it, and subtract one.&lt;br /&gt;
 *  idx = -error - 1;&lt;br /&gt;
 */&lt;br /&gt;
char    *errormsgs[] = {&lt;br /&gt;
        &amp;quot;End of file (not an error).&amp;quot;, &amp;quot;End of context (not an error).&amp;quot;, &amp;quot;No lexical scope.&amp;quot;,&lt;br /&gt;
        &amp;quot;Insufficient memory.&amp;quot;, &amp;quot;Stream read error.&amp;quot;, &amp;quot;Stream write error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Stream seek error.&amp;quot;, &amp;quot;File is corrupt.&amp;quot;, &amp;quot;IFF syntax error.&amp;quot;,&lt;br /&gt;
        &amp;quot;Not an IFF file.&amp;quot;, &amp;quot;Required call-back hook missing.&amp;quot;, &amp;quot;Return to client.  You should never see this.&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct IFFParseIFace *IIFFParse = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IFFHandle    *iff = NULL;&lt;br /&gt;
    int32                error;&lt;br /&gt;
    int16                cbio;&lt;br /&gt;
&lt;br /&gt;
        /* if not enough args or &#039;?&#039;, print usage */&lt;br /&gt;
        if(((argc)&amp;amp;&amp;amp;(argc&amp;lt;MINARGS))||(argv[argc-1][0]==&#039;?&#039;))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;, usage);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if we are doing I/O to the Clipboard. */&lt;br /&gt;
        cbio = (argv[1][0] == &#039;-&#039;  &amp;amp;&amp;amp;  argv[1][1] == &#039;c&#039;);&lt;br /&gt;
&lt;br /&gt;
        struct Library *IFFParseBase = IExec-&amp;gt;OpenLibrary(&amp;quot;iffparse.library&amp;quot;, 50);&lt;br /&gt;
        IIFFParse = (struct IFFParseIFace*)IExec-&amp;gt;GetInterface(IFFParseBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
        if (IIFFParse == NULL)&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open iff parsing library.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Allocate IFF_File structure. */&lt;br /&gt;
        if (!(iff = IIFFParse-&amp;gt;AllocIFF()))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;AllocIFF() failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Internal support is provided for both AmigaDOS files, and the clipboard.device. This bizarre&lt;br /&gt;
         * &#039;if&#039; statement performs the appropriate machinations for each case.&lt;br /&gt;
         */&lt;br /&gt;
        if (cbio)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * Set up IFF_File for Clipboard I/O.&lt;br /&gt;
                 */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = (ULONG) IIFFParse-&amp;gt;OpenClipboard(PRIMARY_CLIP)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Clipboard open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasClip(iff);&lt;br /&gt;
                }&lt;br /&gt;
        else&lt;br /&gt;
                {&lt;br /&gt;
                /* Set up IFF_File for AmigaDOS I/O.  */&lt;br /&gt;
                if (!(iff-&amp;gt;iff_Stream = IDOS-&amp;gt;Open(argv[1], MODE_OLDFILE)))&lt;br /&gt;
                        {&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;File open failed.\n&amp;quot;);&lt;br /&gt;
                        goto bye;&lt;br /&gt;
                        }&lt;br /&gt;
                IIFFParse-&amp;gt;InitIFFasDOS(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /* Start the IFF transaction. */&lt;br /&gt;
        if (error = IIFFParse-&amp;gt;OpenIFF(iff, IFFF_READ))&lt;br /&gt;
                {&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;OpenIFF failed.\n&amp;quot;);&lt;br /&gt;
                goto bye;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        while (1)&lt;br /&gt;
                {&lt;br /&gt;
                /*&lt;br /&gt;
                 * The interesting bit. IFFPARSE_RAWSTEP permits us to have precision monitoring of the&lt;br /&gt;
                 * parsing process, which is necessary if we wish to print the structure of an IFF file.&lt;br /&gt;
                 * ParseIFF() with _RAWSTEP will return the following things for the following reasons:&lt;br /&gt;
                 *&lt;br /&gt;
                 * Return code:                 Reason:&lt;br /&gt;
                 * 0                            Entered new context.&lt;br /&gt;
                 * IFFERR_EOC                   About to leave a context.&lt;br /&gt;
                 * IFFERR_EOF                   Encountered end-of-file.&lt;br /&gt;
                 * &amp;lt;anything else&amp;gt;              A parsing error.&lt;br /&gt;
                 */&lt;br /&gt;
                error = IIFFParse-&amp;gt;ParseIFF(iff, IFFPARSE_RAWSTEP);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Since we&#039;re only interested in when we enter a context, we &amp;quot;discard&amp;quot; end-of-context&lt;br /&gt;
                 * (_EOC) events.&lt;br /&gt;
                 */&lt;br /&gt;
                if (error == IFFERR_EOC)&lt;br /&gt;
                        continue;&lt;br /&gt;
                else if (error)&lt;br /&gt;
                        /*&lt;br /&gt;
                         * Leave the loop if there is any other error.&lt;br /&gt;
                         */&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                /* If we get here, error was zero. Print out the current state of affairs. */&lt;br /&gt;
                PrintTopChunk(iff);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * If error was IFFERR_EOF, then the parser encountered the end of&lt;br /&gt;
         * the file without problems. Otherwise, we print a diagnostic.&lt;br /&gt;
         */&lt;br /&gt;
        if (error == IFFERR_EOF)&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan complete.\n&amp;quot;);&lt;br /&gt;
        else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;File scan aborted, error %ld: %s\n&amp;quot;,&lt;br /&gt;
                        error, errormsgs[-error - 1]);&lt;br /&gt;
&lt;br /&gt;
bye:&lt;br /&gt;
        if (iff) {&lt;br /&gt;
                /* Terminate the IFF transaction with the stream. Free all associated structures. */&lt;br /&gt;
                IIFFParse-&amp;gt;CloseIFF(iff);&lt;br /&gt;
&lt;br /&gt;
                /*&lt;br /&gt;
                 * Close the stream itself.&lt;br /&gt;
                 */&lt;br /&gt;
                if (iff-&amp;gt;iff_Stream)&lt;br /&gt;
                        if (cbio)&lt;br /&gt;
                                IIFFParse-&amp;gt;CloseClipboard((struct ClipboardHandle *)iff-&amp;gt;iff_Stream);&lt;br /&gt;
                        else&lt;br /&gt;
                                IDOS-&amp;gt;Close(iff-&amp;gt;iff_Stream);&lt;br /&gt;
&lt;br /&gt;
                /* Free the IFF_File structure itself. */&lt;br /&gt;
                IIFFParse-&amp;gt;FreeIFF(iff);&lt;br /&gt;
                }&lt;br /&gt;
        IExec-&amp;gt;DropInterface((struct Interface*)IIFFParse);&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(IFFParseBase);&lt;br /&gt;
&lt;br /&gt;
        return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void PrintTopChunk(struct IFFHandle *iff)&lt;br /&gt;
{&lt;br /&gt;
        struct ContextNode      *top;&lt;br /&gt;
        int32                   i;&lt;br /&gt;
        char                    idbuf[5];&lt;br /&gt;
&lt;br /&gt;
        /* Get a pointer to the context node describing the current context. */&lt;br /&gt;
        if (!(top = IIFFParse-&amp;gt;CurrentChunk(iff)))&lt;br /&gt;
                return;&lt;br /&gt;
&lt;br /&gt;
        /*&lt;br /&gt;
         * Print a series of dots equivalent to the current nesting depth of chunks processed so far.&lt;br /&gt;
         * This will cause nested chunks to be printed out indented.&lt;br /&gt;
         */&lt;br /&gt;
        for (i = iff-&amp;gt;iff_Depth;  i--; )&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;. &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Print out the current chunk&#039;s ID and size. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;%s %ld &amp;quot;, IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_ID, idbuf), top-&amp;gt;cn_Size);&lt;br /&gt;
&lt;br /&gt;
        /* Print the current chunk&#039;s type, with a newline. */&lt;br /&gt;
        IDOS-&amp;gt;Printf(IIFFParse-&amp;gt;IDtoStr(top-&amp;gt;cn_Type, idbuf));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the IFFParse functions discussed in this article. Further information about these and other IFFParse functions can be found in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocIFF()&lt;br /&gt;
| Creates an IFFHandle structure.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIFF()&lt;br /&gt;
| Frees the IFFHandle structure created with AllocIFF().&lt;br /&gt;
|-&lt;br /&gt;
| OpenIFF()&lt;br /&gt;
| Initialize an IFFHandle structure to read or write an IFF stream.&lt;br /&gt;
|-&lt;br /&gt;
| CloseIFF()&lt;br /&gt;
| Closes an IFF context.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFF()&lt;br /&gt;
| Initialize an IFFHandle as a user-defined stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasDOS()&lt;br /&gt;
| Initialize an IFFHandle as an AmigaDOS stream.&lt;br /&gt;
|-&lt;br /&gt;
| InitIFFasClip()&lt;br /&gt;
| Initialize an IFFHandle as a clipboard stream.&lt;br /&gt;
|-&lt;br /&gt;
| OpenClipboard()&lt;br /&gt;
| Create a handle on a clipboard unit for InitIFFasClip().&lt;br /&gt;
|-&lt;br /&gt;
| ParseIFF()&lt;br /&gt;
| Parse an IFF file from an IFFHandle stream.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkBytes()&lt;br /&gt;
| Read bytes from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| ReadChunkRecords()&lt;br /&gt;
| Read record elements from the current chunk into a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| StopChunk()&lt;br /&gt;
| Declare a chunk that should cause ParseIFF() to return.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| PropChunk()&lt;br /&gt;
| Specify a property chunk to store.&lt;br /&gt;
|-&lt;br /&gt;
| FindProp()&lt;br /&gt;
| Search for a stored property in a given context.&lt;br /&gt;
|-&lt;br /&gt;
| CollectionChunk()&lt;br /&gt;
| Declare a chunk type for collection.&lt;br /&gt;
|-&lt;br /&gt;
| FindCollection()&lt;br /&gt;
| Get a pointer to the current list of collection items.&lt;br /&gt;
|-&lt;br /&gt;
| StopOnExit()&lt;br /&gt;
| Declare a stop condition for exiting a chunk.&lt;br /&gt;
|-&lt;br /&gt;
| EntryHandler()&lt;br /&gt;
| Add an entry handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| ExitHandler()&lt;br /&gt;
| Add an exit handler to the IFFHandle context.&lt;br /&gt;
|-&lt;br /&gt;
| PushChunk()&lt;br /&gt;
| Push a given context node onto the top of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| PopChunk()&lt;br /&gt;
| Pop the top context node off of the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| CurrentChunk()&lt;br /&gt;
| Get the top context node for the current chunk.&lt;br /&gt;
|-&lt;br /&gt;
| ParentChunk()&lt;br /&gt;
| Get the nesting context node for a given chunk.&lt;br /&gt;
|-&lt;br /&gt;
| AllocLocalItem()&lt;br /&gt;
| Create a LocalContextItem (LCI) structure.&lt;br /&gt;
|-&lt;br /&gt;
| LocalItemData()&lt;br /&gt;
| Returns a pointer to the user data of a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreLocalItem()&lt;br /&gt;
| Insert a LocalContextItem (LCI).&lt;br /&gt;
|-&lt;br /&gt;
| StoreItemInContext()&lt;br /&gt;
| Store a LocalContextItem in a given context node.&lt;br /&gt;
|-&lt;br /&gt;
| FindPropContext()&lt;br /&gt;
| Find the property context for the current state.&lt;br /&gt;
|-&lt;br /&gt;
| FindLocalItem()&lt;br /&gt;
| Return a LocalContextItem from the context stack.&lt;br /&gt;
|-&lt;br /&gt;
| FreeLocalItem()&lt;br /&gt;
| Free a LocalContextItem (LCI) created with AllocLocalItem().&lt;br /&gt;
|-&lt;br /&gt;
| SetLocalItemPurge()&lt;br /&gt;
| Set purge vector for a local context item.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=9150</id>
		<title>Application Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=9150"/>
		<updated>2017-05-31T14:37:30Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Minimum Application Library Support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of &#039;&#039;application&#039;&#039; 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 &#039;&#039;background services&#039;&#039;), and genuine full-blown &#039;&#039;applications&#039;&#039; with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can turn into a “watchdog” and get notified when other applications register.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can easily create and manage lists of recently-used documents.&lt;br /&gt;
&lt;br /&gt;
* It can register as a &#039;&#039;unique application&#039;&#039;, preventing other instances of itself from running.&lt;br /&gt;
&lt;br /&gt;
* It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.&lt;br /&gt;
&lt;br /&gt;
* 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”.&lt;br /&gt;
&lt;br /&gt;
=== Frequently Asked Questions ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Should my programs register with the Application Library?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: After registering with the library, will my application become controlled by the OS or by other applications?&#039;&#039;&lt;br /&gt;
|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&#039;t want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming [[#Control Messages|control messages]].&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Doesn&#039;t the Application Library duplicate functionality already present in commodities?&#039;&#039;&lt;br /&gt;
|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]]&#039;s event stream. The Application Library&#039;s field and scope of operation is completely different (and much wider).&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Why is this a library? Wouldn&#039;t it be wiser to implement it as a class, like MUI does it?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Can a program be registered both as an application and as a commodity?&#039;&#039;&lt;br /&gt;
|A: Yes, that&#039;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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system?&#039;&#039;&lt;br /&gt;
|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&#039;t put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Minimum Application Library Support ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* Provide a short [[#Description|description]] for a better identification of the application.&lt;br /&gt;
* Tell the OS whether your application supports iconification, that is, hiding/showing its GUI. If iconification is supported:&lt;br /&gt;
** set the REGAPP_HasIconifyFeature registration tag to TRUE;&lt;br /&gt;
** make the application respond to the [[#Control Messages|APPLIBMT_Hide and APPLIBMT_Unhide]] control messages;&lt;br /&gt;
** update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies.&lt;br /&gt;
* Allow the application to be shut down externally in a safe and graceful manner in reaction to the [[#Control Messages|APPLIBMT_Quit]] message.&lt;br /&gt;
&lt;br /&gt;
A failure to implement this suggested minimum will mean that the application manager will be unable to control your application properly and consistently. This may cause confusion on the part of the users and degrade their AmigaOS experience.&lt;br /&gt;
&lt;br /&gt;
== Library Opening Chores ==&lt;br /&gt;
&lt;br /&gt;
{{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.}}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Library *ApplicationBase = NULL;&lt;br /&gt;
struct ApplicationIFace *IApplication = NULL;&lt;br /&gt;
struct PrefsObjectsIFace *IPrefsObjects = NULL;&lt;br /&gt;
&lt;br /&gt;
if ( (ApplicationBase = IExec-&amp;gt;OpenLibrary(&amp;quot;application.library&amp;quot;, 52)) )&lt;br /&gt;
{&lt;br /&gt;
   IApplication  = (struct ApplicationIFace *)  IExec-&amp;gt;GetInterface(ApplicationBase, &lt;br /&gt;
                                                                    &amp;quot;application&amp;quot;, 2, NULL);&lt;br /&gt;
   IPrefsObjects = (struct PrefsObjectsIFace *) IExec-&amp;gt;GetInterface(ApplicationBase,&lt;br /&gt;
                                                                    &amp;quot;prefsobjects&amp;quot;, 2, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if ( !ApplicationBase || !IApplication || !IPrefsObjects )&lt;br /&gt;
{&lt;br /&gt;
   /* handle library opening error */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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. &#039;&#039;&#039;Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2.&#039;&#039;&#039; This is due to certain changes in the Application Library API.&lt;br /&gt;
&lt;br /&gt;
When your application has run its course, don’t forget to clean up and close both the library and its interface(s):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IPrefsObjects);&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IApplication);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(ApplicationBase);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registering the Application ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
*hint* Avoid using the  _ underscore character in the registered name. These may cause &amp;quot;charsetconvert&amp;quot; errors when the system boots up later.&lt;br /&gt;
&lt;br /&gt;
=== Application Identifiers ===&lt;br /&gt;
&lt;br /&gt;
A successfully registered application receives a numeric identifier, which in this documentation will be referred to as &#039;&#039;appID&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* the application name;&lt;br /&gt;
* an instance count (if more than one instance of the same application is started);&lt;br /&gt;
* a URL identifier (for example, the domain name of the application’s homepage – optional).&lt;br /&gt;
&lt;br /&gt;
The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.&lt;br /&gt;
&lt;br /&gt;
=== Registration Functions ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (!appID)&lt;br /&gt;
{&lt;br /&gt;
   /* report registration error and quit */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    do whatever your program does&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
IApplication-&amp;gt;UnregisterApplication(appID, NULL);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.)&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available.&lt;br /&gt;
&lt;br /&gt;
== Application Attributes ==&lt;br /&gt;
&lt;br /&gt;
An application is described by a set of &#039;&#039;attributes&#039;&#039;. There are attributes that describe the application&#039;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 &amp;quot;static&amp;quot; application features in one place.&lt;br /&gt;
&lt;br /&gt;
=== Description ===&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The application description cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Uniqueness ===&lt;br /&gt;
A program can register as a &#039;&#039;unique application&#039;&#039;, 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.&lt;br /&gt;
&lt;br /&gt;
The following function call will register Audio Monster as a unique application:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_UniqueApplication, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Quite logically, uniqueness is an attribute that cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Feature Set / Control Scope ===&lt;br /&gt;
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):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasIconifyFeature&lt;br /&gt;
|The application supports iconification and uniconification.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasPrefsWindow&lt;br /&gt;
|The application has a dedicated Preferences (Settings) window.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanCreateNewDocs&lt;br /&gt;
|The application can create new documents (projects).&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanPrintDocs&lt;br /&gt;
|The application can print documents.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_PrintDoc message.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These four attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Receiving Notifications ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_AppNotifications&lt;br /&gt;
|Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_BlankerNotifications&lt;br /&gt;
|Receive notifications concerning blanker activity.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream.&lt;br /&gt;
&lt;br /&gt;
These two attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Changing Application Attributes ===&lt;br /&gt;
&lt;br /&gt;
Certain attributes describe &amp;quot;dynamic&amp;quot; application properties and, as such, can be set or changed after registration. Some of these are &#039;&#039;state attributes&#039;&#039; 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.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AllowsBlanker&lt;br /&gt;
|Same as REGAPP_AllowsBlanker above.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppOpenedDocument&lt;br /&gt;
|Adds a new entry to the application&#039;s Last Used Documents list.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppNotifications&amp;lt;br /&amp;gt;APPATTR_BlankerNotifications&lt;br /&gt;
|Same as the two corresponding [[#Receiving Notifications|registration tags]] above.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_CanCreateNewDocs&amp;lt;br /&amp;gt;APPATTR_CanPrintDocs&amp;lt;br /&amp;gt;APPATTR_HasIconifyFeature&amp;lt;br /&amp;gt;APPATTR_HasPrefsWindow&lt;br /&gt;
|Same as the four corresponding [[#Feature Set / Control Scope|registration tags]] above.&lt;br /&gt;
|Prefer setting these properties at registration time.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_ClearLastUsedDocs&lt;br /&gt;
|Clears the application&#039;s list of last used documents.&lt;br /&gt;
|Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.)&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_FlushPrefs&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_Hidden&lt;br /&gt;
|A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not.&lt;br /&gt;
|Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_IconType&lt;br /&gt;
|Changes the application icon type.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_MainPrefsDict&lt;br /&gt;
|Allows changing the application&#039;s Prefs dictionary.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_NeedsGameMode&lt;br /&gt;
|A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode.&lt;br /&gt;
|By the &amp;quot;game mode&amp;quot; we understand a mode in which an application doesn&#039;t want to be &amp;quot;disturbed&amp;quot; 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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_SavePrefs&lt;br /&gt;
|Same as REGAPP_SavePrefs above.&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;SetApplicationAttrs(appID,&lt;br /&gt;
            APPATTR_Hidden, TRUE,&lt;br /&gt;
            APPATTR_AllowsBlanker, TRUE,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finding Applications ==&lt;br /&gt;
&lt;br /&gt;
The Application Library maintains a public list of all registered applications. Certain special-purpose programs – &#039;&#039;application managers&#039;&#039; – 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?&lt;br /&gt;
&lt;br /&gt;
Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library&#039;s messaging system. To do this you need to know at least one of the following:&lt;br /&gt;
&lt;br /&gt;
* the application name, ie. the one under which it was registered via RegisterApplication();&lt;br /&gt;
* 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;&lt;br /&gt;
* the pathname pointing to the program file on disk, e.g. “Work:Utils/AudioMonster”.&lt;br /&gt;
&lt;br /&gt;
Based on this information, the respective piece of code that will find our application in the system might look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
/* if you only know the application name */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the application name identifier */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you specifically want to talk to the second running instance */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster_1.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the pathname to the program file */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_FileName, &amp;quot;Work:Utils/AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have obtained the appID you can start communicating with the respective application.&lt;br /&gt;
&lt;br /&gt;
== Messaging ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
{{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 &#039;&#039;AppMessages&#039;&#039;. 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).&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Data Structures ===&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
The following three structures are defined for carrying Application Library message data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg&lt;br /&gt;
{&lt;br /&gt;
	struct Message msg;&lt;br /&gt;
	uint32 senderAppID;  // the appID of the sender application&lt;br /&gt;
	uint32 type;         // identifies the type of message&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationOpenPrintDocMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR fileName;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationCustomMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR customMsg;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, structure &#039;&#039;ApplicationMsg&#039;&#039; contains a standard &#039;&#039;[[Exec_Messages_and_Ports#Messages|struct Message]]&#039;&#039;, the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: &#039;&#039;ApplicationOpenPrintDocMsg&#039;&#039; is used by certain [[#Control Messages|control messages]] that require a pointer to a filename; the &#039;&#039;ApplicationCustomMsg&#039;&#039; structure is used for [[#Custom Messages|custom messages]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Control Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Message name&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Quit&lt;br /&gt;
|The application is asked to shut down itself and quit.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ForceQuit&lt;br /&gt;
|Same as before but this time the application shall quit immediately, without asking for saving documents etc.&lt;br /&gt;
|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&#039;t implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Hide&lt;br /&gt;
|The application shall hide its interface and iconify on the Workbench screen.&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Unhide&lt;br /&gt;
|The application shall come back from the iconified (hidden) state.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ToFront&lt;br /&gt;
|The application window shall come to front.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenPrefs&lt;br /&gt;
|The application shall open its preferences window.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ReloadPrefs&lt;br /&gt;
|The application shall reload its preferences.&lt;br /&gt;
|Whatever this command is useful for.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_NewBlankDoc&lt;br /&gt;
|The application shall open a new, blank document or project.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenDoc&lt;br /&gt;
|The application shall try to open a specific document or project.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_PrintDoc&lt;br /&gt;
|The application shall try to print a specific document.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Custom Messages ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The pointer to the message text string is contained in a special [[#Data Structures|data structure]], &#039;&#039;struct ApplicationCustomMsg&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Special Messages ===&lt;br /&gt;
&lt;br /&gt;
There are also some special messages that an application can receive from the library or another running application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Message name !! Description !! Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Message Handling ===&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;notification port&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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&#039;ll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void event_loop(uint32 appID)&lt;br /&gt;
{&lt;br /&gt;
 struct MsgPort              *notificationPort = NULL;&lt;br /&gt;
 struct ApplicationMsg       *appLibMsg = NULL;&lt;br /&gt;
 struct ApplicationCustomMsg *customMsg = NULL;&lt;br /&gt;
 uint32 appLibSignal = 0, sigGot = 0;&lt;br /&gt;
 BOOL   done = FALSE;&lt;br /&gt;
&lt;br /&gt;
 /* Obtain pointer to Application Library&#039;s notification port and set the signal bit. */&lt;br /&gt;
 IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;amp;notificationPort, TAG_END);&lt;br /&gt;
 appLibSignal = (1L &amp;lt;&amp;lt; notificationPort-&amp;gt;mp_SigBit);&lt;br /&gt;
 &lt;br /&gt;
 /* Go into the event loop. */&lt;br /&gt;
 while (!done)&lt;br /&gt;
  {&lt;br /&gt;
   /* Wait for a signal to arrive. */&lt;br /&gt;
   sigGot = IExec-&amp;gt;Wait(appLibSignal);&lt;br /&gt;
&lt;br /&gt;
   /* Process all Application Library messages. */&lt;br /&gt;
   if ( sigGot &amp;amp; appLibSignal )&lt;br /&gt;
    {&lt;br /&gt;
     /* Obtain pointer to the message. */&lt;br /&gt;
     while ( (appLibMsg = (struct ApplicationMsg *) IExec-&amp;gt;GetMsg(notificationPort)) )&lt;br /&gt;
      {&lt;br /&gt;
       /* Identify the type of message. */&lt;br /&gt;
       switch (appLibMsg-&amp;gt;type)&lt;br /&gt;
        {&lt;br /&gt;
         case APPLIBMT_Quit:&lt;br /&gt;
         case APPLIBMT_ForceQuit:&lt;br /&gt;
           done = TRUE;&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_ToFront:&lt;br /&gt;
           /* Make the program window come to front. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Hide:&lt;br /&gt;
           /* Iconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Unhide:&lt;br /&gt;
           /* Uniconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*  Process the custom message as you like.&lt;br /&gt;
             Here we just use printf() to output the message text. */&lt;br /&gt;
         case APPLIBMT_CustomMsg:&lt;br /&gt;
           customMsg = (struct ApplicationCustomMsg *) appLibMsg;&lt;br /&gt;
           printf(&amp;quot;The message text is: %s\n&amp;quot;, customMsg-&amp;gt; customMsg);&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*&lt;br /&gt;
             Process any other Application Library messages.&lt;br /&gt;
          */&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
       /* Return the processed message to the sender so that it can be freed. */&lt;br /&gt;
       IExec-&amp;gt;ReplyMsg( (struct Message *) appLibMsg);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Also remember that the notifications are addressed to an abstract &#039;&#039;application&#039;&#039; – this makes them rather different from IDCMP messages, which are always sent to a particular &#039;&#039;window&#039;&#039; (Intuition is unaware of the application concept). 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, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first 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!&lt;br /&gt;
&lt;br /&gt;
=== Sending Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
# Prepare the respective [[#Data Structures|data structure]]: specify the type of message and supply the sender&#039;s [[#Application Identifiers|identifier]] (appID).&lt;br /&gt;
# [[#Finding Applications|Find the receiver]] application.&lt;br /&gt;
# Send a message to it via the SendApplicationMsg() function.&lt;br /&gt;
&lt;br /&gt;
For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg appMsg;    // message data structure&lt;br /&gt;
uint32 audioMonsterID;           // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Step 1: Prepare the message data structure. */&lt;br /&gt;
appMsg.senderAppID = appID;      // identifier of the sender&lt;br /&gt;
appMsg.type = APPLIBMT_Quit;     // type of message&lt;br /&gt;
&lt;br /&gt;
/* Step 2: Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Step 3: Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 &amp;amp;appMsg,&lt;br /&gt;
                 APPLIBMT_Quit);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0.&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;struct ApplicationMsg&#039;&#039;. 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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationCustomMsg customMsg;      // custom message data structure&lt;br /&gt;
uint32 audioMonsterID;                      // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Prepare the message data structure. */&lt;br /&gt;
customMsg.almsg.senderAppID = appID;        // identifier of the sender&lt;br /&gt;
customMsg.almsg.type = APPLIBMT_CustomMsg;  // type of message&lt;br /&gt;
customMsg.customMsg = &amp;quot;Start playback&amp;quot;;     // message text&lt;br /&gt;
&lt;br /&gt;
/* Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 (struct ApplicationMsg *) &amp;amp;customMsg,&lt;br /&gt;
                 APPLIBMT_CustomMsg);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pop-up Notifications (Ringhio Messages) ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;Ringhio messages&#039;&#039; 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).&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;All right, ma!&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&#039;t mean “Yes” or “No”.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;URL:http://www.supercoders.com&amp;quot;,&lt;br /&gt;
               APPNOTIFY_CloseOnDC, TRUE,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pop-up Message Style Guide ===&lt;br /&gt;
&lt;br /&gt;
* Keep the message text short. Give the user chance to read the entire message before it disappears.&lt;br /&gt;
* 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).&lt;br /&gt;
* Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| FindApplication()&lt;br /&gt;
| Searches for a previously registered application.&lt;br /&gt;
|-&lt;br /&gt;
| FreeApplicationList()&lt;br /&gt;
| Frees the list of applications generated by GetApplicationList().&lt;br /&gt;
|-&lt;br /&gt;
| GetAppLibAttrs()&lt;br /&gt;
| Obtains global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationAttrs()&lt;br /&gt;
| Obtains attributes of a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationList()&lt;br /&gt;
| Obtains the list of all currently registered applications.&lt;br /&gt;
|-&lt;br /&gt;
| LockApplicationIcon()&lt;br /&gt;
| Attempts to lock an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| Notify()&lt;br /&gt;
| Displays a pop-up message box.&lt;br /&gt;
|-&lt;br /&gt;
| RegisterApplication()&lt;br /&gt;
| Registers an application.&lt;br /&gt;
|-&lt;br /&gt;
| SendApplicationMsg()&lt;br /&gt;
| Sends a message to a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| SetAppLibAttrs()&lt;br /&gt;
| Sets or changes global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| SetApplicationAttrs()&lt;br /&gt;
| Sets or changes application attributes.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockApplicationIcon()&lt;br /&gt;
| Unlocks an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| UnregisterApplication()&lt;br /&gt;
| Unregisters an application.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Intuition_Screens&amp;diff=9148</id>
		<title>Intuition Screens</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Intuition_Screens&amp;diff=9148"/>
		<updated>2017-04-14T07:08:53Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Function Reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Intuition Screens ==&lt;br /&gt;
&lt;br /&gt;
Intuition screens are the basis of any display Intuition can make. Screens determine the fundamental characteristics of the display such as the resolution and palette and they set up the environment for multiple, overlapping windows that makes it possible for each application to have its own separate visual context. This section shows how to use existing screens and how to create new screens.&lt;br /&gt;
&lt;br /&gt;
=== Classic Intuition Screens ===&lt;br /&gt;
&lt;br /&gt;
Intuition screens have evolved since they were closely tied to the Classic Amiga&#039;s hardware chip set. See [[Classic_Intuition_Screens|Classic Intuition Screens]] for more information on the now obsolete API that was used to work with screens.&lt;br /&gt;
&lt;br /&gt;
== Types of Screens ==&lt;br /&gt;
&lt;br /&gt;
Screens are important because they determine the basic resolution and maximum number of colors in the display. Once a screen is set up, these attributes cannot be changed so any graphics work done on a given screen is restricted to that screen&#039;s resolution and number of colors. Hence, the type of screen used is a basic design decision.&lt;br /&gt;
&lt;br /&gt;
With Intuition screens, a video display can be created in any one of the many Amiga &#039;&#039;display modes&#039;&#039;. The basic parameters of the video display such as resolution, total size, frame rate, genlock compatibility, support of screen movement and number of colors are defined by these modes.&lt;br /&gt;
&lt;br /&gt;
Many display modes are available. All these display modes, including the specialized modes, are integrated through the graphics library &#039;&#039;display database&#039;&#039;. See [[Graphics_Primitives|Graphics Primitives]] for a complete list of all Amiga display modes.&lt;br /&gt;
&lt;br /&gt;
=== Multiple Screens ===&lt;br /&gt;
&lt;br /&gt;
All Intuition display objects (such as windows and menus) take graphical characteristics from the screen. These objects are restricted to the same resolution and maximum number of colors as the screen they operate in. Other characteristics such as the palette, pens and fonts are inherited from the screen (but may be changed on a case by case basis).&lt;br /&gt;
&lt;br /&gt;
This is not too much of a restriction because the Amiga can maintain multiple screens in memory at the same time. In other words, one application can use a high resolution screen (with 16 colors) while another application uses a low resolution screen (with 32 colors) at the same time. Screens typically take up the entire viewing area so only one is usually visible. But screens can be moved up and down or rearranged allowing the user (or application) to move between screens easily.&lt;br /&gt;
&lt;br /&gt;
=== Public Screens and Custom Screens ===&lt;br /&gt;
&lt;br /&gt;
An application may choose to use an existing screen or to create its own screen. For instance, the normal Amiga startup process opens the Workbench screen (Workbench is the Amiga&#039;s default user interface). Any application is free to use the Workbench screen instead of opening a new one. Screens that can be shared this way are called &#039;&#039;public&#039;&#039; screens.&lt;br /&gt;
&lt;br /&gt;
[[Public_Screen_Type|Public screens]] allow applications to open windows on them. Any screen may be set up as a public screen so that other applications may use it.&lt;br /&gt;
&lt;br /&gt;
The use of an existing public screen, like the Workbench screen, requires little effort by the application and does not use up any memory. However, using Workbench or another existing public screen means some flexibility is lost; the resolution, maximum number of colors and other attributes are already set. If the application cannot function under these limitations, it may open its own &#039;&#039;custom&#039;&#039; screen.&lt;br /&gt;
&lt;br /&gt;
[[Custom_Screen_Type|Custom screens]] allow for complete control of the display space so an application can get exactly the kind of display it wants. However, since creating a new, custom screen uses up memory, they should only be used when there are no suitable public screens available.&lt;br /&gt;
&lt;br /&gt;
Owners of a custom screen can keep their screen private, or they may allow other applications to share their screen by registering the screen with the operating system as a public screen. See the section on [[Public_Screen_Type|public screens]] for more about public screens and Workbench.&lt;br /&gt;
&lt;br /&gt;
=== Screen Components ===&lt;br /&gt;
&lt;br /&gt;
Screens have very little visual impact, they simply provide a resolution specific area to place other objects such as windows and menus. Screens have no borders. Only the title bar marks the screen limits (specifying the left and top edges, and the width of the screen), and the title bar may be hidden, or obscured by graphics or windows.&lt;br /&gt;
&lt;br /&gt;
The title bar also serves as the menu bar when the user presses the menu button on the mouse. The menu bar area is shared by all applications operating within the screen.&lt;br /&gt;
&lt;br /&gt;
Within the title bar, there are two gadgets: a screen drag gadget and a depth-arrangement gadget. The screen drag gadget allows the screen to be moved up and down. The depth-arrangement gadget allows the screen to be placed in front or behind all other screens.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig3-1.png|frame|center|An Intuition Screen (Workbench)]]&lt;br /&gt;
&lt;br /&gt;
Screens are always rectangular, and the areas at the sides and bottom of the display that are not within the screen&#039;s limits are filled with the background color. The area above all visible screens is filled with the background color of the highest screen. These areas surrounding the screen (normally unused) are known as the &#039;&#039;overscan&#039;&#039; area. The Amiga display system allows the overscan area to be used for graphics under special circumstances (see the section on [[Intuition_Screens#Overscan_and_the_Display_Clip|Overscan and the Display Clip]]).&lt;br /&gt;
&lt;br /&gt;
== Screen Attributes ==&lt;br /&gt;
&lt;br /&gt;
The sections above discuss only the basic functions and screen types that Intuition programmers need to understand to create a custom screen. Intuition supports an astonishing number of additional display features and options. In this section and the sections to follow, the finer points of screen attributes and the functions that control them are presented.&lt;br /&gt;
&lt;br /&gt;
Screen attributes are specified using the tag item scheme described [[Utility_Library|Utility Library]]. Therefore, the screen attributes are listed here by tag values.&lt;br /&gt;
&lt;br /&gt;
; SA_ErrorCode&lt;br /&gt;
: Extended error code. Data is a pointer to a long which will contain the error code on return if OpenScreenTagList() returns NULL. The error codes are described above.&lt;br /&gt;
&lt;br /&gt;
; SA_Left, SA_Top&lt;br /&gt;
: Initial screen position (left edge and top edge). Data is a long, signed value. Offsets are relative to the text overscan rectangle.&lt;br /&gt;
&lt;br /&gt;
: If SA_Left is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Width is not specified or is specified as STDSCREENWIDTH, then the left edge of the screen will default to the left edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Width is specified, then the left edge defaults to zero (text overscan rectangle left edge). Likewise, the top edge may, independent of the left edge value, default to zero or to the top edge of the actual display clip. If SA_Top is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Height is not specified or specified as STDSCREENHEIGHT, then the top edge of the screen will default to the top edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Height is specified, then the top edge defaults to zero (text overscan rectangle top edge).&lt;br /&gt;
&lt;br /&gt;
: When opening a full sized overscan screen, SA_Left should be set to the MinX value of the display clip Rectangle used for the screen and SA_Top should be set to the MinY value of the display clip. This may be taken from the defaults, as explained above, or explicitly set by the application. See the section below on &amp;quot;Overscan and the Display Clip&amp;quot; and the OpenScreen() Autodoc for more details.&lt;br /&gt;
&lt;br /&gt;
: If your screen is larger than your display clip, you may wish to set the SA_Left and SA_Top to values less than your display clip MinX and MinY in order to center a large screen on a smaller display. For an example of how to open a centered overscan screen, see &amp;quot;module/screen.c&amp;quot; in the [[IFF_Source_Code|IFF Source Code]].&lt;br /&gt;
&lt;br /&gt;
; SA_Width, SA_Height&lt;br /&gt;
: Screen dimensions. Data is a long, unsigned value. These may be larger, smaller or the same as the dimensions of the display clip Rectangle. The use of STDSCREENWIDTH and STDSCREENHEIGHT will make the screen size equal to the display clip size.&lt;br /&gt;
&lt;br /&gt;
: To calculate the width of the display clip Rectangle, subtract the MinX value from the MaxX value plus one. Similarly, the height of the display clip may be calculated by subtracting the MinY value from the MaxY value plus one.&lt;br /&gt;
&lt;br /&gt;
; SA_Depth&lt;br /&gt;
: Screen bitmap depth. Data is a long, unsigned value. The depth of the screen determines the number of available colors. See the &amp;quot;Graphics Primitives&amp;quot; for more information on hardware limitations of the display. Do not set the depth to a value greater than that supported by the specific display mode. This information is available to the application through the graphics library display database. The default is one bitplane.&lt;br /&gt;
&lt;br /&gt;
; SA_DisplayID&lt;br /&gt;
: Extended display mode key for the screen. Data is a long, unsigned value. By using DisplayIDs and the display database, applications can open a screen in any display mode available on a user&#039;s system, including PAL and NTSC modes. See the discussion of the display database in [[Display_Database|Display Database]] and the include file &amp;amp;lt;graphics/displayinfo.h&amp;amp;gt; for more information.&lt;br /&gt;
&lt;br /&gt;
; SA_Pens&lt;br /&gt;
: Pen specification for the screen. Data is a pointer to a UWORD array terminated with ¬†0, as found in the DrawInfo structure. Specifying the SA_Pens tag informs the system that the application is prepared to handle a screen rendered with the new 3D look of Intuition. See the section below on &amp;quot;DrawInfo and the 3D Look&amp;quot;. Omitting this tag produces a screen with a flat look, but whose color usage is more backwards compatible.&lt;br /&gt;
&lt;br /&gt;
; SA_DetailPen&lt;br /&gt;
: Detail pen for the screen. Data is a long, unsigned value. Used for rendering details in the screen title bar and menus. Use SA_Pens beginning with V36 for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look of Intuition. Instead this value will be used as the detail pen.&lt;br /&gt;
&lt;br /&gt;
; SA_BlockPen&lt;br /&gt;
: Block pen for the screen. Data is a long, unsigned value. Used for rendering block fills in the screen title bar and menus. Use SA_Pens for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look and this value will be used as the block pen.&lt;br /&gt;
&lt;br /&gt;
; SA_Title&lt;br /&gt;
: Default screen title. Data is a pointer to a character string. This is the title displayed when the active window has no screen title or when no window is active on the screen.&lt;br /&gt;
&lt;br /&gt;
; SA_Colors&lt;br /&gt;
: Specifies initial screen palette colors. Data is a pointer to an array of ColorSpec structures, terminated by a ColorSpec structure with ColorIndex = -1. Screen colors may be changed after the screen is opened with the graphics library functions SetRGB4() and LoadRGB4(). ColorSpec colors are right-justified, four bits per gun.&lt;br /&gt;
&lt;br /&gt;
; SA_FullPalette&lt;br /&gt;
: Initialize color table to entire preferences palette (32 colors), rather than the subset from V34 and earlier, namely pens 0-3, 17-19, with remaining palette as returned by GetColorMap(). Data is a boolean value (use TRUE to set the flag). Defaults to FALSE.&lt;br /&gt;
&lt;br /&gt;
; SA_Font&lt;br /&gt;
: Data is a pointer to a TextAttr structure (defined in &amp;amp;lt;graphics/text.h&amp;amp;gt;) which specifies the font, size and style to use for the screen. Equivalent to NewScreen.Font.&lt;br /&gt;
&lt;br /&gt;
; SA_SysFont&lt;br /&gt;
: Alternative to SA_Font. Selects one of the preferences system fonts. Data is a long, unsigned value, with the following values defined:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| 0 || Open screen with the user&#039;s preferred fixed width font (the default).&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Open screen with the user&#039;s preferred font, which may be proportional.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
: The Workbench screen is opened with {SA_SysFont, 1}. The following table summarizes how the font selected at OpenScreen() time effects subsequent text operations in screens and windows.&lt;br /&gt;
&lt;br /&gt;
: Intuition Font Selection Chart&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! What you tell OpenScreen()&lt;br /&gt;
! Screen font&lt;br /&gt;
! Window.RPort font&lt;br /&gt;
|-&lt;br /&gt;
| A&lt;br /&gt;
| NewScreen.Font = myfont&lt;br /&gt;
| myfont&lt;br /&gt;
| myfont&lt;br /&gt;
|-&lt;br /&gt;
| B&lt;br /&gt;
| NewScreen.Font = NULL&lt;br /&gt;
| GfxBase-&amp;gt;DefaultFont&lt;br /&gt;
| GfxBase-&amp;gt;DefaultFont&lt;br /&gt;
|-&lt;br /&gt;
| C&lt;br /&gt;
| {SA_Font, myfont}&lt;br /&gt;
| myfont&lt;br /&gt;
| myfont&lt;br /&gt;
|-&lt;br /&gt;
| D&lt;br /&gt;
| {SA_SysFont, 0}&lt;br /&gt;
| GfxBase-&amp;amp;gt;DefaultFont&lt;br /&gt;
| GfxBase-&amp;amp;gt;DefaultFont&lt;br /&gt;
|-&lt;br /&gt;
| E&lt;br /&gt;
| {SA_SysFont, 1}&lt;br /&gt;
| Font Prefs Screen text&lt;br /&gt;
| GfxBase-&amp;amp;gt;DefaultFont&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
: Notes:&lt;br /&gt;
&lt;br /&gt;
:* &#039;&#039;&#039;A&#039;&#039;&#039; and &#039;&#039;&#039;B&#039;&#039;&#039; are the options that existed in V34 and earlier OS versions.&lt;br /&gt;
&lt;br /&gt;
:* &#039;&#039;&#039;C&#039;&#039;&#039; and &#039;&#039;&#039;D&#039;&#039;&#039; are tags equivalent to &#039;&#039;&#039;A&#039;&#039;&#039; and &#039;&#039;&#039;B&#039;&#039;&#039; respectively.&lt;br /&gt;
&lt;br /&gt;
:* &#039;&#039;&#039;E&#039;&#039;&#039; is an option for V36 and higher. The Workbench screen uses this option.&lt;br /&gt;
&lt;br /&gt;
:* For myfont, any font may be used including a proportional one. This is true under all releases of the OS.&lt;br /&gt;
&lt;br /&gt;
:* GfxBase-&amp;amp;gt;DefaultFont is always monospace. (This is the &amp;quot;System Default Text&amp;quot; from Font Preferences.)&lt;br /&gt;
&lt;br /&gt;
:* &amp;quot;Font Prefs Screen&amp;quot; text (the &amp;quot;Screen Text&amp;quot; choice from Font Preferences) can be monospace or proportional.&lt;br /&gt;
&lt;br /&gt;
: The screen&#039;s font may not legally be changed after a screen is opened. The menu bar, window titles, menu items, and the contents of a string gadget all use the screen&#039;s font. The font used for menu items can be overridden in the menu item&#039;s IntuiText structure. Under V36 and higher, the font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.&lt;br /&gt;
&lt;br /&gt;
: The Window.RPort font shown above is the initial font that Intuition sets in your window&#039;s RastPort. It is legal to change that subsequently with SetFont(). IntuiText rendered into a window (either through PrintIText() or as a gadget&#039;s GadgetText) defaults to the window&#039;s RastPort font, but can be overridden using its ITextFont field. Text rendered with the graphics library call Text() uses the window&#039;s RastPort font.&lt;br /&gt;
&lt;br /&gt;
; SA_Type&lt;br /&gt;
: Equivalent to the SCREENTYPE bits of the NewScreen.Type field. Data is a long, unsigned value which may be set to either CUSTOMSCREEN or PUBLICSCREEN (WBENCHSCREEN is reserved for system use). See the tags SA_BitMap, SA_Behind, SA_Quiet, SA_ShowTitle and SA_AutoScroll for the other attributes of the NewScreen.Type field.&lt;br /&gt;
&lt;br /&gt;
; SA_BitMap&lt;br /&gt;
: Use a custom bitmap for this screen. Data is a pointer to a BitMap structure. This tag is equivalent to NewScreen.CustomBitMap and implies the CUSTOMBITMAP flag of the NewScreen.Type field. The application is responsible for allocating and freeing the screen&#039;s bitmap.&lt;br /&gt;
&lt;br /&gt;
; SA_Behind&lt;br /&gt;
: Open this screen behind all other screens in the system. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENBEHIND flag of the NewScreen.Type field.&lt;br /&gt;
&lt;br /&gt;
; SA_Quiet&lt;br /&gt;
: Disable Intuition rendering into screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENQUIET flag of the NewScreen.Type field. The screen will have no visible title bar or gadgets, but dragging and depth arrangement still function. In order to completely prevent Intuition from rendering into the screen, menu operations must be disabled for each window in the screen using WFLG_RMBTRAP.&lt;br /&gt;
&lt;br /&gt;
; SA_ShowTitle&lt;br /&gt;
: Setting this flag places the screen&#039;s title bar in front of any backdrop windows that are opened on the screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SHOWTITLE flag of the NewScreen.Type field. The title bar of the screen is always displayed behind any non-backdrop windows on that screen. This attribute can be changed after the screen is open with the ShowTitle() function.&lt;br /&gt;
&lt;br /&gt;
; SA_AutoScroll&lt;br /&gt;
: Setting this flag will enable autoscroll for this screen when it is the active screen. (Currently, the screen may only be made active by activating a window in that screen either under user or application control.) Data is a boolean value (TRUE to set flag). This tag is equivalent to the AUTOSCROLL flag of the NewScreen.Type field.&lt;br /&gt;
&lt;br /&gt;
: Autoscroll means that screens larger than the visible display will automatically scroll when the user moves the mouse to the edge of the screen. Without this tag, the user moves the screen either by using the screen drag bar, or by pressing the mouse select button anywhere within the screen while holding down the left Amiga key and moving the mouse.&lt;br /&gt;
&lt;br /&gt;
; SA_PubName&lt;br /&gt;
: Presence of this tag means that the screen is to be a public screen. Data is a pointer to a string. The string is the name of the public screen which is used by other applications to find the screen. This tag is order dependent, specify &#039;&#039;before&#039;&#039; SA_PubSig and SA_PubTask.&lt;br /&gt;
&lt;br /&gt;
; SA_PubSig, SA_PubTask&lt;br /&gt;
: Task ID (returned by FindTask()) and signal for notification that the last window has closed on a public screen. Data for SA_PubSig is a long, unsigned value. Data for SA_PubTask is a pointer to a Task structure. These two tags are order dependent, and must be specified &#039;&#039;after&#039;&#039; the tag SA_PubName.&lt;br /&gt;
&lt;br /&gt;
; SA_Overscan&lt;br /&gt;
: Set to one of the OSCAN_ specifiers to use a system standard overscan display clip and screen dimensions (unless otherwise specified). Data is a long, unsigned value. Do not specify this tag and SA_DClip. SA_Overscan is used to get one of the standard overscan dimensions, while SA_DClip is for custom dimensions. If a display clip is not specified with either SA_Overscan or SA_DClip, the display clip defaults to OSCAN_TEXT. See the section below on &amp;quot;Overscan and the Display Clip&amp;quot; for more information.&lt;br /&gt;
&lt;br /&gt;
; SA_DClip&lt;br /&gt;
: Custom display clip specification. Data is a pointer to a Rectangle structure that defines the screen display clip region.&lt;br /&gt;
&lt;br /&gt;
== DrawInfo and the 3D Look ==&lt;br /&gt;
&lt;br /&gt;
Whenever a new screen is created, Intuition also creates an auxiliary data structure called a DrawInfo. The DrawInfo structure provides information Intuition uses to support the 3D look and specifies graphical information for applications that use the screen. The information includes such items as aspect ratio (resolution), font, number of colors and drawing pens.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DrawInfo&lt;br /&gt;
{&lt;br /&gt;
    UWORD       dri_Version;    /* will be  DRI_VERSION                 */&lt;br /&gt;
    UWORD       dri_NumPens;    /* guaranteed to be &amp;amp;gt;= numDrIPens       */&lt;br /&gt;
    UWORD       *dri_Pens;      /* pointer to pen array                 */&lt;br /&gt;
&lt;br /&gt;
    struct TextFont     *dri_Font;  /* screen default font              */&lt;br /&gt;
    UWORD       dri_Depth;          /* (initial) depth of screen bitmap */&lt;br /&gt;
&lt;br /&gt;
    struct {    /* from DisplayInfo database for initial display mode   */&lt;br /&gt;
        UWORD   X;&lt;br /&gt;
        UWORD   Y;&lt;br /&gt;
    }           dri_Resolution;&lt;br /&gt;
&lt;br /&gt;
    ULONG       dri_Flags;              /* defined below                */&lt;br /&gt;
    ULONG       dri_Reserved[7];        /* avoid recompilation ;^)      */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before an application uses fields in the DrawInfo structure, it should check the version of the structure to ensure that all fields are available. If the field dri_Version is greater than or equal to the constant DRI_VERSION that the application was compiled with, it can be assured that all fields in DrawInfo that it knows about are being supported by Intuition.&lt;br /&gt;
&lt;br /&gt;
=== The Pen Specification in DrawInfo ===&lt;br /&gt;
&lt;br /&gt;
The drawing pen specification in DrawInfo.dri_Pens allows applications to use appropriate colors for graphic operations such as drawing text, shading 3D objects and filling items selected by the user.&lt;br /&gt;
&lt;br /&gt;
Intuition has two default sets of pens, one for multi-bitplane screens and one for single bitplane screens. In addition, there is a special compatibility mode for screens that do not specify the SA_Pens tag.&lt;br /&gt;
&lt;br /&gt;
; 3D Look&lt;br /&gt;
: The is the full 3D look as found by default on the Workbench screen. Objects are drawn so that light appears to come from the upper left of the screen with shadows cast to the lower right giving them a three-dimensional look.&lt;br /&gt;
&lt;br /&gt;
; Monochrome Look&lt;br /&gt;
: It is impossible to produce the full 3D look in a single bitplane (two color) screen. Intuition provides a fallback pen specification that is used in monochrome screens with no loss of information.&lt;br /&gt;
&lt;br /&gt;
; Compatible Look&lt;br /&gt;
: Custom screens that do not provide the SA_Pens tag are assumed to have no knowledge of the pen array. They are rendered in a special version of the monochrome new look, which uses the screen&#039;s DetailPen and BlockPen to get its colors. This is provided for compatibility with V34 and older versions of the operating system.&lt;br /&gt;
&lt;br /&gt;
It is very easy for an application to use the default pen specification. Simply specify an empty pen specification (in C, {~0}), and Intuition will fill in all of the values with defaults appropriate for the screen. This technique is demonstrated in the first two examples listed earlier in this article.&lt;br /&gt;
&lt;br /&gt;
For certain applications, a custom pen specification is required. A custom pen specification is set up when the screen is opened by using the SA_Pens tag and a pointer to a pen array. Currently, Intuition uses nine pens to support the 3D look. The application can specify all of these, or only a few pens and Intuition will fill in the rest. Intuition will only fill in pens that are past the end of those specified by the application, there is no facility for using default values for &amp;quot;leading&amp;quot; pens (those at the beginning of the array) without using the defaults for the rest of the pens.&lt;br /&gt;
&lt;br /&gt;
Using the pen specification of an existing public screen is a bit more involved. First, the application must get a pointer to the screen structure of the public screen using the LockPubScreen() call. A copy of the screen&#039;s DrawInfo structure may then be obtained by calling GetScreenDrawInfo(). The DrawInfo structure contains a copy of the pen specification for the screen that can be used in the OpenScreenTagList() call with the SA_Pens tag. The pen array is copied to the data structures of the new screen (it is not kept as a pointer to the information passed), so the application may immediately call FreeScreenDrawInfo() and UnlockPubScreen() after the new screen is open.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* publicscreen.c&lt;br /&gt;
** open a screen with the pens from a public screen.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/screens.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
VOID usePubScreenPens(void);&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
/* main(): open libraries, clean up when done.&lt;br /&gt;
*/&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  if ( IIntuition != NULL )&lt;br /&gt;
  {&lt;br /&gt;
    usePubScreenPens();&lt;br /&gt;
  }&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Open a screen that uses the pens of an existing public screen&lt;br /&gt;
** (the Workbench screen in this case).&lt;br /&gt;
*/&lt;br /&gt;
VOID usePubScreenPens(void)&lt;br /&gt;
{&lt;br /&gt;
struct Screen *my_screen;&lt;br /&gt;
struct TagItem screen_tags[2];&lt;br /&gt;
UBYTE *pubScreenName = &amp;quot;Workbench&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
struct Screen *pub_screen = NULL;&lt;br /&gt;
struct DrawInfo *screen_drawinfo = NULL;&lt;br /&gt;
&lt;br /&gt;
/* Get a lock on the Workbench screen */&lt;br /&gt;
pub_screen = IIntuition-&amp;gt;LockPubScreen(pubScreenName);&lt;br /&gt;
if ( pub_screen != NULL )&lt;br /&gt;
    {&lt;br /&gt;
    /* get the DrawInfo structure from the locked screen */&lt;br /&gt;
    screen_drawinfo = IIntuition-&amp;gt;GetScreenDrawInfo(pub_screen);&lt;br /&gt;
    if ( screen_drawinfo != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        /* the pens are copied in the OpenScreenTagList() call,&lt;br /&gt;
        ** so we can simply use a pointer to the pens in the tag list.&lt;br /&gt;
        **&lt;br /&gt;
        ** This works better if the depth and colors of the new screen&lt;br /&gt;
        ** matches that of the public screen.  Here we are forcing the&lt;br /&gt;
        ** workbench screen pens on a monochrome screen (which may not&lt;br /&gt;
        ** be a good idea).  You could add the tag:&lt;br /&gt;
        **      (SA_Depth, screen_drawinfo-&amp;gt;dri_Depth)&lt;br /&gt;
        */&lt;br /&gt;
        screen_tags[0].ti_Tag  = SA_Pens;&lt;br /&gt;
        screen_tags[0].ti_Data = (ULONG)(screen_drawinfo-&amp;gt;dri_Pens);&lt;br /&gt;
        screen_tags[1].ti_Tag  = TAG_END;&lt;br /&gt;
        screen_tags[1].ti_Data = NULL;&lt;br /&gt;
&lt;br /&gt;
        my_screen = IIntuition-&amp;gt;OpenScreenTagList(NULL, screen_tags);&lt;br /&gt;
        if (my_screen != NULL)&lt;br /&gt;
            {&lt;br /&gt;
            /* We no longer need to hold the lock on the public screen&lt;br /&gt;
            ** or a copy of its DrawInfo structure as we now have our&lt;br /&gt;
            ** own screen.  Release the screen.&lt;br /&gt;
            */&lt;br /&gt;
            IIntuition-&amp;gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);&lt;br /&gt;
            screen_drawinfo = NULL;&lt;br /&gt;
            IIntuition-&amp;gt;UnlockPubScreen(pubScreenName,pub_screen);&lt;br /&gt;
            pub_screen = NULL;&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Delay(90);   /* should be rest_of_program */&lt;br /&gt;
&lt;br /&gt;
            IIntuition-&amp;gt;CloseScreen(my_screen);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
/* These are freed in the main loop if OpenScreenTagList() does&lt;br /&gt;
** not fail.  If something goes wrong, free them here.&lt;br /&gt;
*/&lt;br /&gt;
if ( screen_drawinfo != NULL )&lt;br /&gt;
    IIntuition-&amp;gt;FreeScreenDrawInfo(pub_screen,screen_drawinfo);&lt;br /&gt;
if ( pub_screen!= NULL )&lt;br /&gt;
    IIntuition-&amp;gt;UnlockPubScreen(pubScreenName,pub_screen);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pen specification for the Workbench screen happens to match the Intuition default specification, however, this is not required and may change in the future. To create a screen that uses the pens defined in the Workbench screen, the application must get a copy of the pen array from the Workbench screen and use this copy with the SA_Pens tag as described above.&lt;br /&gt;
&lt;br /&gt;
Here is a list of the pens that support the 3D look along with their uses. To read the value of a particular pen, use UWORD penvalue = myDrawInfo-&amp;gt;dri_Pens[PENNAME], where myDrawInfo is a pointer to a DrawInfo structure and PENNAME is taken from the list below:&lt;br /&gt;
&lt;br /&gt;
; DETAILPEN&lt;br /&gt;
: Pen compatible with V34. Used to render text in the screen&#039;s title bar.&lt;br /&gt;
&lt;br /&gt;
; BLOCKPEN&lt;br /&gt;
: Pen compatible with V34. Used to fill the screen&#039;s title bar.&lt;br /&gt;
&lt;br /&gt;
; TEXTPEN&lt;br /&gt;
: Pen for regular text on BACKGROUNDPEN.&lt;br /&gt;
&lt;br /&gt;
; SHINEPEN&lt;br /&gt;
: Pen for the bright edge on 3D objects.&lt;br /&gt;
&lt;br /&gt;
; SHADOWPEN&lt;br /&gt;
: Pen for the dark edge on 3D objects.&lt;br /&gt;
&lt;br /&gt;
; FILLPEN&lt;br /&gt;
: Pen for filling the active window borders and selected gadgets.&lt;br /&gt;
&lt;br /&gt;
; FILLTEXTPEN&lt;br /&gt;
: Pen for text rendered over FILLPEN.&lt;br /&gt;
&lt;br /&gt;
; BACKGROUNDPEN&lt;br /&gt;
: Pen for the background color. Currently must be zero.&lt;br /&gt;
&lt;br /&gt;
; HIGHLIGHTTEXTPEN&lt;br /&gt;
: Pen for &amp;quot;special color&amp;quot; or highlighted text on BACKGROUNDPEN.&lt;br /&gt;
&lt;br /&gt;
=== The Font Specification in DrawInfo ===&lt;br /&gt;
&lt;br /&gt;
Font information for a screen comes from a number of different places.&lt;br /&gt;
&lt;br /&gt;
The application may specify the font to be used in a screen by providing the SA_Font tag with a TextAttr structure. In this case, the font will be used by the screen and will be the default font for the RastPort of any window opening in the screen.&lt;br /&gt;
&lt;br /&gt;
If the application requests the user&#039;s preferred monospace font, it is taken from GfxBase-&amp;amp;gt;DefaultFont. Any window&#039;s RastPorts are also initialized to use this same font.&lt;br /&gt;
&lt;br /&gt;
The screen font selected by the user from the Preferences font editor may be used for the screen by using the SA_SysFont tag. This font, the &amp;quot;preferred screen font&amp;quot;, may be proportional. For compatibility reasons, if this font is specified for the screen, the window&#039;s RastPort will be initialized to GfxBase-&amp;amp;gt;DefaultFont (a non-proportional font).&lt;br /&gt;
&lt;br /&gt;
To access information on an open screen&#039;s font, the application may reference Screen.Font or DrawInfo.dri_Font. These fonts are identical, the DrawInfo structure simply provides an alternate method of accessing the information. Note that Screen.Font is a pointer to a TextAttr structure and that DrawInfo.dri_Font is a pointer to a TextFont structure. The application may use whichever form is best suited to its requirements.&lt;br /&gt;
&lt;br /&gt;
It is illegal to change the screen&#039;s font after the screen is opened. This means that the font specified in the Screen and DrawInfo structures is guaranteed to remain open as long is the screen is open.&lt;br /&gt;
&lt;br /&gt;
The menu bar, window titles, menu items, and the contents of a string gadget all use the screen&#039;s font. The font used for menu items can be overridden in the menu item&#039;s IntuiText structure. The font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.&lt;br /&gt;
&lt;br /&gt;
For more information on screen fonts, see the description of the SA_Font and SA_SysFont tags in the &amp;quot;Screen Attributes&amp;quot; section above.&lt;br /&gt;
&lt;br /&gt;
== Overscan and the Display Clip ==&lt;br /&gt;
&lt;br /&gt;
Screens may be larger or smaller than the defined display area (&#039;&#039;overscan rectangle&#039;&#039; or &#039;&#039;display clip&#039;&#039;). When a screen is smaller than the display area, the display clip acts as a &amp;quot;container&amp;quot; for the screen. The screen may be moved anywhere within the display clip. When a screen is larger than the display area, the display clip acts as a &amp;quot;window&amp;quot; into the screen. The screen may be moved so that different parts are visible. Each dimension of the screen is independent and may be larger than, the same as, or smaller than the dimensions of the display clip.&lt;br /&gt;
&lt;br /&gt;
The system is very flexible in its specification of screen size. Unless an application fixes its screen size with hard coded values, it should be prepared to handle the possibility that the user has changed the default overscan presets or the default monitor.&lt;br /&gt;
&lt;br /&gt;
Use the constants STDSCREENHEIGHT and STDSCREENWIDTH with the SA_Width and SA_Height tags to open a screen the same size as the display clip. These constants will work with any of the preset overscan values set with SA_Overscan, and with custom overscan values set with SA_DClip.&lt;br /&gt;
&lt;br /&gt;
=== Preset Overscan Values ===&lt;br /&gt;
&lt;br /&gt;
Four preset overscan dimensions are provided. Applications that support overscan should use these preset values where possible since they will be tailored to each individual system. Avoid using custom values that happen to look good on a specific system. However, be aware that the size and positioning of overscan screens can be different on every system depending on how the user has set Overscan Preferences. These preset values are also dependent on the underlying display mode so keep in mind that both offset and size parameters will change under different screen modes. Overscan presets can be used, among other things, with the SA_Overscan tag to set the size of the screen&#039;s display clip or passed as an argument to QueryOverscan() to find their current overscan settings.&lt;br /&gt;
&lt;br /&gt;
; OSCAN_TEXT&lt;br /&gt;
: This overscan region is based on user preference settings and indicates a display that is completely within the visible bounds of the monitor. The View origin is set to the upper left corner of the text overscan rectangle which is the highest leftmost point known to be visible on the physical display. This position is set by the user through the Overscan Preferences editor. All screen positions and display clips are relative to this origin.&lt;br /&gt;
&lt;br /&gt;
; OSCAN_STANDARD&lt;br /&gt;
: The edges of OSCAN_STANDARD display are also based on user preferences and are set to be just outside the visible bounds of the monitor. OSCAN_STANDARD provides the smallest possible display that will fill the entire screen with no border around it. Parts of the display created with OSCAN_STANDARD may not be visible to the user.&lt;br /&gt;
&lt;br /&gt;
; OSCAN_MAX&lt;br /&gt;
: Create the largest display fully supported by Intuition and the graphics library. This is the largest size for which all enclosed sizes and positions are legal. Parts of the display created with OSCAN_MAX may not be visible to the user.&lt;br /&gt;
&lt;br /&gt;
; OSCAN_VIDEO&lt;br /&gt;
: Create the largest display, restricted by the hardware. This is the only legal size and position that is possibly (but not necessarily) larger than OSCAN_MAX. You must use the exact size and position specified. OSCAN_VIDEO does not support variable left edge, top edge positioning. Parts of the display created with OSCAN_VIDEO may not be visible to the user.&lt;br /&gt;
&lt;br /&gt;
If custom clipping is required, a display clip may be explicitly specified using the SA_DClip tag and a Rectangle structure specification. This custom rectangle must fit within the OSCAN_MAX rectangle, offset included. It is not permitted to specify custom rectangles whose values are in between OSCAN_MAX and OSCAN_VIDEO, nor is it permitted to specify rectangles larger than OSCAN_VIDEO. For an example of how to open a centered overscan screen based on user preferences, see the &amp;quot;module/screen.c&amp;quot; listing in the [[IFF_Source_Code|IFF Source Code]].&lt;br /&gt;
&lt;br /&gt;
Use the Graphics library call VideoControl() to find the true display clip of a screen. See the Graphics Autodocs and [[Graphics_Primitives|Graphics Primitives]] for more information on VideoControl(). The ViewPortExtra structure contains the display clip information.&lt;br /&gt;
&lt;br /&gt;
If any dimension of a screen is not equal to the equivalent display clip dimension, then the screen may be scrolled. If the screen&#039;s dimensions are smaller than the display clip, then the screen may be positioned within the display clip. If the screen is larger than the display clip, then it may be positioned such that any part of the screen is visible.&lt;br /&gt;
&lt;br /&gt;
AutoScroll may be activated by setting the tag SA_AutoScroll. Screens will only scroll when they are the active screen. Activate a window in the screen to make the screen active.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=About the Default Display Clip|text=The default display clip for a screen is the entire screen, that is, the rectangle starting from the upper left corner of the screen and ending at the lower right corner of the screen. This display clip is only used if the application does not specify SA_Overscan or SA_DClip. When using this default display clip the screen will not scroll as the screen exactly fits into the clipping region.}}&lt;br /&gt;
&lt;br /&gt;
When opening a window in an overscanned screen, it is often useful to open it relative to the visible part of the screen rather than relative to the entire screen. Use QueryOverscan() to find the overscan region and where the screen is positioned relative to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG QueryOverscan(ULONG displayID, struct Rectangle *rect, WORD overscanType );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example was taken from [[Intuition_Windows|Intuition Windows]] in the section &amp;quot;Visible Display Sized Window Example&amp;quot;. The complete example is reproduced there.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* this technique returns the text overscan rectangle of the screen that we&lt;br /&gt;
** are opening on.  If you really need the actual value set into the display&lt;br /&gt;
** clip of the screen, use the VideoControl() command of the graphics library&lt;br /&gt;
** to return a copy of the ViewPortExtra structure.  See the Graphics&lt;br /&gt;
** library articles and Autodocs for more details.&lt;br /&gt;
**&lt;br /&gt;
** GetVPModeID() is a graphics call...&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
screen_modeID = IGraphics-&amp;gt;GetVPModeID(&amp;amp;(pub_screen-&amp;gt;ViewPort))))&lt;br /&gt;
if (screen_modeID != INVALID_ID)&lt;br /&gt;
    {&lt;br /&gt;
    if ( IGraphics-&amp;gt;QueryOverscan(screen_modeID, &amp;amp;rect, OSCAN_TEXT) )&lt;br /&gt;
        {&lt;br /&gt;
        /* if this screen&#039;s origin is up or to the left of the */&lt;br /&gt;
        /* view origin then move the window down and to the right */&lt;br /&gt;
        left = max(0, -pub_screen-&amp;gt;LeftEdge);&lt;br /&gt;
        top  = max(0, -pub_screen-&amp;gt;TopEdge);&lt;br /&gt;
&lt;br /&gt;
        /* get width and height from size of display clip */&lt;br /&gt;
        width  = rect.MaxX - rect.MinX + 1;&lt;br /&gt;
        height = rect.MaxY - rect.MinY + 1;&lt;br /&gt;
&lt;br /&gt;
        /* adjust height for pulled-down screen (only show visible part) */&lt;br /&gt;
        if (pub_screen-&amp;gt;TopEdge &amp;gt; 0)&lt;br /&gt;
            height -= pub_screen-&amp;gt;TopEdge;&lt;br /&gt;
&lt;br /&gt;
        /* ensure that window fits on screen */&lt;br /&gt;
        height = min(height, pub_screen-&amp;gt;Height);&lt;br /&gt;
        width  = min(width,  pub_screen-&amp;gt;Width);&lt;br /&gt;
&lt;br /&gt;
        /* make sure window is at least minimum size */&lt;br /&gt;
        width  = max(width,  MIN_WINDOW_WIDTH);&lt;br /&gt;
        height = max(height, MIN_WINDOW_HEIGHT);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Intuition Screens and the Graphics Library ==&lt;br /&gt;
&lt;br /&gt;
As previously mentioned, an Intuition screen is related to a number of underlying graphics library structures.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Graphics Data Structures Used with Screens&lt;br /&gt;
! Structure Name&lt;br /&gt;
! Description&lt;br /&gt;
! Include File&lt;br /&gt;
|-&lt;br /&gt;
| View || Root structure of the graphics display system || &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ViewPort || The graphics structure that corresponds to a screen || &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ColorMap || Contains size and pointer to the screen&#039;s color table || &amp;amp;lt;graphics/view.h&amp;amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RastPort || Holds drawing, pen and font settings and the BitMap address || &amp;amp;lt;graphics/rastport.h&amp;amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These data structures are unified in Intuition&#039;s Screen structure (which also incorporates higher level Intuition constructs such as menus and windows). Here&#039;s a brief explanation of the graphics library structures used with Intuition.&lt;br /&gt;
&lt;br /&gt;
; View&lt;br /&gt;
: The View is the graphics structure that corresponds to the whole display, including all visible screens. The system has just one View; it&#039;s what you see on the monitor. The address of the View may be obtained from any screen by using ViewAddress().&lt;br /&gt;
&lt;br /&gt;
; ViewPort&lt;br /&gt;
: The ViewPort is the underlying graphics structure corresponding to a screen. Every screen has one ViewPort. To get the address of the ViewPort from the Screen structure, use (&amp;amp;amp;my_screen-&amp;amp;gt;ViewPort). From the ViewPort an application may obtain pointers to all the screen&#039;s bitplanes and to its color table.&lt;br /&gt;
&lt;br /&gt;
; ColorMap&lt;br /&gt;
: The ColorMap contains a pointer to the color table, an array of 32 WORDs for the hardware color registers. Use SetRGB4(), GetRGB4(), SetRGB4CM() and LoadRGB4() from the graphics library to access the color table. Do not read or write it directly.&lt;br /&gt;
&lt;br /&gt;
; RastPort&lt;br /&gt;
: A RastPort controls the graphics rendering to &#039;&#039;any&#039;&#039; display area (not just screens). Screens have a RastPort to allow direct rendering into the screen. Applications may find the RastPort address of a screen with (&amp;amp;amp;my_screen-&amp;amp;gt;RastPort). The screen&#039;s BitMap can also be found via the RastPort in the RastPort.BitMap field. This generally is not useful since applications normally render into windows.&lt;br /&gt;
&lt;br /&gt;
=== Changing Screen Colors ===&lt;br /&gt;
&lt;br /&gt;
Screen colors are set at the time the screen is opened with the SA_Colors tag. If the colors need to be changed after the screen is opened, the graphics library function, LoadRGB4() should be used. To change a single entry in the color table, use SetRGB4() and SetRGB4CM(). See [[Graphics_Primitives|Graphics Primitives]] for more information on these functions.&lt;br /&gt;
&lt;br /&gt;
=== Direct Screen Access ===&lt;br /&gt;
&lt;br /&gt;
Sometimes an application may want direct access to the custom screen&#039;s bitmap to use with low-level graphics library calls. This may be useful if the application needs to do custom manipulation of the display but also needs Intuition functionality. For instance, an application may want to use the graphics library primitives to perform double buffering then, when detecting user input, switch to Intuition control of the screen so that windows, gadgets and menus may be used to process the user input. If an application chooses to combine these techniques, it must take special care to avoid conflicts with Intuition rendered graphics. An example of how to do this is listed in the next section, &amp;quot;Advanced Screen Programming&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Application programs that open custom screens may use the screen&#039;s display memory in any way they choose. However, this memory is also used by Intuition for windows and other high level display components on the screen. Writing directly to the screen memory, whether through direct access or through graphics library calls that access the screen&#039;s RastPort, is not compatible with many Intuition constructs such as windows and menus.&lt;br /&gt;
&lt;br /&gt;
Techniques such as this require great care and understanding of the Amiga. If possible, the application should avoid these techniques and only use standard Intuition display and input processing. Directly accessing the screen&#039;s bitmap, while possible, is not recommended. A better way to access the screen display is through windows. Windows provide access to the screen through layers which perform clipping and arbitration between multiple drawing areas.&lt;br /&gt;
&lt;br /&gt;
Alternatives to writing directly to a screen, such as using a backdrop window, greatly limit the number of cases where an application must access screen memory. The ShowTitle() function allows the screen&#039;s title bar layer to be positioned in front of or behind any backdrop windows that are opened on the screen. Hence, a backdrop window may be created that uses the entire visible area of the monitor.&lt;br /&gt;
&lt;br /&gt;
Application programs that use existing public screens do not have the same freedom to access the screen&#039;s display memory as they do with custom screens. In general, public screens must be shared through the use of windows and menus rather than directly accessing the screen&#039;s display memory.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Use Direct Access Only On Screens You &amp;quot;Own&amp;quot;|text=An application may not steal the bitmap of a screen that it does not own. Stealing the Workbench screen&#039;s bitmap, or that of any other public screen, is strictly illegal. Accessing the underlying graphics structures of a screen may only be done on custom screens opened by the application itself.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do Not Perform Layers Operations Directly|text=While layers are not part of the graphics library, it is appropriate to mention them here. Certain types of layers operations are not allowed with Intuition. You may not, for example, call SizeLayer() on a window (use SizeWindow() instead). To access layers library features with screens, use Intuition windows!}}&lt;br /&gt;
&lt;br /&gt;
A custom screen may be created to allow for modification of the screen&#039;s Copper list. The Copper is the display synchronized co-processor that handles the actual video display by directly affecting the hardware registers. See the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; or the graphics library articles for more information on programming the Copper.&lt;br /&gt;
&lt;br /&gt;
=== Limitations of the Graphics Subsystem ===&lt;br /&gt;
&lt;br /&gt;
If each of the visible screens does not have the same physical attributes, it may not be possible to display the data in its proper screen mode. &#039;&#039;Screen coercion&#039;&#039; is the technique that allows multiple screens with differing physical attributes to be displayed simultaneously. When a coerced screen is visible, its aspect ratio and colors may appear significantly changed. This is normal and the screen will be displayed correctly when it is the frontmost screen.&lt;br /&gt;
&lt;br /&gt;
Hardware restrictions prevent certain types of displays. For instance, screens always use the full width of the display, regardless of the width of the overscan rectangle. This prevents any changes in display mode within a video line. Other modes, such as the VGA modes, require specific revisions of the custom chips and may not be available on all machines. See [[Graphics_Primitives|Graphics Primitives]] and the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; for more information on Amiga display organization and limitations.&lt;br /&gt;
&lt;br /&gt;
== Advanced Screen Programming ==&lt;br /&gt;
&lt;br /&gt;
This section discusses how to perform double-buffering of Intuition screens and other advanced topics. Dual-playfield Intuition screens are covered in the [[Classic_Intuition_Screens|Classic Intuition Screens]] section.&lt;br /&gt;
&lt;br /&gt;
=== Screen Locking ===&lt;br /&gt;
&lt;br /&gt;
Applications wishing to block all screen rendering so that they can do &amp;quot;exceptional&amp;quot; rendering (eg. dragging icons,&lt;br /&gt;
pop-up menus, etc.) need to LockScreen(). By using LockScreen() and UnlockScreen(), such applications avoid the risk of deadlocking with Intuition. Intuition will behave much like it does when menus are down; that is to say that layer-related events such as window size, depth, or position changes are deferred until the screen is unfrozen.&lt;br /&gt;
&lt;br /&gt;
The application has the following obligations:&lt;br /&gt;
# When UnlockScreen() is finally called, the screen&#039;s bitmap must be bit-for-bit the same as when you called LockScreen(), with the exception of rendering into your own windows&#039; RastPorts. That is to say, you are responsible for doing non-destructive rendering during the time you have the screen frozen.&lt;br /&gt;
#  You may only make regular rendering calls (graphics calls, plus Intuition imagery functions such as PrintIText(), DrawImage(), DrawBorder()). Do not call any gadget refresh functions, OpenWindow(), Begin/EndRefresh(), etc. or any Intuition function that involves locking IntuitionBase (namely LockIBase()).&lt;br /&gt;
# Don&#039;t freeze the screen on the user. Freeze it only in response to the user. Typically, if you don&#039;t unfreeze the screen when the user lets the mouse button go, you&#039;re freezing for too long.&lt;br /&gt;
# Do not freeze a screen you don&#039;t own. It&#039;s OK to freeze a public screen (including the Workbench screen), or your own custom screen. If it&#039;s a public screen, be sure you hold a public-screen lock (see LockPubScreen()) or have a window open on it.&lt;br /&gt;
&lt;br /&gt;
The active window will still receive mouse-, keyboard- and timer-messages. So if you want your window to get the IDCMP messages, the following must be done:&lt;br /&gt;
# make your window the active one, if it isn&#039;t (see ActivateWindow)&lt;br /&gt;
# wait for IDCMP_ACTIVEWINDOW&lt;br /&gt;
# then call LockScreen()&lt;br /&gt;
# do your input processing. When receiving IDCMP_INACTIVEWINDOW abort and call UnlockScreen() (your window could have gone inactive before you had the chance to call LockScreen())&lt;br /&gt;
# when done, call UnlockScreen()&lt;br /&gt;
&lt;br /&gt;
{{Note|BOOPSI class implementers must use the LockScreenGI() and UnlockScreenGI() functions.}}&lt;br /&gt;
&lt;br /&gt;
==== Locking the Screen List ====&lt;br /&gt;
&lt;br /&gt;
Sometimes there is a need for applications to lock Intuition&#039;s entire screen list. For these situations use the LockScreenList() and UnlockScreenList functions. While the screen list is locked, Intuition is &amp;quot;frozen&amp;quot; from the user&#039;s point of view. It is best to copy any required information (e.g. screen titles) and then release the list.&lt;br /&gt;
&lt;br /&gt;
=== Double Buffering ===&lt;br /&gt;
&lt;br /&gt;
Intuition supports double (or multiple) buffering inside an Intuition screen, with full support for menus, and support for certain&lt;br /&gt;
kinds of gadget.&lt;br /&gt;
&lt;br /&gt;
The AllocScreenBuffer() call allows you to create other BitMap buffers for your screen. The SB_SCREEN_BITMAP flag is used to get a buffer handle for the BitMap currently in use in the screen.  Subsequent buffers are allocated with the SB_COPY_BITMAP flag instead, which instructs Intuition to copy the current imagery (e.g., the screen title-bar and any of your rendering) into the alternate BitMap. Normally you let Intuition allocate these alternate BitMaps, but if your screen is CUSTOMBITMAP, you should allocate the alternate BitMaps yourself.&lt;br /&gt;
&lt;br /&gt;
To swap buffers, call the ChangeScreenBuffer() function, which attempts to install the new buffer. ChangeScreenBuffer() will fail if Intuition is temporarily unable to make the change (say while gadgets or menus are active). Intuition builds these functions on top of the graphics.library ChangeVPBitMap() function, so the signalling information that graphics provides is also available to the Intuition user.&lt;br /&gt;
&lt;br /&gt;
To clean up, call FreeScreenBuffer() for each screen buffer. It is not necessary to restore the original buffer before freeing things. Consult the autodocs for full details.&lt;br /&gt;
&lt;br /&gt;
When the user accesses the screen&#039;s menus, buffer-swapping will stop. The ChangeScreenBuffer() call will return failure during this time. When the user finishes his menu selection, buffer-swapping will be possible again. Only a small subset of gadgets are supportable in double-buffered screens. These gadgets are those whose imagery returns to the initial state when you release them (e.g., action buttons or the screen&#039;s depth gadget). To use other kinds of gadgets (such as sliders or string gadgets) you need to put them on a separate screen, which can be an attached screen.&lt;br /&gt;
&lt;br /&gt;
Windows with borders are not supportable on double-buffered screens. Double-buffered screens are expected to consist nearly entirely of custom rendering.&lt;br /&gt;
&lt;br /&gt;
An example program illustrating double-buffering under Intuition, with menu-lending and an attached screen to hold two slider gadgets is provided.&lt;br /&gt;
&lt;br /&gt;
==== doublebuffer.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * doublebuffer.c - shows double-buffering, attached screens, menu lending&lt;br /&gt;
 *&lt;br /&gt;
 * Example shows use of double-buffering functions to achieve&lt;br /&gt;
 * 60 frame-per-second animation.  Also shows use of menus in&lt;br /&gt;
 * double-buffered screens, and the use of attached screens with&lt;br /&gt;
 * menu-lending to achieve the appearance of slider gadgets in&lt;br /&gt;
 * double-buffered screens.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/videocontrol.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfxbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuitionbase.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/gadtools.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
STRPTR init_all( void  );&lt;br /&gt;
void error_exit( STRPTR errorstring );&lt;br /&gt;
struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi );&lt;br /&gt;
BOOL handleIntuiMessage( struct IntuiMessage *imsg );&lt;br /&gt;
void handleDBufMessage( struct Message *dbmsg );&lt;br /&gt;
ULONG handleBufferSwap( void );&lt;br /&gt;
struct BitMap *makeImageBM( void );&lt;br /&gt;
void CloseWindowSafely( struct Window *win );&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Some constants to handle the rendering of the animated face */&lt;br /&gt;
#define BM_WIDTH	120&lt;br /&gt;
#define BM_HEIGHT	60&lt;br /&gt;
#define BM_DEPTH	2&lt;br /&gt;
&lt;br /&gt;
/* Odd numbers to give a non-repeating bounce */&lt;br /&gt;
#define CONTROLSC_TOP		191&lt;br /&gt;
#define SC_ID			HIRES_KEY&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* User interface constants and variables */&lt;br /&gt;
&lt;br /&gt;
#define GAD_HORIZ	1&lt;br /&gt;
#define GAD_VERT	2&lt;br /&gt;
&lt;br /&gt;
#define MENU_RUN	1&lt;br /&gt;
#define MENU_STEP	2&lt;br /&gt;
#define MENU_QUIT	3&lt;br /&gt;
#define MENU_HSLOW	4&lt;br /&gt;
#define MENU_HFAST	5&lt;br /&gt;
#define MENU_VSLOW	6&lt;br /&gt;
#define MENU_VFAST	7&lt;br /&gt;
&lt;br /&gt;
struct TextAttr Topaz80 =&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;topaz.font&amp;quot;, 		/* Name */&lt;br /&gt;
    8, 				/* YSize */&lt;br /&gt;
    FS_NORMAL,			/* Style */&lt;br /&gt;
    FPF_ROMFONT|FPF_DESIGNED,	/* Flags */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct TagItem vctags[] =&lt;br /&gt;
{&lt;br /&gt;
    VTAG_BORDERSPRITE_SET, TRUE,&lt;br /&gt;
    TAG_END, 0,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
UWORD pens[] =&lt;br /&gt;
{&lt;br /&gt;
    0, /* DETAILPEN */&lt;br /&gt;
    1, /* BLOCKPEN	*/&lt;br /&gt;
    1, /* TEXTPEN	*/&lt;br /&gt;
    2, /* SHINEPEN	*/&lt;br /&gt;
    1, /* SHADOWPEN	*/&lt;br /&gt;
    3, /* FILLPEN	*/&lt;br /&gt;
    1, /* FILLTEXTPEN	*/&lt;br /&gt;
    0, /* BACKGROUNDPEN	*/&lt;br /&gt;
    2, /* HIGHLIGHTTEXTPEN	*/&lt;br /&gt;
&lt;br /&gt;
    1, /* BARDETAILPEN	*/&lt;br /&gt;
    2, /* BARBLOCKPEN	*/&lt;br /&gt;
    1, /* BARTRIMPEN	*/&lt;br /&gt;
&lt;br /&gt;
    (UWORD)~0,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct NewMenu demomenu[] =&lt;br /&gt;
{&lt;br /&gt;
	{ NM_TITLE, &amp;quot;Project&amp;quot;, 		        0 , 0, 0, 0, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Run&amp;quot;, 		           &amp;quot;R&amp;quot;, 0, 0, ( APTR ) MENU_RUN, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Step&amp;quot;, 		         &amp;quot;S&amp;quot;, 0, 0, ( APTR ) MENU_STEP, },&lt;br /&gt;
	{  NM_ITEM, NM_BARLABEL, 	        0 , 0, 0, 0, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Slower Horizontal&amp;quot;, &amp;quot;1&amp;quot;, 0, 0, ( APTR ) MENU_HSLOW, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Faster Horizontal&amp;quot;, &amp;quot;2&amp;quot;, 0, 0, ( APTR ) MENU_HFAST, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Slower Vertical&amp;quot;, 	 &amp;quot;3&amp;quot;, 0, 0, ( APTR ) MENU_VSLOW, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Faster Vertical&amp;quot;, 	 &amp;quot;4&amp;quot;, 0, 0, ( APTR ) MENU_VFAST, },&lt;br /&gt;
&lt;br /&gt;
	{  NM_ITEM, NM_BARLABEL, 	        0 , 0, 0, 0, },&lt;br /&gt;
	{  NM_ITEM, &amp;quot;Quit&amp;quot;, 		         &amp;quot;Q&amp;quot;, 0, 0, ( APTR ) MENU_QUIT, },&lt;br /&gt;
&lt;br /&gt;
	{   NM_END, 0, 			              0 , 0, 0, 0, },&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Screen *canvassc = NULL;&lt;br /&gt;
struct Screen *controlsc = NULL;&lt;br /&gt;
struct Window *controlwin = NULL;&lt;br /&gt;
struct Window *canvaswin = NULL;&lt;br /&gt;
struct Gadget *glist = NULL;&lt;br /&gt;
struct Gadget *horizgad, *vertgad;&lt;br /&gt;
struct Menu *menu = NULL;&lt;br /&gt;
void *canvasvi = NULL;&lt;br /&gt;
void *controlvi = NULL;&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
#define	OK_REDRAW	1	/* Buffer fully detached, ready for redraw */&lt;br /&gt;
#define OK_SWAPIN	2	/* Buffer redrawn, ready for swap-in */&lt;br /&gt;
&lt;br /&gt;
struct GraphicsIFace *IGraphics = NULL;&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
struct GadToolsIFace *IGadTools = NULL;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *dbufport = NULL;&lt;br /&gt;
struct MsgPort *userport = NULL;&lt;br /&gt;
&lt;br /&gt;
struct ScreenBuffer *scbuf[] =&lt;br /&gt;
{&lt;br /&gt;
    NULL,&lt;br /&gt;
    NULL,&lt;br /&gt;
};&lt;br /&gt;
struct RastPort rport[ 2 ];&lt;br /&gt;
&lt;br /&gt;
ULONG status[ 2 ];&lt;br /&gt;
&lt;br /&gt;
LONG prevx[ 2 ] =&lt;br /&gt;
{&lt;br /&gt;
    50, 50,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
LONG prevy[ 2 ] =&lt;br /&gt;
{&lt;br /&gt;
    50, 50,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ULONG buf_current, buf_nextdraw, buf_nextswap;&lt;br /&gt;
ULONG count;&lt;br /&gt;
struct BitMap *face = NULL;&lt;br /&gt;
LONG x, y, xstep, xdir, ystep, ydir;&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    STRPTR errorstring;&lt;br /&gt;
    ULONG sigs;&lt;br /&gt;
    BOOL terminated = FALSE;&lt;br /&gt;
&lt;br /&gt;
    /* Let&#039;s get everything initialized */&lt;br /&gt;
    if ( errorstring = init_all() )&lt;br /&gt;
    {&lt;br /&gt;
	error_exit( errorstring );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    count = 0;&lt;br /&gt;
    buf_current = 0;&lt;br /&gt;
    buf_nextdraw = 1;&lt;br /&gt;
    buf_nextswap = 1;&lt;br /&gt;
    sigs = 0;&lt;br /&gt;
&lt;br /&gt;
    while ( !terminated )&lt;br /&gt;
    {&lt;br /&gt;
	/* Check for and handle any IntuiMessages */&lt;br /&gt;
	if ( sigs &amp;amp; ( 1 &amp;lt;&amp;lt; userport-&amp;gt;mp_SigBit ) )&lt;br /&gt;
	{&lt;br /&gt;
	    struct IntuiMessage *imsg;&lt;br /&gt;
&lt;br /&gt;
	    while ( imsg = IGadTools-&amp;gt;GT_GetIMsg( userport ) )&lt;br /&gt;
	    {&lt;br /&gt;
		terminated |= handleIntuiMessage( imsg );&lt;br /&gt;
		IGadTools-&amp;gt;GT_ReplyIMsg( imsg );&lt;br /&gt;
	    }&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Check for and handle any double-buffering messages.&lt;br /&gt;
	 * Note that double-buffering messages are &amp;quot;replied&amp;quot; to&lt;br /&gt;
	 * us, so we don&#039;t want to reply them to anyone.&lt;br /&gt;
	 */&lt;br /&gt;
	if ( sigs &amp;amp; ( 1 &amp;lt;&amp;lt; dbufport-&amp;gt;mp_SigBit ) )&lt;br /&gt;
	{&lt;br /&gt;
	    struct Message *dbmsg;&lt;br /&gt;
	    while ( dbmsg = IExec-&amp;gt;GetMsg( dbufport ) )&lt;br /&gt;
	    {&lt;br /&gt;
		handleDBufMessage( dbmsg );&lt;br /&gt;
	    }&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if ( !terminated )&lt;br /&gt;
	{&lt;br /&gt;
	    ULONG held_off = 0;&lt;br /&gt;
	    /* Only handle swapping buffers if count is non-zero */&lt;br /&gt;
	    if ( count )&lt;br /&gt;
	    {&lt;br /&gt;
		held_off = handleBufferSwap();&lt;br /&gt;
	    }&lt;br /&gt;
	    if ( held_off )&lt;br /&gt;
	    {&lt;br /&gt;
		/* If were held-off at ChangeScreenBuffer() time, then we&lt;br /&gt;
		 * need to try ChangeScreenBuffer() again, without awaiting&lt;br /&gt;
		 * a signal.  We WaitTOF() to avoid busy-looping.&lt;br /&gt;
		 */&lt;br /&gt;
		 IGraphics-&amp;gt;WaitTOF();&lt;br /&gt;
	    }&lt;br /&gt;
	    else&lt;br /&gt;
	    {&lt;br /&gt;
		/* If we were not held-off, then we&#039;re all done&lt;br /&gt;
		 * with what we have to do.  We&#039;ll have no work to do&lt;br /&gt;
		 * until some kind of signal arrives.  This will normally&lt;br /&gt;
		 * be the arrival of the dbi_SafeMessage from the ROM&lt;br /&gt;
		 * double-buffering routines, but it might also be an&lt;br /&gt;
		 * IntuiMessage.&lt;br /&gt;
		 */&lt;br /&gt;
		sigs = IExec-&amp;gt;Wait( ( 1 &amp;lt;&amp;lt; dbufport-&amp;gt;mp_SigBit ) | ( 1 &amp;lt;&amp;lt; userport-&amp;gt;mp_SigBit ) );&lt;br /&gt;
	    }&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    error_exit( NULL );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Handle the rendering and swapping of the buffers */&lt;br /&gt;
&lt;br /&gt;
ULONG handleBufferSwap( void )&lt;br /&gt;
{&lt;br /&gt;
    ULONG held_off = 0;&lt;br /&gt;
    /* &#039;buf_nextdraw&#039; is the next buffer to draw into.&lt;br /&gt;
     * The buffer is ready for drawing when we&#039;ve received the&lt;br /&gt;
     * dbi_SafeMessage for that buffer.  Our routine to handle&lt;br /&gt;
     * messaging from the double-buffering functions sets the&lt;br /&gt;
     * OK_REDRAW flag when this message has appeared.&lt;br /&gt;
     *&lt;br /&gt;
     * Here, we set the OK_SWAPIN flag after we&#039;ve redrawn&lt;br /&gt;
     * the imagery, since the buffer is ready to be swapped in.&lt;br /&gt;
     * We clear the OK_REDRAW flag, since we&#039;re done with redrawing&lt;br /&gt;
     */&lt;br /&gt;
    if ( status[ buf_nextdraw ] == OK_REDRAW )&lt;br /&gt;
    {&lt;br /&gt;
	x += xstep*xdir;&lt;br /&gt;
	if ( x &amp;lt; 0 )&lt;br /&gt;
	{&lt;br /&gt;
	    x = 0;&lt;br /&gt;
	    xdir = 1;&lt;br /&gt;
	}&lt;br /&gt;
	else if ( x &amp;gt; canvassc-&amp;gt;Width - BM_WIDTH )&lt;br /&gt;
	{&lt;br /&gt;
	    x = canvassc-&amp;gt;Width - BM_WIDTH - 1;&lt;br /&gt;
	    xdir = -1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	y += ystep*ydir;&lt;br /&gt;
	if ( y &amp;lt; canvassc-&amp;gt;BarLayer-&amp;gt;Height )&lt;br /&gt;
	{&lt;br /&gt;
	    y = canvassc-&amp;gt;BarLayer-&amp;gt;Height;&lt;br /&gt;
	    ydir = 1;&lt;br /&gt;
	}&lt;br /&gt;
	else if ( y &amp;gt;= CONTROLSC_TOP - BM_HEIGHT )&lt;br /&gt;
	{&lt;br /&gt;
	    y = CONTROLSC_TOP - BM_HEIGHT - 1;&lt;br /&gt;
	    ydir = -1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	IGraphics-&amp;gt;SetAPen( &amp;amp;rport[ buf_nextdraw ], 0 );&lt;br /&gt;
	IGraphics-&amp;gt;RectFill( &amp;amp;rport[ buf_nextdraw ],&lt;br /&gt;
	    prevx[ buf_nextdraw ], prevy[ buf_nextdraw ],&lt;br /&gt;
	    prevx[ buf_nextdraw ] + BM_WIDTH - 1, prevy[ buf_nextdraw ] + BM_HEIGHT - 1 );&lt;br /&gt;
	prevx[ buf_nextdraw ] = x;&lt;br /&gt;
	prevy[ buf_nextdraw ] = y;&lt;br /&gt;
&lt;br /&gt;
	IGraphics-&amp;gt;BltBitMapRastPort( face, 0, 0, &amp;amp;rport[ buf_nextdraw ], x, y,&lt;br /&gt;
	    BM_WIDTH, BM_HEIGHT, 0xc0 );&lt;br /&gt;
&lt;br /&gt;
	IGraphics-&amp;gt;WaitBlit(); /* Gots to let the BBMRP finish */&lt;br /&gt;
&lt;br /&gt;
	status[ buf_nextdraw ] = OK_SWAPIN;&lt;br /&gt;
&lt;br /&gt;
	/* Toggle which the next buffer to draw is.&lt;br /&gt;
	 * If you&#039;re using multiple ( &amp;gt;2 ) buffering, you&lt;br /&gt;
	 * would use&lt;br /&gt;
	 *&lt;br /&gt;
	 *    buf_nextdraw = ( buf_nextdraw+1 ) % NUMBUFFERS;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	buf_nextdraw ^= 1;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Let&#039;s make sure that the next frame is rendered before we swap...&lt;br /&gt;
     */&lt;br /&gt;
    if ( status[ buf_nextswap ] == OK_SWAPIN )&lt;br /&gt;
    {&lt;br /&gt;
	scbuf[ buf_nextswap ]-&amp;gt;sb_DBufInfo-&amp;gt;dbi_SafeMessage.mn_ReplyPort = dbufport;&lt;br /&gt;
&lt;br /&gt;
	if ( IIntuition-&amp;gt;ChangeScreenBuffer( canvassc, scbuf[ buf_nextswap ] ) )&lt;br /&gt;
	{&lt;br /&gt;
	    status[ buf_nextswap ] = 0;&lt;br /&gt;
&lt;br /&gt;
	    buf_current = buf_nextswap;&lt;br /&gt;
	    /* Toggle which the next buffer to swap in is.&lt;br /&gt;
	     * If you&#039;re using multiple ( &amp;gt;2 ) buffering, you&lt;br /&gt;
	     * would use&lt;br /&gt;
	     *&lt;br /&gt;
	     *    buf_nextswap = ( buf_nextswap+1 ) % NUMBUFFERS;&lt;br /&gt;
	     *&lt;br /&gt;
	     */&lt;br /&gt;
	    buf_nextswap ^= 1;&lt;br /&gt;
&lt;br /&gt;
	    count--;&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
	    held_off = 1;&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
    return( held_off );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Handle Intuition messages */&lt;br /&gt;
&lt;br /&gt;
BOOL handleIntuiMessage( struct IntuiMessage *imsg )&lt;br /&gt;
{&lt;br /&gt;
    UWORD code = imsg-&amp;gt;Code;&lt;br /&gt;
    BOOL terminated = FALSE;&lt;br /&gt;
&lt;br /&gt;
    switch ( imsg-&amp;gt;Class )&lt;br /&gt;
    {&lt;br /&gt;
	case IDCMP_GADGETDOWN:&lt;br /&gt;
	case IDCMP_GADGETUP:&lt;br /&gt;
	case IDCMP_MOUSEMOVE:&lt;br /&gt;
	    switch ( ( ( struct Gadget * )imsg-&amp;gt;IAddress )-&amp;gt;GadgetID )&lt;br /&gt;
	    {&lt;br /&gt;
		case GAD_HORIZ:&lt;br /&gt;
		    xstep = code;&lt;br /&gt;
		    break;&lt;br /&gt;
&lt;br /&gt;
		case GAD_VERT:&lt;br /&gt;
		    ystep = code;&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
	    break;&lt;br /&gt;
&lt;br /&gt;
	case IDCMP_VANILLAKEY:&lt;br /&gt;
	    switch ( code )&lt;br /&gt;
	    {&lt;br /&gt;
		case &#039;S&#039;:&lt;br /&gt;
		case &#039;s&#039;:&lt;br /&gt;
		    count = 1;&lt;br /&gt;
		    break;&lt;br /&gt;
&lt;br /&gt;
		case &#039;R&#039;:&lt;br /&gt;
		case &#039;r&#039;:&lt;br /&gt;
		    count = ~0;&lt;br /&gt;
		    break;&lt;br /&gt;
&lt;br /&gt;
		case &#039;Q&#039;:&lt;br /&gt;
		case &#039;q&#039;:&lt;br /&gt;
		    count = 0;&lt;br /&gt;
		    terminated = TRUE;&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
	    break;&lt;br /&gt;
&lt;br /&gt;
	case IDCMP_MENUPICK:&lt;br /&gt;
	    while ( code != MENUNULL )&lt;br /&gt;
	    {&lt;br /&gt;
		struct MenuItem *item;&lt;br /&gt;
&lt;br /&gt;
		item = IIntuition-&amp;gt;ItemAddress( menu, code );&lt;br /&gt;
		switch ( ( ULONG ) GTMENUITEM_USERDATA( item ) )&lt;br /&gt;
		{&lt;br /&gt;
		    case MENU_RUN:&lt;br /&gt;
			count = ~0;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_STEP:&lt;br /&gt;
			count = 1;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_QUIT:&lt;br /&gt;
			count = 0;&lt;br /&gt;
			terminated = TRUE;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_HSLOW:&lt;br /&gt;
			if ( xstep &amp;gt; 0 )&lt;br /&gt;
			{&lt;br /&gt;
			    xstep--;&lt;br /&gt;
			}&lt;br /&gt;
			IGadTools-&amp;gt;GT_SetGadgetAttrs( horizgad, controlwin, NULL,&lt;br /&gt;
			    GTSL_Level, xstep,&lt;br /&gt;
			    TAG_END );&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_HFAST:&lt;br /&gt;
			if ( xstep &amp;lt; 9 )&lt;br /&gt;
			{&lt;br /&gt;
			    xstep++;&lt;br /&gt;
			}&lt;br /&gt;
			IGadTools-&amp;gt;GT_SetGadgetAttrs( horizgad, controlwin, NULL,&lt;br /&gt;
			    GTSL_Level, xstep,&lt;br /&gt;
			    TAG_END );&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_VSLOW:&lt;br /&gt;
			if ( ystep &amp;gt; 0 )&lt;br /&gt;
			{&lt;br /&gt;
			    ystep--;&lt;br /&gt;
			}&lt;br /&gt;
			IGadTools-&amp;gt;GT_SetGadgetAttrs( vertgad, controlwin, NULL,&lt;br /&gt;
			    GTSL_Level, ystep,&lt;br /&gt;
			    TAG_END );&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		    case MENU_VFAST:&lt;br /&gt;
			if ( ystep &amp;lt; 9 )&lt;br /&gt;
			{&lt;br /&gt;
			    ystep++;&lt;br /&gt;
			}&lt;br /&gt;
			IGadTools-&amp;gt;GT_SetGadgetAttrs( vertgad, controlwin, NULL,&lt;br /&gt;
			    GTSL_Level, ystep,&lt;br /&gt;
			    TAG_END );&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		code = item-&amp;gt;NextSelect;&lt;br /&gt;
	    }&lt;br /&gt;
	    break;&lt;br /&gt;
    }&lt;br /&gt;
    return( terminated );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
void handleDBufMessage( struct Message *dbmsg )&lt;br /&gt;
{&lt;br /&gt;
    ULONG buffer;&lt;br /&gt;
&lt;br /&gt;
    /* dbi_SafeMessage is followed by an APTR dbi_UserData1, which&lt;br /&gt;
     * contains the buffer number.  This is an easy way to extract&lt;br /&gt;
     * it.&lt;br /&gt;
     * The dbi_SafeMessage tells us that it&#039;s OK to redraw the&lt;br /&gt;
     * in the previous buffer.&lt;br /&gt;
     */&lt;br /&gt;
    buffer = ( ULONG ) *( ( APTR ** ) ( dbmsg+1 ) );&lt;br /&gt;
    /* Mark the previous buffer as OK to redraw into.&lt;br /&gt;
     * If you&#039;re using multiple ( &amp;gt;2 ) buffering, you&lt;br /&gt;
     * would use&lt;br /&gt;
     *&lt;br /&gt;
     *    ( buffer + NUMBUFFERS - 1 ) % NUMBUFFERS&lt;br /&gt;
     *&lt;br /&gt;
     */&lt;br /&gt;
    status[ buffer^1 ] = OK_REDRAW;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Get the resources and objects we need */&lt;br /&gt;
&lt;br /&gt;
STRPTR init_all( void )&lt;br /&gt;
{&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if ( IGraphics == NULL )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open Gfx V50\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if ( IIntuition == NULL )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open Intuition V50\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    struct Library *GadToolsBase = IExec-&amp;gt;OpenLibrary(&amp;quot;gadtools.library&amp;quot;, 50);&lt;br /&gt;
    IGadTools = (struct GadToolsIFace*)IExec-&amp;gt;GetInterface(GadToolsBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if ( IGadtools == NULL )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open GadTools V50\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( dbufport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Failed to create port\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( userport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Failed to create port\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( canvassc = IIntuition-&amp;gt;OpenScreenTags( NULL,&lt;br /&gt;
	SA_DisplayID, SC_ID,&lt;br /&gt;
	SA_Overscan, OSCAN_TEXT,&lt;br /&gt;
	SA_Depth, 2,&lt;br /&gt;
	SA_AutoScroll, 1,&lt;br /&gt;
	SA_Pens, pens,&lt;br /&gt;
	SA_ShowTitle, TRUE,&lt;br /&gt;
	SA_Title, &amp;quot;Intuition double-buffering example&amp;quot;,&lt;br /&gt;
	SA_VideoControl, vctags,&lt;br /&gt;
	SA_Font, &amp;amp;Topaz80,&lt;br /&gt;
	TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open screen\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( canvasvi = IGadTools-&amp;gt;GetVisualInfo( canvassc, TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t get VisualInfo\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( canvaswin = IIntuition-&amp;gt;OpenWindowTags( NULL,&lt;br /&gt;
	WA_NoCareRefresh, TRUE,&lt;br /&gt;
	WA_Activate, TRUE,&lt;br /&gt;
	WA_Borderless, TRUE,&lt;br /&gt;
	WA_Backdrop, TRUE,&lt;br /&gt;
	WA_CustomScreen, canvassc,&lt;br /&gt;
	WA_NewLookMenus, TRUE,&lt;br /&gt;
	TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open window\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
    canvaswin-&amp;gt;UserPort = userport;&lt;br /&gt;
&lt;br /&gt;
    IIntuition-&amp;gt;ModifyIDCMP( canvaswin, IDCMP_MENUPICK | IDCMP_VANILLAKEY );&lt;br /&gt;
&lt;br /&gt;
    if ( !( controlsc = IIntuition-&amp;gt;OpenScreenTags( NULL,&lt;br /&gt;
	SA_DisplayID, SC_ID,&lt;br /&gt;
	SA_Overscan, OSCAN_TEXT,&lt;br /&gt;
	SA_Depth, 2,&lt;br /&gt;
	SA_Pens, pens,&lt;br /&gt;
	SA_Top, CONTROLSC_TOP,&lt;br /&gt;
	SA_Height, 28,&lt;br /&gt;
	SA_Parent, canvassc,&lt;br /&gt;
	SA_ShowTitle, FALSE,&lt;br /&gt;
	SA_Draggable, FALSE,&lt;br /&gt;
	SA_VideoControl, vctags,&lt;br /&gt;
	SA_Quiet, TRUE,&lt;br /&gt;
	SA_Font, &amp;amp;Topaz80,&lt;br /&gt;
	TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open screen\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( controlvi = IGadTools-&amp;gt;GetVisualInfo( controlsc, TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t get VisualInfo\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( menu = IGadTools-&amp;gt;CreateMenus( demomenu, TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t create menus\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !IGadTools-&amp;gt;LayoutMenus( menu, canvasvi,&lt;br /&gt;
	GTMN_NewLookMenus, TRUE,&lt;br /&gt;
	TAG_END ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t layout menus\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !createAllGadgets( &amp;amp;glist, controlvi ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t create gadgets\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* A borderless backdrop window so we can get input */&lt;br /&gt;
    if ( !( controlwin = IIntuition-&amp;gt;OpenWindowTags( NULL,&lt;br /&gt;
	WA_NoCareRefresh, TRUE,&lt;br /&gt;
	WA_Activate, TRUE,&lt;br /&gt;
	WA_Borderless, TRUE,&lt;br /&gt;
	WA_Backdrop, TRUE,&lt;br /&gt;
	WA_CustomScreen, controlsc,&lt;br /&gt;
	WA_NewLookMenus, TRUE,&lt;br /&gt;
	WA_Gadgets, glist,&lt;br /&gt;
	TAG_END ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t open window\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    controlwin-&amp;gt;UserPort = userport;&lt;br /&gt;
    IIntuition-&amp;gt;ModifyIDCMP( controlwin, SLIDERIDCMP | IDCMP_MENUPICK | IDCMP_VANILLAKEY );&lt;br /&gt;
&lt;br /&gt;
    IGadTools-&amp;gt;GT_RefreshWindow( controlwin, NULL );&lt;br /&gt;
    IIntuition-&amp;gt;SetMenuStrip( canvaswin, menu );&lt;br /&gt;
    IIntuition-&amp;gt;LendMenus( controlwin, canvaswin );&lt;br /&gt;
&lt;br /&gt;
    if ( !( scbuf[ 0 ] = IIntuition-&amp;gt;AllocScreenBuffer( canvassc, NULL, SB_SCREEN_BITMAP ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t allocate ScreenBuffer 1\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !( scbuf[ 1 ] = IIntuition-&amp;gt;AllocScreenBuffer( canvassc, NULL, SB_COPY_BITMAP ) ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t allocate ScreenBuffer 2\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Let&#039;s use the UserData to store the buffer number, for&lt;br /&gt;
     * easy identification when the message comes back.&lt;br /&gt;
     */&lt;br /&gt;
    scbuf[ 0 ]-&amp;gt;sb_DBufInfo-&amp;gt;dbi_UserData1 = ( APTR ) ( 0 );&lt;br /&gt;
    scbuf[ 1 ]-&amp;gt;sb_DBufInfo-&amp;gt;dbi_UserData1 = ( APTR ) ( 1 );&lt;br /&gt;
    status[ 0 ] = OK_REDRAW;&lt;br /&gt;
    status[ 1 ] = OK_REDRAW;&lt;br /&gt;
&lt;br /&gt;
    if ( !( face = makeImageBM() ) )&lt;br /&gt;
    {&lt;br /&gt;
	return( &amp;quot;Couldn&#039;t allocate image bitmap\n&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
    IGraphics-&amp;gt;InitRastPort( &amp;amp;rport[ 0 ] );&lt;br /&gt;
    IGraphics-&amp;gt;InitRastPort( &amp;amp;rport[ 1 ] );&lt;br /&gt;
    rport[ 0 ].BitMap = scbuf[ 0 ]-&amp;gt;sb_BitMap;&lt;br /&gt;
    rport[ 1 ].BitMap = scbuf[ 1 ]-&amp;gt;sb_BitMap;&lt;br /&gt;
&lt;br /&gt;
    x = 50;&lt;br /&gt;
    y = 70;&lt;br /&gt;
    xstep = 1;&lt;br /&gt;
    xdir = 1;&lt;br /&gt;
    ystep = 1;&lt;br /&gt;
    ydir = -1;&lt;br /&gt;
&lt;br /&gt;
    /* All is OK */&lt;br /&gt;
    return( 0 );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Draw a crude &amp;quot;face&amp;quot; for animation */&lt;br /&gt;
&lt;br /&gt;
#define MAXVECTORS	10&lt;br /&gt;
&lt;br /&gt;
struct BitMap *makeImageBM( void )&lt;br /&gt;
{&lt;br /&gt;
    struct BitMap *bm;&lt;br /&gt;
    struct RastPort rport;&lt;br /&gt;
    struct AreaInfo area;&lt;br /&gt;
    struct TmpRas tmpRas;&lt;br /&gt;
    PLANEPTR planePtr;&lt;br /&gt;
&lt;br /&gt;
    BYTE areabuffer[ MAXVECTORS*5 ];&lt;br /&gt;
&lt;br /&gt;
    if ( bm = ( struct BitMap * )IGraphics-&amp;gt;AllocBitMap( BM_WIDTH, BM_HEIGHT,&lt;br /&gt;
	BM_DEPTH, BMF_CLEAR, NULL ) )&lt;br /&gt;
    {&lt;br /&gt;
	if ( planePtr = IGraphics-&amp;gt;AllocRaster( BM_WIDTH, BM_HEIGHT ) )&lt;br /&gt;
	{&lt;br /&gt;
	    IGraphics-&amp;gt;InitRastPort( &amp;amp;rport );&lt;br /&gt;
	    rport.BitMap = bm;&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;InitArea( &amp;amp;area, areabuffer, MAXVECTORS );&lt;br /&gt;
	    rport.AreaInfo = &amp;amp;area;&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;InitTmpRas( &amp;amp;tmpRas, planePtr, RASSIZE( BM_WIDTH, BM_HEIGHT ) );&lt;br /&gt;
	    rport.TmpRas = &amp;amp;tmpRas;&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;SetABPenDrMd( &amp;amp;rport, 3, 0, JAM1 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEllipse( &amp;amp;rport, BM_WIDTH/2, BM_HEIGHT/2,&lt;br /&gt;
		BM_WIDTH/2-4, BM_HEIGHT/2-4 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEnd( &amp;amp;rport );&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;SetAPen( &amp;amp;rport, 2 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEllipse( &amp;amp;rport, 5*BM_WIDTH/16, BM_HEIGHT/4,&lt;br /&gt;
		BM_WIDTH/9, BM_HEIGHT/9 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEllipse( &amp;amp;rport, 11*BM_WIDTH/16, BM_HEIGHT/4,&lt;br /&gt;
		BM_WIDTH/9, BM_HEIGHT/9 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEnd( &amp;amp;rport );&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;SetAPen( &amp;amp;rport, 1 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEllipse( &amp;amp;rport, BM_WIDTH/2, 3*BM_HEIGHT/4,&lt;br /&gt;
		BM_WIDTH/3, BM_HEIGHT/9 );&lt;br /&gt;
	    IGraphics-&amp;gt;AreaEnd( &amp;amp;rport );&lt;br /&gt;
&lt;br /&gt;
	    IGraphics-&amp;gt;FreeRaster( planePtr, BM_WIDTH, BM_HEIGHT );&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
	    IGraphics-&amp;gt;FreeBitMap( bm );&lt;br /&gt;
	    bm = NULL;&lt;br /&gt;
	}&lt;br /&gt;
    return( bm );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Make a pair of slider gadgets to control horizontal and vertical&lt;br /&gt;
 * speed of motion.&lt;br /&gt;
 */&lt;br /&gt;
struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi )&lt;br /&gt;
{&lt;br /&gt;
    struct NewGadget ng;&lt;br /&gt;
    struct Gadget *gad;&lt;br /&gt;
&lt;br /&gt;
    gad = IGadTools-&amp;gt;CreateContext( glistptr );&lt;br /&gt;
&lt;br /&gt;
    ng.ng_LeftEdge = 100;&lt;br /&gt;
    ng.ng_TopEdge = 1;&lt;br /&gt;
    ng.ng_Width = 100;&lt;br /&gt;
    ng.ng_Height = 12;&lt;br /&gt;
    ng.ng_GadgetText = &amp;quot;Horiz:  &amp;quot;;&lt;br /&gt;
    ng.ng_TextAttr = &amp;amp;Topaz80;&lt;br /&gt;
    ng.ng_VisualInfo = vi;&lt;br /&gt;
    ng.ng_GadgetID = GAD_HORIZ;&lt;br /&gt;
    ng.ng_Flags = 0;&lt;br /&gt;
&lt;br /&gt;
    horizgad = gad = IGadTools-&amp;gt;CreateGadget( SLIDER_KIND, gad, &amp;amp;ng,&lt;br /&gt;
	GTSL_Min, 0,&lt;br /&gt;
	GTSL_Max, 9,&lt;br /&gt;
	GTSL_Level, 1,&lt;br /&gt;
	GTSL_MaxLevelLen, 1,&lt;br /&gt;
	GTSL_LevelFormat, &amp;quot;%ld&amp;quot;,&lt;br /&gt;
	TAG_END );&lt;br /&gt;
&lt;br /&gt;
    ng.ng_LeftEdge += 200;&lt;br /&gt;
    ng.ng_GadgetID = GAD_VERT;&lt;br /&gt;
    ng.ng_GadgetText = &amp;quot;Vert:  &amp;quot;;&lt;br /&gt;
    vertgad = gad = IGadTools-&amp;gt;CreateGadget( SLIDER_KIND, gad, &amp;amp;ng,&lt;br /&gt;
	GTSL_Min, 0,&lt;br /&gt;
	GTSL_Max, 9,&lt;br /&gt;
	GTSL_Level, 1,&lt;br /&gt;
	GTSL_MaxLevelLen, 1,&lt;br /&gt;
	GTSL_LevelFormat, &amp;quot;%ld&amp;quot;,&lt;br /&gt;
	TAG_END );&lt;br /&gt;
&lt;br /&gt;
    return( gad );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* Clean up everything and exit, printing the errorstring if any */&lt;br /&gt;
void error_exit( STRPTR errorstring )&lt;br /&gt;
{&lt;br /&gt;
    if ( controlwin )&lt;br /&gt;
    {&lt;br /&gt;
	IIntuition-&amp;gt;ClearMenuStrip( controlwin );&lt;br /&gt;
	CloseWindowSafely( controlwin );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( canvaswin )&lt;br /&gt;
    {&lt;br /&gt;
	IIntuition-&amp;gt;ClearMenuStrip( canvaswin );&lt;br /&gt;
	CloseWindowSafely( canvaswin );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( controlsc )&lt;br /&gt;
    {&lt;br /&gt;
	IIntuition-&amp;gt;CloseScreen( controlsc );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( canvassc )&lt;br /&gt;
    {&lt;br /&gt;
	IIntuition-&amp;gt;FreeScreenBuffer( canvassc, scbuf[ 1 ] );&lt;br /&gt;
	IIntuition-&amp;gt;FreeScreenBuffer( canvassc, scbuf[ 0 ] );&lt;br /&gt;
	IIntuition-&amp;gt;CloseScreen( canvassc );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( dbufport )&lt;br /&gt;
    {&lt;br /&gt;
	IExec-&amp;gt;FreeSysObject(ASOT_PORT, dbufport );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( userport )&lt;br /&gt;
    {&lt;br /&gt;
	IExec-&amp;gt;FreeSysObject(ASOT_PORT, userport );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( IGadTools )&lt;br /&gt;
    {&lt;br /&gt;
	IGadTools-&amp;gt;FreeGadgets( glist );&lt;br /&gt;
	IGadTools-&amp;gt;FreeMenus( menu );&lt;br /&gt;
	IGadTools-&amp;gt;FreeVisualInfo( canvasvi );&lt;br /&gt;
	IGadTools-&amp;gt;FreeVisualInfo( controlvi );&lt;br /&gt;
  struct Library *libBase = IGadTools-&amp;gt;Data.LibBase;&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGadTools);&lt;br /&gt;
	IExec-&amp;gt;CloseLibrary( libBase );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( IIntuition )&lt;br /&gt;
    {&lt;br /&gt;
  struct Library *libBase = IIntuition-&amp;gt;Data.LibBase;&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
	IExec-&amp;gt;CloseLibrary( libBase );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( face )&lt;br /&gt;
    {&lt;br /&gt;
	IGraphics-&amp;gt;FreeBitMap( face );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( IGraphics )&lt;br /&gt;
    {&lt;br /&gt;
  struct Library *libBase = IGraphics-&amp;gt;Data.LibBase;&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
	IExec-&amp;gt;CloseLibrary( libBase );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( errorstring )&lt;br /&gt;
    {&lt;br /&gt;
	IDOS-&amp;gt;Printf( errorstring );&lt;br /&gt;
	exit( 20 );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    exit( 0 );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*----------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* these functions close an Intuition window&lt;br /&gt;
 * that shares a port with other Intuition&lt;br /&gt;
 * windows or IPC customers.&lt;br /&gt;
 *&lt;br /&gt;
 * We are careful to set the UserPort to&lt;br /&gt;
 * null before closing, and to free&lt;br /&gt;
 * any messages that it might have been&lt;br /&gt;
 * sent.&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;quot;exec/types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;exec/nodes.h&amp;quot;&lt;br /&gt;
#include &amp;quot;exec/lists.h&amp;quot;&lt;br /&gt;
#include &amp;quot;exec/ports.h&amp;quot;&lt;br /&gt;
#include &amp;quot;intuition/intuition.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void&lt;br /&gt;
CloseWindowSafely( struct Window *win )&lt;br /&gt;
{&lt;br /&gt;
    /* we forbid here to keep out of race conditions with Intuition */&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
    /* send back any messages for this window&lt;br /&gt;
     * that have not yet been processed&lt;br /&gt;
     */&lt;br /&gt;
    IIntuition-&amp;gt;StripIntuiMessages( win-&amp;gt;UserPort, win );&lt;br /&gt;
&lt;br /&gt;
    /* clear UserPort so Intuition will not free it */&lt;br /&gt;
    win-&amp;gt;UserPort = NULL;&lt;br /&gt;
&lt;br /&gt;
    /* tell Intuition to stop sending more messages */&lt;br /&gt;
    IIntuition-&amp;gt;ModifyIDCMP( win, 0L );&lt;br /&gt;
&lt;br /&gt;
    /* turn multitasking back on */&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
    /* and really close the window */&lt;br /&gt;
    IIntuition-&amp;gt;CloseWindow( win );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other Screen Functions ==&lt;br /&gt;
&lt;br /&gt;
Other screen functions provided by Intuition control screen depth arrangement, screen movement, the screen title bar and provide a visual &amp;quot;error beep&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Screen Depth Arrangement ===&lt;br /&gt;
&lt;br /&gt;
ScreenToFront() and ScreenToBack() make a screen either the frontmost or the backmost screen. If an application needs to render into a screen before the screen becomes visible to the user, the screen may be opened behind all other screens and later moved to the front when ready with ScreenToFront().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID ScreenToFront( struct Screen * );&lt;br /&gt;
VOID ScreenToBack ( struct Screen * );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depth control of screens is also available through the depth arrangement gadget in the screen&#039;s title bar or through keyboard shortcuts. The N key with the Left-Amiga qualifier moves the Workbench screen to front. The M key with the Left-Amiga qualifier moves the frontmost screen to back. Repeated selection of Left-Amiga-M will cycle through available screens. These keys are processed through the keymap and will retain their value even if the key location changes.&lt;br /&gt;
&lt;br /&gt;
=== Screen Movement and Scrolling ===&lt;br /&gt;
&lt;br /&gt;
The MoveScreen() function moves the screen origin by the number of pixels specified in dx and dy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID MoveScreen( struct Screen *myscreen, WORD dx, WORD dy );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Calls to MoveScreen() are asynchronous; the screen is not necessarily moved upon return of this function. If the calls happen too quickly, there may be unexpected results. One way to pace these calls is to call the function one time for each IDCMP_INTUITICKS event.&lt;br /&gt;
&lt;br /&gt;
Screen movement is also available through the screen&#039;s drag gadget in the title bar and through a keyboard/mouse shortcut. Left-Amiga with the select button of the mouse anywhere within the screen will drag the screen (even if the title bar is totally concealed by a window). Dragging a screen down will reveal any screen(s) behind it. Screens are never revealed to the left, right or bottom of another screen.&lt;br /&gt;
&lt;br /&gt;
Additionally, oversized screens may be moved with the autoscroll feature. With autoscroll, the screen is automatically scrolled as the pointer reaches one of the edges of the display. Autoscroll only works on the active screen.&lt;br /&gt;
&lt;br /&gt;
Another screen movement feature is &#039;&#039;menu snap&#039;&#039;. When a screen much larger than the viewing area is scrolled such that the upper left corner is not visible (scrolled down or to the right), menus may could be out of the visible portion of the screen. To prevent this, &#039;&#039;menu snap&#039;&#039; moves the screen to a position where the menus will be visible before rendering them. The screen appears to snap to the home position as the menus are selected, moving back when the operation is complete. If the Left-Amiga qualifier is held when the menus are selected then the screen will remain in the home position when the menu button is released.&lt;br /&gt;
&lt;br /&gt;
The Intuition preferences editor, IControl, allows the user to change a number of Intuition features. Some of these features include the ability to globally disable menu snap, and to change the select qualifier for dragging the screen. See the User&#039;s Manual for more information on Preferences editors.&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Screen Functions ===&lt;br /&gt;
&lt;br /&gt;
Three other functions used with screens are DisplayBeep(), ShowTitle() and GetScreenData(). DisplayBeep() flashes the screen colors to inform the user of an error or problem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DisplayBeep( struct Screen *myscreen );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since not all users will have speakers attached to the system, DisplayBeep() can be used to provide a visible bell. DisplayBeep() can beep any single screen or, if myscreen is set to NULL, all screens.&lt;br /&gt;
&lt;br /&gt;
ShowTitle() determines whether the screen&#039;s title bar will be displayed in front of or behind any backdrop windows on the screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID ShowTitle( struct Screen *myscreen, BOOL infront );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By default, the screen&#039;s title bar is set to display in front of backdrop windows. Call this function with infront set to FALSE to put the screen title bar behind backdrop windows. This can also be set when the screen is opened with the SA_ShowTitle tag.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions that relate to the use of Intuition screens. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| OpenScreenTagList()&lt;br /&gt;
| Open a screen.&lt;br /&gt;
|-&lt;br /&gt;
| OpenScreenTags()&lt;br /&gt;
| Alternate calling sequence for OpenScreenTagList().&lt;br /&gt;
|-&lt;br /&gt;
| OpenScreen()&lt;br /&gt;
| Pre-V36 open screen function.&lt;br /&gt;
|-&lt;br /&gt;
| CloseScreen()&lt;br /&gt;
| Close an open screen.&lt;br /&gt;
|-&lt;br /&gt;
| MoveScreen()&lt;br /&gt;
| Change the position of an open screen.&lt;br /&gt;
|-&lt;br /&gt;
| ScreenToBack()&lt;br /&gt;
| Move a screen behind all other screens.&lt;br /&gt;
|-&lt;br /&gt;
| ScreenToFront()&lt;br /&gt;
| Move a screen in front of all other screens.&lt;br /&gt;
|-&lt;br /&gt;
| ShowTitle()&lt;br /&gt;
| Show the screen in front of through backdrop windows.&lt;br /&gt;
|-&lt;br /&gt;
| GetScreenDrawInfo()&lt;br /&gt;
| Get the DrawInfo information for an open screen.&lt;br /&gt;
|-&lt;br /&gt;
| FreeScreenDrawInfo()&lt;br /&gt;
| Free the DrawInfo information for a screen.&lt;br /&gt;
|-&lt;br /&gt;
| QueryOverscan()&lt;br /&gt;
| Find overscan information for a specific display type.&lt;br /&gt;
|-&lt;br /&gt;
| LockPubScreen()&lt;br /&gt;
| Obtain a lock on a public screen.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockPubScreen()&lt;br /&gt;
| Release a lock on a public screen.&lt;br /&gt;
|-&lt;br /&gt;
| NextPubScreen()&lt;br /&gt;
| Return the name of the next public screen in the list.&lt;br /&gt;
|-&lt;br /&gt;
| PubScreenStatus()&lt;br /&gt;
| Make a public screen private or private screen public.&lt;br /&gt;
|-&lt;br /&gt;
| LockPubScreenList()&lt;br /&gt;
| Lock the public screen list (for a public screen utility).&lt;br /&gt;
|-&lt;br /&gt;
| UnlockPubScreenList()&lt;br /&gt;
| Unlock the public screen list.&lt;br /&gt;
|-&lt;br /&gt;
| SetDefaultPubScreen()&lt;br /&gt;
| Change the default public screen.&lt;br /&gt;
|-&lt;br /&gt;
| SetPubScreenModes()&lt;br /&gt;
| Establish global public screen behavior.&lt;br /&gt;
|-&lt;br /&gt;
| GetDefaultPubScreen()&lt;br /&gt;
| Copies the name of the default public screen to a buffer.&lt;br /&gt;
|-&lt;br /&gt;
| OpenWorkBench()&lt;br /&gt;
| Open the Workbench screen, if closed.&lt;br /&gt;
|-&lt;br /&gt;
| CloseWorkBench()&lt;br /&gt;
| Close the Workbench screen, if possible.&lt;br /&gt;
|-&lt;br /&gt;
| WBenchToBack()&lt;br /&gt;
| Move the Workbench screen behind all other screens.&lt;br /&gt;
|-&lt;br /&gt;
| WBenchToFront()&lt;br /&gt;
| Move the Workbench screen in front of all other screens.&lt;br /&gt;
|-&lt;br /&gt;
| GetScreenData()&lt;br /&gt;
| Pre-V36 way to return information on an open screen.&lt;br /&gt;
|-&lt;br /&gt;
| GetScreenAttr()&lt;br /&gt;
| Query a screen attribute.&lt;br /&gt;
|-&lt;br /&gt;
| GetScreenAttr() / GetScreenAttrsA()&lt;br /&gt;
| Query screen attributes.&lt;br /&gt;
|-&lt;br /&gt;
| ViewAddress()&lt;br /&gt;
| Return the address of a screen&#039;s View.&lt;br /&gt;
|-&lt;br /&gt;
| ViewPortAddress()&lt;br /&gt;
| Use &amp;amp;screen-&amp;gt;ViewPort instead.&lt;br /&gt;
|-&lt;br /&gt;
| MakeScreen()&lt;br /&gt;
| Low level screen handling-rebuild Copper list.&lt;br /&gt;
|-&lt;br /&gt;
| RethinkDisplay()&lt;br /&gt;
| Low level screen handling-incorporate Copper list changes.&lt;br /&gt;
|-&lt;br /&gt;
| RemakeDisplay()&lt;br /&gt;
| MakeScreen() for all screens, then RethinkDisplay().&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=DeveloperDoc:Main&amp;diff=9147</id>
		<title>DeveloperDoc:Main</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=DeveloperDoc:Main&amp;diff=9147"/>
		<updated>2017-04-11T12:25:25Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Many programming languages can be used on AmigaOS, ranging from interpreted scripting languages to comprehensive compiled languages:&lt;br /&gt;
&lt;br /&gt;
* [[AmigaOS Manual: ARexx|ARexx]]: the native AmigaOS scripting language, good for beginners,small projects and talking between applications.&lt;br /&gt;
* Python: a common cross platform scripting language ported to &amp;amp; included with AmigaOS.&lt;br /&gt;
* C: the primary compiled language for AmigaOS.&lt;br /&gt;
* [https://aglet.web.runbox.net/ Modula2]: the compiled language that evolved from Pascal.&lt;br /&gt;
* [http://cshandley.co.uk/portable/PortablE.html E]: an Amiga native compiled language.&lt;br /&gt;
&lt;br /&gt;
ARexx is built into AmigaOS and its original documentation can be found in [[AmigaOS Manual: ARexx|this part]] of the Developer&#039;s Wiki.&lt;br /&gt;
&lt;br /&gt;
A native compiler for the C language is part of the downloadable [http://www.hyperion-entertainment.biz/index.php?option=com_registration&amp;amp;view=files&amp;amp;parent=30&amp;amp;Itemid=63 AmigaOS SDK].&lt;br /&gt;
&lt;br /&gt;
Most of the remaining AmigaOS Developers Wiki provides the latest information on how to program systems running AmigaOS 4.x with the C language. It has been updated for Release 4.0 and higher of AmigaOS and covers all the computer systems which run the Amiga operating system from the Commodore A1200 all the way up to the AmigaOne X5000.&lt;br /&gt;
&lt;br /&gt;
The Wiki assumes some previous experience with programming and familiarity with computers in general. Although it is not required, a knowledge of the C programming language will make it much easier to understand the material in this book. Most of the Amiga operating system is written in C, hence C is the language used for the programming examples. &lt;br /&gt;
&lt;br /&gt;
Many of the AmigaOS concepts, methods described in the wiki for C will also be applicable to other compiled languages, with some variations. Please see the documentation of the respective languages.&lt;br /&gt;
&lt;br /&gt;
This wiki is intended for the following audiences:&lt;br /&gt;
&lt;br /&gt;
* Programmers who want to create application software for AmigaOS systems.&lt;br /&gt;
* Software developers who want to upgrade their software for Release 4.0 or higher of the operating system.&lt;br /&gt;
* Anyone who wants to know more about how AmigaOS works.&lt;br /&gt;
&lt;br /&gt;
The system software is organized into related groups of functions called libraries. The same basic organization is used for this wiki.&lt;br /&gt;
&lt;br /&gt;
== Software Development Kit (SDK) ==&lt;br /&gt;
&lt;br /&gt;
You can find the [http://www.hyperion-entertainment.biz/index.php?option=com_registration&amp;amp;view=files&amp;amp;parent=30&amp;amp;Itemid=63 AmigaOS SDK at this location].&lt;br /&gt;
&lt;br /&gt;
[[SDK Release Notes|Release Notes]]&lt;br /&gt;
&lt;br /&gt;
[[SDK FAQ|Frequency Asked Questions]]&lt;br /&gt;
&lt;br /&gt;
[[Autodocs:Main|Autodocs]]&lt;br /&gt;
&lt;br /&gt;
[[Fundamental Types]]&lt;br /&gt;
&lt;br /&gt;
== Reference ==&lt;br /&gt;
&lt;br /&gt;
[[Kernel]]&lt;br /&gt;
&lt;br /&gt;
[[Libraries]]&lt;br /&gt;
&lt;br /&gt;
[[Devices]]&lt;br /&gt;
&lt;br /&gt;
[[Resources]]&lt;br /&gt;
&lt;br /&gt;
[[User Interface Style Guide]]&lt;br /&gt;
&lt;br /&gt;
== General Information ==&lt;br /&gt;
&lt;br /&gt;
[[AmigaOS Versions]]&lt;br /&gt;
&lt;br /&gt;
[[Controlling Application Stack]]&lt;br /&gt;
&lt;br /&gt;
[[GUI Programming]]&lt;br /&gt;
&lt;br /&gt;
[[Programming in the Amiga Environment]]&lt;br /&gt;
&lt;br /&gt;
[[Libraries and Devices]]&lt;br /&gt;
&lt;br /&gt;
[[Migration Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Troubleshooting Your Software]]&lt;br /&gt;
&lt;br /&gt;
[[Understanding the alert error numbers]]&lt;br /&gt;
&lt;br /&gt;
[[Release 4 Compatibility]]&lt;br /&gt;
&lt;br /&gt;
[[Writing apps for Xena]]&lt;br /&gt;
&lt;br /&gt;
[[AmigaGuide 101]]&lt;br /&gt;
&lt;br /&gt;
[[Installation Utility]]&lt;br /&gt;
&lt;br /&gt;
[[Optimized Window Refreshing]]&lt;br /&gt;
&lt;br /&gt;
[[Autodoc Style Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Debug Kernel]]&lt;br /&gt;
&lt;br /&gt;
[[AmiDock and Dockies]]&lt;br /&gt;
&lt;br /&gt;
[[Command Line Interface]]&lt;br /&gt;
&lt;br /&gt;
[[DCFS and LNFS Low Level Data Structures]]&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Memory_Pools&amp;diff=8540</id>
		<title>Exec Memory Pools</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Memory_Pools&amp;diff=8540"/>
		<updated>2016-04-09T19:36:35Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed a typo: MEMF_CLEARED doesn&amp;#039;t exist.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
A memory pool is a block of memory that an application allocates for all its memory needs. The application draws from this pool rather than the system&#039;s free memory list whenever it requires memory.&lt;br /&gt;
&lt;br /&gt;
Memory pools add to the efficiency of the system and applications in two ways. The first is a potential decrease in memory fragmentation. Memory fragmentation is a condition where system memory is made up of blocks of allocated memory interspersed with blocks of free memory. The second benefit of memory pools is faster memory allocations and deallocations.&lt;br /&gt;
&lt;br /&gt;
By having an application take a pool of memory and allocate from it, fragmentation of system memory is decreased. An application may require six allocations ranging from 20 to 678 bytes in size which can be scattered throughout system memory. However, if those same six allocations are taken from a pool, the fragmentation occurs within the pool, but as far as the system is concerned, it&#039;s missing one large block instead of six small ones.&lt;br /&gt;
&lt;br /&gt;
An application using a memory pool will have faster memory allocations because the memory list of a pool is smaller, and therefore easier to traverse than the memory list of the system memory. For deallocations, it&#039;s even faster because deleting the pool itself deletes all the individual allocations made from it instead of having to deallocate each piece that was allocated.&lt;br /&gt;
&lt;br /&gt;
If you know that your memory pool allocations will always be of the same size, prefer using an [[Exec_Item_Pools|item pool]].&lt;br /&gt;
&lt;br /&gt;
== Memory Pool Organization ==&lt;br /&gt;
&lt;br /&gt;
A memory pool consists of units called puddles. Other than that, it has no formal organization. There is no pool structure defined anywhere. All you know about a pool is its address. Exec&#039;s pool manager handles the rest of the details.&lt;br /&gt;
&lt;br /&gt;
When you create a pool, you specify the size of its puddles and a threshold value, not the size of the pool. This is because memory pools are dynamic, they have no fixed size. The pool manager takes the memory for your application from the puddles. As you require memory, the pool manager expands the pool by adding puddles, and as you free memory, the pool manager shrinks the pool by deleting puddles.&lt;br /&gt;
&lt;br /&gt;
The size of the puddles is important because the pool manager will try to use as much of a puddle as possible before adding a new puddle. Each time you allocate memory from the pool, the pool manager takes the memory from a puddle unless an allocation exceeds the amount of memory remaining in a puddle. If the allocation request exceeds the memory remaining in a puddle, the pool manager adds a new puddle. If the allocation exceeds the pool&#039;s puddle size, the pool manager will create a special over-sized puddle to accommodate the allocation.&lt;br /&gt;
&lt;br /&gt;
The key is to use all the drops in a puddle. If you create a pool with puddles of 200 bytes and allocate 150 bytes at a time, you&#039;ll waste 50 bytes in every puddle. Set the puddle size in accordance with your memory requirements.&lt;br /&gt;
&lt;br /&gt;
The threshold value is the size of the largest allocation to use within a single puddle. An allocation request larger than the threshold value causes the pool manager to add a new puddle. Ideally, the threshold value should be the size of the largest allocation you will make.&lt;br /&gt;
&lt;br /&gt;
The relationship between puddle size and threshold can be tuned for optimal utilization of a pool. Threshold sizes cannot exceed puddle sizes and are recommended to be one half the puddle size, so that you can fit two of your largest allocations in a single puddle. However, if you are going to have a mix of large and small allocations, you might want to set a threshold value that allocates a majority of a puddle for a large allocation, and then uses up the remainder of the puddle with the smaller allocations.&lt;br /&gt;
&lt;br /&gt;
== Creating a Memory Pool ==&lt;br /&gt;
&lt;br /&gt;
You create a memory pool by calling AllocSysObject() with the ASOT_MEMPOOL type and specifying the puddle size, threshold size and memory type. The memory type must be of type MEMF_PRIVATE or MEMF_SHARED. The MEMF_ANY type may be used to indicate that either of these memory types is suitable. The MEMF_CLEAR flag may also be specified to automatically clear memory allocated from the pool.&lt;br /&gt;
&lt;br /&gt;
If successful, AllocSysObject() returns the address of the memory pool.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
APTR mem_pool = IExec-&amp;gt;AllocSysObjectTags(ASOT_MEMPOOL,&lt;br /&gt;
  ASOPOOL_Puddle, 4096,&lt;br /&gt;
  ASOPOOL_Threshold, 2048,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (mem_pool != NULL)&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Pool could not be created.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example above attempts to create a pool of memory with a puddle size of 4096 bytes and a threshold size of 2048 bytes.&lt;br /&gt;
&lt;br /&gt;
If your application requires memory of different types (for example, private memory and shared memory), it must create a pool for each type.&lt;br /&gt;
&lt;br /&gt;
Take note, again, that all you know about a pool is its address. Do not poke around it trying to figure out its organization. It&#039;s private for a reason!&lt;br /&gt;
&lt;br /&gt;
== Allocating Memory from a Pool&#039;s Puddles ==&lt;br /&gt;
&lt;br /&gt;
Memory is obtained from a pool&#039;s puddles by calling AllocPooled(). AllocPooled() requires a pointer to the memory pool and the size of the memory block needed. If successful, AllocPooled() returns a pointer to the memory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Rectangle *rect = IExec-&amp;gt;AllocPooled(mem_pool, sizeof(struct Rectangle);&lt;br /&gt;
&lt;br /&gt;
if (rect != NULL)&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Memory could not be allocated.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Freeing Memory from a Pool&#039;s Puddles ==&lt;br /&gt;
&lt;br /&gt;
An application can free a block of memory it allocated from a pool by calling FreePooled():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;FreePooled(mem_pool, mem_drop, mem_size);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function requires a pointer to the memory pool (mem_pool), a pointer to the block of memory being freed (mem_drop), and the size of the memory being freed (mem_size).&lt;br /&gt;
&lt;br /&gt;
== Deleting a Memory Pool ==&lt;br /&gt;
&lt;br /&gt;
A memory pool is deleted by calling FreeSysObject() with the ASOT_MEMPOOL type and a pointer to the memory pool you wish to delete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;FreeSysObject(ASOT_MEMPOOL, mem_pool);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deleting a pool also frees the puddles in it. This is quicker than doing individual FreePooled() calls. For example, a text editor will allocate memory for each line of the file being edited. When the editor is exited, each of the allocations has to be freed. With FreeSysObject(), it would take only one call instead of many.&lt;br /&gt;
&lt;br /&gt;
== Concurrent Access ==&lt;br /&gt;
&lt;br /&gt;
Memory pools may be protected from concurrent access by specifying the ASOPOOL_Protected tag when creating the pool.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
APTR mem_pool = IExec-&amp;gt;AllocSysObjectTags(ASOT_MEMPOOL,&lt;br /&gt;
  ASOPOOL_MFlags, MEMF_SHARED,&lt;br /&gt;
  ASOPOOL_Puddle, 4096,&lt;br /&gt;
  ASOPOOL_Threshold, 2048,&lt;br /&gt;
  ASOPOOL_Protected, TRUE,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, the memory is being shared between two Tasks or Processes so it must be of type MEMF_SHARED. By specifying that the memory pool is protected we are guaranteed all operations performed on the memory pool are thread safe.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following table gives a brief description of the Exec functions that control memory pools. See the SDK/Autodocs for more details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocPooled()&lt;br /&gt;
| Allocate memory with the pool manager.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_MEMPOOL)&lt;br /&gt;
| Allocate and initialize a new memory pool.&lt;br /&gt;
|-&lt;br /&gt;
| AllocVecPooled()&lt;br /&gt;
| Allocate memory with the pool manager and track size.&lt;br /&gt;
|-&lt;br /&gt;
| FreePooled()&lt;br /&gt;
| Free pooled memory.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_MEMPOOL)&lt;br /&gt;
| Free a memory pool.&lt;br /&gt;
|-&lt;br /&gt;
| FreeVecPooled()&lt;br /&gt;
| Free pooled memory allocated with AllocVecPooled().&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Obsolete ===&lt;br /&gt;
&lt;br /&gt;
These Exec functions are now practically obsolete and not recommended for use in new code:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CreatePool()&lt;br /&gt;
| Use AllocSysObject(ASOT_MEMPOOL) instead.&lt;br /&gt;
|-&lt;br /&gt;
| DeletePool()&lt;br /&gt;
| Use FreeSysObject(ASOT_MEMPOOL) instead.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_in_the_Amiga_Environment&amp;diff=8533</id>
		<title>Programming in the Amiga Environment</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_in_the_Amiga_Environment&amp;diff=8533"/>
		<updated>2016-03-20T19:00:24Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed a typo.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{NeedUpdate}}&lt;br /&gt;
== Programming in the Amiga Environment ==&lt;br /&gt;
&lt;br /&gt;
To program in the Amiga&#039;s dynamic environment you need to understand these special features of the Amiga&#039;s design:&lt;br /&gt;
&lt;br /&gt;
* Multitasking (without memory protection)&lt;br /&gt;
* Shared libraries of functions&lt;br /&gt;
* Dynamic memory architecture (no memory map)&lt;br /&gt;
* Operating system versions&lt;br /&gt;
* Custom chips with DMA access (two kinds of memory)&lt;br /&gt;
&lt;br /&gt;
=== Multitasking ===&lt;br /&gt;
&lt;br /&gt;
The key feature of the Amiga&#039;s operating system design is &#039;&#039;multitasking&#039;&#039;. Multitasking means many programs, or tasks, reside in memory at the same time sharing system resources with one another. Programs take turns running so it appears that many programs are running simultaneously.&lt;br /&gt;
&lt;br /&gt;
Multitasking is based on the concept that a program spends most of its time waiting for things to happen. A program waits for events like key presses, mouse movement, or disk activity. While a program is waiting, the CPU is idle. The CPU could be used to run a different program during this idle period if there was a convenient method for rapidly switching from one program to another. This is what multitasking does.&lt;br /&gt;
&lt;br /&gt;
=== What the System Does For You ===&lt;br /&gt;
&lt;br /&gt;
The Amiga uses &#039;&#039;preemptive multitasking&#039;&#039; which means that the operating system keeps track of all the tasks in memory and decides which one should run. The system checks hundreds of times per second to see which task should be run based on whether or not it is waiting, and other factors. Since the system handles all the work of task switching, multitasking is transparent to the application. From the application&#039;s point of view, it appears to have the machine all to itself.&lt;br /&gt;
&lt;br /&gt;
The Amiga OS also manages the sharing of resources between tasks. This is important because in order for a variety of tasks to run independently in the Amiga&#039;s multitasking environment, tasks must be prevented from interfering with one another. Imagine if five tasks were allowed to use the parallel port at the same time. The result would be I/O chaos. To prevent this, the operating system provides an arbitration method (usually a function call) for every system resource. For instance you must call a function, AllocMem(), to get exclusive access to a block of memory.&lt;br /&gt;
&lt;br /&gt;
=== What the System Doesn&#039;t Do For You ===&lt;br /&gt;
&lt;br /&gt;
The Amiga operating system handles most of the housekeeping needed for multitasking, but this does not mean that applications don&#039;t have to worry about multitasking at all. The current generation of Amiga systems do not have hardware memory protection, so there is nothing to stop a task from using memory it has not legally acquired. An errant task can easily corrupt some other task by accidentally overwriting its instructions or data. Amiga programmers need to be extra careful with memory; one bad memory pointer can cause the machine to crash (debugging utilities such as MungWall and Enforcer will prevent this).&lt;br /&gt;
&lt;br /&gt;
In fact, Amiga programmers need to be careful with every system resource, not just memory. All system resources from audio channels to the floppy disk drives are shared among tasks. Before using a resource, you must ask the system for access to the resource. This may fail if the resource is already being used by another task. Once you have control of a resource, no other task can use it, so give it up as soon as you are finished. When your program exits, you must give everything back whether it&#039;s memory, access to a file, or an I/O port. You are responsible for this, the system will not do it for you automatically.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=What Every Amiga Programmer Should Know|text=The Amiga is a &#039;&#039;multitasking&#039;&#039; computer. Keep in mind that other tasks are running at the same time as your application. Always ask the system for control of any resource you need; some other task may already be using it. Give it back as soon as you are done; another task may want to use it. This applies to just about every computing activity your application can perform.}}&lt;br /&gt;
&lt;br /&gt;
=== Libraries of functions ===&lt;br /&gt;
&lt;br /&gt;
Most of the routines that make up the Amiga&#039;s operating system are organized into groups called libraries. Each library then contains one or more interfaces. In order to call a function on the Amiga you must first open the library that contains the function. Next, you get the interface which contains the function you want to call. For example, if you want to call the Read() function to read data from disk you must first open the [[AmigaDOS Introduction|DOS]] library and then get the &amp;quot;main&amp;quot; interface the Read() function is defined in.&lt;br /&gt;
&lt;br /&gt;
The system&#039;s master library, called [[Exec]], is always open. [[Exec]] keeps track of all the other [[Exec Libraries|libraries]] and is in charge of opening and closing them. [[Exec]]&#039;s &amp;quot;main&amp;quot; interface contains the OpenLibrary() function which is used to open all the other libraries.&lt;br /&gt;
&lt;br /&gt;
Almost any program you write for the Amiga will have to call the OpenLibrary() and GetInterface() functions. Usage is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// Global: declare this above main()&lt;br /&gt;
struct Library *LibBase;&lt;br /&gt;
struct Interface *LibIFace;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  LibBase  = IExec-&amp;gt;OpenLibrary(&amp;quot;library.name&amp;quot;, libversion);&lt;br /&gt;
  if(LibBase == NULL)&lt;br /&gt;
  {&lt;br /&gt;
    // Library did not open, so exit.&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    LibIFace = IExec-&amp;gt;GetInterface(LibBase, &amp;quot;main&amp;quot;, ifaceversion, NULL);&lt;br /&gt;
    if(LibIFace == NULL)&lt;br /&gt;
    {&lt;br /&gt;
        IExec-&amp;gt;CloseLibrary(LibBase);&lt;br /&gt;
       // Could not get Interface, so exit.&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      // Interface obtained, so use its functions.&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; LibBase&lt;br /&gt;
: This is a pointer to the library structure in memory, often referred to as the &#039;&#039;library base&#039;&#039;. The library may or may not be global depending on your needs. The name of this pointer may be changed although it is proper to use the established standard name. Refer to the list below for the appropriate name.&lt;br /&gt;
&lt;br /&gt;
; LibIFace&lt;br /&gt;
: This is a pointer to the interface structure in memory. The interface may or may not be global depending on your needs. The name of this pointer may be changed although it is proper to use the established standard name. Refer to the list below for the appropriate name.&lt;br /&gt;
&lt;br /&gt;
; library.name&lt;br /&gt;
: This is a C string that describes the name of the library you wish to open. The list of Amiga library names is given below.&lt;br /&gt;
&lt;br /&gt;
; main&lt;br /&gt;
: This is a C string that describes the name of the interface you wish to use. The list of Amiga interface names depends on the library and is given below.&lt;br /&gt;
&lt;br /&gt;
; libversion&lt;br /&gt;
: This should be set to the earliest acceptable library version. A value of 0 matches any version. A value of 53 means you require at least version 53 or a later version of the library. If the library version in the system is older than the one you specify, OpenLibrary() will fail (return 0).&lt;br /&gt;
&lt;br /&gt;
; ifaceversion&lt;br /&gt;
: This should be set to the interface version you require. Each interface has a unique version number which defines all the functions in that interface. Most often an interface will have a single interface version of 1. If the interface version in the system does not match, GetInterface() will fail (return 0).&lt;br /&gt;
&lt;br /&gt;
The table listed below shows all the function libraries that are currently part of the Amiga system software.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Parameters to use with OpenLibrary()&lt;br /&gt;
!Library Name&lt;br /&gt;
!Library Base&lt;br /&gt;
!Oldest Library Version In Use&lt;br /&gt;
!Interface Name:Version&lt;br /&gt;
|-&lt;br /&gt;
| library.name&amp;lt;sup&amp;gt;*&amp;lt;/sup&amp;gt; || LibBase || version || iface.name:version&lt;br /&gt;
|-&lt;br /&gt;
| asl.library || AslBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| commodities.library || CxBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| diskfont.library || DiskfontBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| dos.library || DOSBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| [[Introduction to Exec|exec.library]] || ExecBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| [[Expansion Library|expansion.library]] || ExpansionBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| gadtools.library || GadToolsBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| graphics.library || GfxBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| icon.library || IconBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| iffparse.library || IFFParseBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| intuition.library || IntuitionBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| keymap.library || KeymapBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| layers.library || LayersBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathffp.library || MathBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathtrans.library || MathTransBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathieeedoubbas.library || MathIeeeDoubBasBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathieeedoubtrans.library || MathIeeeDoubTransBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathieeesingbas.library || MathIeeeSingBasBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| mathieeesingtrans.library || MathIeeeSingTransBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| rexxsyslib.library || RexxSysBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| translator.library || TranslatorBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| [[Utility Library|utility.library]] || UtilityBase || 50 || main:1&lt;br /&gt;
|-&lt;br /&gt;
| workbench.library || WorkbenchBase || 50 || main:1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;*&amp;lt;/sup&amp;gt; Other libraries may exist that are not supplied by the AmigaOS development team since it is a feature of the operating system to allow such libraries.&lt;br /&gt;
&lt;br /&gt;
==== Opening a Library in C ====&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a brief example showing how OpenLibrary() and GetInterface() are used in C.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* easy.c: a complete example of how to open an Amiga function library in C.&lt;br /&gt;
 * In this case the function library is Intuition. Once the Intuition&lt;br /&gt;
 * function library is open and the interface obtains, any Intuition function&lt;br /&gt;
 * can be called. This example uses the DisplayBeep() function of Intuition to&lt;br /&gt;
 * flash the screen.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase;&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
&lt;br /&gt;
    // Note it is safe to call GetInterface() with a NULL library pointer.&lt;br /&gt;
    IIntuition = (struct IntuitionIFace *)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if(IIntuition != NULL)           /* Check to see if it actually opened.   */&lt;br /&gt;
    {                                /* The Intuition library is now open so  */&lt;br /&gt;
        IIntuition-&amp;gt;DisplayBeep(0);  /* any of its functions may be used.     */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Always drop the interface and close the library if not in use.&lt;br /&gt;
    // Note it is safe to call DropInterface() and CloseLibrary() with NULL pointers.&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface *)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Another Kind of Function Library ====&lt;br /&gt;
&lt;br /&gt;
The Amiga has two kinds of libraries: run-time libraries and link libraries. All the libraries discussed so far are run-time libraries. Run-time libraries make up most of the Amiga&#039;s operating system and are the main topic of this book.&lt;br /&gt;
&lt;br /&gt;
There is another type of library known as a link library. Even though a link library is a collection of functions just like a run-time library, there are some major differences in the two types.&lt;br /&gt;
&lt;br /&gt;
A run-time, or shared library is a group of functions managed by [[Exec]] that resides either in ROM or on disk (in the &amp;quot;LIBS:&amp;quot; directory). A run-time library must be opened before it can be used (as explained above). The functions in a run-time library are accessed dynamically at run-time and can be used by many programs at once even though only one copy of the library is in memory. A disk based run-time library is loaded into memory only if requested by a program and can be automatically flushed from memory when no longer needed.&lt;br /&gt;
&lt;br /&gt;
A link library is a group of functions on disk that are managed by the compiler at link time. Link libraries do not have to be opened before they are used, instead you must link your code with the library when you compile a program. The functions in a link library are actually copied into every program that uses them. For instance the exit() function used in the C program listed above is not part of any of the libraries that make up the Amiga OS. It comes from the link library supplied with the compiler. The code that performs the exit() function is copied into the program when it is compiled.&lt;br /&gt;
&lt;br /&gt;
==== Libraries, Devices and Resources ====&lt;br /&gt;
&lt;br /&gt;
Most of the Amiga&#039;s OS routines are organized into groups of shared run-time libraries. The Amiga also has specialized function groups called &#039;&#039;devices&#039;&#039; and &#039;&#039;resources&#039;&#039; that programmers use to perform basic I/O operations or access low-level hardware.&lt;br /&gt;
&lt;br /&gt;
Devices and resources are similar in concept to a shared run-time library. They are managed by [[Exec]] and must be opened before they can be used. Their functions are separate from the programs that use them and are accessed dynamically at run time. Multiple programs can access the device or resource even though only one copy exists in memory (a few resources can only be used by one program at a time.)&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig1-1a.png|frame|center|&#039;&#039;&#039;Figure 1-1: Amiga System Software Hierarchy&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Devices and resources are managed by [[Exec]] just as libraries are. For more information on devices and resources, see the respective sections.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=What Every Amiga Programmer Should Know|text=The functions in the Amiga OS are accessed through shared run-time libraries. Libraries must be opened before their functions may be used. The system&#039;s master library, [[Exec]], is always open. The [[Exec]] function OpenLibrary() is used to open all other libraries.}}&lt;br /&gt;
&lt;br /&gt;
=== Dynamic memory architecture ===&lt;br /&gt;
&lt;br /&gt;
Unlike some microcomputer operating systems, the AmigaOS relies on absolute memory addresses as little as possible. Instead the Amiga OS uses a technique (sometimes referred to as soft machine architecture) which allows system routines and data structures to be positioned anywhere in memory.&lt;br /&gt;
&lt;br /&gt;
Amiga run-time libraries may be positioned anywhere in memory because they are always accessed through a jump table. Each library whether in ROM or loaded from disk has an associated Library structure and jump table in RAM.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig1-1b.png|frame|center|Amiga Library Structure and Jump Table]]&lt;br /&gt;
&lt;br /&gt;
The system knows where the jump table starts in RAM because when a library is opened for the first time, [[Exec]] creates the library structure and keeps track of its location. The order of the entries in the library&#039;s jump table is always preserved between versions of the OS but the functions they point to can be anywhere in memory. Hence, system routines in ROM may be moved from one version of the OS to another. Given the location of the jump table and the appropriate offset into the table, any function can always be found.&lt;br /&gt;
&lt;br /&gt;
Not only are system routines relocatable but system data structures are too. In the Amiga&#039;s multitasking environment, multiple applications run at the same time and each may have its own screen, memory, open files, and even its own subtasks. Since any number of application tasks are run and stopped at the user&#039;s option, system data structures have to be set up as needed. They cannot be set up ahead of time at a fixed memory location because there is no way to tell how many and what type will be needed.&lt;br /&gt;
&lt;br /&gt;
The Amiga system software manages this confusion by using &#039;&#039;linked lists&#039;&#039; of information about items such as libraries, tasks, screens, files and available memory. A linked list is a chain of data items with each data item containing a pointer to the next item in the chain. Given a pointer to the first item in a linked list, pointers to all the other items in the chain can be found.&lt;br /&gt;
&lt;br /&gt;
==== Exec: The System Executive ====&lt;br /&gt;
&lt;br /&gt;
On the Amiga, the module that keeps track of linked lists is [[Exec]], the system executive. [[Exec]] is the heart of the Amiga operating system since it also is in charge of multitasking, granting access to system resources (like memory) and managing the Amiga library system.&lt;br /&gt;
&lt;br /&gt;
As previously discussed, memory location 4 ($0000 0004), also known as SysBase, contains a pointer to the [[Exec]] library structure. This is the only absolutely defined location in the Amiga operating system. A program need only know where to find the [[Exec]] library to find, use and manipulate all other system code and data.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig1-2.png|frame|center|[[Exec]] and the Organization of the Amiga OS]]&lt;br /&gt;
&lt;br /&gt;
The diagram above shows how the entire Amiga operating system is built as a tree starting at SysBase. [[Exec]] keeps linked lists of all the system libraries, devices, memory, tasks and other data structures. Each of these in turn can have its own variables and linked lists of data structures built onto it. In this way, the flexibility of the OS is preserved so that upgrades can be made without jeopardizing compatibility.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=What Every Amiga Programmer Should Know|text=The Amiga has a dynamic memory map. There are no fixed locations for operating system variables and routines. Do not call ROM routines or access system data structures directly. Instead use the indirect access methods provided by the system.}}&lt;br /&gt;
&lt;br /&gt;
=== Operating system versions ===&lt;br /&gt;
&lt;br /&gt;
The Amiga operating system has undergone several major revisions. The latest revision is Release 4.1 (corresponds to library version 53).&lt;br /&gt;
&lt;br /&gt;
See the table in the [[Introduction_to_Exec#Libraries_and_Devices|Libraries and Devices]] section for details on which version corresponds to which OS release.&lt;br /&gt;
&lt;br /&gt;
The examples listed throughout this wiki assume you are using Release 4.0 or higher.&lt;br /&gt;
&lt;br /&gt;
Many of the libraries and functions documented in this manual are available in all versions of the Amiga operating system. Others are completely new and cannot be used unless you have successfully opened the appropriate version of the library.&lt;br /&gt;
&lt;br /&gt;
To find out which functions are new, refer to the SDK. The functions which are new are marked with &#039;&#039;(V50)&#039;&#039; or &#039;&#039;(V51)&#039;&#039; in the NAME line of the function Autodoc. These new functions require you to use the corresponding version number (50, 51, or higher) when opening the library.&lt;br /&gt;
&lt;br /&gt;
Exit gracefully and informatively if the required library version is not available.&lt;br /&gt;
&lt;br /&gt;
==== About Release 4 ====&lt;br /&gt;
&lt;br /&gt;
Release 4 first appeared on the &#039;&#039;AmigaOne XE&#039;&#039;. This initial version corresponds to Kickstart 4.0, system library version number V50.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=What Every Amiga Programmer Should Know|text=Some libraries or specific functions are not available in older versions of the Amiga operating system. Be sure to ask for the lowest library version that meets the requirements of your program.}}&lt;br /&gt;
&lt;br /&gt;
==== Two Kinds of Memory ====&lt;br /&gt;
&lt;br /&gt;
To keep the Classic Amiga running efficiently, the Classic Amiga has two memory buses and two kinds of memory. &#039;&#039;Chip memory&#039;&#039; is memory that both the CPU and custom chips can access. &#039;&#039;Fast memory&#039;&#039; is memory that only the CPU (and certain expansion cards) can access. Since Chip memory is shared, CPU access may be slowed down if the custom chips are doing heavy-duty processing. CPU access to Fast memory is never slowed down by contention with the custom chips.&lt;br /&gt;
&lt;br /&gt;
The distinction between Chip memory and Fast memory is very important for Classic Amiga programmers to keep in mind because any data accessed directly by the custom chips such as video display data, audio data or sprite data &#039;&#039;must be in Chip memory&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &#039;&#039;What Every Amiga Programmer Should Know&#039;&#039;: The Classic Amiga has &#039;&#039;two&#039;&#039; kinds of memory: &#039;&#039;Chip&#039;&#039; memory and &#039;&#039;Fast&#039;&#039; memory. Use the right kind.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTE&#039;&#039;&#039;&lt;br /&gt;
There is no such thing as &#039;&#039;Chip&#039;&#039; memory in any of new PowerPC-based Amiga systems. The two types of memory only applies to the Classic Amiga hardware platforms such as the A1200 and A4000 models.&lt;br /&gt;
&lt;br /&gt;
== About the Examples ==&lt;br /&gt;
&lt;br /&gt;
For the most part, the examples in this book are written in C.&lt;br /&gt;
&lt;br /&gt;
C examples have been compiled using the standard Software Development Kit (SDK) using GCC.&lt;br /&gt;
&lt;br /&gt;
In general, the examples are also compatible with vbcc and other C compilers, however some changes will usually be necessary.&lt;br /&gt;
&lt;br /&gt;
Specifically, all the C examples assume that the automatic Ctrl-C feature of the compiler has been disabled. For SAS C (and Lattice C revisions 4.0 and greater) this is handled with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* Add this before main() to override the default Ctrl-C handling&lt;br /&gt;
 * provided in SAS (Lattice) C.  Ctrl-C event will be ignored */&lt;br /&gt;
&lt;br /&gt;
int CXBRK ( void )   { return(0); }&lt;br /&gt;
int chkabort( void ) { return(0); }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other changes may be required depending on the example and the C compiler you are using. Most of the C examples do not require any special options and rely on the defaults provided by the SDK.&lt;br /&gt;
&lt;br /&gt;
Except where noted, each example was linked with the standard newlib startup code which provides the following interfaces: IExec, IDOS and IUtility.&lt;br /&gt;
&lt;br /&gt;
== General Amiga Development Guidelines ==&lt;br /&gt;
&lt;br /&gt;
This sections presents specific guidelines that all Amiga programmers must follow. Some of these guidelines are for advanced programmers.&lt;br /&gt;
&lt;br /&gt;
* Check for memory loss. Arrange your Workbench screen so that you have a Shell available and can start your program without rearranging any windows. In the Shell window type Avail flush several times (the flush option requires the Release 2 version of the Avail command). Note the total amount of free memory. Run your program (do not rearrange any windows other than those created by the program) and then exit. At the Shell, type Avail flush several times again. Compare the total amount of free memory with the earlier figure. They should be the same. Any difference indicates that your application is not freeing some memory it used or is not closing a disk-loaded library, device or font it opened. Note that under Release 2, a small amount of memory loss is normal if your application is the first to use the audio or narrator device.&lt;br /&gt;
&lt;br /&gt;
* Use all of the program debugging and stress tools that are available when writing and testing your code. New debugging tools such as Enforcer, MungWall, and Scratch can help find uninitialized pointers, attempted use of freed memory and misuse of scratch registers or condition codes (even in programs that appear to work perfectly).&lt;br /&gt;
&lt;br /&gt;
* Always make sure you actually get any system resource that you ask for. This applies to memory, windows, screens, file handles, libraries, devices, ports, etc. Where an error value or return is possible, ensure that there is a reasonable failure path. Many poorly written programs will appear to be reliable, until some error condition (such as memory full or a disk problem) causes the program to continue with an invalid or null pointer, or branch to untested error handling code.&lt;br /&gt;
&lt;br /&gt;
* Always clean up after yourself. This applies for both normal program exit and program termination due to error conditions. Anything that was opened must be closed, anything allocated must be deallocated. It is generally correct to do closes and deallocations in reverse order of the opens and allocations. Be sure to check your development language manual and startup code; some items may be closed or deallocated automatically for you, especially in abort conditions. If you write in the C language, make sure your code handles Ctrl-C properly.&lt;br /&gt;
&lt;br /&gt;
* Remember that memory, peripheral configurations, and ROMs differ between models and between individual systems. Do not make assumptions about memory address ranges, storage device names, or the locations of system structures or code. Never call ROM routines directly. Beware of any example code you find that calls routines at addresses in the $F00000 $FFFFFF range. These are ROM routines and they will move with every OS release. The only supported interface to system ROM code is through the library, device, and resource calls.&lt;br /&gt;
&lt;br /&gt;
* Never assume library bases or structures will exist at any particular memory location. The only absolute address in the system is $00000004, which contains a pointer to the Exec library base. Do not modify or depend on the format of private system structures. This includes the poking of copper lists, memory lists, and library bases.&lt;br /&gt;
&lt;br /&gt;
* Never assume that programs can access hardware resources directly. Most hardware is controlled by system software that will not respond well to interference from other programs. Shared hardware requires programs to use the proper sharing protocols. Use the defined interface; it is the best way to ensure that your software will continue to operate on future models of the Amiga.&lt;br /&gt;
&lt;br /&gt;
* Never access shared data structures directly without the proper mutual exclusion (locking). Remember that other tasks may be accessing the same structures.&lt;br /&gt;
&lt;br /&gt;
* The system does not monitor the size of a program&#039;s stack. (Your compiler may have an option to do this for you.) Take care that your program does not cause stack overflow and provide extra stack space for the possibility that some functions may use up additional stack space in future versions of the OS.&lt;br /&gt;
&lt;br /&gt;
* Never use a polling loop to test signal bits. If your program waits for external events like menu selection or keystrokes, do not bog down the multitasking system by busy-waiting in a loop. Instead, let your task go to sleep by &#039;&#039;Wait()&#039;&#039;ing on its signal bits. For example:&lt;br /&gt;
  signals = (ULONG)Wait(  (1&amp;amp;lt;&amp;amp;lt;windowPtr-&amp;amp;gt;UserPort-&amp;amp;gt;mp_SigBit) |&lt;br /&gt;
                          (1&amp;amp;lt;&amp;amp;lt;consoleMsgPortPtr-&amp;amp;gt;mp_SigBit)  );&lt;br /&gt;
&lt;br /&gt;
* This turns the signal bit number for each port into a mask, then combines them as the argument for the Exec library Wait() function. When your task wakes up, handle all of the messages at each port where the mp_SigBit is set. There may be more than one message per port, or no messages at the port. Make sure that you ReplyMsg() to all messages that are not replies themselves. If you have no signal bits to Wait() on, use Delay() or WaitTOF() to provide a measured delay.&lt;br /&gt;
&lt;br /&gt;
* Tasks (and processes) execute in &#039;&#039;680x0&#039;&#039; user mode. Supervisor mode is reserved for interrupts, traps, and task dispatching. Take extreme care if your code executes in supervisor mode. Exceptions while in supervisor mode are deadly.&lt;br /&gt;
&lt;br /&gt;
* Most system functions require a particular execution environment. All DOS functions and any functions that might call DOS (such as the opening of a disk-resident library, font, or device) can only be executed from a process. A task is not sufficient. Most other kernel functions may be executed from tasks. Only a few may be executed from interrupts.&lt;br /&gt;
&lt;br /&gt;
* Never disable interrupts or multitasking for long periods. If you use Forbid() or Disable(), you should be aware that execution of any system function that performs the Wait() function will temporarily suspend the Forbid() or Disable() state, and allow multitasking and interrupts to occur. Such functions include almost all forms of DOS and device I/O, including common &#039;&#039;stdio&#039;&#039; functions like printf().&lt;br /&gt;
&lt;br /&gt;
* Never tie up system resources unless it is absolutely necessary. For example, if your program does not require constant use of the printer, open the printer device only when you need it. This will allow other tasks to use the printer while your program is running. You must provide a reasonable error response if a resource is not available when you need it.&lt;br /&gt;
&lt;br /&gt;
* All data for the custom chips must reside in Chip memory (type MEMF_CHIP). This includes bitplanes, sound samples, trackdisk buffers, and images for sprites, bobs, pointers, and gadgets. The AllocMem() call takes a flag for specifying the type of memory. A program that specifies the wrong type of memory may appear to run correctly because many Amigas have only Chip memory. (On all models of the Amiga, the first 512K of memory is Chip memory. In later models, Chip memory may occupy up to the first one or two megabytes).&lt;br /&gt;
&lt;br /&gt;
* However, once expansion memory has been added to an Amiga (type MEMF_FAST), any memory allocations will be made in the expansion memory area by default. Hence, a program can run correctly on an unexpanded Amiga which has only Chip memory while crashing on an Amiga which has expanded memory. A developer with only Chip memory may fail to notice that memory was incorrectly specified.&lt;br /&gt;
&lt;br /&gt;
* Most compilers have options to mark specific data structures or object modules so that they will load into Chip RAM. Some older compilers provide the Atom utility for marking object modules. If this method is unacceptable, use the AllocMem() call to dynamically allocate Chip memory, and copy your data there.&lt;br /&gt;
&lt;br /&gt;
* When making allocations that do not require Chip memory, do not explicitly ask for Fast memory. Instead ask for memory type MEMF_PUBLIC or 0L as appropriate. If Fast memory is available, you will get it.&lt;br /&gt;
&lt;br /&gt;
* Never use software delay loops! Under the multitasking operating system, the time spent in a loop can be better used by other tasks. Even ignoring the effect it has on multitasking, timing loops are inaccurate and will wait different amounts of time depending on the specific model of Amiga computer. The timer device provides precision timing for use under the multitasking system and it works the same on all models of the Amiga. The AmigaDOS Delay() function or the graphics library WaitTOF() function provide a simple interface for longer delays. The &#039;&#039;8520&#039;&#039; I/O chips provide timers for developers who are bypassing the operating system (see the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; for more information).&lt;br /&gt;
&lt;br /&gt;
Always obey structure conventions!&lt;br /&gt;
&lt;br /&gt;
* All non-byte fields must be word-aligned. Longwords should be longword-aligned for performance.&lt;br /&gt;
&lt;br /&gt;
* All address pointers should be 32 bits (not 24 bits). Never use the upper byte for data.&lt;br /&gt;
&lt;br /&gt;
* Fields that are not defined to contain particular initial values must be initialized to zero. This includes pointer fields.&lt;br /&gt;
&lt;br /&gt;
* All reserved or unused fields must be initialized to zero for future compatibility.&lt;br /&gt;
&lt;br /&gt;
* Data structures to be accessed by the custom chips, public data structures (such as a task control block), and structures which must be longword aligned must &#039;&#039;&#039;not&#039;&#039;&#039; be allocated on a program&#039;s stack.&lt;br /&gt;
&lt;br /&gt;
* Dynamic allocation of structures with AllocMem() provides longword aligned memory of a specified type with optional initialization to zero, which is useful in the allocation of structures.&lt;br /&gt;
&lt;br /&gt;
=== Hardware programming guidelines ===&lt;br /&gt;
&lt;br /&gt;
If you find it necessary to program the hardware directly, then it is your responsibility to write code that will work correctly on the various models and configurations of the Amiga. Be sure to properly request and gain control of the hardware resources you are manipulating, and be especially careful in the following areas:&lt;br /&gt;
&lt;br /&gt;
* Kickstart 2.0 uses the &#039;&#039;8520&#039;&#039; Complex Interface Adaptor (CIA) chips differently than 1.3 did. To ensure compatibility, you must always ask for CIA access using the cia.resource AddICRVector() and RemICRVector() functions. Do not make assumptions about what the system might be using the CIA chips for. If you write directly to the CIA chip registers, do not expect system services such as the trackdisk device to function. If you are leaving the system up, do not read or write to the CIA Interrupt Control Registers directly; use the cia.resource AbleICR(), and SetICR() functions. Even if you are taking over the machine, do not assume the initial contents of any of the CIA registers or the state of any enabled interrupts.&lt;br /&gt;
&lt;br /&gt;
* All custom chip registers are Read-only or Write-only. Do not read Write-only registers, and do not write to Read-only registers.&lt;br /&gt;
&lt;br /&gt;
* Never write data to, or interpret data from the unused bits or addresses in the custom chip space. To be software-compatible with future chip revisions, all undefined bits must be set to zeros on writes, and must be masked out on reads before interpreting the contents of the register.&lt;br /&gt;
&lt;br /&gt;
* Never write past the current end of custom chip space. Custom chips may be extended or enhanced to provide additional registers, or to use bits that are currently undefined in existing registers.&lt;br /&gt;
&lt;br /&gt;
* Never read, write, or use any currently undefined address ranges or registers. The current and future usage of such areas is reserved by the AmigaOS development team and is subject to change.&lt;br /&gt;
&lt;br /&gt;
* Never assume that a hardware register will be initialized to any particular value. Different versions of the OS may leave registers set to different values. Check the &#039;&#039;Amiga Hardware Reference Manual&#039;&#039; to ensure that you are setting up all the registers that affect your code.&lt;br /&gt;
&lt;br /&gt;
=== AmigaOS Interface Style Guide ===&lt;br /&gt;
&lt;br /&gt;
In order to make all programs in AmigaOS look consistent, AmigaOS provides some default thumbnails that developers must use in their GUI. These pictures are located in the &#039;&#039;&#039;tbimages:&#039;&#039;&#039; assign. They are installed in any AmigaOS installation.&lt;br /&gt;
Note that something may still go wrong and these pictures may not be found. In this case, the program must provide a fallback method instead of just failing. The developer can choose to display text-only toolbars or include default pictures with their program.&lt;br /&gt;
&lt;br /&gt;
While the look of windows and gadgets can be changed by the user, the default look is similar to this: &lt;br /&gt;
&lt;br /&gt;
[[File:About.png]]&lt;br /&gt;
&lt;br /&gt;
== Error Reports ==&lt;br /&gt;
&lt;br /&gt;
All corrections and bug report should be made by posting a new topic at AmigaOS&#039; support forum. The support forum is located at http://support.amigaos.net&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8530</id>
		<title>Commodities Exchange Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8530"/>
		<updated>2015-12-06T21:09:12Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Fixed wrong qualifier strings for the &amp;quot;rawmouse&amp;quot; class string.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{CodeReview}}&lt;br /&gt;
== Commodities Exchange Library ==&lt;br /&gt;
&lt;br /&gt;
This article describes Commodities Exchange, the library of routines used to add a custom input handler to the Amiga. With Commodities Exchange, any program function can be associated with key combinations or other input events &#039;&#039;globally&#039;&#039; allowing the creation utility programs that run in the background for all tasks.&lt;br /&gt;
&lt;br /&gt;
== Custom Input Handlers ==&lt;br /&gt;
&lt;br /&gt;
The input.device has a hand in almost all user input on the Amiga. It gathers input events from the keyboard, the gameport (mouse), and several other sources, into one input &amp;quot;stream&amp;quot;. Special programs called input event handlers intercept input events along this stream, examining and sometimes changing the input events. Both Intuition and the console device use input handlers to process user input.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-1.png|frame|center|The Amiga Input Stream]]&lt;br /&gt;
&lt;br /&gt;
Using the input.device, a program can introduce its own custom handler into the chain of input handlers at almost any point in the chain. &amp;quot;Hot key&amp;quot; programs, shell pop-up programs, and screen blankers all commonly use custom input handlers to monitor user input before it gets to the Intuition input handler.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-2.png|frame|center|A Custom Input Handler]]&lt;br /&gt;
&lt;br /&gt;
Custom input handlers do have their drawbacks, however. Not only are these handlers hard to program, but because there is no standard way to implement and control them, multiple handlers often do not work well together. Their antisocial behavior can result in load order dependencies and incompatibilities between different custom input handlers. Even for the expert user, having several custom input handlers coexist peacefully can be next to impossible.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-3.png|frame|center|The Commodities Network]]&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange eliminates these problems by providing a simple, standardized way to program and control custom input handlers. It is divided into two parts: an Exec library and a controller program.&lt;br /&gt;
&lt;br /&gt;
The Exec library is called commodities.library. When it is first opened, commodities.library establishes a single input handler just before Intuition in the input chain. When this input handler receives an input event, it creates a CxMessage (Commodities Exchange Message) corresponding to the input event, and diverts the CxMessage through the network of Commodities Exchange input handlers.&lt;br /&gt;
&lt;br /&gt;
These handlers are made up of trees of different CxObjects (Commodities Exchange Objects), each of which performs a simple operation on the CxMessages. Any CxMessages that exit the network are returned to the input.device&#039;s input stream as input events.&lt;br /&gt;
&lt;br /&gt;
Through function calls to the commodities.library, an application can install a custom input handler. A Commodities Exchange application, sometimes simply referred to as a commodity, uses the CxObject primitives to do things such as filter certain CxMessages, translate CxMessages, signal a task when a CxObject receives a CxMessage, send a message when a CxObject receives a CxMessage, or if necessary, call a custom function when a CxObject receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== Commodities Control ===&lt;br /&gt;
&lt;br /&gt;
The standard controller program is called Exchange. It is located in the Utilities/Commodities drawer on your system partition. With Exchange, the user can monitor and control all the currently running Commodities Exchange applications from this one program. The user can enable and disable a commodity, kill a commodity, or, if the commodity has a window, ask the commodity to show or hide its window. When the user requests any of these actions, the controller program sends the commodity a message, telling it which action to perform.&lt;br /&gt;
&lt;br /&gt;
Starting with version 53.4 of the commodities.library, functions are available that allow developers to write their own commodity control programs (Exchange replacements). These functions were not public previously.&lt;br /&gt;
&lt;br /&gt;
=== Important Notes ===&lt;br /&gt;
&lt;br /&gt;
* Commodities are special-purpose programs. In fact, most programs or applications are &#039;&#039;&#039;not&#039;&#039;&#039; suitable for becoming commodities. The Commodities Exchange framework is ideal for programs that need to monitor all user input: hotkey utilities, screen blankers, mouse blankers, etc.&lt;br /&gt;
* In the past, some developers turned their programs into commodities only to provide them with a handy keyboard shortcut to bring up/close their GUI. Please note that such practice is actually a misuse of the commodities framework.&lt;br /&gt;
* Commodities Exchange should never be used as an alternate method of receiving user input for an application. Other applications depend on getting user input in some form or another from the input stream. A greedy program that diverts input to itself rather than letting the input go to where the user expects it can seriously confuse the user, not to mention compromise the advantages of multitasking.&lt;br /&gt;
&lt;br /&gt;
== CxObjects ==&lt;br /&gt;
&lt;br /&gt;
CxObjects are the basic building blocks used to construct a commodity. A commodity uses CxObjects to take care of all manipulations of CxMessages. When a CxMessage &amp;quot;arrives&amp;quot; at a CxObject, that CxObject carries out its primitive action and then, if it has not deleted the CxMessage, it passes the CxMessage on to the next CxObject. A commodity links together CxObjects into a tree, organizing these simple action objects to perform some higher function.&lt;br /&gt;
&lt;br /&gt;
A CxObject is in one of two states, active or inactive. An active CxObject performs its primitive action every time it receives a CxMessage. If a CxObject is inactive, CxMessages bypass it, continuing to the CxObject that follows the inactive one. By default, all CxObjects except the type called brokers are created in the active state.&lt;br /&gt;
&lt;br /&gt;
Currently, there are seven types of CxObjects:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Commodities Exchange Object Types&lt;br /&gt;
! Object Type&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Broker || Registers a new commodity with the commodity network&lt;br /&gt;
|-&lt;br /&gt;
| Filter || Accepts or rejects input events based on criteria set up by the application&lt;br /&gt;
|-&lt;br /&gt;
| Sender || Sends a message to a message port&lt;br /&gt;
|-&lt;br /&gt;
| Translate || Replaces the input event with a different one&lt;br /&gt;
|-&lt;br /&gt;
| Signal || Signals a task&lt;br /&gt;
|-&lt;br /&gt;
| Custom || Calls a custom function provided by the commodity&lt;br /&gt;
|-&lt;br /&gt;
| Debug || Sends debug information out the serial port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installing A Broker Object ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Exchange input handler maintains a master list of CxObjects to which it diverts input events using CxMessages. The CxObjects in this master list are a special type of CxObject called &#039;&#039;brokers&#039;&#039;. The only thing a broker CxObject does is divert CxMessages to its own personal list of CxObjects. A commodity creates a broker and attaches other CxObjects to it. These attached objects take care of the actual input handler related work of the commodity and make up the broker&#039;s personal list.&lt;br /&gt;
&lt;br /&gt;
The first program listing, &amp;quot;Broker.c&amp;quot;, is a very simple example of a working commodity. It serves only to illustrate the basics of a commodity, not to actually perform any useful function. It shows how to set up a broker and process commands from the controller program.&lt;br /&gt;
&lt;br /&gt;
Besides opening commodities.library and creating an Exec message port, setting up a commodity requires creating a broker. The function CxBroker() creates a broker and adds it to the master list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxBroker(struct NewBroker *nb, LONG *error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s first argument is a pointer to a NewBroker structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct NewBroker {&lt;br /&gt;
   BYTE     nb_Version;   /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   BYTE     *nb_Name;&lt;br /&gt;
   BYTE     *nb_Title;&lt;br /&gt;
   BYTE     *nb_Descr;&lt;br /&gt;
   SHORT    nb_Unique;&lt;br /&gt;
   SHORT    nb_Flags;&lt;br /&gt;
   BYTE     nb_Pri;       /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   struct   MsgPort   *nb_Port;&lt;br /&gt;
   WORD     nb_ReservedChannel;  /* Unused, make zero for future compatibility */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange gets all the information it needs about the broker from this structure. NewBroker&#039;s nb_Version field contains the version number of the NewBroker structure. This should be set to NB_VERSION which is defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. The nb_Name, nb_Title, and nb_Descr point to strings which hold the name, title, and description of the broker. The two bit fields, nb_Unique and nb_Flags, toggle certain features of Commodities Exchange based on their values. They are discussed in detail later in this article.&lt;br /&gt;
&lt;br /&gt;
The nb_Pri field contains the broker&#039;s priority. Commodities Exchange inserts the broker into the master list based on this number. Higher priority brokers get CxMessages before lower priority brokers.&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s second argument is a pointer to a LONG. If this pointer is not NULL, CxBroker() fills in this field with one of the following error return codes from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CBERR_OK        0        /* No error                         */&lt;br /&gt;
CBERR_SYSERR    1        /* System error , no memory, etc    */&lt;br /&gt;
CBERR_DUP       2        /* uniqueness violation             */&lt;br /&gt;
CBERR_VERSION   3        /* didn&#039;t understand nb_VERSION     */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the broker object is created with CxBroker(), it must be activated with ActivateCxObject().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG oldactivationvalue = ActivateCxObj(CxObj *co, LONG newactivationvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After successfully completing the initial set up and activating the broker, a commodity can begin its input processing loop waiting for CxMessages to arrive.&lt;br /&gt;
&lt;br /&gt;
== CxMessages ==&lt;br /&gt;
&lt;br /&gt;
There are actually two types of CxMessages. The first, CXM_IEVENT, corresponds to an input event and travels through the Commodities Exchange network. The other type, CXM_COMMAND, carries a command to a commodity. A CXM_COMMAND normally comes from the controller program and is used to pass user commands on to a commodity. A commodity receives these commands through an Exec message port that the commodity sets up before it calls CxBroker(). The NewBroker&#039;s nb_Port field points to this message port. A commodity can tell the difference between the two types of CxMessages by calling the CxMsgType() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG  CxMsgType( CxMsg *cxm );&lt;br /&gt;
UBYTE *CxMsgData( CxMsg *cxm );&lt;br /&gt;
LONG   CxMsgID  ( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A CxMessage not only has a type, it can also have a data pointer as well as an ID associated with it. The data associated with a CXM_IEVENT CxMessage is an InputEvent structure. By using the CxMsgData() function, a commodity can obtain a pointer to the corresponding InputEvent of a CXM_IEVENT message. Commodities Exchange gives an ID of zero to any CXM_IEVENT CxMessage that it introduces to the Commodities network but certain CxObjects can assign an ID to them.&lt;br /&gt;
&lt;br /&gt;
For a CXM_COMMAND CxMessages, the data pointer is generally not used but the ID specifies a command passed to the commodity from the user operating the controller program. The CxMsgID() macro extracts the ID from a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== A Simple Commodity Example ===&lt;br /&gt;
&lt;br /&gt;
The example below, &amp;quot;Broker.c&amp;quot;, receives input from one source, the controller program. The controller program sends a CxMessage each time the user clicks its Enable, Disable, or Kill gadgets. Using the CxMsgID() function, the commodity finds out what the command is and executes it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// broker.c - Simple skeletal example of opening a broker&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
CxObj *broker;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
ULONG cxsigflag;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,   /* nb_Version - Version of the NewBroker structure */&lt;br /&gt;
    &amp;quot;Amiga broker&amp;quot;, /* nb_Name - Name Commodities uses to identify this commodity */&lt;br /&gt;
    &amp;quot;Broker&amp;quot;,     /* nb_Title - Title of commodity that appears in CXExchange */&lt;br /&gt;
    &amp;quot;A simple example of a broker&amp;quot;,  /* nb_Descr - Description of the commodity */&lt;br /&gt;
    0,            /* nb_Unique - Tells CX not to launch another commodity with same name */&lt;br /&gt;
    0,            /* nb_Flags - Tells CX if this commodity has a window */&lt;br /&gt;
    0,            /* nb_Pri - This commodity&#039;s priority */&lt;br /&gt;
    0,            /* nb_Port - MsgPort CX talks to */&lt;br /&gt;
    0             /* nb_ReservedChannel - reserved for later use */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (ICommodities != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* Commodities talks to a Commodities application through */&lt;br /&gt;
        /* an Exec Message port, which the application provides   */&lt;br /&gt;
        if (broker_mp = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            newbroker.nb_Port = broker_mp;&lt;br /&gt;
&lt;br /&gt;
            /* The commodities.library function CxBroker() adds a borker to the&lt;br /&gt;
             * master list.  It takes two arguments, a pointer to a NewBroker&lt;br /&gt;
             * structure and a pointer to a LONG.  The NewBroker structure contains&lt;br /&gt;
             * information to set up the broker.  If the second argument is not&lt;br /&gt;
             * NULL, CxBroker will fill it in with an error code.&lt;br /&gt;
             */&lt;br /&gt;
            if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
            {&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* After it&#039;s set up correctly, the broker has to be activated */&lt;br /&gt;
                ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
&lt;br /&gt;
                /* the main processing loop */&lt;br /&gt;
                ProcessMsg();&lt;br /&gt;
&lt;br /&gt;
                /* It&#039;s time to clean up.  Start by removing the broker from the&lt;br /&gt;
                 * Commodities master list.  The DeleteCxObjAll() function will&lt;br /&gt;
                 * take care of removing a CxObject and all those connected&lt;br /&gt;
                 * to it from the Commodities network&lt;br /&gt;
                 */&lt;br /&gt;
                ICommodities-&amp;gt;DeleteCxObj(broker);&lt;br /&gt;
&lt;br /&gt;
                /* Empty the port of CxMsgs */&lt;br /&gt;
                while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        /* wait for something to happen */&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        /* process any messages */&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            /* Extract necessary information from the CxMessage and return it */&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    /* Shouldn&#039;t get any of these in this example */&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    /* Commodities has sent a command */&lt;br /&gt;
                    printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            /* The user clicked Commodities Exchange disable&lt;br /&gt;
                             * gadget better disable&lt;br /&gt;
                             */&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            /* user clicked enable gadget */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            /* user clicked kill gadget, better quit */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Test to see if user tried to break */&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that &amp;quot;Broker.c&amp;quot; uses Ctrl-C as a break key. The break key for any commodity should be Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
=== Controller Commands ===&lt;br /&gt;
&lt;br /&gt;
The commands that a commodity can receive from the controller program (as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CXCMD_DISABLE     /* please disable yourself       */&lt;br /&gt;
CXCMD_ENABLE      /* please enable yourself        */&lt;br /&gt;
CXCMD_KILL        /* go away for good              */&lt;br /&gt;
CXCMD_APPEAR      /* open your window, if you can  */&lt;br /&gt;
CXCMD_DISAPPEAR   /* hide your window              */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CXCMD_DISABLE, CXCMD_ENABLE, and CXCMD_KILL commands correspond to the similarly named controller program gadgets, Disable, Enable, and Kill; CXCMD_APPEAR and CXCMD_DISAPPEAR correspond to the controller program gadgets, Show and Hide. These gadgets are ghosted in Broker.c because it has no window (It doesn&#039;t make much sense to give the user a chance to click the Show and Hide gadgets). In order to do this, Broker.c has to tell Commodities Exchange to ghost these gadgets. When CxBroker() sets up a broker, it looks at the NewBroker.nb_Flags field to see if the COF_SHOW_HIDE bit (from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) is set. If it is, the &amp;quot;Show&amp;quot; and &amp;quot;Hide&amp;quot; gadgets for this broker will be selectable. Otherwise they are ghosted and disabled.&lt;br /&gt;
&lt;br /&gt;
=== Shutting Down the Commodity ===&lt;br /&gt;
&lt;br /&gt;
Shutting down a commodity is easy. After replying to all CxMessages waiting at the broker&#039;s message port, a commodity can delete its CxObjects. The DeleteCxObj() function removes a single CxObject from the Commodities network. DeleteCxObjectAll() removes multiple objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DeleteCxObj( CxObj *co );&lt;br /&gt;
VOID DeleteCxObjAll( CxObj *delete_co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a commodity has a lot of CxObjects, deleting each individually can be a bit tedious. DeleteCxObjAll() will delete a CxObject and any other CxObjects that are attached to it. The HotKey.c example given later in this article uses this function to delete all its CxObjects. A commodity that uses DeleteCxObjAll() to delete all its CxObjects should make sure that they are all connected to the main one. (See the &amp;quot;Connecting CxObjects&amp;quot; section below.)&lt;br /&gt;
&lt;br /&gt;
After deleting its CxObjects, a commodity must take care of any CxMessages that might have arrived at the message port just before the commodity deleted its objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commodity Tool Types ==&lt;br /&gt;
&lt;br /&gt;
A goal of Commodities Exchange is to improve user control over input handlers. One way in which it accomplishes this goal is through the use of standard icon Tool Types. The user will expect commodities to recognize the set of standard Tool Types:&lt;br /&gt;
&lt;br /&gt;
* CX_PRIORITY&lt;br /&gt;
* CX_POPUP&lt;br /&gt;
* CX_POPKEY&lt;br /&gt;
&lt;br /&gt;
CX_PRIORITY lets the user set the priority of a commodity. The string &amp;amp;quot;CX_PRIORITY=&amp;amp;quot; is a number from -128 to 127. The higher the number, the higher the priority of the commodity, giving it access to input events before lower priority commodities. All commodities should recognize CX_PRIORITY.&lt;br /&gt;
&lt;br /&gt;
CX_POPUP and CX_POPKEY are only relevant to commodities with a window. The string &amp;amp;quot;CX_POPUP=&amp;amp;quot; should be followed by a &amp;quot;yes&amp;quot; or &amp;quot;no&amp;quot;, telling the commodity if it should or shouldn&#039;t show its window when it is first launched. CX_POPKEY is followed by a string describing the key to use as a hot key for making the commodity&#039;s window appear (pop up). The description string for CX_POPKEY describes an input event. The specific format of the string is discussed in the next section (&amp;quot;Filter Objects and the Input Description String&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Parsing Functions ===&lt;br /&gt;
&lt;br /&gt;
Prior to AmigaOS 4.0, the Commodities Library had the following support functions which were included in an external link library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
UBYTE **ArgArrayInit(LONG argc, UBYTE **argv);&lt;br /&gt;
VOID ArgArrayDone(void);&lt;br /&gt;
STRPTR ArgString(UBYTE **tooltypearray, STRPTR tooltype, STRPTR defaultvalue);&lt;br /&gt;
LONG *ArgInt(UBYTE **tooltypearray, STRPTR tooltype, LONG defaultvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are no longer available. Use IDOS-&amp;gt;ReadArgs(), IIcon-&amp;gt;FindToolType() and IIcon-&amp;gt;MatchToolValue() instead, depending on whether the commodity was launched from Workbench or the Shell (CLI).&lt;br /&gt;
&lt;br /&gt;
== Filter Objects and Input Description Strings ==&lt;br /&gt;
&lt;br /&gt;
Because not all commodities are interested in every input event that makes it way down the input chain, Commodities Exchange has a method for filtering them. A filter CxObject compares the CxMessages it receives to a pattern. If a CxMessage matches the pattern, the filter diverts the CxMessage down its personal list of CxObjects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxFilter(STRPTR descriptionstring);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The C macro CxFilter() (defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) returns a pointer to a filter CxObject. The macro has only one argument, a pointer to a string describing which input events to filter. The following regular expression outlines the format of the input event description string (CX_POPKEY uses the same description string format):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[class]  { [-] (qualifier | synonym) ) }  [ [-] upstroke]  [highmap | ANSICode]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Class&#039;&#039; can be any one of the class strings in the table below. Each class string corresponds to a class of input event as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;. Commodities Exchange will assume the class is rawkey if the class is not explicitly stated.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawkey&amp;amp;quot; || IECLASS_RAWKEY&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawmouse&amp;amp;quot; || IECLASS_RAWMOUSE&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;event&amp;amp;quot; || IECLASS_EVENT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;pointerpos&amp;amp;quot; || IECLASS_POINTERPOS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;timer&amp;amp;quot; || IECLASS_TIMER&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;newprefs&amp;amp;quot; || IECLASS_NEWPREFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskremoved&amp;amp;quot; || IECLASS_DISKREMOVED&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskinserted&amp;amp;quot; || IECLASS_DISKINSERTED&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Qualifier&#039;&#039; is one of the qualifier strings from the table below. Each string corresponds to an input event qualifier as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;). A dash preceding the qualifier string tells the filter object not to care if that qualifier is present in the input event. Notice that there can be more than one qualifier (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Qualifier String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lshift&amp;amp;quot; || IEQUALIFIER_LSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rshift&amp;amp;quot; || IEQUALIFIER_RSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;capslock&amp;amp;quot; || IEQUALIFIER_CAPSLOCK&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;control&amp;amp;quot; || IEQUALIFIER_CONTROL&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lalt&amp;amp;quot; || IEQUALIFIER_LALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;ralt&amp;amp;quot; || IEQUALIFIER_RALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lcommand&amp;amp;quot; || IEQUALIFIER_LCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rcommand&amp;amp;quot; || IEQUALIFIER_RCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;numericpad&amp;amp;quot; || IEQUALIFIER_NUMERICPAD&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;repeat&amp;amp;quot; || IEQUALIFIER_REPEAT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;mouse_middlepress&amp;amp;quot; || IEQUALIFIER_MIDBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;mouse_rightpress&amp;amp;quot; || IEQUALIFIER_RBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;mouse_leftpress&amp;amp;quot; || IEQUALIFIER_LEFTBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;relativemouse&amp;amp;quot; || IEQUALIFIER_RELATIVEMOUSE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Synonym&#039;&#039; is one of the synonym strings from the table below. These strings act as synonyms for groups of qualifiers. Each string corresponds to a synonym identifier as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. A dash preceding the synonym string tells the filter object not to care if that synonym is present in the input event. Notice that there can be more than one synonym (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Synonym String&lt;br /&gt;
! Synonym Identifier&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;shift&amp;amp;quot; || IXSYM_SHIFT || Look for either Shift key&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;caps&amp;amp;quot; || IXSYM_CAPS || Look for either Shift key or Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;alt&amp;amp;quot; || IXSYM_ALT || Look for either Alt key&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Upstroke&#039;&#039; is the literal string &amp;amp;quot;upstroke&amp;amp;quot;. If this string is absent, the filter considers only downstrokes. If it is present alone, the filter considers only upstrokes. If preceded by a dash, the filter considers both upstrokes and downstrokes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Highmap&#039;&#039; is one of the following strings:&lt;br /&gt;
&lt;br /&gt;
&amp;amp;quot;backspace&amp;amp;quot;, &amp;amp;quot;del&amp;amp;quot;, &amp;amp;quot;down&amp;amp;quot;, &amp;amp;quot;enter&amp;amp;quot;, &amp;amp;quot;esc&amp;amp;quot;, &amp;amp;quot;f1&amp;amp;quot;, &amp;amp;quot;f2&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f3&amp;amp;quot;, &amp;amp;quot;f4&amp;amp;quot;, &amp;amp;quot;f5&amp;amp;quot;, &amp;amp;quot;f6&amp;amp;quot;, &amp;amp;quot;f7&amp;amp;quot;, &amp;amp;quot;f8&amp;amp;quot;, &amp;amp;quot;f9&amp;amp;quot;, &amp;amp;quot;f10&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f11, &amp;amp;quot;f12&amp;amp;quot;, &amp;amp;quot;help&amp;amp;quot;, &amp;amp;quot;left&amp;amp;quot;, &amp;amp;quot;return&amp;amp;quot;, &amp;amp;quot;right&amp;amp;quot;, &amp;amp;quot;space&amp;amp;quot;, &amp;amp;quot;tab&amp;amp;quot;, &amp;amp;quot;up&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;ANSICode&#039;&#039; is a single character (for example &amp;quot;a&amp;quot; that Commodities Exchange looks up in the system default keymap.&lt;br /&gt;
&lt;br /&gt;
Here are some example description strings. For function key F2 with the left Shift and either Alt key pressed, the input description string would be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawkey lshift alt f2&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify the key that produces an &amp;quot;a&amp;quot; (this may or may not be the A key depending on the keymap), with or without any Shift, Alt, or Control keys pressed use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;-shift -alt -control a&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a mouse move with the right mouse button down, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawmouse mouse_rightpress&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify a timer event use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;timer&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Connecting CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A CxObject has to be inserted into the Commodities network before it can process any CxMessages. AttachCxObj() adds a CxObject to the personal list of another CxObject. The HotKey.c example uses it to attach its filter to a broker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID AttachCxObj ( CxObj *headobj, CxObj *co);&lt;br /&gt;
VOID InsertCxObj ( CxObj *headobj, CxObj *co, CxObj *co_pred );&lt;br /&gt;
VOID EnqueueCxObj( CxObj *headobj, CxObj *co );&lt;br /&gt;
VOID SetCxObjPri ( CxObj *co, LONG pri );&lt;br /&gt;
VOID RemoveCxObj ( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AttachCxObj() adds the CxObject to the end of headobj&#039;s personal list. The ordering of a CxObject list determines which object gets CxMessages first. InsertCxObj() also inserts a CxObject, but it inserts it after another CxObject already in the personal list (co_pred in the prototype above).&lt;br /&gt;
&lt;br /&gt;
Brokers aren&#039;t the only CxObjects with a priority. All CxObjects have a priority associated with them. To change the priority of any CxObject, use the SetCxObjPri() function. A commodity can use the priority to keep CxObjects in a personal list sorted by their priority. The commodities.library function EnqueueCxObj() inserts a CxObject into another CxObject&#039;s personal list based on priority.&lt;br /&gt;
&lt;br /&gt;
Like its name implies, the RemoveCxObj() function removes a CxObject from a personal list. Note that it is not necessary to remove a CxObject from a list in order to delete it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* HotKey.c - Simple hot key commodity&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/icon.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities;&lt;br /&gt;
struct IconIFace *IIcon;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *filter, *sender, *translate;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;quot;Amiga HotKey&amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;quot;A Simple HotKey&amp;quot;,&lt;br /&gt;
    &amp;quot;A simple hot key commodity&amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,    /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                  /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint32 cxsigflag;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    uint8 *hotkey, **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    /* open the icon.library for the support library */&lt;br /&gt;
    /* functions, ArgArrayInit() and ArgArrayDone()  */&lt;br /&gt;
    struct Library *IconBase = IExec-&amp;gt;OpenLibrary(&amp;quot;icon.library&amp;quot;, 50);&lt;br /&gt;
    struct IconIFace *IIcon = (struct IconIFace*)IExec-&amp;gt;GetInterface(IconBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
    if (ICommodities != NULL &amp;amp;&amp;amp; IIcon != NULL)&lt;br /&gt;
    {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (from the 2.0 version&lt;br /&gt;
                 * of amiga.lib) that makes it easy to read arguments from either a&lt;br /&gt;
                 * CLI or from Workbench&#039;s ToolTypes.  Because it uses icon.library,&lt;br /&gt;
                 * the library has to be open before calling this function.&lt;br /&gt;
                 * ArgArrayDone() cleans up after this function.&lt;br /&gt;
                 */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (also from amiga.lib) searches through the array set up&lt;br /&gt;
                 * by ArgArrayInit() for a specific ToolType.  If it finds one, it&lt;br /&gt;
                 * returns the numeric value of the number that followed the&lt;br /&gt;
                 * ToolType (i.e., CX_PRIORITY=7). If it doesn&#039;t find the ToolType,&lt;br /&gt;
                 * it returns the default value (the third argument)&lt;br /&gt;
                 */&lt;br /&gt;
                newbroker.nb_Pri = (int8)ArgInt(ttypes, &amp;quot;CX_PRIORITY&amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                /* ArgString() works just like ArgInt(), except it returns a pointer to a string&lt;br /&gt;
                 * rather than an integer. In the example below, if there is no ToolType&lt;br /&gt;
                 * &amp;quot;HOTKEY&amp;quot;, the function returns a pointer to &amp;quot;rawkey control esc&amp;quot;.&lt;br /&gt;
                 */&lt;br /&gt;
                hotkey = ArgString(ttypes, &amp;quot;HOTKEY&amp;quot;, &amp;quot;rawkey control esc&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxFilter() is a macro that creates a filter CxObject.  This filter&lt;br /&gt;
                     * passes input events that match the string pointed to by hotkey.&lt;br /&gt;
                     */&lt;br /&gt;
                    if (filter = ICommodities-&amp;gt;CxFilter(hotkey))&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Add a CxObject to another&#039;s personal list */&lt;br /&gt;
                        ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
                        /* CxSender() creates a sender CxObject.  Every time a sender gets&lt;br /&gt;
                         * a CxMessage, it sends a new CxMessage to the port pointed to in&lt;br /&gt;
                         * the first argument. CxSender()&#039;s second argument will be the ID&lt;br /&gt;
                         * of any CxMessages the sender sends to the port.  The data pointer&lt;br /&gt;
                         * associated with the CxMessage will point to a *COPY* of the&lt;br /&gt;
                         * InputEvent structure associated with the orginal CxMessage.&lt;br /&gt;
                         */&lt;br /&gt;
                        if (sender = CxSender(broker_mp, EVT_HOTKEY))&lt;br /&gt;
                        {&lt;br /&gt;
                            ICommodities-&amp;gt;AttachCxObj(filter, sender);&lt;br /&gt;
&lt;br /&gt;
                            /* CxTranslate() creates a translate CxObject. When a translate&lt;br /&gt;
                             * CxObject gets a CxMessage, it deletes the original CxMessage&lt;br /&gt;
                             * and adds a new input event to the input.device&#039;s input stream&lt;br /&gt;
                             * after the Commodities input handler. CxTranslate&#039;s argument&lt;br /&gt;
                             * points to an InputEvent structure from which to create the new&lt;br /&gt;
                             * input event.  In this example, the pointer is NULL, meaning no&lt;br /&gt;
                             * new event should be introduced, which causes any event that&lt;br /&gt;
                             * reaches this object to disappear from the input stream.&lt;br /&gt;
                             */&lt;br /&gt;
                            if (translate = ICommodities-&amp;gt;CxTranslate(NULL))&lt;br /&gt;
                            {&lt;br /&gt;
                                ICommodities-&amp;gt;AttachCxObj(filter, translate);&lt;br /&gt;
&lt;br /&gt;
                                /* CxObjError() is a commodities.library function that returns&lt;br /&gt;
                                 * the internal accumulated error code of a CxObject.&lt;br /&gt;
                                 */&lt;br /&gt;
                                if (! CxObjError(filter))&lt;br /&gt;
                                {&lt;br /&gt;
                                    ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                                    ProcessMsg();&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only&lt;br /&gt;
                     * deletes the CxObject pointed to in its argument, but it deletes&lt;br /&gt;
                     * all of the CxObjects that are attached to it.&lt;br /&gt;
                     */&lt;br /&gt;
                    ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            /* this amiga.lib function cleans up after ArgArrayInit() */&lt;br /&gt;
            ArgArrayDone();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIcon);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IconBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while(returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case EVT_HOTKEY: /* We got the message from the sender CxObject */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_UNIQUE:&lt;br /&gt;
                        /* Commodities Exchange can be told not only to refuse to launch a&lt;br /&gt;
                         * commodity with a name already in use but also can notify the&lt;br /&gt;
                         * already running commodity that it happened. It does this by&lt;br /&gt;
                         * sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE.  If the&lt;br /&gt;
                         * user tries to run a windowless commodity that is already running,&lt;br /&gt;
                         * the user wants the commodity to shut down. */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sender CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A filter CxObject by itself is not especially useful. It needs some other CxObjects attached to it. A commodity interested in knowing if a specific key was pressed uses a filter to detect and divert the corresponding CxMessage down the filter&#039;s personal list. The filter does this without letting the commodity know what happened. The sender CxObject can be attached to a filter to notify a commodity that it received a CxMessage. CxSender() is a macro that creates a sender CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
senderCxObj = CxObj *CxSender(struct MsgPort *senderport, LONG cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSender() supplies the sender with an Exec message port and an ID. For every CxMessage a sender receives, it sends a new CxMessage to the Exec message port passed in CxSender(). Normally, the commodity creates this port. It is not unusual for a commodity&#039;s broker and sender(s) to share an Exec message port. The HotKey.c example does this to avoid creating unnecessary message ports. A sender uses the ID (cxmID) passed to CxSender() as the ID for all the CxMessages that the it transmits. A commodity uses the ID to monitor CxMessages from several senders at a single message port.&lt;br /&gt;
&lt;br /&gt;
A sender does several things when it receives a CxMessage. First, it duplicates the CxMessage&#039;s corresponding input event and creates a new CxMessage. Then, it points the new CxMessage&#039;s data field to the copy of the input event and sets the new CxMessage&#039;s ID to the ID passed to CxSender(). Finally, it sends the new CxMessage to the port passed to CxSender(), asynchronously.&lt;br /&gt;
&lt;br /&gt;
Because HotKey uses only one message port between its broker and sender object, it has to extract the CxMessage&#039;s type so it can tell if it is a CXM_IEVENT or a CXM_COMMAND. If HotKey gets a CXM_IEVENT, it compares the CxMessage&#039;s ID to the sender&#039;s ID, EVT_HOTKEY, to see which sender sent the CxMessage. Of course HotKey has only one sender, so it only checks for only one ID. If it had more senders, HotKey would check for the ID of each of the other senders as well.&lt;br /&gt;
&lt;br /&gt;
Although HotKey doesn&#039;t use it, a CXM_IEVENT CxMessage contains a pointer to the copy of an input event. A commodity can extract this pointer (using CxMsgData()) if it needs to examine the input event copy. This pointer is only valid before the CxMessage reply. Note that it does not make any sense to modify the input event copy.&lt;br /&gt;
&lt;br /&gt;
Senders are attached almost exclusively to CxObjects that filter out most input events (usually a filter CxObject). Because a sender sends a CxMessage for every single input event it gets, it should only get a select few input events. The AttachCxObj() function can add a CxObject to the end of a filter&#039;s (or some other filtering CxObject&#039;s) personal list. A commodity should not attach a CxObject to a sender as a sender ignores any CxObjects in its personal list.&lt;br /&gt;
&lt;br /&gt;
== Translate CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Normally, after a commodity processes a hot key input event, it needs to eliminate that input event. Other commodities may need to replace an input event with a different one. The translate CxObject can be used for these purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
translateCxObj = CxObj  *CxTranslate(struct InputEvent *newinputevent);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The macro CxTranslate() creates a new translate CxObject. CxTranslate()&#039;s only argument is a pointer to a chain of one or more InputEvent structures.&lt;br /&gt;
&lt;br /&gt;
When a translate CxObject receives a CxMessage, it eliminates the CxMessage and its corresponding input event from the system. The translator introduces a new input event, which Commodities Exchange copies from the InputEvent structure passed to CxTranslate() (newinputevent from the function prototype above), in place of the deleted input event.&lt;br /&gt;
&lt;br /&gt;
A translator is normally attached to some kind of filtering CxObject. If it wasn&#039;t, it would translate all input events into the same exact input event. Like the sender CxObject, a translator does not divert CxMessages down its personal list, so it doesn&#039;t serve any purpose to add any to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetTranslate( CxObj *translator, struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is possible to change the InputEvent structure that a translator looks at when it creates and introduces new input events into the input stream. The function SetTranslate() accepts a pointer to the new InputEvent structure, which the translator will duplicate and introduce when it receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
HotKey utilizes a special kind of translator. Instead of supplying a new input event, HotKey passes a NULL to CxTranslate(). If a translator has a NULL new input event pointer, it does not introduce a new input event, but still eliminates any CxMessages and corresponding input events it receives.&lt;br /&gt;
&lt;br /&gt;
== CxObject Errors ==&lt;br /&gt;
&lt;br /&gt;
A Commodities Exchange function that acts on a CxObject records errors in the CxObject&#039;s accumulated error field. The function CxObjError() returns a CxObject&#039;s error field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 co_errorfield = CxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each bit in the error field corresponds to a specific type of error. The following is a list of the currently defined CxObject errors  and their corresponding bit mask constants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Error Constant&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| COERR_ISNULL&lt;br /&gt;
| CxObjError() was passed a NULL.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_NULLATTACH&lt;br /&gt;
| Someone tried to attach a NULL CxObject to this CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADFILTER&lt;br /&gt;
| This filter CxObject currently has an invalid filter description.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADTYPE&lt;br /&gt;
| Someone tried to perform a type specific function on the wrong type of CxObject (for example calling SetFilter() on a sender CxObject).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The remaining bits are reserved for future use. HotKey.c checks the error field of its filter CxObject to make sure the filter is valid. HotKey.c does not need to check the other objects with CxObjError() because it already makes sure that these other objects are not NULL, which is the only other kind of error the other objects can cause in this situation.&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange has a function that clears a CxObject&#039;s accumulated error field, ClearCxObjError().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID ClearCxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A commodity should be careful about using this, especially on a filter. If a commodity clears a filter&#039;s error field and the COERR_BADFILTER bit is set, Commodities Exchange will think that the filter is OK and start sending messages through it.&lt;br /&gt;
&lt;br /&gt;
== Uniqueness ==&lt;br /&gt;
&lt;br /&gt;
When a commodity opens its broker, it can ask Commodities Exchange not to launch another broker with the same name (nb_Name). The purpose of the uniqueness feature is to prevent the user from starting duplicate commodities. If a commodity asks, Commodities Exchange will not only refuse to create a new, similarly named broker, but it will also notify the original commodity if someone tries to do so.&lt;br /&gt;
&lt;br /&gt;
A commodity tells Commodities Exchange not to allow duplicates by setting certain bits in the nb_Unique field of the NewBroker structure it sends to CxBroker():&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NBU_UNIQUE&lt;br /&gt;
| bit 0&lt;br /&gt;
|-&lt;br /&gt;
| NBU_NOTIFY&lt;br /&gt;
| bit 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Setting the NBU_UNIQUE bit prevents duplicate commodities. Setting the NBU_NOTIFY bit tells Commodities Exchange to notify a commodity if an attempt was made to launch a duplicate. Such a commodity will receive a CXM_COMMAND CxMessage with an ID of CXCMD_UNIQUE when someone tries to duplicate it. Because the uniqueness feature uses the name a programmer gives a commodity to differentiate it from other commodities, it is possible for completely different commodities to share the same name, preventing the two from coexisting. For this reason, a commodity should not use a name that is likely to be in use by other commodities (like &amp;quot;filter&amp;quot; or &amp;quot;hotkey&amp;quot;). Instead, use a name that matches the commodity name.&lt;br /&gt;
&lt;br /&gt;
When &amp;quot;HotKey.c&amp;quot; gets a CXCMD_UNIQUE CxMessage, it shuts itself down. &amp;quot;HotKey.c&amp;quot; and all the windowless commodities that come with Workbench shut themselves down when they get a CXCMD_UNIQUE CxMessage. Because the user will expect all windowless commodities to work this way, all windowless commodities should follow this standard.&lt;br /&gt;
&lt;br /&gt;
When the user tries to launch a duplicate of a system commodity that has a window, the system commodity moves its window to the front of the display, as if the user had clicked the &amp;quot;Show&amp;quot; gadget in the controller program&#039;s window. A windowed commodity should mimic conventions set by existing windowed system commodities, and move its window to the front of the display.&lt;br /&gt;
&lt;br /&gt;
== Signal CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A commodity can use a sender CxObject to find out if a CxMessage has &amp;quot;visited&amp;quot; a CxObject, but this method unnecessarily uses system resources. A commodity that is only interested in knowing if such a visitation took place does not need to see a corresponding input event or a CxMessage ID. Instead, Commodities Exchange has a CxObject that uses an Exec signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *signalCxObj = CxSignal(struct Task *, LONG cx_signal);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSignal() sets up a signal CxObject. When a signal CxObject receives a CxMessage, it signals a task. The commodity is responsible for determining the proper task ID and allocating the signal. Normally, a commodity wants to be signaled so it uses FindTask(NULL) to find it&#039;s own task address. Note that cx_signal from the above prototype is the signal number as returned by AllocSignal(), not the signal mask made from that number. For more information on signals, see [[Exec_Signals|Exec Signals]].&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; (shown a little later) uses a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
== Custom CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Although the CxObjects mentioned so far take care of most of the input event handling a commodity needs to do, they cannot do it all. This is why Commodities Exchange has a custom CxObject. When a custom CxObject receives a CxMessage, it calls a function provided by the commodity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObject *customCxObj = CxCustom(int32 *customfunction(), int32 cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A custom CxObject is the only means by which a commodity can directly modify input events as they pass through the Commodities network as CxMessages. For this reason, it is probably the most dangerous of the CxObjects to use.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Warning About Custom CxObjects|text=Unlike the rest of the code a commodities programmer writes, the code passed to a custom CxObject runs as part of the input.device task, putting severe restrictions on the function. No DOS or Intuition functions can be called. No assumptions can be made about the values of registers upon entry. Any function passed to CxCustom() should be very quick and very simple, with a minimum of stack usage.}}&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange calls a custom CxObject&#039;s function as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID customfunction(CxMsg *cxm, CxObj *customcxobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where cxm is a pointer to a CxMessage corresponding to a real input event, and customcxobj is a pointer to the custom CxObject. The custom function can extract the pointer to the input event by calling CxMsgData(). Before passing the CxMessage to the custom function, Commodities Exchange sets the CxMessage&#039;s ID to the ID passed to CxCustom().&lt;br /&gt;
&lt;br /&gt;
The following is an example of a custom CxObject function that swaps the function of the left and right mouse buttons.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
custom = CxCustom(CxFunction, 0)&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be */&lt;br /&gt;
/* short and sweet. This code runs as part of the input.device task */&lt;br /&gt;
#define CODEMASK (0x00FF &amp;amp; IECODE_LBUTTON &amp;amp; IECODE_RBUTTON)&lt;br /&gt;
&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    uint16 mousequals = 0x0000;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg.  Unlike the InputEvent&lt;br /&gt;
     * extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.&lt;br /&gt;
     */&lt;br /&gt;
    struct InputEvent *ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* Check to see if this input event is a left or right mouse button   */&lt;br /&gt;
    /* by itself (a mouse button can also be a qualifier).  If it is, flip the   */&lt;br /&gt;
    /* low order bit to switch leftbutton &amp;lt;--&amp;gt; rightbutton. */&lt;br /&gt;
    if (ie-&amp;gt;ie_Class == IECLASS_RAWMOUSE)&lt;br /&gt;
        if ((ie-&amp;gt;ie_Code &amp;amp; CODEMASK) == CODEMASK)  ie-&amp;gt;ie_Code ^= 0x0001;&lt;br /&gt;
&lt;br /&gt;
    /* Check the qualifiers. If a mouse button was down when this */&lt;br /&gt;
    /* input event occurred, set the other mouse button bit.      */&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_RBUTTON)  mousequals |= IEQUALIFIER_LEFTBUTTON;&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_LEFTBUTTON)  mousequals |= IEQUALIFIER_RBUTTON;&lt;br /&gt;
&lt;br /&gt;
    /* clear the RBUTTON and LEFTBUTTON qualifier bits */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier &amp;amp;= ~(IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_RBUTTON);&lt;br /&gt;
&lt;br /&gt;
    /* set the mouse button qualifier bits to their new values */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier |= mousequals;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Debug CxObjects ==&lt;br /&gt;
&lt;br /&gt;
The final CxObject is the debug CxObject. When a debug CxObject receives a CxMessage, it sends debugging information to the serial port using DebugPrintF().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *debugCxObj = CxDebug(int32 ID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The debug CxObject will DebugPrintF() the following information about itself, the CxMsg, and the corresponding InputEvent structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DEBUG NODE: 7CB5AB0, ID: 2&lt;br /&gt;
 CxMsg: 7CA6EF2, type: 0, data 2007CA destination 6F1E07CB&lt;br /&gt;
dump IE: 7CA6F1E&lt;br /&gt;
 Class 1&lt;br /&gt;
 Code 40&lt;br /&gt;
 Qualifier 8000&lt;br /&gt;
 EventAddress 40001802&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There has to be a terminal connected to the Amiga&#039;s serial port to receive this information.&lt;br /&gt;
&lt;br /&gt;
== The IX Structure ==&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange does not use the input event description strings discussed earlier to match input events. Instead, Commodities Exchange converts these strings to its own internal format. These input expressions are available for commodities to use instead of the input description strings. The following is the IX structure as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IX_VERSION   2&lt;br /&gt;
&lt;br /&gt;
struct InputXpression {&lt;br /&gt;
   UBYTE   ix_Version;     /* must be set to IX_VERSION  */&lt;br /&gt;
   UBYTE   ix_Class;       /* class must match exactly   */&lt;br /&gt;
   UWORD   ix_Code;&lt;br /&gt;
   UWORD   ix_CodeMask;    /* normally used for UPCODE   */&lt;br /&gt;
   UWORD   ix_Qualifier;&lt;br /&gt;
   UWORD   ix_QualMask;&lt;br /&gt;
   UWORD   ix_QualSame;    /* synonyms in qualifier      */&lt;br /&gt;
   };&lt;br /&gt;
&lt;br /&gt;
typedef struct InputXpression IX;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ix_Version field contains the current version number of the InputXpression structure. The current version is defined as IX_VERSION. The ix_Class field contains the IECLASS_ constant (defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;) of the class of input event sought. Commodities Exchange uses the ix_Code and ix_CodeMask fields to match the ie_Code field of a struct InputEvent. The bits of ix_CodeMask indicate which bits are relevant in the ix_Code field when trying to match against a ie_Code. If any bits in ix_CodeMask are off, Commodities Exchange does not consider the corresponding bit in ie_Code when trying to match input events. This is used primarily to mask out the IECODE_UP_PREFIX bit of rawkey events, making it easier to match both up and down presses of a particular key.&lt;br /&gt;
&lt;br /&gt;
IX&#039;s qualifier fields, ix_Qualifier, ix_QualMask, and ix_QualSame, are used to match the ie_Qualifier field of an InputEvent structure. The ix_Qualifier and ix_QualMask fields work just like ix_Code and ix_CodeMask. The bits of ix_QualMask indicate which bits are relevant when comparing ix_Qualifier to ie_Qualifier. The ix_QualSame field tells Commodities Exchange that certain qualifiers are equivalent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IXSYM_SHIFT  1     /* left- and right- shift are equivalent     */&lt;br /&gt;
#define IXSYM_CAPS   2     /* either shift or caps lock are equivalent  */&lt;br /&gt;
#define IXSYM_ALT    4     /* left- and right- alt are equivalent       */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, the input description string&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;rawkey -caps -lalt -relativemouse -upstroke ralt tab&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
matches a tab upstroke or downstroke with the right Alt key pressed whether or not the left Alt, either Shift, or the Caps Lock keys are down. The following IX structure corresponds to that input description string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IX ix = {&lt;br /&gt;
    IX_VERSION,                   /* The version */&lt;br /&gt;
    IECLASS_RAWKEY,               /* We&#039;re looking for a RAWKEY event */&lt;br /&gt;
    0x42,                         /* The key the usa0 keymap maps to a tab*/&lt;br /&gt;
    0x00FF &amp;amp; (~IECODE_UP_PREFIX), /* We want up and down key presses */&lt;br /&gt;
    IEQUALIFIER_RALT,             /* The right alt key must be down */&lt;br /&gt;
    0xFFFF &amp;amp; ~(IEQUALIFIER_LALT | IEQUALIFIER_LSHIFT |&lt;br /&gt;
        IEQUALIFIER_RSHIFT | IEQUALIFIER_CAPSLOCK | IEQUALIFIER_RELATIVEMOUSE),&lt;br /&gt;
        /* don&#039;t care about left alt, shift, capslock, or relativemouse qualifiers   */&lt;br /&gt;
    IXSYM_CAPS  /* The shift keys and the capslock key qualifiers are all equivalent */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CxFilter() macro only accepts a description string to describe an input event. A commodity can change this filter, however, with the SetFilter() and SetFilterIX() function calls.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetFilter( CxObj *filter, STRPTR descrstring );&lt;br /&gt;
VOID SetFilterIX( CxObj *filter, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetFilter() and SetFilterIX() change which input events a filter CxObject diverts. SetFilter() accepts a pointer to an input description string. SetFilterIX() accepts a pointer to an IX input expression. A commodity that uses either of these functions should check the filter&#039;s error code with CxObjError() to make sure the change worked.&lt;br /&gt;
&lt;br /&gt;
The function ParseIX() parses an input description string and translates it into an IX input expression.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG errorcode = ParseIX( STRPTR descrstring, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange uses ParseIX() to convert the description string in CxFilter() to an IX input expression. As was mentioned previously, ParseIX() does not work with certain kinds of input strings.&lt;br /&gt;
&lt;br /&gt;
== Controlling CxMessages ==&lt;br /&gt;
&lt;br /&gt;
A Custom CxObject has the power to directly manipulate the CxMessages that travel around the Commodities network. One way is to directly change values in the corresponding input event. Another way is to redirect (or dispose of) the CxMessages.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DivertCxMsg ( CxMsg *cxm, CxObj *headobj, CxObj *retobj );&lt;br /&gt;
VOID RouteCxMsg  ( CxMsg *cxm, CxObj *co );&lt;br /&gt;
VOID DisposeCxMsg( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DivertCxMsg() and RouteCxMsg() dictate where the CxMessage will go next. Conceptually, DivertCxMsg() is analogous to a subroutine in a program; the CxMessage will travel down the personal list of a CxObject (headobj in the prototype) until it gets to the end of that list. It then returns and visits the CxObject that follows the return CxObject (the return CxObject in the prototype above is retobj). RouteCxMsg() is analogous to a goto in a program; it has no CxObject to return to.&lt;br /&gt;
&lt;br /&gt;
DisposeCxMsg() removes a CxMessage from the network and releases its resources. The translate CxObject uses this function to remove a CxMessage.&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; shows how to use DivertCxMsg() as well as a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/* divert.c - commodity to monitor user inactivity - compiled with SASC 5.10&lt;br /&gt;
LC -b0 -cfist -v -j73 divert.c&lt;br /&gt;
Blink FROM LIB:c.o,divert.o TO divert LIBRARY LIB:LC.lib,LIB:Amiga.lib NODEBUG SC SD&lt;br /&gt;
quit; */&lt;br /&gt;
#include &amp;amp;lt;exec/libraries.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/commodities.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_stdio_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/commodities_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/inputevent.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define TIMER_CLICKS 100&lt;br /&gt;
&lt;br /&gt;
void main(int, char **);&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
void CxFunction(CxMsg *, CxObj *);&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase, *IconBase;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *cocustom, *cosignal;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,&lt;br /&gt;
    &amp;amp;quot;show divert&amp;amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,  /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                /* If someone tries it, let me know                        */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Task *task;&lt;br /&gt;
ULONG cxsigflag, signal, cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
void main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    UBYTE **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    if (CxBase = OpenLibrary(&amp;amp;quot;commodities.library&amp;amp;quot;, 37L))&lt;br /&gt;
    {&lt;br /&gt;
        /* open the icon.library for support library functions, ArgArrayInit() and ArgArrayDone() */&lt;br /&gt;
        if (IconBase = OpenLibrary(&amp;amp;quot;icon.library&amp;amp;quot;, 36L))&lt;br /&gt;
        {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;amp;lt;&amp;amp;lt; broker_mp-&amp;amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (in the 2.0 version of amiga.lib) */&lt;br /&gt;
                /* that makes it easy to read arguments from either a CLI or from Workbench&#039;s     */&lt;br /&gt;
                /* ToolTypes. Because it uses icon.library, the library has to be open before     */&lt;br /&gt;
                /* before calling this function.  ArgArrayDone() cleans up after this function.   */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (in amiga.lib) searches through the array set up by ArgArrayInit()    */&lt;br /&gt;
                /* for a specific ToolType.  If it finds one, it returns the numeric value of the */&lt;br /&gt;
                /* number that followed the ToolType (i.e., CX_PRIORITY=7).  If it  doesn&#039;t find  */&lt;br /&gt;
                /* the ToolType, it returns the default value (the third argument)                */&lt;br /&gt;
                newbroker.nb_Pri = (BYTE)ArgInt(ttypes, &amp;amp;quot;CX_PRIORITY&amp;amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                if (broker = CxBroker(&amp;amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxCustom() takes two arguments, a pointer to the custom function           */&lt;br /&gt;
                    /* and an ID. Commodities Exchange will assign that ID to any CxMsg           */&lt;br /&gt;
                    /* passed to the custom  function.                                            */&lt;br /&gt;
                    if (cocustom = CxCustom(CxFunction, 0L))&lt;br /&gt;
                    {&lt;br /&gt;
                        AttachCxObj(broker, cocustom);&lt;br /&gt;
&lt;br /&gt;
                        /* Allocate a signal bit for the signal CxObj */&lt;br /&gt;
                        if ( (signal = (ULONG)AllocSignal(-1L)) != -1)&lt;br /&gt;
                        {&lt;br /&gt;
                            /* set up the signal mask */&lt;br /&gt;
                            cxobjsignal = 1L &amp;amp;lt;&amp;amp;lt; signal;&lt;br /&gt;
                            cxsigflag |= cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
                            /* CxSignal takes two arguments, a pointer to the task to signal      */&lt;br /&gt;
                            /* (normally the commodity) and the number of the signal bit the      */&lt;br /&gt;
                            /* commodity acquired to signal with.                                 */&lt;br /&gt;
                            task = FindTask(NULL);&lt;br /&gt;
                            if (cosignal = CxSignal(task, signal))&lt;br /&gt;
                            {&lt;br /&gt;
                                AttachCxObj(cocustom, cosignal);&lt;br /&gt;
                                ActivateCxObj(broker, 1L);&lt;br /&gt;
                                ProcessMsg();&lt;br /&gt;
                            }&lt;br /&gt;
                            FreeSignal(signal);&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only deletes   */&lt;br /&gt;
                    /* the CxObject pointed to in its argument, but it deletes all of the         */&lt;br /&gt;
                    /* CxObjects that are attached to it.                                         */&lt;br /&gt;
                    DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
                        ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            ArgArrayDone();   /* this amiga.lib function cleans up after ArgArrayInit()           */&lt;br /&gt;
            CloseLibrary(IconBase);&lt;br /&gt;
        }&lt;br /&gt;
        CloseLibrary(CxBase);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    ULONG sigrcvd, msgid;&lt;br /&gt;
    LONG returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = CxMsgID(msg);&lt;br /&gt;
            ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgid)&lt;br /&gt;
            {&lt;br /&gt;
                case CXCMD_DISABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 0L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_ENABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 1L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_KILL:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_UNIQUE:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; SIGBREAKF_CTRL_C) returnvalue = 0L;&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if the signal CxObj signalled us. */&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; cxobjsignal) printf(&amp;amp;quot;Got Signal\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be short        */&lt;br /&gt;
/* and sweet because it runs as part of the input.device task.                                    */&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    struct InputEvent *ie;&lt;br /&gt;
    static ULONG time = 0L;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg. Unlike the InputEvent                */&lt;br /&gt;
    /* extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.       */&lt;br /&gt;
    ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* This custom function counts the number of timer events that go by while no other input     */&lt;br /&gt;
    /* events occur.  If it counts more than a certain amount of timer events, it clears the      */&lt;br /&gt;
    /* count and diverts the timer event CxMsg to the custom object&#039;s personal                    */&lt;br /&gt;
    /* list.  If an event besides a timer event passes by, the timer event count is reset.        */&lt;br /&gt;
    if (ie-&amp;amp;gt;ie_Class == IECLASS_TIMER)&lt;br /&gt;
    {&lt;br /&gt;
        time++;&lt;br /&gt;
        if (time &amp;amp;gt;= TIMER_CLICKS)&lt;br /&gt;
        {&lt;br /&gt;
            time = 0L;&lt;br /&gt;
            DivertCxMsg(cxm, co, co);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        time = 0L;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== New Input Events ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Library also has functions used to introduce new input events to the input stream.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct InputEvent *InvertString( UBYTE *string, ULONG *keymap );&lt;br /&gt;
VOID               FreeIEvents( struct InputEvent *ie );&lt;br /&gt;
VOID               AddIEvents( struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
InvertString() is a function that accepts an ASCII string and creates a linked list of input events that translate into the string using the supplied keymap (or the system default if the key map is NULL). The NULL terminated string may contain ANSI character codes, an input description enclosed in angle (&amp;amp;lt;&amp;amp;gt;) brackets, or one of the following backslash escape characters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| \r&lt;br /&gt;
| Return&lt;br /&gt;
|-&lt;br /&gt;
| \t&lt;br /&gt;
| Tab&lt;br /&gt;
|-&lt;br /&gt;
| \\&lt;br /&gt;
| Backslash&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abc&amp;amp;lt;alt f1&amp;amp;gt;\rhi there.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FreeIEvents() frees a list of input events allocated by InvertString(). AddIEvents() is a commodities.library function that adds a linked list of input events at the the top of the Commodities network. Each input event in the list is made into an individual CxMessage. Note that if passed a linked list of input events created by InvertString(), the order the events appear in the string will be reversed.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* PopShell.c - Simple hot key commodity example.&lt;br /&gt;
   Adapted by xenic from the original source code.&lt;br /&gt;
   &lt;br /&gt;
   Compile commands: gcc PopShell.c -o popshell -lamiga -Wall&lt;br /&gt;
&lt;br /&gt;
   To run the compiled program - Open 2 shell windows. Execute&lt;br /&gt;
   PopShell in the first shell window with a line like:&lt;br /&gt;
     PopShell HOTKEY &amp;quot;ctrl alt f&amp;quot;&lt;br /&gt;
   Activate the second shell window and enter the keyboard shortcut.&lt;br /&gt;
   A new shell window should open on the Workbench screen.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/alib_protos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static CONST_STRPTR version USED = &amp;quot;$VER: PopShell 1.1 (05.11.2015)&amp;quot;;&lt;br /&gt;
static CONST_STRPTR stackcookie USED = &amp;quot;$STACK: 32768&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase = NULL;&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp = NULL;&lt;br /&gt;
CxObj *broker, *filter;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
	NB_VERSION,&lt;br /&gt;
	&amp;quot;Amiga PopShell&amp;quot;,       /* string to identify this broker */&lt;br /&gt;
	&amp;quot;A Simple PopShell&amp;quot;,&lt;br /&gt;
	&amp;quot;A simple PopShell commodity&amp;quot;,&lt;br /&gt;
	NBU_UNIQUE | NBU_NOTIFY,      /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
	0, 0, 0, 0                    /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR newshell = &amp;quot;\rllehswen&amp;quot;;  /* &amp;quot;newshell&amp;quot; spelled backwards */&lt;br /&gt;
struct InputEvent *ie = NULL;&lt;br /&gt;
uint32 cxsigflag;&lt;br /&gt;
&lt;br /&gt;
#define TEMPLATE &amp;quot;HOTKEY/K,CX_PRIORITY/N&amp;quot;&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
	ARG_HOTKEY, ARG_PRIORITY, ARG_MAX&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	STRPTR hotkey = NULL;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	struct RDArgs *argsdata = NULL;&lt;br /&gt;
	int32 rargs[ARG_MAX] = {0};&lt;br /&gt;
	int8  priority = 0;&lt;br /&gt;
&lt;br /&gt;
	signal(SIGINT, SIG_IGN);&lt;br /&gt;
&lt;br /&gt;
	if ((CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 37L)))&lt;br /&gt;
	{&lt;br /&gt;
		if ((ICommodities = (struct CommoditiesIFace *)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL)))&lt;br /&gt;
		{&lt;br /&gt;
			if ((argsdata = IDOS-&amp;gt;ReadArgs(TEMPLATE, rargs, NULL)))&lt;br /&gt;
			{&lt;br /&gt;
				if (rargs[ARG_PRIORITY])&lt;br /&gt;
					priority = (int8)*(uint32 *)rargs[ARG_PRIORITY];&lt;br /&gt;
				if ((broker_mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)))&lt;br /&gt;
				{&lt;br /&gt;
					newbroker.nb_Port = broker_mp;&lt;br /&gt;
					cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
					newbroker.nb_Pri = priority;&lt;br /&gt;
					hotkey = (STRPTR)rargs[ARG_HOTKEY];&lt;br /&gt;
&lt;br /&gt;
					if ((broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL)))&lt;br /&gt;
					{&lt;br /&gt;
						/* CxFilter(), CxSender() &amp;amp; CxTranslate() are    */&lt;br /&gt;
						/* macros defined in libraries/commodities.h.    */&lt;br /&gt;
						/* CxFilter() creates a filter CxObject.         */&lt;br /&gt;
						/* CxSender() creates a sender CxObject.         */&lt;br /&gt;
						/* CxTranslate() creates a translation CxObject. */&lt;br /&gt;
						if ((filter = CxFilter(hotkey)))&lt;br /&gt;
						{&lt;br /&gt;
							/* Add a filter CxObject to another&#039;s personal list. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
							/* Add a sender CxObject to the filter CxObject. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxSender(broker_mp, EVT_HOTKEY));&lt;br /&gt;
&lt;br /&gt;
							/* Add a translation CxObject to the filter CxObject */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxTranslate(NULL));&lt;br /&gt;
&lt;br /&gt;
							if (!(ICommodities-&amp;gt;CxObjError(filter)))&lt;br /&gt;
							{&lt;br /&gt;
								/* InvertString() is an amiga.lib function that creates a linked */&lt;br /&gt;
								/* list of input events which would translate into the string    */&lt;br /&gt;
								/* passed to it.  Note that it puts the input events in the      */&lt;br /&gt;
								/* opposite order in which the corresponding letters appear in   */&lt;br /&gt;
								/* the string.  A translate CxObject expects them backwards.     */&lt;br /&gt;
								if ((ie = InvertString(newshell, NULL)))&lt;br /&gt;
								{&lt;br /&gt;
									ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
									ProcessMsg();&lt;br /&gt;
									/* we have to release the memory allocated by InvertString. */&lt;br /&gt;
									FreeIEvents(ie);&lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						/* DeleteCxObjAll() is a commodities.library function that */&lt;br /&gt;
						/* deletes the CxObject pointed to in its argument and     */&lt;br /&gt;
						/* deletes all of the CxObjects attached to it.            */&lt;br /&gt;
						ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
						/* Empty the port of all CxMsgs */&lt;br /&gt;
						while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
							IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
					}&lt;br /&gt;
					IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
				}&lt;br /&gt;
				IDOS-&amp;gt;FreeArgs(argsdata); /* cleans up after ReadArgs() */&lt;br /&gt;
			}&lt;br /&gt;
			IExec-&amp;gt;DropInterface((struct Interface *)ICommodities);&lt;br /&gt;
		}&lt;br /&gt;
		IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
	}&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
	extern struct MsgPort *broker_mp;&lt;br /&gt;
	extern CxObj *broker;&lt;br /&gt;
	extern uint32 cxsigflag;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
	int32  returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
	while (returnvalue)&lt;br /&gt;
	{&lt;br /&gt;
		sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
		while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
		{&lt;br /&gt;
			msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
			msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
			IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
			switch(msgtype)&lt;br /&gt;
			{&lt;br /&gt;
				case CXM_IEVENT:&lt;br /&gt;
					printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case EVT_HOTKEY:&lt;br /&gt;
							/* We got the message from the sender CxObject  */&lt;br /&gt;
							printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
							/* Add the string &amp;quot;newshell&amp;quot; to input * stream. */&lt;br /&gt;
							/*  If a shell gets it, it&#039;ll open a new shell. */&lt;br /&gt;
							ICommodities-&amp;gt;AddIEvents(ie);&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				case CXM_COMMAND:&lt;br /&gt;
					printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case CXCMD_DISABLE:&lt;br /&gt;
						printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_ENABLE:&lt;br /&gt;
							printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_KILL:&lt;br /&gt;
							printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_UNIQUE:&lt;br /&gt;
						/* Commodities Exchange can be told not only to refuse to launch a    */&lt;br /&gt;
						/* commodity with a name already in use but also can notify the       */&lt;br /&gt;
						/* already running commodity that it happened.  It does this by       */&lt;br /&gt;
						/* sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE. If the     */&lt;br /&gt;
						/* user tries to run a windowless commodity that is already running,  */&lt;br /&gt;
						/* the user wants the commodity to shut down.                         */&lt;br /&gt;
							printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				default:&lt;br /&gt;
					printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
		{&lt;br /&gt;
			returnvalue = 0L;&lt;br /&gt;
			printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Commodities Exchange functions covered in this article. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CxBroker()&lt;br /&gt;
| Creates a CxObject of type Broker.&lt;br /&gt;
|-&lt;br /&gt;
| CxFilter()&lt;br /&gt;
| Creates a CxObject of type Filter.&lt;br /&gt;
|-&lt;br /&gt;
| CxSender()&lt;br /&gt;
| Creates a CxObject of type Sender.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Creates a CxObject of type Translate.&lt;br /&gt;
|-&lt;br /&gt;
| CxSignal()&lt;br /&gt;
| Creates a CxObject of type Signal.&lt;br /&gt;
|-&lt;br /&gt;
| CxCustom()&lt;br /&gt;
| Creates a CxObject of type Custom.&lt;br /&gt;
|-&lt;br /&gt;
| CxDebug()&lt;br /&gt;
| Creates a CxObject of type Debug.&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObj()&lt;br /&gt;
| Frees a single CxObject&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObjAll()&lt;br /&gt;
| Frees a group of connected CxObjects&lt;br /&gt;
|-&lt;br /&gt;
| ActivateCxObj()&lt;br /&gt;
| Activates a newly created CxObject in the commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Sets up substitution of one input event for another by translate CxObjects.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgType()&lt;br /&gt;
| Finds the type of a CxMessage.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgData()&lt;br /&gt;
| Returns the CxMessage data.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgID()&lt;br /&gt;
| Returns the CxMessage ID.&lt;br /&gt;
|-&lt;br /&gt;
| CxObjError()&lt;br /&gt;
| Returns the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| ClearCxObjError()&lt;br /&gt;
| Clear the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| AttachCxObj()&lt;br /&gt;
| Attaches a CxObject to the end of a given CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| InsertCxObj()&lt;br /&gt;
| Inserts a CxObject in a given position in a CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| EnqueueCxObj()&lt;br /&gt;
| Inserts a CxObject in a CxObject&#039;s list by priority.&lt;br /&gt;
|-&lt;br /&gt;
| SetCxObjPri()&lt;br /&gt;
| Sets a CxObject&#039;s priority for EnqueueCxObj().&lt;br /&gt;
|-&lt;br /&gt;
| RemoveCxObj()&lt;br /&gt;
| Removes a CxObject from a list.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilter()&lt;br /&gt;
| Set a filter for a CxObject from an input description string.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilterIX()&lt;br /&gt;
| Set a filter for a CxObject from an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| ParseIX()&lt;br /&gt;
| Convert an input description string to an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| DivertCxMsg()&lt;br /&gt;
| Divert a CxMessage to one CxObject and return it to another.&lt;br /&gt;
|-&lt;br /&gt;
| RouteCxMsg()&lt;br /&gt;
| Redirect a CxMessage to a new CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeCxMsg()&lt;br /&gt;
| Cancel a CxMessage removing it from the Commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| InvertString()&lt;br /&gt;
| Creates a linked list of input events that correspond to a given string.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIEvents()&lt;br /&gt;
| Frees the linked list of input events created with InvertString().&lt;br /&gt;
|-&lt;br /&gt;
| AddIEvents()&lt;br /&gt;
| Converts a list of input events to CxMessages and puts them into the network.&lt;br /&gt;
|-&lt;br /&gt;
| CopyBrokerList()&lt;br /&gt;
| Creates a local copy of the current broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| FreeBrokerList()&lt;br /&gt;
| Frees the local broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| BrokerCommand()&lt;br /&gt;
| Sends a command to a commodity broker (V53.4).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Functions ===&lt;br /&gt;
&lt;br /&gt;
The following functions are obsolete and no longer used:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayInit()&lt;br /&gt;
| Create a Tool Types array from argc and argv (Workbench or Shell).&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayDone()&lt;br /&gt;
| Free the resources used by ArgArrayInit().&lt;br /&gt;
|-&lt;br /&gt;
| ArgString()&lt;br /&gt;
| Return the string associated with a given Tool Type in the array.&lt;br /&gt;
|-&lt;br /&gt;
| ArgInt()&lt;br /&gt;
| Return the integer associated with a given Tool Type in the array.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8516</id>
		<title>Commodities Exchange Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8516"/>
		<updated>2015-12-06T09:06:16Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Replaced BYTE, ULONG and LONG with int8, uint32 and int32, respectively.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{CodeReview}}&lt;br /&gt;
== Commodities Exchange Library ==&lt;br /&gt;
&lt;br /&gt;
This article describes Commodities Exchange, the library of routines used to add a custom input handler to the Amiga. With Commodities Exchange, any program function can be associated with key combinations or other input events &#039;&#039;globally&#039;&#039; allowing the creation utility programs that run in the background for all tasks.&lt;br /&gt;
&lt;br /&gt;
== Custom Input Handlers ==&lt;br /&gt;
&lt;br /&gt;
The input.device has a hand in almost all user input on the Amiga. It gathers input events from the keyboard, the gameport (mouse), and several other sources, into one input &amp;quot;stream&amp;quot;. Special programs called input event handlers intercept input events along this stream, examining and sometimes changing the input events. Both Intuition and the console device use input handlers to process user input.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-1.png|frame|center|The Amiga Input Stream]]&lt;br /&gt;
&lt;br /&gt;
Using the input.device, a program can introduce its own custom handler into the chain of input handlers at almost any point in the chain. &amp;quot;Hot key&amp;quot; programs, shell pop-up programs, and screen blankers all commonly use custom input handlers to monitor user input before it gets to the Intuition input handler.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-2.png|frame|center|A Custom Input Handler]]&lt;br /&gt;
&lt;br /&gt;
Custom input handlers do have their drawbacks, however. Not only are these handlers hard to program, but because there is no standard way to implement and control them, multiple handlers often do not work well together. Their antisocial behavior can result in load order dependencies and incompatibilities between different custom input handlers. Even for the expert user, having several custom input handlers coexist peacefully can be next to impossible.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-3.png|frame|center|The Commodities Network]]&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange eliminates these problems by providing a simple, standardized way to program and control custom input handlers. It is divided into two parts: an Exec library and a controller program.&lt;br /&gt;
&lt;br /&gt;
The Exec library is called commodities.library. When it is first opened, commodities.library establishes a single input handler just before Intuition in the input chain. When this input handler receives an input event, it creates a CxMessage (Commodities Exchange Message) corresponding to the input event, and diverts the CxMessage through the network of Commodities Exchange input handlers.&lt;br /&gt;
&lt;br /&gt;
These handlers are made up of trees of different CxObjects (Commodities Exchange Objects), each of which performs a simple operation on the CxMessages. Any CxMessages that exit the network are returned to the input.device&#039;s input stream as input events.&lt;br /&gt;
&lt;br /&gt;
Through function calls to the commodities.library, an application can install a custom input handler. A Commodities Exchange application, sometimes simply referred to as a commodity, uses the CxObject primitives to do things such as filter certain CxMessages, translate CxMessages, signal a task when a CxObject receives a CxMessage, send a message when a CxObject receives a CxMessage, or if necessary, call a custom function when a CxObject receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== Commodities Control ===&lt;br /&gt;
&lt;br /&gt;
The standard controller program is called Exchange. It is located in the Utilities/Commodities drawer on your system partition. With Exchange, the user can monitor and control all the currently running Commodities Exchange applications from this one program. The user can enable and disable a commodity, kill a commodity, or, if the commodity has a window, ask the commodity to show or hide its window. When the user requests any of these actions, the controller program sends the commodity a message, telling it which action to perform.&lt;br /&gt;
&lt;br /&gt;
Starting with version 53.4 of the commodities.library, functions are available that allow developers to write their own commodity control programs (Exchange replacements). These functions were not public previously.&lt;br /&gt;
&lt;br /&gt;
=== Important Notes ===&lt;br /&gt;
&lt;br /&gt;
* Commodities are special-purpose programs. In fact, most programs or applications are &#039;&#039;&#039;not&#039;&#039;&#039; suitable for becoming commodities. The Commodities Exchange framework is ideal for programs that need to monitor all user input: hotkey utilities, screen blankers, mouse blankers, etc.&lt;br /&gt;
* In the past, some developers turned their programs into commodities only to provide them with a handy keyboard shortcut to bring up/close their GUI. Please note that such practice is actually a misuse of the commodities framework.&lt;br /&gt;
* Commodities Exchange should never be used as an alternate method of receiving user input for an application. Other applications depend on getting user input in some form or another from the input stream. A greedy program that diverts input to itself rather than letting the input go to where the user expects it can seriously confuse the user, not to mention compromise the advantages of multitasking.&lt;br /&gt;
&lt;br /&gt;
== CxObjects ==&lt;br /&gt;
&lt;br /&gt;
CxObjects are the basic building blocks used to construct a commodity. A commodity uses CxObjects to take care of all manipulations of CxMessages. When a CxMessage &amp;quot;arrives&amp;quot; at a CxObject, that CxObject carries out its primitive action and then, if it has not deleted the CxMessage, it passes the CxMessage on to the next CxObject. A commodity links together CxObjects into a tree, organizing these simple action objects to perform some higher function.&lt;br /&gt;
&lt;br /&gt;
A CxObject is in one of two states, active or inactive. An active CxObject performs its primitive action every time it receives a CxMessage. If a CxObject is inactive, CxMessages bypass it, continuing to the CxObject that follows the inactive one. By default, all CxObjects except the type called brokers are created in the active state.&lt;br /&gt;
&lt;br /&gt;
Currently, there are seven types of CxObjects:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Commodities Exchange Object Types&lt;br /&gt;
! Object Type&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Broker || Registers a new commodity with the commodity network&lt;br /&gt;
|-&lt;br /&gt;
| Filter || Accepts or rejects input events based on criteria set up by the application&lt;br /&gt;
|-&lt;br /&gt;
| Sender || Sends a message to a message port&lt;br /&gt;
|-&lt;br /&gt;
| Translate || Replaces the input event with a different one&lt;br /&gt;
|-&lt;br /&gt;
| Signal || Signals a task&lt;br /&gt;
|-&lt;br /&gt;
| Custom || Calls a custom function provided by the commodity&lt;br /&gt;
|-&lt;br /&gt;
| Debug || Sends debug information out the serial port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installing A Broker Object ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Exchange input handler maintains a master list of CxObjects to which it diverts input events using CxMessages. The CxObjects in this master list are a special type of CxObject called &#039;&#039;brokers&#039;&#039;. The only thing a broker CxObject does is divert CxMessages to its own personal list of CxObjects. A commodity creates a broker and attaches other CxObjects to it. These attached objects take care of the actual input handler related work of the commodity and make up the broker&#039;s personal list.&lt;br /&gt;
&lt;br /&gt;
The first program listing, &amp;quot;Broker.c&amp;quot;, is a very simple example of a working commodity. It serves only to illustrate the basics of a commodity, not to actually perform any useful function. It shows how to set up a broker and process commands from the controller program.&lt;br /&gt;
&lt;br /&gt;
Besides opening commodities.library and creating an Exec message port, setting up a commodity requires creating a broker. The function CxBroker() creates a broker and adds it to the master list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxBroker(struct NewBroker *nb, LONG *error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s first argument is a pointer to a NewBroker structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct NewBroker {&lt;br /&gt;
   BYTE     nb_Version;   /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   BYTE     *nb_Name;&lt;br /&gt;
   BYTE     *nb_Title;&lt;br /&gt;
   BYTE     *nb_Descr;&lt;br /&gt;
   SHORT    nb_Unique;&lt;br /&gt;
   SHORT    nb_Flags;&lt;br /&gt;
   BYTE     nb_Pri;       /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   struct   MsgPort   *nb_Port;&lt;br /&gt;
   WORD     nb_ReservedChannel;  /* Unused, make zero for future compatibility */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange gets all the information it needs about the broker from this structure. NewBroker&#039;s nb_Version field contains the version number of the NewBroker structure. This should be set to NB_VERSION which is defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. The nb_Name, nb_Title, and nb_Descr point to strings which hold the name, title, and description of the broker. The two bit fields, nb_Unique and nb_Flags, toggle certain features of Commodities Exchange based on their values. They are discussed in detail later in this article.&lt;br /&gt;
&lt;br /&gt;
The nb_Pri field contains the broker&#039;s priority. Commodities Exchange inserts the broker into the master list based on this number. Higher priority brokers get CxMessages before lower priority brokers.&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s second argument is a pointer to a LONG. If this pointer is not NULL, CxBroker() fills in this field with one of the following error return codes from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CBERR_OK        0        /* No error                         */&lt;br /&gt;
CBERR_SYSERR    1        /* System error , no memory, etc    */&lt;br /&gt;
CBERR_DUP       2        /* uniqueness violation             */&lt;br /&gt;
CBERR_VERSION   3        /* didn&#039;t understand nb_VERSION     */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the broker object is created with CxBroker(), it must be activated with ActivateCxObject().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG oldactivationvalue = ActivateCxObj(CxObj *co, LONG newactivationvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After successfully completing the initial set up and activating the broker, a commodity can begin its input processing loop waiting for CxMessages to arrive.&lt;br /&gt;
&lt;br /&gt;
== CxMessages ==&lt;br /&gt;
&lt;br /&gt;
There are actually two types of CxMessages. The first, CXM_IEVENT, corresponds to an input event and travels through the Commodities Exchange network. The other type, CXM_COMMAND, carries a command to a commodity. A CXM_COMMAND normally comes from the controller program and is used to pass user commands on to a commodity. A commodity receives these commands through an Exec message port that the commodity sets up before it calls CxBroker(). The NewBroker&#039;s nb_Port field points to this message port. A commodity can tell the difference between the two types of CxMessages by calling the CxMsgType() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG  CxMsgType( CxMsg *cxm );&lt;br /&gt;
UBYTE *CxMsgData( CxMsg *cxm );&lt;br /&gt;
LONG   CxMsgID  ( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A CxMessage not only has a type, it can also have a data pointer as well as an ID associated with it. The data associated with a CXM_IEVENT CxMessage is an InputEvent structure. By using the CxMsgData() function, a commodity can obtain a pointer to the corresponding InputEvent of a CXM_IEVENT message. Commodities Exchange gives an ID of zero to any CXM_IEVENT CxMessage that it introduces to the Commodities network but certain CxObjects can assign an ID to them.&lt;br /&gt;
&lt;br /&gt;
For a CXM_COMMAND CxMessages, the data pointer is generally not used but the ID specifies a command passed to the commodity from the user operating the controller program. The CxMsgID() macro extracts the ID from a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== A Simple Commodity Example ===&lt;br /&gt;
&lt;br /&gt;
The example below, &amp;quot;Broker.c&amp;quot;, receives input from one source, the controller program. The controller program sends a CxMessage each time the user clicks its Enable, Disable, or Kill gadgets. Using the CxMsgID() function, the commodity finds out what the command is and executes it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// broker.c - Simple skeletal example of opening a broker&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
CxObj *broker;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
ULONG cxsigflag;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,   /* nb_Version - Version of the NewBroker structure */&lt;br /&gt;
    &amp;quot;Amiga broker&amp;quot;, /* nb_Name - Name Commodities uses to identify this commodity */&lt;br /&gt;
    &amp;quot;Broker&amp;quot;,     /* nb_Title - Title of commodity that appears in CXExchange */&lt;br /&gt;
    &amp;quot;A simple example of a broker&amp;quot;,  /* nb_Descr - Description of the commodity */&lt;br /&gt;
    0,            /* nb_Unique - Tells CX not to launch another commodity with same name */&lt;br /&gt;
    0,            /* nb_Flags - Tells CX if this commodity has a window */&lt;br /&gt;
    0,            /* nb_Pri - This commodity&#039;s priority */&lt;br /&gt;
    0,            /* nb_Port - MsgPort CX talks to */&lt;br /&gt;
    0             /* nb_ReservedChannel - reserved for later use */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (ICommodities != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* Commodities talks to a Commodities application through */&lt;br /&gt;
        /* an Exec Message port, which the application provides   */&lt;br /&gt;
        if (broker_mp = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            newbroker.nb_Port = broker_mp;&lt;br /&gt;
&lt;br /&gt;
            /* The commodities.library function CxBroker() adds a borker to the&lt;br /&gt;
             * master list.  It takes two arguments, a pointer to a NewBroker&lt;br /&gt;
             * structure and a pointer to a LONG.  The NewBroker structure contains&lt;br /&gt;
             * information to set up the broker.  If the second argument is not&lt;br /&gt;
             * NULL, CxBroker will fill it in with an error code.&lt;br /&gt;
             */&lt;br /&gt;
            if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
            {&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* After it&#039;s set up correctly, the broker has to be activated */&lt;br /&gt;
                ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
&lt;br /&gt;
                /* the main processing loop */&lt;br /&gt;
                ProcessMsg();&lt;br /&gt;
&lt;br /&gt;
                /* It&#039;s time to clean up.  Start by removing the broker from the&lt;br /&gt;
                 * Commodities master list.  The DeleteCxObjAll() function will&lt;br /&gt;
                 * take care of removing a CxObject and all those connected&lt;br /&gt;
                 * to it from the Commodities network&lt;br /&gt;
                 */&lt;br /&gt;
                ICommodities-&amp;gt;DeleteCxObj(broker);&lt;br /&gt;
&lt;br /&gt;
                /* Empty the port of CxMsgs */&lt;br /&gt;
                while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        /* wait for something to happen */&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        /* process any messages */&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            /* Extract necessary information from the CxMessage and return it */&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    /* Shouldn&#039;t get any of these in this example */&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    /* Commodities has sent a command */&lt;br /&gt;
                    printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            /* The user clicked Commodities Exchange disable&lt;br /&gt;
                             * gadget better disable&lt;br /&gt;
                             */&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            /* user clicked enable gadget */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            /* user clicked kill gadget, better quit */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Test to see if user tried to break */&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that &amp;quot;Broker.c&amp;quot; uses Ctrl-C as a break key. The break key for any commodity should be Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
=== Controller Commands ===&lt;br /&gt;
&lt;br /&gt;
The commands that a commodity can receive from the controller program (as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CXCMD_DISABLE     /* please disable yourself       */&lt;br /&gt;
CXCMD_ENABLE      /* please enable yourself        */&lt;br /&gt;
CXCMD_KILL        /* go away for good              */&lt;br /&gt;
CXCMD_APPEAR      /* open your window, if you can  */&lt;br /&gt;
CXCMD_DISAPPEAR   /* hide your window              */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CXCMD_DISABLE, CXCMD_ENABLE, and CXCMD_KILL commands correspond to the similarly named controller program gadgets, Disable, Enable, and Kill; CXCMD_APPEAR and CXCMD_DISAPPEAR correspond to the controller program gadgets, Show and Hide. These gadgets are ghosted in Broker.c because it has no window (It doesn&#039;t make much sense to give the user a chance to click the Show and Hide gadgets). In order to do this, Broker.c has to tell Commodities Exchange to ghost these gadgets. When CxBroker() sets up a broker, it looks at the NewBroker.nb_Flags field to see if the COF_SHOW_HIDE bit (from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) is set. If it is, the &amp;quot;Show&amp;quot; and &amp;quot;Hide&amp;quot; gadgets for this broker will be selectable. Otherwise they are ghosted and disabled.&lt;br /&gt;
&lt;br /&gt;
=== Shutting Down the Commodity ===&lt;br /&gt;
&lt;br /&gt;
Shutting down a commodity is easy. After replying to all CxMessages waiting at the broker&#039;s message port, a commodity can delete its CxObjects. The DeleteCxObj() function removes a single CxObject from the Commodities network. DeleteCxObjectAll() removes multiple objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DeleteCxObj( CxObj *co );&lt;br /&gt;
VOID DeleteCxObjAll( CxObj *delete_co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a commodity has a lot of CxObjects, deleting each individually can be a bit tedious. DeleteCxObjAll() will delete a CxObject and any other CxObjects that are attached to it. The HotKey.c example given later in this article uses this function to delete all its CxObjects. A commodity that uses DeleteCxObjAll() to delete all its CxObjects should make sure that they are all connected to the main one. (See the &amp;quot;Connecting CxObjects&amp;quot; section below.)&lt;br /&gt;
&lt;br /&gt;
After deleting its CxObjects, a commodity must take care of any CxMessages that might have arrived at the message port just before the commodity deleted its objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commodity Tool Types ==&lt;br /&gt;
&lt;br /&gt;
A goal of Commodities Exchange is to improve user control over input handlers. One way in which it accomplishes this goal is through the use of standard icon Tool Types. The user will expect commodities to recognize the set of standard Tool Types:&lt;br /&gt;
&lt;br /&gt;
* CX_PRIORITY&lt;br /&gt;
* CX_POPUP&lt;br /&gt;
* CX_POPKEY&lt;br /&gt;
&lt;br /&gt;
CX_PRIORITY lets the user set the priority of a commodity. The string &amp;amp;quot;CX_PRIORITY=&amp;amp;quot; is a number from -128 to 127. The higher the number, the higher the priority of the commodity, giving it access to input events before lower priority commodities. All commodities should recognize CX_PRIORITY.&lt;br /&gt;
&lt;br /&gt;
CX_POPUP and CX_POPKEY are only relevant to commodities with a window. The string &amp;amp;quot;CX_POPUP=&amp;amp;quot; should be followed by a &amp;quot;yes&amp;quot; or &amp;quot;no&amp;quot;, telling the commodity if it should or shouldn&#039;t show its window when it is first launched. CX_POPKEY is followed by a string describing the key to use as a hot key for making the commodity&#039;s window appear (pop up). The description string for CX_POPKEY describes an input event. The specific format of the string is discussed in the next section (&amp;quot;Filter Objects and the Input Description String&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Parsing Functions ===&lt;br /&gt;
&lt;br /&gt;
Prior to AmigaOS 4.0, the Commodities Library had the following support functions which were included in an external link library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
UBYTE **ArgArrayInit(LONG argc, UBYTE **argv);&lt;br /&gt;
VOID ArgArrayDone(void);&lt;br /&gt;
STRPTR ArgString(UBYTE **tooltypearray, STRPTR tooltype, STRPTR defaultvalue);&lt;br /&gt;
LONG *ArgInt(UBYTE **tooltypearray, STRPTR tooltype, LONG defaultvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are no longer available. Use IDOS-&amp;gt;ReadArgs(), IIcon-&amp;gt;FindToolType() and IIcon-&amp;gt;MatchToolValue() instead, depending on whether the commodity was launched from Workbench or the Shell (CLI).&lt;br /&gt;
&lt;br /&gt;
== Filter Objects and Input Description Strings ==&lt;br /&gt;
&lt;br /&gt;
Because not all commodities are interested in every input event that makes it way down the input chain, Commodities Exchange has a method for filtering them. A filter CxObject compares the CxMessages it receives to a pattern. If a CxMessage matches the pattern, the filter diverts the CxMessage down its personal list of CxObjects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxFilter(STRPTR descriptionstring);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The C macro CxFilter() (defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) returns a pointer to a filter CxObject. The macro has only one argument, a pointer to a string describing which input events to filter. The following regular expression outlines the format of the input event description string (CX_POPKEY uses the same description string format):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[class]  { [-] (qualifier | synonym) ) }  [ [-] upstroke]  [highmap | ANSICode]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Class&#039;&#039; can be any one of the class strings in the table below. Each class string corresponds to a class of input event as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;. Commodities Exchange will assume the class is rawkey if the class is not explicitly stated.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawkey&amp;amp;quot; || IECLASS_RAWKEY&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawmouse&amp;amp;quot; || IECLASS_RAWMOUSE&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;event&amp;amp;quot; || IECLASS_EVENT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;pointerpos&amp;amp;quot; || IECLASS_POINTERPOS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;timer&amp;amp;quot; || IECLASS_TIMER&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;newprefs&amp;amp;quot; || IECLASS_NEWPREFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskremoved&amp;amp;quot; || IECLASS_DISKREMOVED&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskinserted&amp;amp;quot; || IECLASS_DISKINSERTED&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Qualifier&#039;&#039; is one of the qualifier strings from the table below. Each string corresponds to an input event qualifier as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;). A dash preceding the qualifier string tells the filter object not to care if that qualifier is present in the input event. Notice that there can be more than one qualifier (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Qualifier String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lshift&amp;amp;quot; || IEQUALIFIER_LSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rshift&amp;amp;quot; || IEQUALIFIER_RSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;capslock&amp;amp;quot; || IEQUALIFIER_CAPSLOCK&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;control&amp;amp;quot; || IEQUALIFIER_CONTROL&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lalt&amp;amp;quot; || IEQUALIFIER_LALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;ralt&amp;amp;quot; || IEQUALIFIER_RALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lcommand&amp;amp;quot; || IEQUALIFIER_LCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rcommand&amp;amp;quot; || IEQUALIFIER_RCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;numericpad&amp;amp;quot; || IEQUALIFIER_NUMERICPAD&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;repeat&amp;amp;quot; || IEQUALIFIER_REPEAT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;midbutton&amp;amp;quot; || IEQUALIFIER_MIDBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rbutton&amp;amp;quot; || IEQUALIFIER_RBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;leftbutton&amp;amp;quot; || IEQUALIFIER_LEFTBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;relativemouse&amp;amp;quot; || IEQUALIFIER_RELATIVEMOUSE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Synonym&#039;&#039; is one of the synonym strings from the table below. These strings act as synonyms for groups of qualifiers. Each string corresponds to a synonym identifier as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. A dash preceding the synonym string tells the filter object not to care if that synonym is present in the input event. Notice that there can be more than one synonym (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Synonym String&lt;br /&gt;
! Synonym Identifier&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;shift&amp;amp;quot; || IXSYM_SHIFT || Look for either Shift key&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;caps&amp;amp;quot; || IXSYM_CAPS || Look for either Shift key or Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;alt&amp;amp;quot; || IXSYM_ALT || Look for either Alt key&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Upstroke&#039;&#039; is the literal string &amp;amp;quot;upstroke&amp;amp;quot;. If this string is absent, the filter considers only downstrokes. If it is present alone, the filter considers only upstrokes. If preceded by a dash, the filter considers both upstrokes and downstrokes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Highmap&#039;&#039; is one of the following strings:&lt;br /&gt;
&lt;br /&gt;
&amp;amp;quot;backspace&amp;amp;quot;, &amp;amp;quot;del&amp;amp;quot;, &amp;amp;quot;down&amp;amp;quot;, &amp;amp;quot;enter&amp;amp;quot;, &amp;amp;quot;esc&amp;amp;quot;, &amp;amp;quot;f1&amp;amp;quot;, &amp;amp;quot;f2&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f3&amp;amp;quot;, &amp;amp;quot;f4&amp;amp;quot;, &amp;amp;quot;f5&amp;amp;quot;, &amp;amp;quot;f6&amp;amp;quot;, &amp;amp;quot;f7&amp;amp;quot;, &amp;amp;quot;f8&amp;amp;quot;, &amp;amp;quot;f9&amp;amp;quot;, &amp;amp;quot;f10&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f11, &amp;amp;quot;f12&amp;amp;quot;, &amp;amp;quot;help&amp;amp;quot;, &amp;amp;quot;left&amp;amp;quot;, &amp;amp;quot;return&amp;amp;quot;, &amp;amp;quot;right&amp;amp;quot;, &amp;amp;quot;space&amp;amp;quot;, &amp;amp;quot;tab&amp;amp;quot;, &amp;amp;quot;up&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;ANSICode&#039;&#039; is a single character (for example &amp;quot;a&amp;quot; that Commodities Exchange looks up in the system default keymap.&lt;br /&gt;
&lt;br /&gt;
Here are some example description strings. For function key F2 with the left Shift and either Alt key pressed, the input description string would be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawkey lshift alt f2&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify the key that produces an &amp;quot;a&amp;quot; (this may or may not be the A key depending on the keymap), with or without any Shift, Alt, or Control keys pressed use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;-shift -alt -control a&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a mouse move with the right mouse button down, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawmouse rbutton&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify a timer event use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;timer&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Connecting CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A CxObject has to be inserted into the Commodities network before it can process any CxMessages. AttachCxObj() adds a CxObject to the personal list of another CxObject. The HotKey.c example uses it to attach its filter to a broker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID AttachCxObj ( CxObj *headobj, CxObj *co);&lt;br /&gt;
VOID InsertCxObj ( CxObj *headobj, CxObj *co, CxObj *co_pred );&lt;br /&gt;
VOID EnqueueCxObj( CxObj *headobj, CxObj *co );&lt;br /&gt;
VOID SetCxObjPri ( CxObj *co, LONG pri );&lt;br /&gt;
VOID RemoveCxObj ( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AttachCxObj() adds the CxObject to the end of headobj&#039;s personal list. The ordering of a CxObject list determines which object gets CxMessages first. InsertCxObj() also inserts a CxObject, but it inserts it after another CxObject already in the personal list (co_pred in the prototype above).&lt;br /&gt;
&lt;br /&gt;
Brokers aren&#039;t the only CxObjects with a priority. All CxObjects have a priority associated with them. To change the priority of any CxObject, use the SetCxObjPri() function. A commodity can use the priority to keep CxObjects in a personal list sorted by their priority. The commodities.library function EnqueueCxObj() inserts a CxObject into another CxObject&#039;s personal list based on priority.&lt;br /&gt;
&lt;br /&gt;
Like its name implies, the RemoveCxObj() function removes a CxObject from a personal list. Note that it is not necessary to remove a CxObject from a list in order to delete it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* HotKey.c - Simple hot key commodity&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/icon.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities;&lt;br /&gt;
struct IconIFace *IIcon;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *filter, *sender, *translate;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;quot;Amiga HotKey&amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;quot;A Simple HotKey&amp;quot;,&lt;br /&gt;
    &amp;quot;A simple hot key commodity&amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,    /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                  /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint32 cxsigflag;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    uint8 *hotkey, **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    /* open the icon.library for the support library */&lt;br /&gt;
    /* functions, ArgArrayInit() and ArgArrayDone()  */&lt;br /&gt;
    struct Library *IconBase = IExec-&amp;gt;OpenLibrary(&amp;quot;icon.library&amp;quot;, 50);&lt;br /&gt;
    struct IconIFace *IIcon = (struct IconIFace*)IExec-&amp;gt;GetInterface(IconBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
    if (ICommodities != NULL &amp;amp;&amp;amp; IIcon != NULL)&lt;br /&gt;
    {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (from the 2.0 version&lt;br /&gt;
                 * of amiga.lib) that makes it easy to read arguments from either a&lt;br /&gt;
                 * CLI or from Workbench&#039;s ToolTypes.  Because it uses icon.library,&lt;br /&gt;
                 * the library has to be open before calling this function.&lt;br /&gt;
                 * ArgArrayDone() cleans up after this function.&lt;br /&gt;
                 */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (also from amiga.lib) searches through the array set up&lt;br /&gt;
                 * by ArgArrayInit() for a specific ToolType.  If it finds one, it&lt;br /&gt;
                 * returns the numeric value of the number that followed the&lt;br /&gt;
                 * ToolType (i.e., CX_PRIORITY=7). If it doesn&#039;t find the ToolType,&lt;br /&gt;
                 * it returns the default value (the third argument)&lt;br /&gt;
                 */&lt;br /&gt;
                newbroker.nb_Pri = (int8)ArgInt(ttypes, &amp;quot;CX_PRIORITY&amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                /* ArgString() works just like ArgInt(), except it returns a pointer to a string&lt;br /&gt;
                 * rather than an integer. In the example below, if there is no ToolType&lt;br /&gt;
                 * &amp;quot;HOTKEY&amp;quot;, the function returns a pointer to &amp;quot;rawkey control esc&amp;quot;.&lt;br /&gt;
                 */&lt;br /&gt;
                hotkey = ArgString(ttypes, &amp;quot;HOTKEY&amp;quot;, &amp;quot;rawkey control esc&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxFilter() is a macro that creates a filter CxObject.  This filter&lt;br /&gt;
                     * passes input events that match the string pointed to by hotkey.&lt;br /&gt;
                     */&lt;br /&gt;
                    if (filter = ICommodities-&amp;gt;CxFilter(hotkey))&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Add a CxObject to another&#039;s personal list */&lt;br /&gt;
                        ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
                        /* CxSender() creates a sender CxObject.  Every time a sender gets&lt;br /&gt;
                         * a CxMessage, it sends a new CxMessage to the port pointed to in&lt;br /&gt;
                         * the first argument. CxSender()&#039;s second argument will be the ID&lt;br /&gt;
                         * of any CxMessages the sender sends to the port.  The data pointer&lt;br /&gt;
                         * associated with the CxMessage will point to a *COPY* of the&lt;br /&gt;
                         * InputEvent structure associated with the orginal CxMessage.&lt;br /&gt;
                         */&lt;br /&gt;
                        if (sender = CxSender(broker_mp, EVT_HOTKEY))&lt;br /&gt;
                        {&lt;br /&gt;
                            ICommodities-&amp;gt;AttachCxObj(filter, sender);&lt;br /&gt;
&lt;br /&gt;
                            /* CxTranslate() creates a translate CxObject. When a translate&lt;br /&gt;
                             * CxObject gets a CxMessage, it deletes the original CxMessage&lt;br /&gt;
                             * and adds a new input event to the input.device&#039;s input stream&lt;br /&gt;
                             * after the Commodities input handler. CxTranslate&#039;s argument&lt;br /&gt;
                             * points to an InputEvent structure from which to create the new&lt;br /&gt;
                             * input event.  In this example, the pointer is NULL, meaning no&lt;br /&gt;
                             * new event should be introduced, which causes any event that&lt;br /&gt;
                             * reaches this object to disappear from the input stream.&lt;br /&gt;
                             */&lt;br /&gt;
                            if (translate = ICommodities-&amp;gt;CxTranslate(NULL))&lt;br /&gt;
                            {&lt;br /&gt;
                                ICommodities-&amp;gt;AttachCxObj(filter, translate);&lt;br /&gt;
&lt;br /&gt;
                                /* CxObjError() is a commodities.library function that returns&lt;br /&gt;
                                 * the internal accumulated error code of a CxObject.&lt;br /&gt;
                                 */&lt;br /&gt;
                                if (! CxObjError(filter))&lt;br /&gt;
                                {&lt;br /&gt;
                                    ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                                    ProcessMsg();&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only&lt;br /&gt;
                     * deletes the CxObject pointed to in its argument, but it deletes&lt;br /&gt;
                     * all of the CxObjects that are attached to it.&lt;br /&gt;
                     */&lt;br /&gt;
                    ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            /* this amiga.lib function cleans up after ArgArrayInit() */&lt;br /&gt;
            ArgArrayDone();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIcon);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IconBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while(returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case EVT_HOTKEY: /* We got the message from the sender CxObject */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_UNIQUE:&lt;br /&gt;
                        /* Commodities Exchange can be told not only to refuse to launch a&lt;br /&gt;
                         * commodity with a name already in use but also can notify the&lt;br /&gt;
                         * already running commodity that it happened. It does this by&lt;br /&gt;
                         * sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE.  If the&lt;br /&gt;
                         * user tries to run a windowless commodity that is already running,&lt;br /&gt;
                         * the user wants the commodity to shut down. */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sender CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A filter CxObject by itself is not especially useful. It needs some other CxObjects attached to it. A commodity interested in knowing if a specific key was pressed uses a filter to detect and divert the corresponding CxMessage down the filter&#039;s personal list. The filter does this without letting the commodity know what happened. The sender CxObject can be attached to a filter to notify a commodity that it received a CxMessage. CxSender() is a macro that creates a sender CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
senderCxObj = CxObj *CxSender(struct MsgPort *senderport, LONG cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSender() supplies the sender with an Exec message port and an ID. For every CxMessage a sender receives, it sends a new CxMessage to the Exec message port passed in CxSender(). Normally, the commodity creates this port. It is not unusual for a commodity&#039;s broker and sender(s) to share an Exec message port. The HotKey.c example does this to avoid creating unnecessary message ports. A sender uses the ID (cxmID) passed to CxSender() as the ID for all the CxMessages that the it transmits. A commodity uses the ID to monitor CxMessages from several senders at a single message port.&lt;br /&gt;
&lt;br /&gt;
A sender does several things when it receives a CxMessage. First, it duplicates the CxMessage&#039;s corresponding input event and creates a new CxMessage. Then, it points the new CxMessage&#039;s data field to the copy of the input event and sets the new CxMessage&#039;s ID to the ID passed to CxSender(). Finally, it sends the new CxMessage to the port passed to CxSender(), asynchronously.&lt;br /&gt;
&lt;br /&gt;
Because HotKey uses only one message port between its broker and sender object, it has to extract the CxMessage&#039;s type so it can tell if it is a CXM_IEVENT or a CXM_COMMAND. If HotKey gets a CXM_IEVENT, it compares the CxMessage&#039;s ID to the sender&#039;s ID, EVT_HOTKEY, to see which sender sent the CxMessage. Of course HotKey has only one sender, so it only checks for only one ID. If it had more senders, HotKey would check for the ID of each of the other senders as well.&lt;br /&gt;
&lt;br /&gt;
Although HotKey doesn&#039;t use it, a CXM_IEVENT CxMessage contains a pointer to the copy of an input event. A commodity can extract this pointer (using CxMsgData()) if it needs to examine the input event copy. This pointer is only valid before the CxMessage reply. Note that it does not make any sense to modify the input event copy.&lt;br /&gt;
&lt;br /&gt;
Senders are attached almost exclusively to CxObjects that filter out most input events (usually a filter CxObject). Because a sender sends a CxMessage for every single input event it gets, it should only get a select few input events. The AttachCxObj() function can add a CxObject to the end of a filter&#039;s (or some other filtering CxObject&#039;s) personal list. A commodity should not attach a CxObject to a sender as a sender ignores any CxObjects in its personal list.&lt;br /&gt;
&lt;br /&gt;
== Translate CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Normally, after a commodity processes a hot key input event, it needs to eliminate that input event. Other commodities may need to replace an input event with a different one. The translate CxObject can be used for these purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
translateCxObj = CxObj  *CxTranslate(struct InputEvent *newinputevent);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The macro CxTranslate() creates a new translate CxObject. CxTranslate()&#039;s only argument is a pointer to a chain of one or more InputEvent structures.&lt;br /&gt;
&lt;br /&gt;
When a translate CxObject receives a CxMessage, it eliminates the CxMessage and its corresponding input event from the system. The translator introduces a new input event, which Commodities Exchange copies from the InputEvent structure passed to CxTranslate() (newinputevent from the function prototype above), in place of the deleted input event.&lt;br /&gt;
&lt;br /&gt;
A translator is normally attached to some kind of filtering CxObject. If it wasn&#039;t, it would translate all input events into the same exact input event. Like the sender CxObject, a translator does not divert CxMessages down its personal list, so it doesn&#039;t serve any purpose to add any to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetTranslate( CxObj *translator, struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is possible to change the InputEvent structure that a translator looks at when it creates and introduces new input events into the input stream. The function SetTranslate() accepts a pointer to the new InputEvent structure, which the translator will duplicate and introduce when it receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
HotKey utilizes a special kind of translator. Instead of supplying a new input event, HotKey passes a NULL to CxTranslate(). If a translator has a NULL new input event pointer, it does not introduce a new input event, but still eliminates any CxMessages and corresponding input events it receives.&lt;br /&gt;
&lt;br /&gt;
== CxObject Errors ==&lt;br /&gt;
&lt;br /&gt;
A Commodities Exchange function that acts on a CxObject records errors in the CxObject&#039;s accumulated error field. The function CxObjError() returns a CxObject&#039;s error field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 co_errorfield = CxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each bit in the error field corresponds to a specific type of error. The following is a list of the currently defined CxObject errors  and their corresponding bit mask constants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Error Constant&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| COERR_ISNULL&lt;br /&gt;
| CxObjError() was passed a NULL.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_NULLATTACH&lt;br /&gt;
| Someone tried to attach a NULL CxObject to this CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADFILTER&lt;br /&gt;
| This filter CxObject currently has an invalid filter description.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADTYPE&lt;br /&gt;
| Someone tried to perform a type specific function on the wrong type of CxObject (for example calling SetFilter() on a sender CxObject).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The remaining bits are reserved for future use. HotKey.c checks the error field of its filter CxObject to make sure the filter is valid. HotKey.c does not need to check the other objects with CxObjError() because it already makes sure that these other objects are not NULL, which is the only other kind of error the other objects can cause in this situation.&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange has a function that clears a CxObject&#039;s accumulated error field, ClearCxObjError().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID ClearCxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A commodity should be careful about using this, especially on a filter. If a commodity clears a filter&#039;s error field and the COERR_BADFILTER bit is set, Commodities Exchange will think that the filter is OK and start sending messages through it.&lt;br /&gt;
&lt;br /&gt;
== Uniqueness ==&lt;br /&gt;
&lt;br /&gt;
When a commodity opens its broker, it can ask Commodities Exchange not to launch another broker with the same name (nb_Name). The purpose of the uniqueness feature is to prevent the user from starting duplicate commodities. If a commodity asks, Commodities Exchange will not only refuse to create a new, similarly named broker, but it will also notify the original commodity if someone tries to do so.&lt;br /&gt;
&lt;br /&gt;
A commodity tells Commodities Exchange not to allow duplicates by setting certain bits in the nb_Unique field of the NewBroker structure it sends to CxBroker():&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NBU_UNIQUE&lt;br /&gt;
| bit 0&lt;br /&gt;
|-&lt;br /&gt;
| NBU_NOTIFY&lt;br /&gt;
| bit 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Setting the NBU_UNIQUE bit prevents duplicate commodities. Setting the NBU_NOTIFY bit tells Commodities Exchange to notify a commodity if an attempt was made to launch a duplicate. Such a commodity will receive a CXM_COMMAND CxMessage with an ID of CXCMD_UNIQUE when someone tries to duplicate it. Because the uniqueness feature uses the name a programmer gives a commodity to differentiate it from other commodities, it is possible for completely different commodities to share the same name, preventing the two from coexisting. For this reason, a commodity should not use a name that is likely to be in use by other commodities (like &amp;quot;filter&amp;quot; or &amp;quot;hotkey&amp;quot;). Instead, use a name that matches the commodity name.&lt;br /&gt;
&lt;br /&gt;
When &amp;quot;HotKey.c&amp;quot; gets a CXCMD_UNIQUE CxMessage, it shuts itself down. &amp;quot;HotKey.c&amp;quot; and all the windowless commodities that come with Workbench shut themselves down when they get a CXCMD_UNIQUE CxMessage. Because the user will expect all windowless commodities to work this way, all windowless commodities should follow this standard.&lt;br /&gt;
&lt;br /&gt;
When the user tries to launch a duplicate of a system commodity that has a window, the system commodity moves its window to the front of the display, as if the user had clicked the &amp;quot;Show&amp;quot; gadget in the controller program&#039;s window. A windowed commodity should mimic conventions set by existing windowed system commodities, and move its window to the front of the display.&lt;br /&gt;
&lt;br /&gt;
== Signal CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A commodity can use a sender CxObject to find out if a CxMessage has &amp;quot;visited&amp;quot; a CxObject, but this method unnecessarily uses system resources. A commodity that is only interested in knowing if such a visitation took place does not need to see a corresponding input event or a CxMessage ID. Instead, Commodities Exchange has a CxObject that uses an Exec signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *signalCxObj = CxSignal(struct Task *, LONG cx_signal);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSignal() sets up a signal CxObject. When a signal CxObject receives a CxMessage, it signals a task. The commodity is responsible for determining the proper task ID and allocating the signal. Normally, a commodity wants to be signaled so it uses FindTask(NULL) to find it&#039;s own task address. Note that cx_signal from the above prototype is the signal number as returned by AllocSignal(), not the signal mask made from that number. For more information on signals, see [[Exec_Signals|Exec Signals]].&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; (shown a little later) uses a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
== Custom CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Although the CxObjects mentioned so far take care of most of the input event handling a commodity needs to do, they cannot do it all. This is why Commodities Exchange has a custom CxObject. When a custom CxObject receives a CxMessage, it calls a function provided by the commodity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObject *customCxObj = CxCustom(int32 *customfunction(), int32 cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A custom CxObject is the only means by which a commodity can directly modify input events as they pass through the Commodities network as CxMessages. For this reason, it is probably the most dangerous of the CxObjects to use.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Warning About Custom CxObjects|text=Unlike the rest of the code a commodities programmer writes, the code passed to a custom CxObject runs as part of the input.device task, putting severe restrictions on the function. No DOS or Intuition functions can be called. No assumptions can be made about the values of registers upon entry. Any function passed to CxCustom() should be very quick and very simple, with a minimum of stack usage.}}&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange calls a custom CxObject&#039;s function as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID customfunction(CxMsg *cxm, CxObj *customcxobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where cxm is a pointer to a CxMessage corresponding to a real input event, and customcxobj is a pointer to the custom CxObject. The custom function can extract the pointer to the input event by calling CxMsgData(). Before passing the CxMessage to the custom function, Commodities Exchange sets the CxMessage&#039;s ID to the ID passed to CxCustom().&lt;br /&gt;
&lt;br /&gt;
The following is an example of a custom CxObject function that swaps the function of the left and right mouse buttons.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
custom = CxCustom(CxFunction, 0)&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be */&lt;br /&gt;
/* short and sweet. This code runs as part of the input.device task */&lt;br /&gt;
#define CODEMASK (0x00FF &amp;amp; IECODE_LBUTTON &amp;amp; IECODE_RBUTTON)&lt;br /&gt;
&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    uint16 mousequals = 0x0000;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg.  Unlike the InputEvent&lt;br /&gt;
     * extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.&lt;br /&gt;
     */&lt;br /&gt;
    struct InputEvent *ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* Check to see if this input event is a left or right mouse button   */&lt;br /&gt;
    /* by itself (a mouse button can also be a qualifier).  If it is, flip the   */&lt;br /&gt;
    /* low order bit to switch leftbutton &amp;lt;--&amp;gt; rightbutton. */&lt;br /&gt;
    if (ie-&amp;gt;ie_Class == IECLASS_RAWMOUSE)&lt;br /&gt;
        if ((ie-&amp;gt;ie_Code &amp;amp; CODEMASK) == CODEMASK)  ie-&amp;gt;ie_Code ^= 0x0001;&lt;br /&gt;
&lt;br /&gt;
    /* Check the qualifiers. If a mouse button was down when this */&lt;br /&gt;
    /* input event occurred, set the other mouse button bit.      */&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_RBUTTON)  mousequals |= IEQUALIFIER_LEFTBUTTON;&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_LEFTBUTTON)  mousequals |= IEQUALIFIER_RBUTTON;&lt;br /&gt;
&lt;br /&gt;
    /* clear the RBUTTON and LEFTBUTTON qualifier bits */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier &amp;amp;= ~(IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_RBUTTON);&lt;br /&gt;
&lt;br /&gt;
    /* set the mouse button qualifier bits to their new values */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier |= mousequals;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Debug CxObjects ==&lt;br /&gt;
&lt;br /&gt;
The final CxObject is the debug CxObject. When a debug CxObject receives a CxMessage, it sends debugging information to the serial port using DebugPrintF().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *debugCxObj = CxDebug(int32 ID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The debug CxObject will DebugPrintF() the following information about itself, the CxMsg, and the corresponding InputEvent structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DEBUG NODE: 7CB5AB0, ID: 2&lt;br /&gt;
 CxMsg: 7CA6EF2, type: 0, data 2007CA destination 6F1E07CB&lt;br /&gt;
dump IE: 7CA6F1E&lt;br /&gt;
 Class 1&lt;br /&gt;
 Code 40&lt;br /&gt;
 Qualifier 8000&lt;br /&gt;
 EventAddress 40001802&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There has to be a terminal connected to the Amiga&#039;s serial port to receive this information.&lt;br /&gt;
&lt;br /&gt;
== The IX Structure ==&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange does not use the input event description strings discussed earlier to match input events. Instead, Commodities Exchange converts these strings to its own internal format. These input expressions are available for commodities to use instead of the input description strings. The following is the IX structure as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IX_VERSION   2&lt;br /&gt;
&lt;br /&gt;
struct InputXpression {&lt;br /&gt;
   UBYTE   ix_Version;     /* must be set to IX_VERSION  */&lt;br /&gt;
   UBYTE   ix_Class;       /* class must match exactly   */&lt;br /&gt;
   UWORD   ix_Code;&lt;br /&gt;
   UWORD   ix_CodeMask;    /* normally used for UPCODE   */&lt;br /&gt;
   UWORD   ix_Qualifier;&lt;br /&gt;
   UWORD   ix_QualMask;&lt;br /&gt;
   UWORD   ix_QualSame;    /* synonyms in qualifier      */&lt;br /&gt;
   };&lt;br /&gt;
&lt;br /&gt;
typedef struct InputXpression IX;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ix_Version field contains the current version number of the InputXpression structure. The current version is defined as IX_VERSION. The ix_Class field contains the IECLASS_ constant (defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;) of the class of input event sought. Commodities Exchange uses the ix_Code and ix_CodeMask fields to match the ie_Code field of a struct InputEvent. The bits of ix_CodeMask indicate which bits are relevant in the ix_Code field when trying to match against a ie_Code. If any bits in ix_CodeMask are off, Commodities Exchange does not consider the corresponding bit in ie_Code when trying to match input events. This is used primarily to mask out the IECODE_UP_PREFIX bit of rawkey events, making it easier to match both up and down presses of a particular key.&lt;br /&gt;
&lt;br /&gt;
IX&#039;s qualifier fields, ix_Qualifier, ix_QualMask, and ix_QualSame, are used to match the ie_Qualifier field of an InputEvent structure. The ix_Qualifier and ix_QualMask fields work just like ix_Code and ix_CodeMask. The bits of ix_QualMask indicate which bits are relevant when comparing ix_Qualifier to ie_Qualifier. The ix_QualSame field tells Commodities Exchange that certain qualifiers are equivalent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IXSYM_SHIFT  1     /* left- and right- shift are equivalent     */&lt;br /&gt;
#define IXSYM_CAPS   2     /* either shift or caps lock are equivalent  */&lt;br /&gt;
#define IXSYM_ALT    4     /* left- and right- alt are equivalent       */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, the input description string&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;rawkey -caps -lalt -relativemouse -upstroke ralt tab&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
matches a tab upstroke or downstroke with the right Alt key pressed whether or not the left Alt, either Shift, or the Caps Lock keys are down. The following IX structure corresponds to that input description string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IX ix = {&lt;br /&gt;
    IX_VERSION,                   /* The version */&lt;br /&gt;
    IECLASS_RAWKEY,               /* We&#039;re looking for a RAWKEY event */&lt;br /&gt;
    0x42,                         /* The key the usa0 keymap maps to a tab*/&lt;br /&gt;
    0x00FF &amp;amp; (~IECODE_UP_PREFIX), /* We want up and down key presses */&lt;br /&gt;
    IEQUALIFIER_RALT,             /* The right alt key must be down */&lt;br /&gt;
    0xFFFF &amp;amp; ~(IEQUALIFIER_LALT | IEQUALIFIER_LSHIFT |&lt;br /&gt;
        IEQUALIFIER_RSHIFT | IEQUALIFIER_CAPSLOCK | IEQUALIFIER_RELATIVEMOUSE),&lt;br /&gt;
        /* don&#039;t care about left alt, shift, capslock, or relativemouse qualifiers   */&lt;br /&gt;
    IXSYM_CAPS  /* The shift keys and the capslock key qualifiers are all equivalent */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CxFilter() macro only accepts a description string to describe an input event. A commodity can change this filter, however, with the SetFilter() and SetFilterIX() function calls.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetFilter( CxObj *filter, STRPTR descrstring );&lt;br /&gt;
VOID SetFilterIX( CxObj *filter, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetFilter() and SetFilterIX() change which input events a filter CxObject diverts. SetFilter() accepts a pointer to an input description string. SetFilterIX() accepts a pointer to an IX input expression. A commodity that uses either of these functions should check the filter&#039;s error code with CxObjError() to make sure the change worked.&lt;br /&gt;
&lt;br /&gt;
The function ParseIX() parses an input description string and translates it into an IX input expression.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG errorcode = ParseIX( STRPTR descrstring, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange uses ParseIX() to convert the description string in CxFilter() to an IX input expression. As was mentioned previously, ParseIX() does not work with certain kinds of input strings.&lt;br /&gt;
&lt;br /&gt;
== Controlling CxMessages ==&lt;br /&gt;
&lt;br /&gt;
A Custom CxObject has the power to directly manipulate the CxMessages that travel around the Commodities network. One way is to directly change values in the corresponding input event. Another way is to redirect (or dispose of) the CxMessages.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DivertCxMsg ( CxMsg *cxm, CxObj *headobj, CxObj *retobj );&lt;br /&gt;
VOID RouteCxMsg  ( CxMsg *cxm, CxObj *co );&lt;br /&gt;
VOID DisposeCxMsg( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DivertCxMsg() and RouteCxMsg() dictate where the CxMessage will go next. Conceptually, DivertCxMsg() is analogous to a subroutine in a program; the CxMessage will travel down the personal list of a CxObject (headobj in the prototype) until it gets to the end of that list. It then returns and visits the CxObject that follows the return CxObject (the return CxObject in the prototype above is retobj). RouteCxMsg() is analogous to a goto in a program; it has no CxObject to return to.&lt;br /&gt;
&lt;br /&gt;
DisposeCxMsg() removes a CxMessage from the network and releases its resources. The translate CxObject uses this function to remove a CxMessage.&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; shows how to use DivertCxMsg() as well as a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/* divert.c - commodity to monitor user inactivity - compiled with SASC 5.10&lt;br /&gt;
LC -b0 -cfist -v -j73 divert.c&lt;br /&gt;
Blink FROM LIB:c.o,divert.o TO divert LIBRARY LIB:LC.lib,LIB:Amiga.lib NODEBUG SC SD&lt;br /&gt;
quit; */&lt;br /&gt;
#include &amp;amp;lt;exec/libraries.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/commodities.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_stdio_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/commodities_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/inputevent.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define TIMER_CLICKS 100&lt;br /&gt;
&lt;br /&gt;
void main(int, char **);&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
void CxFunction(CxMsg *, CxObj *);&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase, *IconBase;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *cocustom, *cosignal;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,&lt;br /&gt;
    &amp;amp;quot;show divert&amp;amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,  /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                /* If someone tries it, let me know                        */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Task *task;&lt;br /&gt;
ULONG cxsigflag, signal, cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
void main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    UBYTE **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    if (CxBase = OpenLibrary(&amp;amp;quot;commodities.library&amp;amp;quot;, 37L))&lt;br /&gt;
    {&lt;br /&gt;
        /* open the icon.library for support library functions, ArgArrayInit() and ArgArrayDone() */&lt;br /&gt;
        if (IconBase = OpenLibrary(&amp;amp;quot;icon.library&amp;amp;quot;, 36L))&lt;br /&gt;
        {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;amp;lt;&amp;amp;lt; broker_mp-&amp;amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (in the 2.0 version of amiga.lib) */&lt;br /&gt;
                /* that makes it easy to read arguments from either a CLI or from Workbench&#039;s     */&lt;br /&gt;
                /* ToolTypes. Because it uses icon.library, the library has to be open before     */&lt;br /&gt;
                /* before calling this function.  ArgArrayDone() cleans up after this function.   */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (in amiga.lib) searches through the array set up by ArgArrayInit()    */&lt;br /&gt;
                /* for a specific ToolType.  If it finds one, it returns the numeric value of the */&lt;br /&gt;
                /* number that followed the ToolType (i.e., CX_PRIORITY=7).  If it  doesn&#039;t find  */&lt;br /&gt;
                /* the ToolType, it returns the default value (the third argument)                */&lt;br /&gt;
                newbroker.nb_Pri = (BYTE)ArgInt(ttypes, &amp;amp;quot;CX_PRIORITY&amp;amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                if (broker = CxBroker(&amp;amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxCustom() takes two arguments, a pointer to the custom function           */&lt;br /&gt;
                    /* and an ID. Commodities Exchange will assign that ID to any CxMsg           */&lt;br /&gt;
                    /* passed to the custom  function.                                            */&lt;br /&gt;
                    if (cocustom = CxCustom(CxFunction, 0L))&lt;br /&gt;
                    {&lt;br /&gt;
                        AttachCxObj(broker, cocustom);&lt;br /&gt;
&lt;br /&gt;
                        /* Allocate a signal bit for the signal CxObj */&lt;br /&gt;
                        if ( (signal = (ULONG)AllocSignal(-1L)) != -1)&lt;br /&gt;
                        {&lt;br /&gt;
                            /* set up the signal mask */&lt;br /&gt;
                            cxobjsignal = 1L &amp;amp;lt;&amp;amp;lt; signal;&lt;br /&gt;
                            cxsigflag |= cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
                            /* CxSignal takes two arguments, a pointer to the task to signal      */&lt;br /&gt;
                            /* (normally the commodity) and the number of the signal bit the      */&lt;br /&gt;
                            /* commodity acquired to signal with.                                 */&lt;br /&gt;
                            task = FindTask(NULL);&lt;br /&gt;
                            if (cosignal = CxSignal(task, signal))&lt;br /&gt;
                            {&lt;br /&gt;
                                AttachCxObj(cocustom, cosignal);&lt;br /&gt;
                                ActivateCxObj(broker, 1L);&lt;br /&gt;
                                ProcessMsg();&lt;br /&gt;
                            }&lt;br /&gt;
                            FreeSignal(signal);&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only deletes   */&lt;br /&gt;
                    /* the CxObject pointed to in its argument, but it deletes all of the         */&lt;br /&gt;
                    /* CxObjects that are attached to it.                                         */&lt;br /&gt;
                    DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
                        ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            ArgArrayDone();   /* this amiga.lib function cleans up after ArgArrayInit()           */&lt;br /&gt;
            CloseLibrary(IconBase);&lt;br /&gt;
        }&lt;br /&gt;
        CloseLibrary(CxBase);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    ULONG sigrcvd, msgid;&lt;br /&gt;
    LONG returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = CxMsgID(msg);&lt;br /&gt;
            ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgid)&lt;br /&gt;
            {&lt;br /&gt;
                case CXCMD_DISABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 0L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_ENABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 1L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_KILL:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_UNIQUE:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; SIGBREAKF_CTRL_C) returnvalue = 0L;&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if the signal CxObj signalled us. */&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; cxobjsignal) printf(&amp;amp;quot;Got Signal\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be short        */&lt;br /&gt;
/* and sweet because it runs as part of the input.device task.                                    */&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    struct InputEvent *ie;&lt;br /&gt;
    static ULONG time = 0L;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg. Unlike the InputEvent                */&lt;br /&gt;
    /* extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.       */&lt;br /&gt;
    ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* This custom function counts the number of timer events that go by while no other input     */&lt;br /&gt;
    /* events occur.  If it counts more than a certain amount of timer events, it clears the      */&lt;br /&gt;
    /* count and diverts the timer event CxMsg to the custom object&#039;s personal                    */&lt;br /&gt;
    /* list.  If an event besides a timer event passes by, the timer event count is reset.        */&lt;br /&gt;
    if (ie-&amp;amp;gt;ie_Class == IECLASS_TIMER)&lt;br /&gt;
    {&lt;br /&gt;
        time++;&lt;br /&gt;
        if (time &amp;amp;gt;= TIMER_CLICKS)&lt;br /&gt;
        {&lt;br /&gt;
            time = 0L;&lt;br /&gt;
            DivertCxMsg(cxm, co, co);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        time = 0L;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== New Input Events ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Library also has functions used to introduce new input events to the input stream.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct InputEvent *InvertString( UBYTE *string, ULONG *keymap );&lt;br /&gt;
VOID               FreeIEvents( struct InputEvent *ie );&lt;br /&gt;
VOID               AddIEvents( struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
InvertString() is a function that accepts an ASCII string and creates a linked list of input events that translate into the string using the supplied keymap (or the system default if the key map is NULL). The NULL terminated string may contain ANSI character codes, an input description enclosed in angle (&amp;amp;lt;&amp;amp;gt;) brackets, or one of the following backslash escape characters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| \r&lt;br /&gt;
| Return&lt;br /&gt;
|-&lt;br /&gt;
| \t&lt;br /&gt;
| Tab&lt;br /&gt;
|-&lt;br /&gt;
| \\&lt;br /&gt;
| Backslash&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abc&amp;amp;lt;alt f1&amp;amp;gt;\rhi there.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FreeIEvents() frees a list of input events allocated by InvertString(). AddIEvents() is a commodities.library function that adds a linked list of input events at the the top of the Commodities network. Each input event in the list is made into an individual CxMessage. Note that if passed a linked list of input events created by InvertString(), the order the events appear in the string will be reversed.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* PopShell.c - Simple hot key commodity example.&lt;br /&gt;
   Adapted by xenic from the original source code.&lt;br /&gt;
   &lt;br /&gt;
   Compile commands: gcc PopShell.c -o popshell -lamiga -Wall&lt;br /&gt;
&lt;br /&gt;
   To run the compiled program - Open 2 shell windows. Execute&lt;br /&gt;
   PopShell in the first shell window with a line like:&lt;br /&gt;
     PopShell HOTKEY &amp;quot;ctrl alt f&amp;quot;&lt;br /&gt;
   Activate the second shell window and enter the keyboard shortcut.&lt;br /&gt;
   A new shell window should open on the Workbench screen.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/alib_protos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static CONST_STRPTR version USED = &amp;quot;$VER: PopShell 1.1 (05.11.2015)&amp;quot;;&lt;br /&gt;
static CONST_STRPTR stackcookie USED = &amp;quot;$STACK: 32768&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase = NULL;&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp = NULL;&lt;br /&gt;
CxObj *broker, *filter;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
	NB_VERSION,&lt;br /&gt;
	&amp;quot;Amiga PopShell&amp;quot;,       /* string to identify this broker */&lt;br /&gt;
	&amp;quot;A Simple PopShell&amp;quot;,&lt;br /&gt;
	&amp;quot;A simple PopShell commodity&amp;quot;,&lt;br /&gt;
	NBU_UNIQUE | NBU_NOTIFY,      /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
	0, 0, 0, 0                    /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR newshell = &amp;quot;\rllehswen&amp;quot;;  /* &amp;quot;newshell&amp;quot; spelled backwards */&lt;br /&gt;
struct InputEvent *ie = NULL;&lt;br /&gt;
uint32 cxsigflag;&lt;br /&gt;
&lt;br /&gt;
#define TEMPLATE &amp;quot;HOTKEY/K,CX_PRIORITY/N&amp;quot;&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
	ARG_HOTKEY, ARG_PRIORITY, ARG_MAX&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	STRPTR hotkey = NULL;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	struct RDArgs *argsdata = NULL;&lt;br /&gt;
	int32 rargs[ARG_MAX] = {0};&lt;br /&gt;
	int8  priority = 0;&lt;br /&gt;
&lt;br /&gt;
	signal(SIGINT, SIG_IGN);&lt;br /&gt;
&lt;br /&gt;
	if ((CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 37L)))&lt;br /&gt;
	{&lt;br /&gt;
		if ((ICommodities = (struct CommoditiesIFace *)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL)))&lt;br /&gt;
		{&lt;br /&gt;
			if ((argsdata = IDOS-&amp;gt;ReadArgs(TEMPLATE, rargs, NULL)))&lt;br /&gt;
			{&lt;br /&gt;
				if (rargs[ARG_PRIORITY])&lt;br /&gt;
					priority = (int8)*(uint32 *)rargs[ARG_PRIORITY];&lt;br /&gt;
				if ((broker_mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)))&lt;br /&gt;
				{&lt;br /&gt;
					newbroker.nb_Port = broker_mp;&lt;br /&gt;
					cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
					newbroker.nb_Pri = priority;&lt;br /&gt;
					hotkey = (STRPTR)rargs[ARG_HOTKEY];&lt;br /&gt;
&lt;br /&gt;
					if ((broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL)))&lt;br /&gt;
					{&lt;br /&gt;
						/* CxFilter(), CxSender() &amp;amp; CxTranslate() are    */&lt;br /&gt;
						/* macros defined in libraries/commodities.h.    */&lt;br /&gt;
						/* CxFilter() creates a filter CxObject.         */&lt;br /&gt;
						/* CxSender() creates a sender CxObject.         */&lt;br /&gt;
						/* CxTranslate() creates a translation CxObject. */&lt;br /&gt;
						if ((filter = CxFilter(hotkey)))&lt;br /&gt;
						{&lt;br /&gt;
							/* Add a filter CxObject to another&#039;s personal list. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
							/* Add a sender CxObject to the filter CxObject. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxSender(broker_mp, EVT_HOTKEY));&lt;br /&gt;
&lt;br /&gt;
							/* Add a translation CxObject to the filter CxObject */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxTranslate(NULL));&lt;br /&gt;
&lt;br /&gt;
							if (!(ICommodities-&amp;gt;CxObjError(filter)))&lt;br /&gt;
							{&lt;br /&gt;
								/* InvertString() is an amiga.lib function that creates a linked */&lt;br /&gt;
								/* list of input events which would translate into the string    */&lt;br /&gt;
								/* passed to it.  Note that it puts the input events in the      */&lt;br /&gt;
								/* opposite order in which the corresponding letters appear in   */&lt;br /&gt;
								/* the string.  A translate CxObject expects them backwards.     */&lt;br /&gt;
								if ((ie = InvertString(newshell, NULL)))&lt;br /&gt;
								{&lt;br /&gt;
									ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
									ProcessMsg();&lt;br /&gt;
									/* we have to release the memory allocated by InvertString. */&lt;br /&gt;
									FreeIEvents(ie);&lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						/* DeleteCxObjAll() is a commodities.library function that */&lt;br /&gt;
						/* deletes the CxObject pointed to in its argument and     */&lt;br /&gt;
						/* deletes all of the CxObjects attached to it.            */&lt;br /&gt;
						ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
						/* Empty the port of all CxMsgs */&lt;br /&gt;
						while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
							IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
					}&lt;br /&gt;
					IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
				}&lt;br /&gt;
				IDOS-&amp;gt;FreeArgs(argsdata); /* cleans up after ReadArgs() */&lt;br /&gt;
			}&lt;br /&gt;
			IExec-&amp;gt;DropInterface((struct Interface *)ICommodities);&lt;br /&gt;
		}&lt;br /&gt;
		IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
	}&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
	extern struct MsgPort *broker_mp;&lt;br /&gt;
	extern CxObj *broker;&lt;br /&gt;
	extern uint32 cxsigflag;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
	int32  returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
	while (returnvalue)&lt;br /&gt;
	{&lt;br /&gt;
		sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
		while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
		{&lt;br /&gt;
			msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
			msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
			IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
			switch(msgtype)&lt;br /&gt;
			{&lt;br /&gt;
				case CXM_IEVENT:&lt;br /&gt;
					printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case EVT_HOTKEY:&lt;br /&gt;
							/* We got the message from the sender CxObject  */&lt;br /&gt;
							printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
							/* Add the string &amp;quot;newshell&amp;quot; to input * stream. */&lt;br /&gt;
							/*  If a shell gets it, it&#039;ll open a new shell. */&lt;br /&gt;
							ICommodities-&amp;gt;AddIEvents(ie);&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				case CXM_COMMAND:&lt;br /&gt;
					printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case CXCMD_DISABLE:&lt;br /&gt;
						printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_ENABLE:&lt;br /&gt;
							printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_KILL:&lt;br /&gt;
							printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_UNIQUE:&lt;br /&gt;
						/* Commodities Exchange can be told not only to refuse to launch a    */&lt;br /&gt;
						/* commodity with a name already in use but also can notify the       */&lt;br /&gt;
						/* already running commodity that it happened.  It does this by       */&lt;br /&gt;
						/* sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE. If the     */&lt;br /&gt;
						/* user tries to run a windowless commodity that is already running,  */&lt;br /&gt;
						/* the user wants the commodity to shut down.                         */&lt;br /&gt;
							printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				default:&lt;br /&gt;
					printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
		{&lt;br /&gt;
			returnvalue = 0L;&lt;br /&gt;
			printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Commodities Exchange functions covered in this article. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CxBroker()&lt;br /&gt;
| Creates a CxObject of type Broker.&lt;br /&gt;
|-&lt;br /&gt;
| CxFilter()&lt;br /&gt;
| Creates a CxObject of type Filter.&lt;br /&gt;
|-&lt;br /&gt;
| CxSender()&lt;br /&gt;
| Creates a CxObject of type Sender.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Creates a CxObject of type Translate.&lt;br /&gt;
|-&lt;br /&gt;
| CxSignal()&lt;br /&gt;
| Creates a CxObject of type Signal.&lt;br /&gt;
|-&lt;br /&gt;
| CxCustom()&lt;br /&gt;
| Creates a CxObject of type Custom.&lt;br /&gt;
|-&lt;br /&gt;
| CxDebug()&lt;br /&gt;
| Creates a CxObject of type Debug.&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObj()&lt;br /&gt;
| Frees a single CxObject&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObjAll()&lt;br /&gt;
| Frees a group of connected CxObjects&lt;br /&gt;
|-&lt;br /&gt;
| ActivateCxObj()&lt;br /&gt;
| Activates a newly created CxObject in the commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Sets up substitution of one input event for another by translate CxObjects.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgType()&lt;br /&gt;
| Finds the type of a CxMessage.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgData()&lt;br /&gt;
| Returns the CxMessage data.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgID()&lt;br /&gt;
| Returns the CxMessage ID.&lt;br /&gt;
|-&lt;br /&gt;
| CxObjError()&lt;br /&gt;
| Returns the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| ClearCxObjError()&lt;br /&gt;
| Clear the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| AttachCxObj()&lt;br /&gt;
| Attaches a CxObject to the end of a given CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| InsertCxObj()&lt;br /&gt;
| Inserts a CxObject in a given position in a CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| EnqueueCxObj()&lt;br /&gt;
| Inserts a CxObject in a CxObject&#039;s list by priority.&lt;br /&gt;
|-&lt;br /&gt;
| SetCxObjPri()&lt;br /&gt;
| Sets a CxObject&#039;s priority for EnqueueCxObj().&lt;br /&gt;
|-&lt;br /&gt;
| RemoveCxObj()&lt;br /&gt;
| Removes a CxObject from a list.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilter()&lt;br /&gt;
| Set a filter for a CxObject from an input description string.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilterIX()&lt;br /&gt;
| Set a filter for a CxObject from an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| ParseIX()&lt;br /&gt;
| Convert an input description string to an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| DivertCxMsg()&lt;br /&gt;
| Divert a CxMessage to one CxObject and return it to another.&lt;br /&gt;
|-&lt;br /&gt;
| RouteCxMsg()&lt;br /&gt;
| Redirect a CxMessage to a new CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeCxMsg()&lt;br /&gt;
| Cancel a CxMessage removing it from the Commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| InvertString()&lt;br /&gt;
| Creates a linked list of input events that correspond to a given string.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIEvents()&lt;br /&gt;
| Frees the linked list of input events created with InvertString().&lt;br /&gt;
|-&lt;br /&gt;
| AddIEvents()&lt;br /&gt;
| Converts a list of input events to CxMessages and puts them into the network.&lt;br /&gt;
|-&lt;br /&gt;
| CopyBrokerList()&lt;br /&gt;
| Creates a local copy of the current broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| FreeBrokerList()&lt;br /&gt;
| Frees the local broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| BrokerCommand()&lt;br /&gt;
| Sends a command to a commodity broker (V53.4).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Functions ===&lt;br /&gt;
&lt;br /&gt;
The following functions are obsolete and no longer used:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayInit()&lt;br /&gt;
| Create a Tool Types array from argc and argv (Workbench or Shell).&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayDone()&lt;br /&gt;
| Free the resources used by ArgArrayInit().&lt;br /&gt;
|-&lt;br /&gt;
| ArgString()&lt;br /&gt;
| Return the string associated with a given Tool Type in the array.&lt;br /&gt;
|-&lt;br /&gt;
| ArgInt()&lt;br /&gt;
| Return the integer associated with a given Tool Type in the array.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8515</id>
		<title>Commodities Exchange Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=8515"/>
		<updated>2015-12-06T08:50:06Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Replaced the popshell.c example source code by a version adapted for OS4 by xenic.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{CodeReview}}&lt;br /&gt;
== Commodities Exchange Library ==&lt;br /&gt;
&lt;br /&gt;
This article describes Commodities Exchange, the library of routines used to add a custom input handler to the Amiga. With Commodities Exchange, any program function can be associated with key combinations or other input events &#039;&#039;globally&#039;&#039; allowing the creation utility programs that run in the background for all tasks.&lt;br /&gt;
&lt;br /&gt;
== Custom Input Handlers ==&lt;br /&gt;
&lt;br /&gt;
The input.device has a hand in almost all user input on the Amiga. It gathers input events from the keyboard, the gameport (mouse), and several other sources, into one input &amp;quot;stream&amp;quot;. Special programs called input event handlers intercept input events along this stream, examining and sometimes changing the input events. Both Intuition and the console device use input handlers to process user input.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-1.png|frame|center|The Amiga Input Stream]]&lt;br /&gt;
&lt;br /&gt;
Using the input.device, a program can introduce its own custom handler into the chain of input handlers at almost any point in the chain. &amp;quot;Hot key&amp;quot; programs, shell pop-up programs, and screen blankers all commonly use custom input handlers to monitor user input before it gets to the Intuition input handler.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-2.png|frame|center|A Custom Input Handler]]&lt;br /&gt;
&lt;br /&gt;
Custom input handlers do have their drawbacks, however. Not only are these handlers hard to program, but because there is no standard way to implement and control them, multiple handlers often do not work well together. Their antisocial behavior can result in load order dependencies and incompatibilities between different custom input handlers. Even for the expert user, having several custom input handlers coexist peacefully can be next to impossible.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig31-3.png|frame|center|The Commodities Network]]&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange eliminates these problems by providing a simple, standardized way to program and control custom input handlers. It is divided into two parts: an Exec library and a controller program.&lt;br /&gt;
&lt;br /&gt;
The Exec library is called commodities.library. When it is first opened, commodities.library establishes a single input handler just before Intuition in the input chain. When this input handler receives an input event, it creates a CxMessage (Commodities Exchange Message) corresponding to the input event, and diverts the CxMessage through the network of Commodities Exchange input handlers.&lt;br /&gt;
&lt;br /&gt;
These handlers are made up of trees of different CxObjects (Commodities Exchange Objects), each of which performs a simple operation on the CxMessages. Any CxMessages that exit the network are returned to the input.device&#039;s input stream as input events.&lt;br /&gt;
&lt;br /&gt;
Through function calls to the commodities.library, an application can install a custom input handler. A Commodities Exchange application, sometimes simply referred to as a commodity, uses the CxObject primitives to do things such as filter certain CxMessages, translate CxMessages, signal a task when a CxObject receives a CxMessage, send a message when a CxObject receives a CxMessage, or if necessary, call a custom function when a CxObject receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== Commodities Control ===&lt;br /&gt;
&lt;br /&gt;
The standard controller program is called Exchange. It is located in the Utilities/Commodities drawer on your system partition. With Exchange, the user can monitor and control all the currently running Commodities Exchange applications from this one program. The user can enable and disable a commodity, kill a commodity, or, if the commodity has a window, ask the commodity to show or hide its window. When the user requests any of these actions, the controller program sends the commodity a message, telling it which action to perform.&lt;br /&gt;
&lt;br /&gt;
Starting with version 53.4 of the commodities.library, functions are available that allow developers to write their own commodity control programs (Exchange replacements). These functions were not public previously.&lt;br /&gt;
&lt;br /&gt;
=== Important Notes ===&lt;br /&gt;
&lt;br /&gt;
* Commodities are special-purpose programs. In fact, most programs or applications are &#039;&#039;&#039;not&#039;&#039;&#039; suitable for becoming commodities. The Commodities Exchange framework is ideal for programs that need to monitor all user input: hotkey utilities, screen blankers, mouse blankers, etc.&lt;br /&gt;
* In the past, some developers turned their programs into commodities only to provide them with a handy keyboard shortcut to bring up/close their GUI. Please note that such practice is actually a misuse of the commodities framework.&lt;br /&gt;
* Commodities Exchange should never be used as an alternate method of receiving user input for an application. Other applications depend on getting user input in some form or another from the input stream. A greedy program that diverts input to itself rather than letting the input go to where the user expects it can seriously confuse the user, not to mention compromise the advantages of multitasking.&lt;br /&gt;
&lt;br /&gt;
== CxObjects ==&lt;br /&gt;
&lt;br /&gt;
CxObjects are the basic building blocks used to construct a commodity. A commodity uses CxObjects to take care of all manipulations of CxMessages. When a CxMessage &amp;quot;arrives&amp;quot; at a CxObject, that CxObject carries out its primitive action and then, if it has not deleted the CxMessage, it passes the CxMessage on to the next CxObject. A commodity links together CxObjects into a tree, organizing these simple action objects to perform some higher function.&lt;br /&gt;
&lt;br /&gt;
A CxObject is in one of two states, active or inactive. An active CxObject performs its primitive action every time it receives a CxMessage. If a CxObject is inactive, CxMessages bypass it, continuing to the CxObject that follows the inactive one. By default, all CxObjects except the type called brokers are created in the active state.&lt;br /&gt;
&lt;br /&gt;
Currently, there are seven types of CxObjects:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Commodities Exchange Object Types&lt;br /&gt;
! Object Type&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Broker || Registers a new commodity with the commodity network&lt;br /&gt;
|-&lt;br /&gt;
| Filter || Accepts or rejects input events based on criteria set up by the application&lt;br /&gt;
|-&lt;br /&gt;
| Sender || Sends a message to a message port&lt;br /&gt;
|-&lt;br /&gt;
| Translate || Replaces the input event with a different one&lt;br /&gt;
|-&lt;br /&gt;
| Signal || Signals a task&lt;br /&gt;
|-&lt;br /&gt;
| Custom || Calls a custom function provided by the commodity&lt;br /&gt;
|-&lt;br /&gt;
| Debug || Sends debug information out the serial port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installing A Broker Object ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Exchange input handler maintains a master list of CxObjects to which it diverts input events using CxMessages. The CxObjects in this master list are a special type of CxObject called &#039;&#039;brokers&#039;&#039;. The only thing a broker CxObject does is divert CxMessages to its own personal list of CxObjects. A commodity creates a broker and attaches other CxObjects to it. These attached objects take care of the actual input handler related work of the commodity and make up the broker&#039;s personal list.&lt;br /&gt;
&lt;br /&gt;
The first program listing, &amp;quot;Broker.c&amp;quot;, is a very simple example of a working commodity. It serves only to illustrate the basics of a commodity, not to actually perform any useful function. It shows how to set up a broker and process commands from the controller program.&lt;br /&gt;
&lt;br /&gt;
Besides opening commodities.library and creating an Exec message port, setting up a commodity requires creating a broker. The function CxBroker() creates a broker and adds it to the master list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxBroker(struct NewBroker *nb, LONG *error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s first argument is a pointer to a NewBroker structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct NewBroker {&lt;br /&gt;
   BYTE     nb_Version;   /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   BYTE     *nb_Name;&lt;br /&gt;
   BYTE     *nb_Title;&lt;br /&gt;
   BYTE     *nb_Descr;&lt;br /&gt;
   SHORT    nb_Unique;&lt;br /&gt;
   SHORT    nb_Flags;&lt;br /&gt;
   BYTE     nb_Pri;       /* There is an implicit pad byte after this BYTE */&lt;br /&gt;
   struct   MsgPort   *nb_Port;&lt;br /&gt;
   WORD     nb_ReservedChannel;  /* Unused, make zero for future compatibility */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange gets all the information it needs about the broker from this structure. NewBroker&#039;s nb_Version field contains the version number of the NewBroker structure. This should be set to NB_VERSION which is defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. The nb_Name, nb_Title, and nb_Descr point to strings which hold the name, title, and description of the broker. The two bit fields, nb_Unique and nb_Flags, toggle certain features of Commodities Exchange based on their values. They are discussed in detail later in this article.&lt;br /&gt;
&lt;br /&gt;
The nb_Pri field contains the broker&#039;s priority. Commodities Exchange inserts the broker into the master list based on this number. Higher priority brokers get CxMessages before lower priority brokers.&lt;br /&gt;
&lt;br /&gt;
CxBroker()&#039;s second argument is a pointer to a LONG. If this pointer is not NULL, CxBroker() fills in this field with one of the following error return codes from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CBERR_OK        0        /* No error                         */&lt;br /&gt;
CBERR_SYSERR    1        /* System error , no memory, etc    */&lt;br /&gt;
CBERR_DUP       2        /* uniqueness violation             */&lt;br /&gt;
CBERR_VERSION   3        /* didn&#039;t understand nb_VERSION     */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the broker object is created with CxBroker(), it must be activated with ActivateCxObject().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG oldactivationvalue = ActivateCxObj(CxObj *co, LONG newactivationvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After successfully completing the initial set up and activating the broker, a commodity can begin its input processing loop waiting for CxMessages to arrive.&lt;br /&gt;
&lt;br /&gt;
== CxMessages ==&lt;br /&gt;
&lt;br /&gt;
There are actually two types of CxMessages. The first, CXM_IEVENT, corresponds to an input event and travels through the Commodities Exchange network. The other type, CXM_COMMAND, carries a command to a commodity. A CXM_COMMAND normally comes from the controller program and is used to pass user commands on to a commodity. A commodity receives these commands through an Exec message port that the commodity sets up before it calls CxBroker(). The NewBroker&#039;s nb_Port field points to this message port. A commodity can tell the difference between the two types of CxMessages by calling the CxMsgType() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG  CxMsgType( CxMsg *cxm );&lt;br /&gt;
UBYTE *CxMsgData( CxMsg *cxm );&lt;br /&gt;
LONG   CxMsgID  ( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A CxMessage not only has a type, it can also have a data pointer as well as an ID associated with it. The data associated with a CXM_IEVENT CxMessage is an InputEvent structure. By using the CxMsgData() function, a commodity can obtain a pointer to the corresponding InputEvent of a CXM_IEVENT message. Commodities Exchange gives an ID of zero to any CXM_IEVENT CxMessage that it introduces to the Commodities network but certain CxObjects can assign an ID to them.&lt;br /&gt;
&lt;br /&gt;
For a CXM_COMMAND CxMessages, the data pointer is generally not used but the ID specifies a command passed to the commodity from the user operating the controller program. The CxMsgID() macro extracts the ID from a CxMessage.&lt;br /&gt;
&lt;br /&gt;
=== A Simple Commodity Example ===&lt;br /&gt;
&lt;br /&gt;
The example below, &amp;quot;Broker.c&amp;quot;, receives input from one source, the controller program. The controller program sends a CxMessage each time the user clicks its Enable, Disable, or Kill gadgets. Using the CxMsgID() function, the commodity finds out what the command is and executes it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// broker.c - Simple skeletal example of opening a broker&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
CxObj *broker;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
ULONG cxsigflag;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,   /* nb_Version - Version of the NewBroker structure */&lt;br /&gt;
    &amp;quot;Amiga broker&amp;quot;, /* nb_Name - Name Commodities uses to identify this commodity */&lt;br /&gt;
    &amp;quot;Broker&amp;quot;,     /* nb_Title - Title of commodity that appears in CXExchange */&lt;br /&gt;
    &amp;quot;A simple example of a broker&amp;quot;,  /* nb_Descr - Description of the commodity */&lt;br /&gt;
    0,            /* nb_Unique - Tells CX not to launch another commodity with same name */&lt;br /&gt;
    0,            /* nb_Flags - Tells CX if this commodity has a window */&lt;br /&gt;
    0,            /* nb_Pri - This commodity&#039;s priority */&lt;br /&gt;
    0,            /* nb_Port - MsgPort CX talks to */&lt;br /&gt;
    0             /* nb_ReservedChannel - reserved for later use */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (ICommodities != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* Commodities talks to a Commodities application through */&lt;br /&gt;
        /* an Exec Message port, which the application provides   */&lt;br /&gt;
        if (broker_mp = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            newbroker.nb_Port = broker_mp;&lt;br /&gt;
&lt;br /&gt;
            /* The commodities.library function CxBroker() adds a borker to the&lt;br /&gt;
             * master list.  It takes two arguments, a pointer to a NewBroker&lt;br /&gt;
             * structure and a pointer to a LONG.  The NewBroker structure contains&lt;br /&gt;
             * information to set up the broker.  If the second argument is not&lt;br /&gt;
             * NULL, CxBroker will fill it in with an error code.&lt;br /&gt;
             */&lt;br /&gt;
            if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
            {&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* After it&#039;s set up correctly, the broker has to be activated */&lt;br /&gt;
                ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
&lt;br /&gt;
                /* the main processing loop */&lt;br /&gt;
                ProcessMsg();&lt;br /&gt;
&lt;br /&gt;
                /* It&#039;s time to clean up.  Start by removing the broker from the&lt;br /&gt;
                 * Commodities master list.  The DeleteCxObjAll() function will&lt;br /&gt;
                 * take care of removing a CxObject and all those connected&lt;br /&gt;
                 * to it from the Commodities network&lt;br /&gt;
                 */&lt;br /&gt;
                ICommodities-&amp;gt;DeleteCxObj(broker);&lt;br /&gt;
&lt;br /&gt;
                /* Empty the port of CxMsgs */&lt;br /&gt;
                while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        /* wait for something to happen */&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        /* process any messages */&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            /* Extract necessary information from the CxMessage and return it */&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    /* Shouldn&#039;t get any of these in this example */&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    /* Commodities has sent a command */&lt;br /&gt;
                    printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            /* The user clicked Commodities Exchange disable&lt;br /&gt;
                             * gadget better disable&lt;br /&gt;
                             */&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            /* user clicked enable gadget */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            /* user clicked kill gadget, better quit */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* Test to see if user tried to break */&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that &amp;quot;Broker.c&amp;quot; uses Ctrl-C as a break key. The break key for any commodity should be Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
=== Controller Commands ===&lt;br /&gt;
&lt;br /&gt;
The commands that a commodity can receive from the controller program (as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CXCMD_DISABLE     /* please disable yourself       */&lt;br /&gt;
CXCMD_ENABLE      /* please enable yourself        */&lt;br /&gt;
CXCMD_KILL        /* go away for good              */&lt;br /&gt;
CXCMD_APPEAR      /* open your window, if you can  */&lt;br /&gt;
CXCMD_DISAPPEAR   /* hide your window              */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CXCMD_DISABLE, CXCMD_ENABLE, and CXCMD_KILL commands correspond to the similarly named controller program gadgets, Disable, Enable, and Kill; CXCMD_APPEAR and CXCMD_DISAPPEAR correspond to the controller program gadgets, Show and Hide. These gadgets are ghosted in Broker.c because it has no window (It doesn&#039;t make much sense to give the user a chance to click the Show and Hide gadgets). In order to do this, Broker.c has to tell Commodities Exchange to ghost these gadgets. When CxBroker() sets up a broker, it looks at the NewBroker.nb_Flags field to see if the COF_SHOW_HIDE bit (from &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) is set. If it is, the &amp;quot;Show&amp;quot; and &amp;quot;Hide&amp;quot; gadgets for this broker will be selectable. Otherwise they are ghosted and disabled.&lt;br /&gt;
&lt;br /&gt;
=== Shutting Down the Commodity ===&lt;br /&gt;
&lt;br /&gt;
Shutting down a commodity is easy. After replying to all CxMessages waiting at the broker&#039;s message port, a commodity can delete its CxObjects. The DeleteCxObj() function removes a single CxObject from the Commodities network. DeleteCxObjectAll() removes multiple objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DeleteCxObj( CxObj *co );&lt;br /&gt;
VOID DeleteCxObjAll( CxObj *delete_co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a commodity has a lot of CxObjects, deleting each individually can be a bit tedious. DeleteCxObjAll() will delete a CxObject and any other CxObjects that are attached to it. The HotKey.c example given later in this article uses this function to delete all its CxObjects. A commodity that uses DeleteCxObjAll() to delete all its CxObjects should make sure that they are all connected to the main one. (See the &amp;quot;Connecting CxObjects&amp;quot; section below.)&lt;br /&gt;
&lt;br /&gt;
After deleting its CxObjects, a commodity must take care of any CxMessages that might have arrived at the message port just before the commodity deleted its objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commodity Tool Types ==&lt;br /&gt;
&lt;br /&gt;
A goal of Commodities Exchange is to improve user control over input handlers. One way in which it accomplishes this goal is through the use of standard icon Tool Types. The user will expect commodities to recognize the set of standard Tool Types:&lt;br /&gt;
&lt;br /&gt;
* CX_PRIORITY&lt;br /&gt;
* CX_POPUP&lt;br /&gt;
* CX_POPKEY&lt;br /&gt;
&lt;br /&gt;
CX_PRIORITY lets the user set the priority of a commodity. The string &amp;amp;quot;CX_PRIORITY=&amp;amp;quot; is a number from -128 to 127. The higher the number, the higher the priority of the commodity, giving it access to input events before lower priority commodities. All commodities should recognize CX_PRIORITY.&lt;br /&gt;
&lt;br /&gt;
CX_POPUP and CX_POPKEY are only relevant to commodities with a window. The string &amp;amp;quot;CX_POPUP=&amp;amp;quot; should be followed by a &amp;quot;yes&amp;quot; or &amp;quot;no&amp;quot;, telling the commodity if it should or shouldn&#039;t show its window when it is first launched. CX_POPKEY is followed by a string describing the key to use as a hot key for making the commodity&#039;s window appear (pop up). The description string for CX_POPKEY describes an input event. The specific format of the string is discussed in the next section (&amp;quot;Filter Objects and the Input Description String&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Parsing Functions ===&lt;br /&gt;
&lt;br /&gt;
Prior to AmigaOS 4.0, the Commodities Library had the following support functions which were included in an external link library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
UBYTE **ArgArrayInit(LONG argc, UBYTE **argv);&lt;br /&gt;
VOID ArgArrayDone(void);&lt;br /&gt;
STRPTR ArgString(UBYTE **tooltypearray, STRPTR tooltype, STRPTR defaultvalue);&lt;br /&gt;
LONG *ArgInt(UBYTE **tooltypearray, STRPTR tooltype, LONG defaultvalue);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are no longer available. Use IDOS-&amp;gt;ReadArgs(), IIcon-&amp;gt;FindToolType() and IIcon-&amp;gt;MatchToolValue() instead, depending on whether the commodity was launched from Workbench or the Shell (CLI).&lt;br /&gt;
&lt;br /&gt;
== Filter Objects and Input Description Strings ==&lt;br /&gt;
&lt;br /&gt;
Because not all commodities are interested in every input event that makes it way down the input chain, Commodities Exchange has a method for filtering them. A filter CxObject compares the CxMessages it receives to a pattern. If a CxMessage matches the pattern, the filter diverts the CxMessage down its personal list of CxObjects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *CxFilter(STRPTR descriptionstring);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The C macro CxFilter() (defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;) returns a pointer to a filter CxObject. The macro has only one argument, a pointer to a string describing which input events to filter. The following regular expression outlines the format of the input event description string (CX_POPKEY uses the same description string format):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[class]  { [-] (qualifier | synonym) ) }  [ [-] upstroke]  [highmap | ANSICode]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Class&#039;&#039; can be any one of the class strings in the table below. Each class string corresponds to a class of input event as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;. Commodities Exchange will assume the class is rawkey if the class is not explicitly stated.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawkey&amp;amp;quot; || IECLASS_RAWKEY&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rawmouse&amp;amp;quot; || IECLASS_RAWMOUSE&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;event&amp;amp;quot; || IECLASS_EVENT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;pointerpos&amp;amp;quot; || IECLASS_POINTERPOS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;timer&amp;amp;quot; || IECLASS_TIMER&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;newprefs&amp;amp;quot; || IECLASS_NEWPREFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskremoved&amp;amp;quot; || IECLASS_DISKREMOVED&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;diskinserted&amp;amp;quot; || IECLASS_DISKINSERTED&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Qualifier&#039;&#039; is one of the qualifier strings from the table below. Each string corresponds to an input event qualifier as defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;). A dash preceding the qualifier string tells the filter object not to care if that qualifier is present in the input event. Notice that there can be more than one qualifier (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Qualifier String&lt;br /&gt;
! Input Event Class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lshift&amp;amp;quot; || IEQUALIFIER_LSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rshift&amp;amp;quot; || IEQUALIFIER_RSHIFT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;capslock&amp;amp;quot; || IEQUALIFIER_CAPSLOCK&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;control&amp;amp;quot; || IEQUALIFIER_CONTROL&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lalt&amp;amp;quot; || IEQUALIFIER_LALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;ralt&amp;amp;quot; || IEQUALIFIER_RALT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;lcommand&amp;amp;quot; || IEQUALIFIER_LCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rcommand&amp;amp;quot; || IEQUALIFIER_RCOMMAND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;numericpad&amp;amp;quot; || IEQUALIFIER_NUMERICPAD&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;repeat&amp;amp;quot; || IEQUALIFIER_REPEAT&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;midbutton&amp;amp;quot; || IEQUALIFIER_MIDBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;rbutton&amp;amp;quot; || IEQUALIFIER_RBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;leftbutton&amp;amp;quot; || IEQUALIFIER_LEFTBUTTON&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;relativemouse&amp;amp;quot; || IEQUALIFIER_RELATIVEMOUSE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Synonym&#039;&#039; is one of the synonym strings from the table below. These strings act as synonyms for groups of qualifiers. Each string corresponds to a synonym identifier as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;. A dash preceding the synonym string tells the filter object not to care if that synonym is present in the input event. Notice that there can be more than one synonym (or none at all) in the input description string.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Synonym String&lt;br /&gt;
! Synonym Identifier&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;shift&amp;amp;quot; || IXSYM_SHIFT || Look for either Shift key&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;caps&amp;amp;quot; || IXSYM_CAPS || Look for either Shift key or Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
| &amp;amp;quot;alt&amp;amp;quot; || IXSYM_ALT || Look for either Alt key&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Upstroke&#039;&#039; is the literal string &amp;amp;quot;upstroke&amp;amp;quot;. If this string is absent, the filter considers only downstrokes. If it is present alone, the filter considers only upstrokes. If preceded by a dash, the filter considers both upstrokes and downstrokes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Highmap&#039;&#039; is one of the following strings:&lt;br /&gt;
&lt;br /&gt;
&amp;amp;quot;backspace&amp;amp;quot;, &amp;amp;quot;del&amp;amp;quot;, &amp;amp;quot;down&amp;amp;quot;, &amp;amp;quot;enter&amp;amp;quot;, &amp;amp;quot;esc&amp;amp;quot;, &amp;amp;quot;f1&amp;amp;quot;, &amp;amp;quot;f2&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f3&amp;amp;quot;, &amp;amp;quot;f4&amp;amp;quot;, &amp;amp;quot;f5&amp;amp;quot;, &amp;amp;quot;f6&amp;amp;quot;, &amp;amp;quot;f7&amp;amp;quot;, &amp;amp;quot;f8&amp;amp;quot;, &amp;amp;quot;f9&amp;amp;quot;, &amp;amp;quot;f10&amp;amp;quot;,&lt;br /&gt;
&amp;amp;quot;f11, &amp;amp;quot;f12&amp;amp;quot;, &amp;amp;quot;help&amp;amp;quot;, &amp;amp;quot;left&amp;amp;quot;, &amp;amp;quot;return&amp;amp;quot;, &amp;amp;quot;right&amp;amp;quot;, &amp;amp;quot;space&amp;amp;quot;, &amp;amp;quot;tab&amp;amp;quot;, &amp;amp;quot;up&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;ANSICode&#039;&#039; is a single character (for example &amp;quot;a&amp;quot; that Commodities Exchange looks up in the system default keymap.&lt;br /&gt;
&lt;br /&gt;
Here are some example description strings. For function key F2 with the left Shift and either Alt key pressed, the input description string would be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawkey lshift alt f2&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify the key that produces an &amp;quot;a&amp;quot; (this may or may not be the A key depending on the keymap), with or without any Shift, Alt, or Control keys pressed use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;-shift -alt -control a&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a mouse move with the right mouse button down, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;rawmouse rbutton&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To specify a timer event use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;amp;quot;timer&amp;amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Connecting CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A CxObject has to be inserted into the Commodities network before it can process any CxMessages. AttachCxObj() adds a CxObject to the personal list of another CxObject. The HotKey.c example uses it to attach its filter to a broker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID AttachCxObj ( CxObj *headobj, CxObj *co);&lt;br /&gt;
VOID InsertCxObj ( CxObj *headobj, CxObj *co, CxObj *co_pred );&lt;br /&gt;
VOID EnqueueCxObj( CxObj *headobj, CxObj *co );&lt;br /&gt;
VOID SetCxObjPri ( CxObj *co, LONG pri );&lt;br /&gt;
VOID RemoveCxObj ( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AttachCxObj() adds the CxObject to the end of headobj&#039;s personal list. The ordering of a CxObject list determines which object gets CxMessages first. InsertCxObj() also inserts a CxObject, but it inserts it after another CxObject already in the personal list (co_pred in the prototype above).&lt;br /&gt;
&lt;br /&gt;
Brokers aren&#039;t the only CxObjects with a priority. All CxObjects have a priority associated with them. To change the priority of any CxObject, use the SetCxObjPri() function. A commodity can use the priority to keep CxObjects in a personal list sorted by their priority. The commodities.library function EnqueueCxObj() inserts a CxObject into another CxObject&#039;s personal list based on priority.&lt;br /&gt;
&lt;br /&gt;
Like its name implies, the RemoveCxObj() function removes a CxObject from a personal list. Note that it is not necessary to remove a CxObject from a list in order to delete it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* HotKey.c - Simple hot key commodity&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/icon.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
struct CommoditiesIFace *ICommodities;&lt;br /&gt;
struct IconIFace *IIcon;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *filter, *sender, *translate;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker = {&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;quot;Amiga HotKey&amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;quot;A Simple HotKey&amp;quot;,&lt;br /&gt;
    &amp;quot;A simple hot key commodity&amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,    /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                  /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
uint32 cxsigflag;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    uint8 *hotkey, **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    struct Library *CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 50);&lt;br /&gt;
    ICommodities = (struct CommoditiesIFace*)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    /* open the icon.library for the support library */&lt;br /&gt;
    /* functions, ArgArrayInit() and ArgArrayDone()  */&lt;br /&gt;
    struct Library *IconBase = IExec-&amp;gt;OpenLibrary(&amp;quot;icon.library&amp;quot;, 50);&lt;br /&gt;
    struct IconIFace *IIcon = (struct IconIFace*)IExec-&amp;gt;GetInterface(IconBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
    if (ICommodities != NULL &amp;amp;&amp;amp; IIcon != NULL)&lt;br /&gt;
    {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (from the 2.0 version&lt;br /&gt;
                 * of amiga.lib) that makes it easy to read arguments from either a&lt;br /&gt;
                 * CLI or from Workbench&#039;s ToolTypes.  Because it uses icon.library,&lt;br /&gt;
                 * the library has to be open before calling this function.&lt;br /&gt;
                 * ArgArrayDone() cleans up after this function.&lt;br /&gt;
                 */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (also from amiga.lib) searches through the array set up&lt;br /&gt;
                 * by ArgArrayInit() for a specific ToolType.  If it finds one, it&lt;br /&gt;
                 * returns the numeric value of the number that followed the&lt;br /&gt;
                 * ToolType (i.e., CX_PRIORITY=7). If it doesn&#039;t find the ToolType,&lt;br /&gt;
                 * it returns the default value (the third argument)&lt;br /&gt;
                 */&lt;br /&gt;
                newbroker.nb_Pri = (int8)ArgInt(ttypes, &amp;quot;CX_PRIORITY&amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                /* ArgString() works just like ArgInt(), except it returns a pointer to a string&lt;br /&gt;
                 * rather than an integer. In the example below, if there is no ToolType&lt;br /&gt;
                 * &amp;quot;HOTKEY&amp;quot;, the function returns a pointer to &amp;quot;rawkey control esc&amp;quot;.&lt;br /&gt;
                 */&lt;br /&gt;
                hotkey = ArgString(ttypes, &amp;quot;HOTKEY&amp;quot;, &amp;quot;rawkey control esc&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                if (broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxFilter() is a macro that creates a filter CxObject.  This filter&lt;br /&gt;
                     * passes input events that match the string pointed to by hotkey.&lt;br /&gt;
                     */&lt;br /&gt;
                    if (filter = ICommodities-&amp;gt;CxFilter(hotkey))&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Add a CxObject to another&#039;s personal list */&lt;br /&gt;
                        ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
                        /* CxSender() creates a sender CxObject.  Every time a sender gets&lt;br /&gt;
                         * a CxMessage, it sends a new CxMessage to the port pointed to in&lt;br /&gt;
                         * the first argument. CxSender()&#039;s second argument will be the ID&lt;br /&gt;
                         * of any CxMessages the sender sends to the port.  The data pointer&lt;br /&gt;
                         * associated with the CxMessage will point to a *COPY* of the&lt;br /&gt;
                         * InputEvent structure associated with the orginal CxMessage.&lt;br /&gt;
                         */&lt;br /&gt;
                        if (sender = CxSender(broker_mp, EVT_HOTKEY))&lt;br /&gt;
                        {&lt;br /&gt;
                            ICommodities-&amp;gt;AttachCxObj(filter, sender);&lt;br /&gt;
&lt;br /&gt;
                            /* CxTranslate() creates a translate CxObject. When a translate&lt;br /&gt;
                             * CxObject gets a CxMessage, it deletes the original CxMessage&lt;br /&gt;
                             * and adds a new input event to the input.device&#039;s input stream&lt;br /&gt;
                             * after the Commodities input handler. CxTranslate&#039;s argument&lt;br /&gt;
                             * points to an InputEvent structure from which to create the new&lt;br /&gt;
                             * input event.  In this example, the pointer is NULL, meaning no&lt;br /&gt;
                             * new event should be introduced, which causes any event that&lt;br /&gt;
                             * reaches this object to disappear from the input stream.&lt;br /&gt;
                             */&lt;br /&gt;
                            if (translate = ICommodities-&amp;gt;CxTranslate(NULL))&lt;br /&gt;
                            {&lt;br /&gt;
                                ICommodities-&amp;gt;AttachCxObj(filter, translate);&lt;br /&gt;
&lt;br /&gt;
                                /* CxObjError() is a commodities.library function that returns&lt;br /&gt;
                                 * the internal accumulated error code of a CxObject.&lt;br /&gt;
                                 */&lt;br /&gt;
                                if (! CxObjError(filter))&lt;br /&gt;
                                {&lt;br /&gt;
                                    ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                                    ProcessMsg();&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only&lt;br /&gt;
                     * deletes the CxObject pointed to in its argument, but it deletes&lt;br /&gt;
                     * all of the CxObjects that are attached to it.&lt;br /&gt;
                     */&lt;br /&gt;
                    ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
                        IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            /* this amiga.lib function cleans up after ArgArrayInit() */&lt;br /&gt;
            ArgArrayDone();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIcon);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IconBase);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)ICommodities);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    uint32 sigrcvd, msgid, msgtype;&lt;br /&gt;
    int32 returnvalue = 1;&lt;br /&gt;
&lt;br /&gt;
    while(returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
            msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgtype)&lt;br /&gt;
            {&lt;br /&gt;
                case CXM_IEVENT:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case EVT_HOTKEY: /* We got the message from the sender CxObject */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXM_COMMAND:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
                    switch(msgid)&lt;br /&gt;
                    {&lt;br /&gt;
                        case CXCMD_DISABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_ENABLE:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
                            ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_KILL:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        case CXCMD_UNIQUE:&lt;br /&gt;
                        /* Commodities Exchange can be told not only to refuse to launch a&lt;br /&gt;
                         * commodity with a name already in use but also can notify the&lt;br /&gt;
                         * already running commodity that it happened. It does this by&lt;br /&gt;
                         * sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE.  If the&lt;br /&gt;
                         * user tries to run a windowless commodity that is already running,&lt;br /&gt;
                         * the user wants the commodity to shut down. */&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
                            returnvalue = 0;&lt;br /&gt;
                            break;&lt;br /&gt;
                        default:&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                default:&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
            returnvalue = 0;&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sender CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A filter CxObject by itself is not especially useful. It needs some other CxObjects attached to it. A commodity interested in knowing if a specific key was pressed uses a filter to detect and divert the corresponding CxMessage down the filter&#039;s personal list. The filter does this without letting the commodity know what happened. The sender CxObject can be attached to a filter to notify a commodity that it received a CxMessage. CxSender() is a macro that creates a sender CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
senderCxObj = CxObj *CxSender(struct MsgPort *senderport, LONG cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSender() supplies the sender with an Exec message port and an ID. For every CxMessage a sender receives, it sends a new CxMessage to the Exec message port passed in CxSender(). Normally, the commodity creates this port. It is not unusual for a commodity&#039;s broker and sender(s) to share an Exec message port. The HotKey.c example does this to avoid creating unnecessary message ports. A sender uses the ID (cxmID) passed to CxSender() as the ID for all the CxMessages that the it transmits. A commodity uses the ID to monitor CxMessages from several senders at a single message port.&lt;br /&gt;
&lt;br /&gt;
A sender does several things when it receives a CxMessage. First, it duplicates the CxMessage&#039;s corresponding input event and creates a new CxMessage. Then, it points the new CxMessage&#039;s data field to the copy of the input event and sets the new CxMessage&#039;s ID to the ID passed to CxSender(). Finally, it sends the new CxMessage to the port passed to CxSender(), asynchronously.&lt;br /&gt;
&lt;br /&gt;
Because HotKey uses only one message port between its broker and sender object, it has to extract the CxMessage&#039;s type so it can tell if it is a CXM_IEVENT or a CXM_COMMAND. If HotKey gets a CXM_IEVENT, it compares the CxMessage&#039;s ID to the sender&#039;s ID, EVT_HOTKEY, to see which sender sent the CxMessage. Of course HotKey has only one sender, so it only checks for only one ID. If it had more senders, HotKey would check for the ID of each of the other senders as well.&lt;br /&gt;
&lt;br /&gt;
Although HotKey doesn&#039;t use it, a CXM_IEVENT CxMessage contains a pointer to the copy of an input event. A commodity can extract this pointer (using CxMsgData()) if it needs to examine the input event copy. This pointer is only valid before the CxMessage reply. Note that it does not make any sense to modify the input event copy.&lt;br /&gt;
&lt;br /&gt;
Senders are attached almost exclusively to CxObjects that filter out most input events (usually a filter CxObject). Because a sender sends a CxMessage for every single input event it gets, it should only get a select few input events. The AttachCxObj() function can add a CxObject to the end of a filter&#039;s (or some other filtering CxObject&#039;s) personal list. A commodity should not attach a CxObject to a sender as a sender ignores any CxObjects in its personal list.&lt;br /&gt;
&lt;br /&gt;
== Translate CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Normally, after a commodity processes a hot key input event, it needs to eliminate that input event. Other commodities may need to replace an input event with a different one. The translate CxObject can be used for these purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
translateCxObj = CxObj  *CxTranslate(struct InputEvent *newinputevent);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The macro CxTranslate() creates a new translate CxObject. CxTranslate()&#039;s only argument is a pointer to a chain of one or more InputEvent structures.&lt;br /&gt;
&lt;br /&gt;
When a translate CxObject receives a CxMessage, it eliminates the CxMessage and its corresponding input event from the system. The translator introduces a new input event, which Commodities Exchange copies from the InputEvent structure passed to CxTranslate() (newinputevent from the function prototype above), in place of the deleted input event.&lt;br /&gt;
&lt;br /&gt;
A translator is normally attached to some kind of filtering CxObject. If it wasn&#039;t, it would translate all input events into the same exact input event. Like the sender CxObject, a translator does not divert CxMessages down its personal list, so it doesn&#039;t serve any purpose to add any to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetTranslate( CxObj *translator, struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is possible to change the InputEvent structure that a translator looks at when it creates and introduces new input events into the input stream. The function SetTranslate() accepts a pointer to the new InputEvent structure, which the translator will duplicate and introduce when it receives a CxMessage.&lt;br /&gt;
&lt;br /&gt;
HotKey utilizes a special kind of translator. Instead of supplying a new input event, HotKey passes a NULL to CxTranslate(). If a translator has a NULL new input event pointer, it does not introduce a new input event, but still eliminates any CxMessages and corresponding input events it receives.&lt;br /&gt;
&lt;br /&gt;
== CxObject Errors ==&lt;br /&gt;
&lt;br /&gt;
A Commodities Exchange function that acts on a CxObject records errors in the CxObject&#039;s accumulated error field. The function CxObjError() returns a CxObject&#039;s error field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int32 co_errorfield = CxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each bit in the error field corresponds to a specific type of error. The following is a list of the currently defined CxObject errors  and their corresponding bit mask constants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Error Constant&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| COERR_ISNULL&lt;br /&gt;
| CxObjError() was passed a NULL.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_NULLATTACH&lt;br /&gt;
| Someone tried to attach a NULL CxObject to this CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADFILTER&lt;br /&gt;
| This filter CxObject currently has an invalid filter description.&lt;br /&gt;
|-&lt;br /&gt;
| COERR_BADTYPE&lt;br /&gt;
| Someone tried to perform a type specific function on the wrong type of CxObject (for example calling SetFilter() on a sender CxObject).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The remaining bits are reserved for future use. HotKey.c checks the error field of its filter CxObject to make sure the filter is valid. HotKey.c does not need to check the other objects with CxObjError() because it already makes sure that these other objects are not NULL, which is the only other kind of error the other objects can cause in this situation.&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange has a function that clears a CxObject&#039;s accumulated error field, ClearCxObjError().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID ClearCxObjError( CxObj *co );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A commodity should be careful about using this, especially on a filter. If a commodity clears a filter&#039;s error field and the COERR_BADFILTER bit is set, Commodities Exchange will think that the filter is OK and start sending messages through it.&lt;br /&gt;
&lt;br /&gt;
== Uniqueness ==&lt;br /&gt;
&lt;br /&gt;
When a commodity opens its broker, it can ask Commodities Exchange not to launch another broker with the same name (nb_Name). The purpose of the uniqueness feature is to prevent the user from starting duplicate commodities. If a commodity asks, Commodities Exchange will not only refuse to create a new, similarly named broker, but it will also notify the original commodity if someone tries to do so.&lt;br /&gt;
&lt;br /&gt;
A commodity tells Commodities Exchange not to allow duplicates by setting certain bits in the nb_Unique field of the NewBroker structure it sends to CxBroker():&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| NBU_UNIQUE&lt;br /&gt;
| bit 0&lt;br /&gt;
|-&lt;br /&gt;
| NBU_NOTIFY&lt;br /&gt;
| bit 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Setting the NBU_UNIQUE bit prevents duplicate commodities. Setting the NBU_NOTIFY bit tells Commodities Exchange to notify a commodity if an attempt was made to launch a duplicate. Such a commodity will receive a CXM_COMMAND CxMessage with an ID of CXCMD_UNIQUE when someone tries to duplicate it. Because the uniqueness feature uses the name a programmer gives a commodity to differentiate it from other commodities, it is possible for completely different commodities to share the same name, preventing the two from coexisting. For this reason, a commodity should not use a name that is likely to be in use by other commodities (like &amp;quot;filter&amp;quot; or &amp;quot;hotkey&amp;quot;). Instead, use a name that matches the commodity name.&lt;br /&gt;
&lt;br /&gt;
When &amp;quot;HotKey.c&amp;quot; gets a CXCMD_UNIQUE CxMessage, it shuts itself down. &amp;quot;HotKey.c&amp;quot; and all the windowless commodities that come with Workbench shut themselves down when they get a CXCMD_UNIQUE CxMessage. Because the user will expect all windowless commodities to work this way, all windowless commodities should follow this standard.&lt;br /&gt;
&lt;br /&gt;
When the user tries to launch a duplicate of a system commodity that has a window, the system commodity moves its window to the front of the display, as if the user had clicked the &amp;quot;Show&amp;quot; gadget in the controller program&#039;s window. A windowed commodity should mimic conventions set by existing windowed system commodities, and move its window to the front of the display.&lt;br /&gt;
&lt;br /&gt;
== Signal CxObjects ==&lt;br /&gt;
&lt;br /&gt;
A commodity can use a sender CxObject to find out if a CxMessage has &amp;quot;visited&amp;quot; a CxObject, but this method unnecessarily uses system resources. A commodity that is only interested in knowing if such a visitation took place does not need to see a corresponding input event or a CxMessage ID. Instead, Commodities Exchange has a CxObject that uses an Exec signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *signalCxObj = CxSignal(struct Task *, LONG cx_signal);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CxSignal() sets up a signal CxObject. When a signal CxObject receives a CxMessage, it signals a task. The commodity is responsible for determining the proper task ID and allocating the signal. Normally, a commodity wants to be signaled so it uses FindTask(NULL) to find it&#039;s own task address. Note that cx_signal from the above prototype is the signal number as returned by AllocSignal(), not the signal mask made from that number. For more information on signals, see [[Exec_Signals|Exec Signals]].&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; (shown a little later) uses a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
== Custom CxObjects ==&lt;br /&gt;
&lt;br /&gt;
Although the CxObjects mentioned so far take care of most of the input event handling a commodity needs to do, they cannot do it all. This is why Commodities Exchange has a custom CxObject. When a custom CxObject receives a CxMessage, it calls a function provided by the commodity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObject *customCxObj = CxCustom(int32 *customfunction(), int32 cxmID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A custom CxObject is the only means by which a commodity can directly modify input events as they pass through the Commodities network as CxMessages. For this reason, it is probably the most dangerous of the CxObjects to use.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Warning About Custom CxObjects|text=Unlike the rest of the code a commodities programmer writes, the code passed to a custom CxObject runs as part of the input.device task, putting severe restrictions on the function. No DOS or Intuition functions can be called. No assumptions can be made about the values of registers upon entry. Any function passed to CxCustom() should be very quick and very simple, with a minimum of stack usage.}}&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange calls a custom CxObject&#039;s function as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID customfunction(CxMsg *cxm, CxObj *customcxobj);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where cxm is a pointer to a CxMessage corresponding to a real input event, and customcxobj is a pointer to the custom CxObject. The custom function can extract the pointer to the input event by calling CxMsgData(). Before passing the CxMessage to the custom function, Commodities Exchange sets the CxMessage&#039;s ID to the ID passed to CxCustom().&lt;br /&gt;
&lt;br /&gt;
The following is an example of a custom CxObject function that swaps the function of the left and right mouse buttons.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
custom = CxCustom(CxFunction, 0)&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be */&lt;br /&gt;
/* short and sweet. This code runs as part of the input.device task */&lt;br /&gt;
#define CODEMASK (0x00FF &amp;amp; IECODE_LBUTTON &amp;amp; IECODE_RBUTTON)&lt;br /&gt;
&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    uint16 mousequals = 0x0000;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg.  Unlike the InputEvent&lt;br /&gt;
     * extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.&lt;br /&gt;
     */&lt;br /&gt;
    struct InputEvent *ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* Check to see if this input event is a left or right mouse button   */&lt;br /&gt;
    /* by itself (a mouse button can also be a qualifier).  If it is, flip the   */&lt;br /&gt;
    /* low order bit to switch leftbutton &amp;lt;--&amp;gt; rightbutton. */&lt;br /&gt;
    if (ie-&amp;gt;ie_Class == IECLASS_RAWMOUSE)&lt;br /&gt;
        if ((ie-&amp;gt;ie_Code &amp;amp; CODEMASK) == CODEMASK)  ie-&amp;gt;ie_Code ^= 0x0001;&lt;br /&gt;
&lt;br /&gt;
    /* Check the qualifiers. If a mouse button was down when this */&lt;br /&gt;
    /* input event occurred, set the other mouse button bit.      */&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_RBUTTON)  mousequals |= IEQUALIFIER_LEFTBUTTON;&lt;br /&gt;
    if (ie-&amp;gt;ie_Qualifier &amp;amp; IEQUALIFIER_LEFTBUTTON)  mousequals |= IEQUALIFIER_RBUTTON;&lt;br /&gt;
&lt;br /&gt;
    /* clear the RBUTTON and LEFTBUTTON qualifier bits */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier &amp;amp;= ~(IEQUALIFIER_LEFTBUTTON | IEQUALIFIER_RBUTTON);&lt;br /&gt;
&lt;br /&gt;
    /* set the mouse button qualifier bits to their new values */&lt;br /&gt;
    ie-&amp;gt;ie_Qualifier |= mousequals;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Debug CxObjects ==&lt;br /&gt;
&lt;br /&gt;
The final CxObject is the debug CxObject. When a debug CxObject receives a CxMessage, it sends debugging information to the serial port using DebugPrintF().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
CxObj *debugCxObj = CxDebug(int32 ID);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The debug CxObject will DebugPrintF() the following information about itself, the CxMsg, and the corresponding InputEvent structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DEBUG NODE: 7CB5AB0, ID: 2&lt;br /&gt;
 CxMsg: 7CA6EF2, type: 0, data 2007CA destination 6F1E07CB&lt;br /&gt;
dump IE: 7CA6F1E&lt;br /&gt;
 Class 1&lt;br /&gt;
 Code 40&lt;br /&gt;
 Qualifier 8000&lt;br /&gt;
 EventAddress 40001802&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There has to be a terminal connected to the Amiga&#039;s serial port to receive this information.&lt;br /&gt;
&lt;br /&gt;
== The IX Structure ==&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange does not use the input event description strings discussed earlier to match input events. Instead, Commodities Exchange converts these strings to its own internal format. These input expressions are available for commodities to use instead of the input description strings. The following is the IX structure as defined in &amp;amp;lt;libraries/commodities.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IX_VERSION   2&lt;br /&gt;
&lt;br /&gt;
struct InputXpression {&lt;br /&gt;
   UBYTE   ix_Version;     /* must be set to IX_VERSION  */&lt;br /&gt;
   UBYTE   ix_Class;       /* class must match exactly   */&lt;br /&gt;
   UWORD   ix_Code;&lt;br /&gt;
   UWORD   ix_CodeMask;    /* normally used for UPCODE   */&lt;br /&gt;
   UWORD   ix_Qualifier;&lt;br /&gt;
   UWORD   ix_QualMask;&lt;br /&gt;
   UWORD   ix_QualSame;    /* synonyms in qualifier      */&lt;br /&gt;
   };&lt;br /&gt;
&lt;br /&gt;
typedef struct InputXpression IX;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ix_Version field contains the current version number of the InputXpression structure. The current version is defined as IX_VERSION. The ix_Class field contains the IECLASS_ constant (defined in &amp;amp;lt;devices/inputevent.h&amp;amp;gt;) of the class of input event sought. Commodities Exchange uses the ix_Code and ix_CodeMask fields to match the ie_Code field of a struct InputEvent. The bits of ix_CodeMask indicate which bits are relevant in the ix_Code field when trying to match against a ie_Code. If any bits in ix_CodeMask are off, Commodities Exchange does not consider the corresponding bit in ie_Code when trying to match input events. This is used primarily to mask out the IECODE_UP_PREFIX bit of rawkey events, making it easier to match both up and down presses of a particular key.&lt;br /&gt;
&lt;br /&gt;
IX&#039;s qualifier fields, ix_Qualifier, ix_QualMask, and ix_QualSame, are used to match the ie_Qualifier field of an InputEvent structure. The ix_Qualifier and ix_QualMask fields work just like ix_Code and ix_CodeMask. The bits of ix_QualMask indicate which bits are relevant when comparing ix_Qualifier to ie_Qualifier. The ix_QualSame field tells Commodities Exchange that certain qualifiers are equivalent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define IXSYM_SHIFT  1     /* left- and right- shift are equivalent     */&lt;br /&gt;
#define IXSYM_CAPS   2     /* either shift or caps lock are equivalent  */&lt;br /&gt;
#define IXSYM_ALT    4     /* left- and right- alt are equivalent       */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, the input description string&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;rawkey -caps -lalt -relativemouse -upstroke ralt tab&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
matches a tab upstroke or downstroke with the right Alt key pressed whether or not the left Alt, either Shift, or the Caps Lock keys are down. The following IX structure corresponds to that input description string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IX ix = {&lt;br /&gt;
    IX_VERSION,                   /* The version */&lt;br /&gt;
    IECLASS_RAWKEY,               /* We&#039;re looking for a RAWKEY event */&lt;br /&gt;
    0x42,                         /* The key the usa0 keymap maps to a tab*/&lt;br /&gt;
    0x00FF &amp;amp; (~IECODE_UP_PREFIX), /* We want up and down key presses */&lt;br /&gt;
    IEQUALIFIER_RALT,             /* The right alt key must be down */&lt;br /&gt;
    0xFFFF &amp;amp; ~(IEQUALIFIER_LALT | IEQUALIFIER_LSHIFT |&lt;br /&gt;
        IEQUALIFIER_RSHIFT | IEQUALIFIER_CAPSLOCK | IEQUALIFIER_RELATIVEMOUSE),&lt;br /&gt;
        /* don&#039;t care about left alt, shift, capslock, or relativemouse qualifiers   */&lt;br /&gt;
    IXSYM_CAPS  /* The shift keys and the capslock key qualifiers are all equivalent */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CxFilter() macro only accepts a description string to describe an input event. A commodity can change this filter, however, with the SetFilter() and SetFilterIX() function calls.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID SetFilter( CxObj *filter, STRPTR descrstring );&lt;br /&gt;
VOID SetFilterIX( CxObj *filter, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetFilter() and SetFilterIX() change which input events a filter CxObject diverts. SetFilter() accepts a pointer to an input description string. SetFilterIX() accepts a pointer to an IX input expression. A commodity that uses either of these functions should check the filter&#039;s error code with CxObjError() to make sure the change worked.&lt;br /&gt;
&lt;br /&gt;
The function ParseIX() parses an input description string and translates it into an IX input expression.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG errorcode = ParseIX( STRPTR descrstring, IX *ix );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commodities Exchange uses ParseIX() to convert the description string in CxFilter() to an IX input expression. As was mentioned previously, ParseIX() does not work with certain kinds of input strings.&lt;br /&gt;
&lt;br /&gt;
== Controlling CxMessages ==&lt;br /&gt;
&lt;br /&gt;
A Custom CxObject has the power to directly manipulate the CxMessages that travel around the Commodities network. One way is to directly change values in the corresponding input event. Another way is to redirect (or dispose of) the CxMessages.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID DivertCxMsg ( CxMsg *cxm, CxObj *headobj, CxObj *retobj );&lt;br /&gt;
VOID RouteCxMsg  ( CxMsg *cxm, CxObj *co );&lt;br /&gt;
VOID DisposeCxMsg( CxMsg *cxm );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DivertCxMsg() and RouteCxMsg() dictate where the CxMessage will go next. Conceptually, DivertCxMsg() is analogous to a subroutine in a program; the CxMessage will travel down the personal list of a CxObject (headobj in the prototype) until it gets to the end of that list. It then returns and visits the CxObject that follows the return CxObject (the return CxObject in the prototype above is retobj). RouteCxMsg() is analogous to a goto in a program; it has no CxObject to return to.&lt;br /&gt;
&lt;br /&gt;
DisposeCxMsg() removes a CxMessage from the network and releases its resources. The translate CxObject uses this function to remove a CxMessage.&lt;br /&gt;
&lt;br /&gt;
The example &amp;quot;Divert.c&amp;quot; shows how to use DivertCxMsg() as well as a signal CxObject.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;/* divert.c - commodity to monitor user inactivity - compiled with SASC 5.10&lt;br /&gt;
LC -b0 -cfist -v -j73 divert.c&lt;br /&gt;
Blink FROM LIB:c.o,divert.o TO divert LIBRARY LIB:LC.lib,LIB:Amiga.lib NODEBUG SC SD&lt;br /&gt;
quit; */&lt;br /&gt;
#include &amp;amp;lt;exec/libraries.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;libraries/commodities.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;dos/dos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/exec_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/alib_stdio_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;clib/commodities_protos.h&amp;amp;gt;&lt;br /&gt;
#include &amp;amp;lt;devices/inputevent.h&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */&lt;br /&gt;
int chkabort(void) { return(0); }&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define TIMER_CLICKS 100&lt;br /&gt;
&lt;br /&gt;
void main(int, char **);&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
void CxFunction(CxMsg *, CxObj *);&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase, *IconBase;&lt;br /&gt;
struct MsgPort *broker_mp;&lt;br /&gt;
CxObj *broker, *cocustom, *cosignal;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
    NB_VERSION,&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,           /* string to identify this broker */&lt;br /&gt;
    &amp;amp;quot;Divert&amp;amp;quot;,&lt;br /&gt;
    &amp;amp;quot;show divert&amp;amp;quot;,&lt;br /&gt;
    NBU_UNIQUE | NBU_NOTIFY,  /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
    0, 0, 0, 0                /* If someone tries it, let me know                        */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Task *task;&lt;br /&gt;
ULONG cxsigflag, signal, cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
void main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    UBYTE **ttypes;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
&lt;br /&gt;
    if (CxBase = OpenLibrary(&amp;amp;quot;commodities.library&amp;amp;quot;, 37L))&lt;br /&gt;
    {&lt;br /&gt;
        /* open the icon.library for support library functions, ArgArrayInit() and ArgArrayDone() */&lt;br /&gt;
        if (IconBase = OpenLibrary(&amp;amp;quot;icon.library&amp;amp;quot;, 36L))&lt;br /&gt;
        {&lt;br /&gt;
            if (broker_mp = CreateMsgPort())&lt;br /&gt;
            {&lt;br /&gt;
                newbroker.nb_Port = broker_mp;&lt;br /&gt;
                cxsigflag = 1L &amp;amp;lt;&amp;amp;lt; broker_mp-&amp;amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
                /* ArgArrayInit() is a support library function (in the 2.0 version of amiga.lib) */&lt;br /&gt;
                /* that makes it easy to read arguments from either a CLI or from Workbench&#039;s     */&lt;br /&gt;
                /* ToolTypes. Because it uses icon.library, the library has to be open before     */&lt;br /&gt;
                /* before calling this function.  ArgArrayDone() cleans up after this function.   */&lt;br /&gt;
                ttypes = ArgArrayInit(argc, argv);&lt;br /&gt;
&lt;br /&gt;
                /* ArgInt() (in amiga.lib) searches through the array set up by ArgArrayInit()    */&lt;br /&gt;
                /* for a specific ToolType.  If it finds one, it returns the numeric value of the */&lt;br /&gt;
                /* number that followed the ToolType (i.e., CX_PRIORITY=7).  If it  doesn&#039;t find  */&lt;br /&gt;
                /* the ToolType, it returns the default value (the third argument)                */&lt;br /&gt;
                newbroker.nb_Pri = (BYTE)ArgInt(ttypes, &amp;amp;quot;CX_PRIORITY&amp;amp;quot;, 0);&lt;br /&gt;
&lt;br /&gt;
                if (broker = CxBroker(&amp;amp;amp;newbroker, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                    /* CxCustom() takes two arguments, a pointer to the custom function           */&lt;br /&gt;
                    /* and an ID. Commodities Exchange will assign that ID to any CxMsg           */&lt;br /&gt;
                    /* passed to the custom  function.                                            */&lt;br /&gt;
                    if (cocustom = CxCustom(CxFunction, 0L))&lt;br /&gt;
                    {&lt;br /&gt;
                        AttachCxObj(broker, cocustom);&lt;br /&gt;
&lt;br /&gt;
                        /* Allocate a signal bit for the signal CxObj */&lt;br /&gt;
                        if ( (signal = (ULONG)AllocSignal(-1L)) != -1)&lt;br /&gt;
                        {&lt;br /&gt;
                            /* set up the signal mask */&lt;br /&gt;
                            cxobjsignal = 1L &amp;amp;lt;&amp;amp;lt; signal;&lt;br /&gt;
                            cxsigflag |= cxobjsignal;&lt;br /&gt;
&lt;br /&gt;
                            /* CxSignal takes two arguments, a pointer to the task to signal      */&lt;br /&gt;
                            /* (normally the commodity) and the number of the signal bit the      */&lt;br /&gt;
                            /* commodity acquired to signal with.                                 */&lt;br /&gt;
                            task = FindTask(NULL);&lt;br /&gt;
                            if (cosignal = CxSignal(task, signal))&lt;br /&gt;
                            {&lt;br /&gt;
                                AttachCxObj(cocustom, cosignal);&lt;br /&gt;
                                ActivateCxObj(broker, 1L);&lt;br /&gt;
                                ProcessMsg();&lt;br /&gt;
                            }&lt;br /&gt;
                            FreeSignal(signal);&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* DeleteCxObjAll() is a commodities.library function that not only deletes   */&lt;br /&gt;
                    /* the CxObject pointed to in its argument, but it deletes all of the         */&lt;br /&gt;
                    /* CxObjects that are attached to it.                                         */&lt;br /&gt;
                    DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
                    /* Empty the port of all CxMsgs */&lt;br /&gt;
                    while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
                        ReplyMsg((struct Message *)msg);&lt;br /&gt;
                }&lt;br /&gt;
                DeletePort(broker_mp);&lt;br /&gt;
            }&lt;br /&gt;
            ArgArrayDone();   /* this amiga.lib function cleans up after ArgArrayInit()           */&lt;br /&gt;
            CloseLibrary(IconBase);&lt;br /&gt;
        }&lt;br /&gt;
        CloseLibrary(CxBase);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
    extern struct MsgPort *broker_mp;&lt;br /&gt;
    extern CxObj *broker;&lt;br /&gt;
    extern ULONG cxsigflag;&lt;br /&gt;
    CxMsg *msg;&lt;br /&gt;
    ULONG sigrcvd, msgid;&lt;br /&gt;
    LONG returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
    while (returnvalue)&lt;br /&gt;
    {&lt;br /&gt;
        sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
        while(msg = (CxMsg *)GetMsg(broker_mp))&lt;br /&gt;
        {&lt;br /&gt;
            msgid = CxMsgID(msg);&lt;br /&gt;
            ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
            switch(msgid)&lt;br /&gt;
            {&lt;br /&gt;
                case CXCMD_DISABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 0L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_ENABLE:&lt;br /&gt;
                    ActivateCxObj(broker, 1L);&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_KILL:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
                case CXCMD_UNIQUE:&lt;br /&gt;
                    returnvalue = 0L;&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; SIGBREAKF_CTRL_C) returnvalue = 0L;&lt;br /&gt;
&lt;br /&gt;
        /* Check to see if the signal CxObj signalled us. */&lt;br /&gt;
        if (sigrcvd &amp;amp;amp; cxobjsignal) printf(&amp;amp;quot;Got Signal\n&amp;amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The custom function for the custom CxObject.  Any code for a custom CxObj must be short        */&lt;br /&gt;
/* and sweet because it runs as part of the input.device task.                                    */&lt;br /&gt;
void CxFunction(register CxMsg *cxm, CxObj *co)&lt;br /&gt;
{&lt;br /&gt;
    struct InputEvent *ie;&lt;br /&gt;
    static ULONG time = 0L;&lt;br /&gt;
&lt;br /&gt;
    /* Get the struct InputEvent associated with this CxMsg. Unlike the InputEvent                */&lt;br /&gt;
    /* extracted from a CxSender&#039;s CxMsg, this is a *REAL* input event, be careful with it.       */&lt;br /&gt;
    ie = (struct InputEvent *)CxMsgData(cxm);&lt;br /&gt;
&lt;br /&gt;
    /* This custom function counts the number of timer events that go by while no other input     */&lt;br /&gt;
    /* events occur.  If it counts more than a certain amount of timer events, it clears the      */&lt;br /&gt;
    /* count and diverts the timer event CxMsg to the custom object&#039;s personal                    */&lt;br /&gt;
    /* list.  If an event besides a timer event passes by, the timer event count is reset.        */&lt;br /&gt;
    if (ie-&amp;amp;gt;ie_Class == IECLASS_TIMER)&lt;br /&gt;
    {&lt;br /&gt;
        time++;&lt;br /&gt;
        if (time &amp;amp;gt;= TIMER_CLICKS)&lt;br /&gt;
        {&lt;br /&gt;
            time = 0L;&lt;br /&gt;
            DivertCxMsg(cxm, co, co);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        time = 0L;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== New Input Events ==&lt;br /&gt;
&lt;br /&gt;
The Commodities Library also has functions used to introduce new input events to the input stream.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct InputEvent *InvertString( UBYTE *string, ULONG *keymap );&lt;br /&gt;
VOID               FreeIEvents( struct InputEvent *ie );&lt;br /&gt;
VOID               AddIEvents( struct InputEvent *ie );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
InvertString() is a function that accepts an ASCII string and creates a linked list of input events that translate into the string using the supplied keymap (or the system default if the key map is NULL). The NULL terminated string may contain ANSI character codes, an input description enclosed in angle (&amp;amp;lt;&amp;amp;gt;) brackets, or one of the following backslash escape characters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| \r&lt;br /&gt;
| Return&lt;br /&gt;
|-&lt;br /&gt;
| \t&lt;br /&gt;
| Tab&lt;br /&gt;
|-&lt;br /&gt;
| \\&lt;br /&gt;
| Backslash&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abc&amp;amp;lt;alt f1&amp;amp;gt;\rhi there.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FreeIEvents() frees a list of input events allocated by InvertString(). AddIEvents() is a commodities.library function that adds a linked list of input events at the the top of the Commodities network. Each input event in the list is made into an individual CxMessage. Note that if passed a linked list of input events created by InvertString(), the order the events appear in the string will be reversed.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* PopShell.c - Simple hot key commodity example.&lt;br /&gt;
   Adapted by xenic from the original source code.&lt;br /&gt;
   &lt;br /&gt;
   Compile commands: gcc PopShell.c -o popshell -lamiga -Wall&lt;br /&gt;
&lt;br /&gt;
   To run the compiled program - Open 2 shell windows. Execute&lt;br /&gt;
   PopShell in the first shell window with a line like:&lt;br /&gt;
     PopShell HOTKEY &amp;quot;ctrl alt f&amp;quot;&lt;br /&gt;
   Activate the second shell window and enter the keyboard shortcut.&lt;br /&gt;
   A new shell window should open on the Workbench screen.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;libraries/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/alib_protos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static CONST_STRPTR version USED = &amp;quot;$VER: PopShell 1.1 (05.11.2015)&amp;quot;;&lt;br /&gt;
static CONST_STRPTR stackcookie USED = &amp;quot;$STACK: 32768&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
#define EVT_HOTKEY 1L&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase = NULL;&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *broker_mp = NULL;&lt;br /&gt;
CxObj *broker, *filter;&lt;br /&gt;
&lt;br /&gt;
struct NewBroker newbroker =&lt;br /&gt;
{&lt;br /&gt;
	NB_VERSION,&lt;br /&gt;
	&amp;quot;Amiga PopShell&amp;quot;,       /* string to identify this broker */&lt;br /&gt;
	&amp;quot;A Simple PopShell&amp;quot;,&lt;br /&gt;
	&amp;quot;A simple PopShell commodity&amp;quot;,&lt;br /&gt;
	NBU_UNIQUE | NBU_NOTIFY,      /* Don&#039;t want any new commodities starting with this name. */&lt;br /&gt;
	0, 0, 0, 0                    /* If someone tries it, let me know */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR newshell = &amp;quot;\rllehswen&amp;quot;;  /* &amp;quot;newshell&amp;quot; spelled backwards */&lt;br /&gt;
struct InputEvent *ie = NULL;&lt;br /&gt;
ULONG cxsigflag;&lt;br /&gt;
&lt;br /&gt;
#define TEMPLATE &amp;quot;HOTKEY/K,CX_PRIORITY/N&amp;quot;&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
	ARG_HOTKEY, ARG_PRIORITY, ARG_MAX&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
	STRPTR hotkey = NULL;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	struct RDArgs *argsdata = NULL;&lt;br /&gt;
	int32 rargs[ARG_MAX] = {0};&lt;br /&gt;
	BYTE priority = 0;&lt;br /&gt;
&lt;br /&gt;
	signal(SIGINT, SIG_IGN);&lt;br /&gt;
&lt;br /&gt;
	if ((CxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;commodities.library&amp;quot;, 37L)))&lt;br /&gt;
	{&lt;br /&gt;
		if ((ICommodities = (struct CommoditiesIFace *)IExec-&amp;gt;GetInterface(CxBase, &amp;quot;main&amp;quot;, 1, NULL)))&lt;br /&gt;
		{&lt;br /&gt;
			if ((argsdata = IDOS-&amp;gt;ReadArgs(TEMPLATE, rargs, NULL)))&lt;br /&gt;
			{&lt;br /&gt;
				if (rargs[ARG_PRIORITY])&lt;br /&gt;
					priority = (BYTE)*(uint32 *)rargs[ARG_PRIORITY];&lt;br /&gt;
				if ((broker_mp = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)))&lt;br /&gt;
				{&lt;br /&gt;
					newbroker.nb_Port = broker_mp;&lt;br /&gt;
					cxsigflag = 1L &amp;lt;&amp;lt; broker_mp-&amp;gt;mp_SigBit;&lt;br /&gt;
					newbroker.nb_Pri = priority;&lt;br /&gt;
					hotkey = (STRPTR)rargs[ARG_HOTKEY];&lt;br /&gt;
&lt;br /&gt;
					if ((broker = ICommodities-&amp;gt;CxBroker(&amp;amp;newbroker, NULL)))&lt;br /&gt;
					{&lt;br /&gt;
						/* CxFilter(), CxSender() &amp;amp; CxTranslate() are    */&lt;br /&gt;
						/* macros defined in libraries/commodities.h.    */&lt;br /&gt;
						/* CxFilter() creates a filter CxObject.         */&lt;br /&gt;
						/* CxSender() creates a sender CxObject.         */&lt;br /&gt;
						/* CxTranslate() creates a translation CxObject. */&lt;br /&gt;
						if ((filter = CxFilter(hotkey)))&lt;br /&gt;
						{&lt;br /&gt;
							/* Add a filter CxObject to another&#039;s personal list. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(broker, filter);&lt;br /&gt;
&lt;br /&gt;
							/* Add a sender CxObject to the filter CxObject. */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxSender(broker_mp, EVT_HOTKEY));&lt;br /&gt;
&lt;br /&gt;
							/* Add a translation CxObject to the filter CxObject */&lt;br /&gt;
							ICommodities-&amp;gt;AttachCxObj(filter, CxTranslate(NULL));&lt;br /&gt;
&lt;br /&gt;
							if (!(ICommodities-&amp;gt;CxObjError(filter)))&lt;br /&gt;
							{&lt;br /&gt;
								/* InvertString() is an amiga.lib function that creates a linked */&lt;br /&gt;
								/* list of input events which would translate into the string    */&lt;br /&gt;
								/* passed to it.  Note that it puts the input events in the      */&lt;br /&gt;
								/* opposite order in which the corresponding letters appear in   */&lt;br /&gt;
								/* the string.  A translate CxObject expects them backwards.     */&lt;br /&gt;
								if ((ie = InvertString(newshell, NULL)))&lt;br /&gt;
								{&lt;br /&gt;
									ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
									ProcessMsg();&lt;br /&gt;
									/* we have to release the memory allocated by InvertString. */&lt;br /&gt;
									FreeIEvents(ie);&lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						/* DeleteCxObjAll() is a commodities.library function that */&lt;br /&gt;
						/* deletes the CxObject pointed to in its argument and     */&lt;br /&gt;
						/* deletes all of the CxObjects attached to it.            */&lt;br /&gt;
						ICommodities-&amp;gt;DeleteCxObjAll(broker);&lt;br /&gt;
&lt;br /&gt;
						/* Empty the port of all CxMsgs */&lt;br /&gt;
						while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
							IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
					}&lt;br /&gt;
					IExec-&amp;gt;FreeSysObject(ASOT_PORT, broker_mp);&lt;br /&gt;
				}&lt;br /&gt;
				IDOS-&amp;gt;FreeArgs(argsdata); /* cleans up after ReadArgs() */&lt;br /&gt;
			}&lt;br /&gt;
			IExec-&amp;gt;DropInterface((struct Interface *)ICommodities);&lt;br /&gt;
		}&lt;br /&gt;
		IExec-&amp;gt;CloseLibrary(CxBase);&lt;br /&gt;
	}&lt;br /&gt;
	return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ProcessMsg(void)&lt;br /&gt;
{&lt;br /&gt;
	extern struct MsgPort *broker_mp;&lt;br /&gt;
	extern CxObj *broker;&lt;br /&gt;
	extern ULONG cxsigflag;&lt;br /&gt;
	CxMsg *msg = NULL;&lt;br /&gt;
	ULONG sigrcvd, msgid, msgtype;&lt;br /&gt;
	LONG returnvalue = 1L;&lt;br /&gt;
&lt;br /&gt;
	while (returnvalue)&lt;br /&gt;
	{&lt;br /&gt;
		sigrcvd = IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C | cxsigflag);&lt;br /&gt;
&lt;br /&gt;
		while((msg = (CxMsg *)IExec-&amp;gt;GetMsg(broker_mp)))&lt;br /&gt;
		{&lt;br /&gt;
			msgid = ICommodities-&amp;gt;CxMsgID(msg);&lt;br /&gt;
			msgtype = ICommodities-&amp;gt;CxMsgType(msg);&lt;br /&gt;
			IExec-&amp;gt;ReplyMsg((struct Message *)msg);&lt;br /&gt;
&lt;br /&gt;
			switch(msgtype)&lt;br /&gt;
			{&lt;br /&gt;
				case CXM_IEVENT:&lt;br /&gt;
					printf(&amp;quot;A CXM_EVENT, &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case EVT_HOTKEY:&lt;br /&gt;
							/* We got the message from the sender CxObject  */&lt;br /&gt;
							printf(&amp;quot;You hit the HotKey.\n&amp;quot;);&lt;br /&gt;
							/* Add the string &amp;quot;newshell&amp;quot; to input * stream. */&lt;br /&gt;
							/*  If a shell gets it, it&#039;ll open a new shell. */&lt;br /&gt;
							ICommodities-&amp;gt;AddIEvents(ie);&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;unknown.\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				case CXM_COMMAND:&lt;br /&gt;
					printf(&amp;quot;A command: &amp;quot;);&lt;br /&gt;
					switch(msgid)&lt;br /&gt;
					{&lt;br /&gt;
						case CXCMD_DISABLE:&lt;br /&gt;
						printf(&amp;quot;CXCMD_DISABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 0L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_ENABLE:&lt;br /&gt;
							printf(&amp;quot;CXCMD_ENABLE\n&amp;quot;);&lt;br /&gt;
							ICommodities-&amp;gt;ActivateCxObj(broker, 1L);&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_KILL:&lt;br /&gt;
							printf(&amp;quot;CXCMD_KILL\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						case CXCMD_UNIQUE:&lt;br /&gt;
						/* Commodities Exchange can be told not only to refuse to launch a    */&lt;br /&gt;
						/* commodity with a name already in use but also can notify the       */&lt;br /&gt;
						/* already running commodity that it happened.  It does this by       */&lt;br /&gt;
						/* sending a CXM_COMMAND with the ID set to CXMCMD_UNIQUE. If the     */&lt;br /&gt;
						/* user tries to run a windowless commodity that is already running,  */&lt;br /&gt;
						/* the user wants the commodity to shut down.                         */&lt;br /&gt;
							printf(&amp;quot;CXCMD_UNIQUE\n&amp;quot;);&lt;br /&gt;
							returnvalue = 0L;&lt;br /&gt;
							break;&lt;br /&gt;
						default:&lt;br /&gt;
							printf(&amp;quot;Unknown msgid\n&amp;quot;);&lt;br /&gt;
							break;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
				default:&lt;br /&gt;
					printf(&amp;quot;Unknown msgtype\n&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if (sigrcvd &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
		{&lt;br /&gt;
			returnvalue = 0L;&lt;br /&gt;
			printf(&amp;quot;CTRL C signal break\n&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Commodities Exchange functions covered in this article. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CxBroker()&lt;br /&gt;
| Creates a CxObject of type Broker.&lt;br /&gt;
|-&lt;br /&gt;
| CxFilter()&lt;br /&gt;
| Creates a CxObject of type Filter.&lt;br /&gt;
|-&lt;br /&gt;
| CxSender()&lt;br /&gt;
| Creates a CxObject of type Sender.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Creates a CxObject of type Translate.&lt;br /&gt;
|-&lt;br /&gt;
| CxSignal()&lt;br /&gt;
| Creates a CxObject of type Signal.&lt;br /&gt;
|-&lt;br /&gt;
| CxCustom()&lt;br /&gt;
| Creates a CxObject of type Custom.&lt;br /&gt;
|-&lt;br /&gt;
| CxDebug()&lt;br /&gt;
| Creates a CxObject of type Debug.&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObj()&lt;br /&gt;
| Frees a single CxObject&lt;br /&gt;
|-&lt;br /&gt;
| DeleteCxObjAll()&lt;br /&gt;
| Frees a group of connected CxObjects&lt;br /&gt;
|-&lt;br /&gt;
| ActivateCxObj()&lt;br /&gt;
| Activates a newly created CxObject in the commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| CxTranslate()&lt;br /&gt;
| Sets up substitution of one input event for another by translate CxObjects.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgType()&lt;br /&gt;
| Finds the type of a CxMessage.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgData()&lt;br /&gt;
| Returns the CxMessage data.&lt;br /&gt;
|-&lt;br /&gt;
| CxMsgID()&lt;br /&gt;
| Returns the CxMessage ID.&lt;br /&gt;
|-&lt;br /&gt;
| CxObjError()&lt;br /&gt;
| Returns the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| ClearCxObjError()&lt;br /&gt;
| Clear the CxObject&#039;s accumulated error field.&lt;br /&gt;
|-&lt;br /&gt;
| AttachCxObj()&lt;br /&gt;
| Attaches a CxObject to the end of a given CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| InsertCxObj()&lt;br /&gt;
| Inserts a CxObject in a given position in a CxObject&#039;s list.&lt;br /&gt;
|-&lt;br /&gt;
| EnqueueCxObj()&lt;br /&gt;
| Inserts a CxObject in a CxObject&#039;s list by priority.&lt;br /&gt;
|-&lt;br /&gt;
| SetCxObjPri()&lt;br /&gt;
| Sets a CxObject&#039;s priority for EnqueueCxObj().&lt;br /&gt;
|-&lt;br /&gt;
| RemoveCxObj()&lt;br /&gt;
| Removes a CxObject from a list.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilter()&lt;br /&gt;
| Set a filter for a CxObject from an input description string.&lt;br /&gt;
|-&lt;br /&gt;
| SetFilterIX()&lt;br /&gt;
| Set a filter for a CxObject from an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| ParseIX()&lt;br /&gt;
| Convert an input description string to an IX data structure.&lt;br /&gt;
|-&lt;br /&gt;
| DivertCxMsg()&lt;br /&gt;
| Divert a CxMessage to one CxObject and return it to another.&lt;br /&gt;
|-&lt;br /&gt;
| RouteCxMsg()&lt;br /&gt;
| Redirect a CxMessage to a new CxObject.&lt;br /&gt;
|-&lt;br /&gt;
| DisposeCxMsg()&lt;br /&gt;
| Cancel a CxMessage removing it from the Commodities network.&lt;br /&gt;
|-&lt;br /&gt;
| InvertString()&lt;br /&gt;
| Creates a linked list of input events that correspond to a given string.&lt;br /&gt;
|-&lt;br /&gt;
| FreeIEvents()&lt;br /&gt;
| Frees the linked list of input events created with InvertString().&lt;br /&gt;
|-&lt;br /&gt;
| AddIEvents()&lt;br /&gt;
| Converts a list of input events to CxMessages and puts them into the network.&lt;br /&gt;
|-&lt;br /&gt;
| CopyBrokerList()&lt;br /&gt;
| Creates a local copy of the current broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| FreeBrokerList()&lt;br /&gt;
| Frees the local broker list (V53.4).&lt;br /&gt;
|-&lt;br /&gt;
| BrokerCommand()&lt;br /&gt;
| Sends a command to a commodity broker (V53.4).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Obsolete Functions ===&lt;br /&gt;
&lt;br /&gt;
The following functions are obsolete and no longer used:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayInit()&lt;br /&gt;
| Create a Tool Types array from argc and argv (Workbench or Shell).&lt;br /&gt;
|-&lt;br /&gt;
| ArgArrayDone()&lt;br /&gt;
| Free the resources used by ArgArrayInit().&lt;br /&gt;
|-&lt;br /&gt;
| ArgString()&lt;br /&gt;
| Return the string associated with a given Tool Type in the array.&lt;br /&gt;
|-&lt;br /&gt;
| ArgInt()&lt;br /&gt;
| Return the integer associated with a given Tool Type in the array.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Library&amp;diff=8119</id>
		<title>Graphics Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Library&amp;diff=8119"/>
		<updated>2015-09-27T20:35:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* RTG Support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Graphics Library (graphics.library) provides many services related to graphics on the Amiga including:&lt;br /&gt;
* [[Graphics_Primitives|Graphics Primitives]]&lt;br /&gt;
* [[Graphics_Sprites,_Bobs_and_Animation|Sprites, Bobs and Animation]]&lt;br /&gt;
* [[Graphics_Library_and_Text|Text Rendering]]&lt;br /&gt;
* [[Graphics_Regions|Region Clipping]]&lt;br /&gt;
* [[Graphics_Composited_Video|Composited Video]]&lt;br /&gt;
* [[Graphics_Video_Overlay|Video Overlay]]&lt;br /&gt;
&lt;br /&gt;
== RTG Support ==&lt;br /&gt;
&lt;br /&gt;
Starting with graphics.library V54, full Re-Targetable Graphics (RTG) support has been added to the graphics subsystem. In AmigaOS 4.0, programmers must juggle both the Picasso96 and graphics.library APIs simultaneously. The situation is worse in AmigaOS 3.x and earlier where different RTG implementations compete for market share.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Graphics_Library&amp;diff=8118</id>
		<title>Graphics Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Graphics_Library&amp;diff=8118"/>
		<updated>2015-09-27T20:34:02Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Graphics Library (graphics.library) provides many services related to graphics on the Amiga including:&lt;br /&gt;
* [[Graphics_Primitives|Graphics Primitives]]&lt;br /&gt;
* [[Graphics_Sprites,_Bobs_and_Animation|Sprites, Bobs and Animation]]&lt;br /&gt;
* [[Graphics_Library_and_Text|Text Rendering]]&lt;br /&gt;
* [[Graphics_Regions|Region Clipping]]&lt;br /&gt;
* [[Graphics_Composited_Video|Composited Video]]&lt;br /&gt;
* [[Graphics_Video_Overlay|Video Overlay]]&lt;br /&gt;
&lt;br /&gt;
== RTG Support ==&lt;br /&gt;
&lt;br /&gt;
Starting with graphics.library V54, full Re-Targetable Graphics (RTG) support has been added to the graphics subsystem. In AmigaOS 4.0, programmers must juggle both the Picasso 96 and graphics.library APIs simultaneously. The situation is worse in AmigaOS 3.x and earlier where different RTG implementations compete for market share.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8098</id>
		<title>UI Style Guide Keyboard</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8098"/>
		<updated>2015-09-25T18:07:57Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* Application Keyboard Shortcuts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:User Interface Style Guide]]&lt;br /&gt;
== The Keyboard ==&lt;br /&gt;
&lt;br /&gt;
Your application should make gadget and menu items selectable with the keyboard as well as with the mouse. Often these keyboard equivalents (&amp;quot;hotkeys&amp;quot;) will provide shortcuts and a smoother workflow that is appreciated by the user. Also, allowing the choice conforms to the Amiga credo: &amp;quot;Power to the User&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The set of conventions you should follow to implement this style rule are presented in this section.&lt;br /&gt;
&lt;br /&gt;
== Keyboard Conventions ==&lt;br /&gt;
&lt;br /&gt;
The arrangement of the Amiga&#039;s keyboard varies from model to model, but in general there are always three sets of keys:&lt;br /&gt;
* the standard keyboard&lt;br /&gt;
* special keys&lt;br /&gt;
* modifier keys&lt;br /&gt;
&lt;br /&gt;
=== The Standard Keyboard ===&lt;br /&gt;
&lt;br /&gt;
The standard keys consist of the familiar alphanumeric keys found on any standard typewriter. (In English, this is referred to as the QWERTY keyboard, but in German, since the standard keys are arranged differently, it&#039;s known as the QWERTZ keyboard. It&#039;s called other names in other languages.)&lt;br /&gt;
&lt;br /&gt;
The actual arrangement of the standard keys can vary. To handle this, AmigaOS uses a special facility known as a keymap that allows the user to change the way the keyboard input is mapped so it will correspond to his country&#039;s keyboard layout and characters. For example, to access all the standard German ASCII characters the user can type &amp;quot;Setmap d&amp;quot; in the Shell. Then German characters such as ü and ß will have the same key designations as they do on a standard German keyboard.&lt;br /&gt;
&lt;br /&gt;
Use the keymap facility to handle key assignments on the standard keyboard.&lt;br /&gt;
&lt;br /&gt;
=== Special Keys ===&lt;br /&gt;
&lt;br /&gt;
The special keys include the function keys on the top row of your computer&#039;s keyboard, the Help key, the cursor (arrow) keys, the Del key, the backspace key and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Modifier Keys ===&lt;br /&gt;
&lt;br /&gt;
The modifier keys are the Ctrl, Shift, Alt and Amiga keys found on either side of the space bar. Modifier keys don&#039;t do anything by themselves; they alter the meaning of a key that is pressed at the same time. They are also used to modify the meaning of a mouse selection. For example, holding down the Shift key while clicking with the mouse is used for multiple object selection.&lt;br /&gt;
&lt;br /&gt;
==== Dead Keys ====&lt;br /&gt;
&lt;br /&gt;
A &amp;quot;dead&amp;quot; key is a key, or combination, that does nothing immediately but modifies the output of the next key. For example, on an American keyboard, Alt-H will superimpose a caret (^) symbol over the next appropriate character.&lt;br /&gt;
&lt;br /&gt;
Although relatively unimportant on the American keyboard, dead keys are important in many languages. Keep in mind that you need to work these in manually if you&#039;re doing your own raw key mapping.&lt;br /&gt;
&lt;br /&gt;
== System Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
To provide mouse functions from the keyboard, only three features a needed: a way to press the left mouse button, a way to press the right mouse button, and a way to move the mouse pointer. These are provided by the system. They are listed here so you are aware of them and can avoid using these key combinations for any other purpose.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Keyboard Shortcut&lt;br /&gt;
! Equivalent Mouse Activity&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Left-Alt&lt;br /&gt;
| Same as clicking left mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Right-Alt&lt;br /&gt;
| Same as clicking right mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Cursor Key&lt;br /&gt;
| Moves mouse pointer&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Shift + Cursor Key&lt;br /&gt;
| Moves mouse pointer in larger steps&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Five additional Amiga system functions have keyboard shortcuts:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Default Keyboard Shortcut&lt;br /&gt;
! Equivalent System Function&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + N&lt;br /&gt;
| Workbench screen to front&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + M&lt;br /&gt;
| Front screen to back&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + B&lt;br /&gt;
| Requester OK (leftmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + V&lt;br /&gt;
| Requester Cancel (rightmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + selection button&lt;br /&gt;
| Drags screen whether the pointer is on the title bar or not&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The first four functions listed above always use Left-Amiga in combination with another key. The second key in the combination can be changed by the user with the IControl preferences editor in your AmigaOS Prefs drawer. For the screen dragging shortcut, the user can change the modifier or combination of modifiers (Ctrl, Amiga, Shift, Alt) that need to be combined with the selection button.&lt;br /&gt;
&lt;br /&gt;
===  The Left-Amiga Key ===&lt;br /&gt;
&lt;br /&gt;
Keep in mind that the Left-Amiga key is reserved at all times for system operations and should never be used as a qualifier for an application keyboard shortcut!&lt;br /&gt;
&lt;br /&gt;
== Application Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
Your application should provide a way for the user to bind an application function or ARexx macro to a key or combination of keys. Keyboard access to program features and common functions can speed up workflow significantly.&lt;br /&gt;
&lt;br /&gt;
On the other hand, it’s not necessary to provide hotkeys for every single function in your program: the user wouldn’t be able to remember them anyway. Plus, keyboard shortcuts work best if they are mnemonic and logical: S for Save, P for Print, etc. Therefore, save handy mnemonics for common and really important functions, don&#039;t waste them on secondary features.&lt;br /&gt;
&lt;br /&gt;
Prefer common character keys for shortcuts: if possible, avoid punctuation characters like brackets, quotation marks, the exclamation mark, the slash, the apostrophe, the tilde etc. While easily accessible with the English keymap, in certain languages the keymap will bind these characters with a qualifier key, making them more difficult to access. This is especially true of the [[#Menus|menu shortcuts]] where the Right Amiga key is also required: the user would then have to press two qualifiers plus the respective character key. If your application implements many functions and you need to resort to using punctuation-based shortcuts, make sure you assign them to less commonly used functions.&lt;br /&gt;
&lt;br /&gt;
If you provide a hotkey for an operation that can be dangerous (such as format disk or delete-type operations), make sure the user will get the opportunity to confirm or cancel the action should he/she make an inadvertent key press. The general rule of application design is: never put the user&#039;s data at stake!&lt;br /&gt;
&lt;br /&gt;
=== Gadgets ===&lt;br /&gt;
&lt;br /&gt;
Place an underscore under the letter in the gadget label that activates the gadget. (Note: although the letter is capitalized on the label, the&lt;br /&gt;
default action should react to the lower-case letter. Some Intuition gadgets have a different action for the shifted version of the letter. See the list in the table below.)&lt;br /&gt;
&lt;br /&gt;
The following three rules should apply:&lt;br /&gt;
&lt;br /&gt;
* All action should occur on the down press of the key.&lt;br /&gt;
* The same visual feedback should be given for keyboard activation as is given for mouse activation.&lt;br /&gt;
* Avoid assigning the Enter key to a gadget.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Feedback from Keystroke-Activated Gadgets&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Action button&lt;br /&gt;
| On the down press of the key, the gadget should appear to be pressed in. On release of the key, the gadget should come back out. (Note: at the time this manual was published, GadTools did not support this function.)&lt;br /&gt;
|-&lt;br /&gt;
| Check box&lt;br /&gt;
| Toggle the state of the check mark.&lt;br /&gt;
|-&lt;br /&gt;
| Scrolling list&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Radio button&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Cycle gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Selection gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Scroll gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Slider&lt;br /&gt;
| Unshifted would increase the level by one unit. Shifted would decrease the level by one unit.&lt;br /&gt;
|-&lt;br /&gt;
| Text box&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|-&lt;br /&gt;
| Numeric entry&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Menus ===&lt;br /&gt;
&lt;br /&gt;
Use a Right-Amiga combination as the default keyboard shortcut for a menu item. Here is a list of recommended hotkeys for common functions found in an application&#039;s menus:&lt;br /&gt;
&lt;br /&gt;
 Project Menu&lt;br /&gt;
  Right-Amiga + N  New&lt;br /&gt;
  Right-Amiga + O  Open...&lt;br /&gt;
  Right-Amiga + S  Save&lt;br /&gt;
  Right-Amiga + A  Save As...&lt;br /&gt;
  Right-Amiga + P  Print&lt;br /&gt;
  Right-Amiga + ?  About...&lt;br /&gt;
  Right-Amiga + Q  Quit Program&lt;br /&gt;
&lt;br /&gt;
 Edit Menu&lt;br /&gt;
  Right-Amiga + X  Cut&lt;br /&gt;
  Right-Amiga + C  Copy&lt;br /&gt;
  Right-Amiga + V  Paste&lt;br /&gt;
  Right-Amiga + Z  Undo&lt;br /&gt;
&lt;br /&gt;
Some of these (namely: N for New, O for Open, S for Save, P for Print, and the four Edit Menu shortcuts) are de facto cross-platform standards, and should therefore be respected as such. On the other hand, the choice of suitable keys is limited so a particular “standard” shortcut may certainly be reused if the application does not implement the respective function. For example, an application that does not print or cannot create new projects (such as a music player) can use the Right-Amiga+N and Right-Amiga+P combination for something else.&lt;br /&gt;
&lt;br /&gt;
The original User Interface Style Guide suggested in this section that menu shortcuts should be [[Locale_Library|localized]] to reflect the user&#039;s preferred language as set in the system. This recommendation has turned out to be rather short-sighted and impractical: certain hotkeys like the ones listed above are so common, universal and &amp;quot;dyed in the wool&amp;quot; that changing them based on the currently selected locale would only be confusing. Also remember that unlike in gadgets, where the user can see the actual shortcut (it is underscored in the gadget label), you get no direct visual cue when using menu hotkeys because they are hidden in the menu. Thus, a menu hotkey could potentially be dangerous if the user pressed a known key combination that has suddenly changed meaning due to locale change.&lt;br /&gt;
&lt;br /&gt;
=== Requesters ===&lt;br /&gt;
&lt;br /&gt;
Hotkeys for requester buttons should also be provided. Requesters require user interaction: they &amp;quot;get in the way&amp;quot; and can slow down workflow. The user would therefore appreciate requester buttons having keyboard shortcuts (unless there’s a good reason not to provide them, such as safety of operation).&lt;br /&gt;
&lt;br /&gt;
== Use of the Special Keys ==&lt;br /&gt;
&lt;br /&gt;
As stated previously, the special keys are the function keys, the Del key, the Help key, the cursor (arrow) keys and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Cursor Keys ===&lt;br /&gt;
&lt;br /&gt;
Cursor keys are a convenient way to control the movement of the cursor inside an application. Here are some standard ways to use them:&lt;br /&gt;
&lt;br /&gt;
==== Unmodified Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move a small amount in the specified direction. Often this is one unit such as a character in a word processor or a pixel in a paint package, but it could be several pixels in an application where navigation is more important than fine control.&lt;br /&gt;
&lt;br /&gt;
==== Shift + Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the window, or shift the view by one window full if you&#039;re already at that extreme.&lt;br /&gt;
&lt;br /&gt;
In applications such as a word processor where the two directions are not symmetrical, shifted cursor keys could take on different meanings. Shifted up and down cursors would page through windowfuls while the shifted left and right cursors would show more on the left and right if there is more to show. Of course it is often the case that the width of the document fits in the window, so the shifted left and right cursors could act as beginning-and end-of-line commands (or edge of window if the cursor isn&#039;t constrained to the form of the text).&lt;br /&gt;
&lt;br /&gt;
==== Alt-Cursor ====&lt;br /&gt;
&lt;br /&gt;
This is used for application-specific functions. It&#039;s usually semantic units such as words in a text processor or fields in a spreadsheet.&lt;br /&gt;
&lt;br /&gt;
==== Ctrl-Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the project (beginning, end, extreme left, extreme right).&lt;br /&gt;
&lt;br /&gt;
In the word processor example, the up and down cursor keys would take the cursor to the beginning and end of the file, respectively. But again, if the file fits within the width of the window, the left and right cursor keys combined with Alt would act like their shifted cousins.&lt;br /&gt;
&lt;br /&gt;
==== Function Keys ====&lt;br /&gt;
&lt;br /&gt;
Function keys should generally be reserved for the user to define. If your application does make use of them, then it should at least allow the user to redefine or modify them.&lt;br /&gt;
&lt;br /&gt;
==== Help Key ====&lt;br /&gt;
&lt;br /&gt;
If your application has built-in help, use the Help key to trigger it from the keyboard. Avoid giving the user that helpless feeling he gets when he presses the Help key and nothing happens.&lt;br /&gt;
&lt;br /&gt;
Note that PC keyboards connected to an AmigaOS-compatible computer will not feature the Help key. On such keyboards the Help key will be mapped to the ScrollLock key.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8079</id>
		<title>UI Style Guide Keyboard</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8079"/>
		<updated>2015-09-20T08:59:18Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added a note on (re)using the &amp;quot;standard&amp;quot; menu shortcuts.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:User Interface Style Guide]]&lt;br /&gt;
== The Keyboard ==&lt;br /&gt;
&lt;br /&gt;
Your application should make gadget and menu items selectable with the keyboard as well as with the mouse. Often these keyboard equivalents (&amp;quot;hotkeys&amp;quot;) will provide shortcuts and a smoother workflow that is appreciated by the user. Also, allowing the choice conforms to the Amiga credo: &amp;quot;Power to the User&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The set of conventions you should follow to implement this style rule are presented in this section.&lt;br /&gt;
&lt;br /&gt;
== Keyboard Conventions ==&lt;br /&gt;
&lt;br /&gt;
The arrangement of the Amiga&#039;s keyboard varies from model to model, but in general there are always three sets of keys:&lt;br /&gt;
* the standard keyboard&lt;br /&gt;
* special keys&lt;br /&gt;
* modifier keys&lt;br /&gt;
&lt;br /&gt;
=== The Standard Keyboard ===&lt;br /&gt;
&lt;br /&gt;
The standard keys consist of the familiar alphanumeric keys found on any standard typewriter. (In English, this is referred to as the QWERTY keyboard, but in German, since the standard keys are arranged differently, it&#039;s known as the QWERTZ keyboard. It&#039;s called other names in other languages.)&lt;br /&gt;
&lt;br /&gt;
The actual arrangement of the standard keys can vary. To handle this, AmigaOS uses a special facility known as a keymap that allows the user to change the way the keyboard input is mapped so it will correspond to his country&#039;s keyboard layout and characters. For example, to access all the standard German ASCII characters the user can type &amp;quot;Setmap d&amp;quot; in the Shell. Then German characters such as ü and ß will have the same key designations as they do on a standard German keyboard.&lt;br /&gt;
&lt;br /&gt;
Use the keymap facility to handle key assignments on the standard keyboard.&lt;br /&gt;
&lt;br /&gt;
=== Special Keys ===&lt;br /&gt;
&lt;br /&gt;
The special keys include the function keys on the top row of your computer&#039;s keyboard, the Help key, the cursor (arrow) keys, the Del key, the backspace key and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Modifier Keys ===&lt;br /&gt;
&lt;br /&gt;
The modifier keys are the Ctrl, Shift, Alt and Amiga keys found on either side of the space bar. Modifier keys don&#039;t do anything by themselves; they alter the meaning of a key that is pressed at the same time. They are also used to modify the meaning of a mouse selection. For example, holding down the Shift key while clicking with the mouse is used for multiple object selection.&lt;br /&gt;
&lt;br /&gt;
==== Dead Keys ====&lt;br /&gt;
&lt;br /&gt;
A &amp;quot;dead&amp;quot; key is a key, or combination, that does nothing immediately but modifies the output of the next key. For example, on an American keyboard, Alt-H will superimpose a caret (^) symbol over the next appropriate character.&lt;br /&gt;
&lt;br /&gt;
Although relatively unimportant on the American keyboard, dead keys are important in many languages. Keep in mind that you need to work these in manually if you&#039;re doing your own raw key mapping.&lt;br /&gt;
&lt;br /&gt;
== System Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
To provide mouse functions from the keyboard, only three features a needed: a way to press the left mouse button, a way to press the right mouse button, and a way to move the mouse pointer. These are provided by the system. They are listed here so you are aware of them and can avoid using these key combinations for any other purpose.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Keyboard Shortcut&lt;br /&gt;
! Equivalent Mouse Activity&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Left-Alt&lt;br /&gt;
| Same as clicking left mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Right-Alt&lt;br /&gt;
| Same as clicking right mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Cursor Key&lt;br /&gt;
| Moves mouse pointer&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Shift + Cursor Key&lt;br /&gt;
| Moves mouse pointer in larger steps&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Five additional Amiga system functions have keyboard shortcuts:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Default Keyboard Shortcut&lt;br /&gt;
! Equivalent System Function&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + N&lt;br /&gt;
| Workbench screen to front&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + M&lt;br /&gt;
| Front screen to back&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + B&lt;br /&gt;
| Requester OK (leftmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + V&lt;br /&gt;
| Requester Cancel (rightmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + selection button&lt;br /&gt;
| Drags screen whether the pointer is on the title bar or not&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The first four functions listed above always use Left-Amiga in combination with another key. The second key in the combination can be changed by the user with the IControl preferences editor in your AmigaOS Prefs drawer. For the screen dragging shortcut, the user can change the modifier or combination of modifiers (Ctrl, Amiga, Shift, Alt) that need to be combined with the selection button.&lt;br /&gt;
&lt;br /&gt;
===  The Left-Amiga Key ===&lt;br /&gt;
&lt;br /&gt;
Keep in mind that the Left-Amiga key is reserved at all times for system operations and should never be used as a qualifier for an application keyboard shortcut!&lt;br /&gt;
&lt;br /&gt;
== Application Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
Your application should provide a way for the user to bind an application function or ARexx macro to a key or combination of keys. Keyboard access to program features and common functions can speed up workflow significantly.&lt;br /&gt;
&lt;br /&gt;
On the other hand, it’s not necessary to provide hotkeys for every single function in your program: the user wouldn’t be able to remember them anyway. Plus, keyboard shortcuts work best if they are mnemonic and logical: S for Save, P for Print, etc. Therefore, save handy mnemonics for common and really important functions, don&#039;t waste them on secondary features.&lt;br /&gt;
&lt;br /&gt;
If you provide a hotkey for an operation that can be dangerous (such as format disk or delete-type operations), make sure the user will get the opportunity to confirm or cancel the action should he/she make an inadvertent key press. The general rule of application design is: never put the user&#039;s data at stake!&lt;br /&gt;
&lt;br /&gt;
=== Gadgets ===&lt;br /&gt;
&lt;br /&gt;
Place an underscore under the letter in the gadget label that activates the gadget. (Note: although the letter is capitalized on the label, the&lt;br /&gt;
default action should react to the lower-case letter. Some Intuition gadgets have a different action for the shifted version of the letter. See the list in the table below.)&lt;br /&gt;
&lt;br /&gt;
The following three rules should apply:&lt;br /&gt;
&lt;br /&gt;
* All action should occur on the down press of the key.&lt;br /&gt;
* The same visual feedback should be given for keyboard activation as is given for mouse activation.&lt;br /&gt;
* Avoid assigning the Enter key to a gadget.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Feedback from Keystroke-Activated Gadgets&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Action button&lt;br /&gt;
| On the down press of the key, the gadget should appear to be pressed in. On release of the key, the gadget should come back out. (Note: at the time this manual was published, GadTools did not support this function.)&lt;br /&gt;
|-&lt;br /&gt;
| Check box&lt;br /&gt;
| Toggle the state of the check mark.&lt;br /&gt;
|-&lt;br /&gt;
| Scrolling list&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Radio button&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Cycle gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Selection gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Scroll gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Slider&lt;br /&gt;
| Unshifted would increase the level by one unit. Shifted would decrease the level by one unit.&lt;br /&gt;
|-&lt;br /&gt;
| Text box&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|-&lt;br /&gt;
| Numeric entry&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Menus ===&lt;br /&gt;
&lt;br /&gt;
Use a Right-Amiga combination as the default keyboard shortcut for a menu item. Here is a list of recommended hotkeys for common functions found in an application&#039;s menus:&lt;br /&gt;
&lt;br /&gt;
 Project Menu&lt;br /&gt;
  Right-Amiga + N  New&lt;br /&gt;
  Right-Amiga + O  Open...&lt;br /&gt;
  Right-Amiga + S  Save&lt;br /&gt;
  Right-Amiga + A  Save As...&lt;br /&gt;
  Right-Amiga + P  Print&lt;br /&gt;
  Right-Amiga + ?  About...&lt;br /&gt;
  Right-Amiga + Q  Quit Program&lt;br /&gt;
&lt;br /&gt;
 Edit Menu&lt;br /&gt;
  Right-Amiga + X  Cut&lt;br /&gt;
  Right-Amiga + C  Copy&lt;br /&gt;
  Right-Amiga + V  Paste&lt;br /&gt;
  Right-Amiga + Z  Undo&lt;br /&gt;
&lt;br /&gt;
Some of these (namely: N for New, O for Open, S for Save, P for Print, and the four Edit Menu shortcuts) are de facto cross-platform standards, and should therefore be respected as such. On the other hand, the choice of suitable keys is limited so a particular “standard” shortcut may certainly be reused if the application does not implement the respective function. For example, an application that does not print or cannot create new projects (such as a music player) can use the Right-Amiga+N and Right-Amiga+P combination for something else.&lt;br /&gt;
&lt;br /&gt;
The original User Interface Style Guide suggested in this section that menu shortcuts should be [[Locale_Library|localized]] to reflect the user&#039;s preferred language as set in the system. This recommendation has turned out to be rather short-sighted and impractical: certain hotkeys like the ones listed above are so common, universal and &amp;quot;dyed in the wool&amp;quot; that changing them based on the currently selected locale would only be confusing. Also remember that unlike in gadgets, where the user can see the actual shortcut (it is underscored in the gadget label), you get no direct visual cue when using menu hotkeys because they are hidden in the menu. Thus, a menu hotkey could potentially be dangerous if the user pressed a known key combination that has suddenly changed meaning due to locale change.&lt;br /&gt;
&lt;br /&gt;
=== Requesters ===&lt;br /&gt;
&lt;br /&gt;
Hotkeys for requester buttons should also be provided. Requesters require user interaction: they &amp;quot;get in the way&amp;quot; and can slow down workflow. The user would therefore appreciate requester buttons having keyboard shortcuts (unless there’s a good reason not to provide them, such as safety of operation).&lt;br /&gt;
&lt;br /&gt;
== Use of the Special Keys ==&lt;br /&gt;
&lt;br /&gt;
As stated previously, the special keys are the function keys, the Del key, the Help key, the cursor (arrow) keys and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Cursor Keys ===&lt;br /&gt;
&lt;br /&gt;
Cursor keys are a convenient way to control the movement of the cursor inside an application. Here are some standard ways to use them:&lt;br /&gt;
&lt;br /&gt;
==== Unmodified Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move a small amount in the specified direction. Often this is one unit such as a character in a word processor or a pixel in a paint package, but it could be several pixels in an application where navigation is more important than fine control.&lt;br /&gt;
&lt;br /&gt;
==== Shift + Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the window, or shift the view by one window full if you&#039;re already at that extreme.&lt;br /&gt;
&lt;br /&gt;
In applications such as a word processor where the two directions are not symmetrical, shifted cursor keys could take on different meanings. Shifted up and down cursors would page through windowfuls while the shifted left and right cursors would show more on the left and right if there is more to show. Of course it is often the case that the width of the document fits in the window, so the shifted left and right cursors could act as beginning-and end-of-line commands (or edge of window if the cursor isn&#039;t constrained to the form of the text).&lt;br /&gt;
&lt;br /&gt;
==== Alt-Cursor ====&lt;br /&gt;
&lt;br /&gt;
This is used for application-specific functions. It&#039;s usually semantic units such as words in a text processor or fields in a spreadsheet.&lt;br /&gt;
&lt;br /&gt;
==== Ctrl-Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the project (beginning, end, extreme left, extreme right).&lt;br /&gt;
&lt;br /&gt;
In the word processor example, the up and down cursor keys would take the cursor to the beginning and end of the file, respectively. But again, if the file fits within the width of the window, the left and right cursor keys combined with Alt would act like their shifted cousins.&lt;br /&gt;
&lt;br /&gt;
==== Function Keys ====&lt;br /&gt;
&lt;br /&gt;
Function keys should generally be reserved for the user to define. If your application does make use of them, then it should at least allow the user to redefine or modify them.&lt;br /&gt;
&lt;br /&gt;
==== Help Key ====&lt;br /&gt;
&lt;br /&gt;
If your application has built-in help, use the Help key to trigger it from the keyboard. Avoid giving the user that helpless feeling he gets when he presses the Help key and nothing happens.&lt;br /&gt;
&lt;br /&gt;
Note that PC keyboards connected to an AmigaOS-compatible computer will not feature the Help key. On such keyboards the Help key will be mapped to the ScrollLock key.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=PrefsObjects&amp;diff=8076</id>
		<title>PrefsObjects</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=PrefsObjects&amp;diff=8076"/>
		<updated>2015-09-18T13:04:15Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: /* The Interface and its Functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Many applications are user-configurable so they need a way to store their settings; we traditionally use the term &#039;&#039;preferences&#039;&#039; or simply &#039;&#039;prefs&#039;&#039; in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows &#039;&#039;.ini&#039;&#039; format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (some [[UI_Style_Guide_Preferences#Preferences|system preferences]] are stored as IFF-PREF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:&lt;br /&gt;
&lt;br /&gt;
# All of the solutions above require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).&lt;br /&gt;
# None of the various formats used have ever provided a smart enough way to administer complex, structured settings.&lt;br /&gt;
&lt;br /&gt;
Since its introduction in AmigaOS, PrefsObjects has largely been promoted as being &#039;&#039;XML-based&#039;&#039; and therefore human-readable and easy to edit. Yet using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is &#039;&#039;object-oriented&#039;&#039;. Among other things, this property also helps address the two drawbacks mentioned above.&lt;br /&gt;
&lt;br /&gt;
Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, as some plug-ins may be delivered by third parties, the particular structure of the prefs file is somewhat beyond the control of the main program: the amount, contents and sequencing of data in the file depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.&lt;br /&gt;
&lt;br /&gt;
== The Interface and its Functions ==&lt;br /&gt;
&lt;br /&gt;
The Application Library contains two interfaces, named “application” and “prefsobjects”. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions and methods are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.&lt;br /&gt;
&lt;br /&gt;
Starting with AmigaOS SDK 53.24, both interfaces (“application” and “prefsobjects”) must be at version 2. See the [[Application_Library#Library Opening Chores|Library Opening Chores]] section of the Application Library documentation for more information.&lt;br /&gt;
&lt;br /&gt;
All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated, sparing the programmer from unnecessary hassle.&lt;br /&gt;
&lt;br /&gt;
== Object Types ==&lt;br /&gt;
&lt;br /&gt;
PrefsObjects tackles the task by seeing data as &#039;&#039;objects&#039;&#039;, and by providing functions and methods for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function – “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.&lt;br /&gt;
&lt;br /&gt;
PrefsObjects distinguishes between six object types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Object Type&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| Dictionary || A container to encapsulate (embed) other objects. The prefs file usually has a Dictionary object at the top level.&lt;br /&gt;
|-&lt;br /&gt;
| Array || A container to encapsulate other objects. The difference between a Dictionary and an Array is in object referencing: in Arrays encapsulated objects are referenced by an index, while in a Dictionary they are referenced by a name (key string).&lt;br /&gt;
|-&lt;br /&gt;
| Number || An object to carry a long integer, double, or a boolean value.&lt;br /&gt;
|-&lt;br /&gt;
| String || An object to carry a text string.&lt;br /&gt;
|-&lt;br /&gt;
| Date || An object to carry date and time information.&lt;br /&gt;
|-&lt;br /&gt;
| Binary || An object to carry arbitrary binary data. However, binary data should be avoided as much as possible because it&#039;s restrictive in its form and is not future-proof, as it does not enable addition or removal of data without compromising compatibility.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== The Preferences File ==&lt;br /&gt;
&lt;br /&gt;
The files generated by the Application Library&#039;s PrefsObjects system are standard XML documents. They are encoded in UTF-8 (which AmigaOS doesn&#039;t support directly) but that doesn&#039;t necessarily pose a problem. First, you rarely need to edit prefs files manually – nor are you encouraged to do so, unless the file has become corrupted or contains a setting that prevents the application from starting up or running properly. Second, characters beyond the ASCII range are rarely used in preferences, so the prefs files will likely be readable and editable even in a text editor that has no notion of UTF-8. And third, you can always use the dedicated [[PrefsObjectsEditor]], located in the Utilities drawer on your system disk, which allows easy and safe editing of PrefsObjects files (the usage of this tool is, however, out of the scope of this page).&lt;br /&gt;
&lt;br /&gt;
As already mentioned above, all data access is handled by the Application Library. On the part of the programmer (or the application user), no actual knowledge of XML is required.&lt;br /&gt;
&lt;br /&gt;
=== File Structure ===&lt;br /&gt;
&lt;br /&gt;
The preferences file normally starts with a Dictionary object at the top level. The advantage of using a Dictionary is that objects stored in it are identified by a name (called a “key”). Using named objects is very practical. Unlike in Arrays, where objects are indexed, the order of data is quite irrelevant as long as you deal with objects on the same level. Your application can retrieve, add, modify, re-sequence or remove individual objects in an arbitrary order without having to worry about breaking anything: a named object will always be addressed properly if it exists.&lt;br /&gt;
&lt;br /&gt;
Object names (keys) must be unique at each particular level. In other words, you cannot have two settings items (objects) with the same key in the same dictionary. However, identical object names can be used in different dictionaries. The following example shows a prefs file structure that embeds two separate dictionaries named “Screen settings” and “Window settings”. As you can see, identical keys (“Name”, “Width” and “Height”) are then used without collision:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;classdiagram type=&amp;quot;orderly&amp;quot; scale=&amp;quot;80&amp;quot; dir=&amp;quot;prf&amp;quot;&amp;gt;&lt;br /&gt;
[“Root”\n(Dictionary)]-&amp;gt;[“Screen settings”\n(Dictionary)]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Height”\n(Number) ]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Width”\n(Number) ]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Name”\n(String) ]&lt;br /&gt;
[“Root”\n(Dictionary)]-&amp;gt;[“Window settings”\n(Dictionary)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Height”\n(Number)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Width”\n(Number)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Name”\n(String)]&lt;br /&gt;
&amp;lt;/classdiagram&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The diagram also illustrates the hierarchical nature of the PrefsObjects system: the fact that objects can be embedded inside other objects.&lt;br /&gt;
&lt;br /&gt;
== Reading Preferences ==&lt;br /&gt;
&lt;br /&gt;
In most cases, there will be just one preferences file associated with your application. If all you need is simply read settings from the file (and save them upon user request), it is recommended that you let the Application Library handle the file access. Registering your application like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_LoadPrefs, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will tell the library to access the prefs file automatically, under its default name and from the default location. The default naming scheme for prefs files uses the application name combined with the [[Application_Library#Application Identifiers|URL identifier]]. Also, the &#039;&#039;.xml&#039;&#039; extension is appended at the end of the name to identify the document format. So with this particular registration call, the default file name will be “AudioMonster.supercoders.com.xml” and the library will try to access it from the ENV: directory. (Don&#039;t worry, both the file name and location can be changed.)&lt;br /&gt;
&lt;br /&gt;
If the file is found, the library will internally allocate a Dictionary object and read the settings into it. If the file is not found, the library will allocate an empty Dictionary for you. In either case the said Dictionary object will become the application&#039;s &#039;&#039;Main Prefs Dictionary&#039;&#039; and will be associated with the application as one of its attributes. (The application may use multiple prefs Dictionaries but at any given point in time, only one of them is main.)&lt;br /&gt;
&lt;br /&gt;
After start-up and registration, the application will typically want to access the settings in order to configure itself. Having used &#039;&#039;REGAPP_LoadPrefs, TRUE&#039;&#039; in the registration call above, we should now have the application&#039;s Main Prefs Dictionary somewhere in memory, to which we need to obtain a pointer. This is done through the function GetApplicationAttrs() because as explained above, the Main Prefs Dictionary is an application attribute. After that we can use dedicated functions to access the individual objects (i.e. settings items) in the Dictionary.&lt;br /&gt;
&lt;br /&gt;
The code fragment below assumes that our prefs file – already loaded by the registration routine and transformed into an object – contains three settings items:&lt;br /&gt;
&lt;br /&gt;
* a boolean value stored in the file under the key of “Switch”;&lt;br /&gt;
* a numeric value of type uint32 stored under the key of “Number”;&lt;br /&gt;
* a text string stored under the key of “String”.&lt;br /&gt;
&lt;br /&gt;
We first obtain the pointer to the prefs Dictionary object, retrieve the three values, and store them in a dedicated data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Default settings */&lt;br /&gt;
#define DEF_SWITCH   TRUE&lt;br /&gt;
#define DEF_NUMBER   100&lt;br /&gt;
#define DEF_STRING   &amp;quot;Play it again, Sam!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
struct Settings&lt;br /&gt;
{&lt;br /&gt;
  BOOL   switch;&lt;br /&gt;
  uint32 number;&lt;br /&gt;
  STRPTR string;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
PrefsObject    *myPrefsDict = NULL;          /* Main Prefs Dictionary          */&lt;br /&gt;
struct Settings mySettings;                  /* Structure to hold the settings */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Obtain the Dictionary object pointer */&lt;br /&gt;
IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_MainPrefsDict, &amp;amp;myPrefsDict, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if ( myPrefsDict )&lt;br /&gt;
 {&lt;br /&gt;
&lt;br /&gt;
   /*   Retrieve the settings values.&lt;br /&gt;
&lt;br /&gt;
        Note: here we need to cast the type of the number and of the string value&lt;br /&gt;
        to avoid compiler warnings. This is because DictGetIntegerForKey()&lt;br /&gt;
        and DictGetStringForKey() return int32 and CONST_STRPTR, respectively,&lt;br /&gt;
        whereas our program declares them as uint32 and STRPTR.&lt;br /&gt;
    */&lt;br /&gt;
   mySettings.switch = IPrefsObjects-&amp;gt;DictGetBoolForKey(myPrefsDict, &amp;quot;Switch&amp;quot;, DEF_SWITCH);&lt;br /&gt;
   mySettings.number = (uint32) IPrefsObjects-&amp;gt;DictGetIntegerForKey(myPrefsDict, &amp;quot;Number&amp;quot;, DEF_NUMBER);&lt;br /&gt;
   mySettings.string = (STRPTR) IPrefsObjects-&amp;gt;DictGetStringForKey(myPrefsDict, &amp;quot;String&amp;quot;, DEF_STRING);&lt;br /&gt;
   &lt;br /&gt;
   /* Test out the string value. */&lt;br /&gt;
   IDOS-&amp;gt;Printf(&amp;quot;All I want to say is: %s\n&amp;quot;, mySettings.string);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should any of the settings items be missing, the respective default value is used. This will be the case, for example, when a physical XML prefs file is not present and the PrefsObjects system supplies an empty Dictionary.&lt;br /&gt;
&lt;br /&gt;
(to be continued)&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the PrefsObjects-related functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| BeginDeserialization()&lt;br /&gt;
| Begins the deserialization of a prefs object.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetBoolForKey()&lt;br /&gt;
| Obtains a boolean value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetIntegerForKey()&lt;br /&gt;
| Obtains an integer value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetObjectForKey()&lt;br /&gt;
| Obtains an object for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetOptionForKey()&lt;br /&gt;
| Obtains a string from an option table.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetStringForKey()&lt;br /&gt;
| Obtains a string value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictSetObjectForKey()&lt;br /&gt;
| Sets an object for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsArray()&lt;br /&gt;
| PrefsObjects array object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsBaseObject()&lt;br /&gt;
| PrefsObjects base object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsBinary()&lt;br /&gt;
| PrefsObjects binary object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsDate()&lt;br /&gt;
| PrefsObjects date object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsDictionary()&lt;br /&gt;
| PrefsObjects dictionary object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsNumber()&lt;br /&gt;
| PrefsObjects number object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsString()&lt;br /&gt;
| PrefsObjects string object access function.&lt;br /&gt;
|-&lt;br /&gt;
| ReadPrefs()&lt;br /&gt;
| Reads a prefs dictionary from a file.&lt;br /&gt;
|-&lt;br /&gt;
| WritePrefs()&lt;br /&gt;
| Writes a prefs dictionary to a file.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=8075</id>
		<title>Application Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Application_Library&amp;diff=8075"/>
		<updated>2015-09-18T12:37:26Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Moved the PrefsObjects section into a new page.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
The Application Library is a multipurpose auxiliary library that provides various functions related to the development and use of applications. The very concept of &#039;&#039;application&#039;&#039; 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 &#039;&#039;background services&#039;&#039;), and genuine full-blown &#039;&#039;applications&#039;&#039; with [[UI_Style_Guide_Glossary#GUI|GUI]] and all.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can turn into a “watchdog” and get notified when other applications register.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* It can easily create and manage lists of recently-used documents.&lt;br /&gt;
&lt;br /&gt;
* It can register as a &#039;&#039;unique application&#039;&#039;, preventing other instances of itself from running.&lt;br /&gt;
&lt;br /&gt;
* It can show its icon or display the current program state in taskbar-like applications, such as AmiDock.&lt;br /&gt;
&lt;br /&gt;
* 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”.&lt;br /&gt;
&lt;br /&gt;
=== Frequently Asked Questions ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Should my programs register with the Application Library?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: After registering with the library, will my application become controlled by the OS or by other applications?&#039;&#039;&lt;br /&gt;
|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&#039;t want your application to be controlled beyond a certain limit, your code will simply not react to certain incoming [[#Control Messages|control messages]].&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Doesn&#039;t the Application Library duplicate functionality already present in commodities?&#039;&#039;&lt;br /&gt;
|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]]&#039;s event stream. The Application Library&#039;s field and scope of operation is completely different (and much wider).&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Why is this a library? Wouldn&#039;t it be wiser to implement it as a class, like MUI does it?&#039;&#039;&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Can a program be registered both as an application and as a commodity?&#039;&#039;&lt;br /&gt;
|A: Yes, that&#039;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.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Q: Being XML-based, do the PrefsObjects introduce any overhead to the operating system?&#039;&#039;&lt;br /&gt;
|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&#039;t put any strain on the OS either, as the Application Library caches the file in a pre-parsed format in memory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Minimum Application Library Support ===&lt;br /&gt;
&lt;br /&gt;
In the foreseeable future, AmigaOS will feature an application manager to control running applications. The idea is to extend or possibly even replace Exchange to offer 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:&lt;br /&gt;
&lt;br /&gt;
* Provide a short [[#Description|description]] for a better identification of the application.&lt;br /&gt;
* Tell the OS whether the application supports iconification, that is, hiding/showing its GUI (for more information, see [[#Feature Set / Control Scope|this section]]). If iconification is supported:&lt;br /&gt;
** make the application respond to the [[#Control Messages|APPLIBMT_Hide and APPLIBMT_Unhide]] control messages;&lt;br /&gt;
** update the APPATTR_Hidden attribute accordingly each time your application iconifies or uniconifies.&lt;br /&gt;
* Allow the application to be shut down externally in a safe and graceful manner in reaction to the [[#Control Messages|APPLIBMT_Quit]] message.&lt;br /&gt;
&lt;br /&gt;
A failure to implement this suggested minimum will mean that the OS application manager will be unable to control your application properly and consistently. This may cause confusion on the part of the users and degrade their AmigaOS experience.&lt;br /&gt;
&lt;br /&gt;
== Library Opening Chores ==&lt;br /&gt;
&lt;br /&gt;
{{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.}}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Library *ApplicationBase = NULL;&lt;br /&gt;
struct ApplicationIFace *IApplication = NULL;&lt;br /&gt;
struct PrefsObjectsIFace *IPrefsObjects = NULL;&lt;br /&gt;
&lt;br /&gt;
if ( (ApplicationBase = IExec-&amp;gt;OpenLibrary(&amp;quot;application.library&amp;quot;, 52)) )&lt;br /&gt;
{&lt;br /&gt;
   IApplication  = (struct ApplicationIFace *)  IExec-&amp;gt;GetInterface(ApplicationBase, &lt;br /&gt;
                                                                    &amp;quot;application&amp;quot;, 2, NULL);&lt;br /&gt;
   IPrefsObjects = (struct PrefsObjectsIFace *) IExec-&amp;gt;GetInterface(ApplicationBase,&lt;br /&gt;
                                                                    &amp;quot;prefsobjects&amp;quot;, 2, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if ( !ApplicationBase || !IApplication || !IPrefsObjects )&lt;br /&gt;
{&lt;br /&gt;
   /* handle library opening error */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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. &#039;&#039;&#039;Starting with AmigaOS SDK 53.24, both Application Library interfaces (application and prefsobjects) must be at version 2.&#039;&#039;&#039; This is due to certain changes in the Application Library API.&lt;br /&gt;
&lt;br /&gt;
When your application has run its course, don’t forget to clean up and close both the library and its interface(s):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IPrefsObjects);&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IApplication);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(ApplicationBase);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registering the Application ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
*hint* Avoid using the  _ underscore character in the registered name. These may cause &amp;quot;charsetconvert&amp;quot; errors when the system boots up later.&lt;br /&gt;
&lt;br /&gt;
=== Application Identifiers ===&lt;br /&gt;
&lt;br /&gt;
A successfully registered application receives a numeric identifier, which in this documentation will be referred to as &#039;&#039;appID&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
* the application name;&lt;br /&gt;
* an instance count (if more than one instance of the same application is started);&lt;br /&gt;
* a URL identifier (for example, the domain name of the application’s homepage – optional).&lt;br /&gt;
&lt;br /&gt;
The same naming scheme is used by the library’s PrefsObjects system to create the name of the preferences file.&lt;br /&gt;
&lt;br /&gt;
=== Registration Functions ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (!appID)&lt;br /&gt;
{&lt;br /&gt;
   /* report registration error and quit */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    do whatever your program does&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
IApplication-&amp;gt;UnregisterApplication(appID, NULL);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.)&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
After calling UnregisterApplication() the program will be removed from the public list, and Application Library features will no longer be available.&lt;br /&gt;
&lt;br /&gt;
== Application Attributes ==&lt;br /&gt;
&lt;br /&gt;
An application is described by a set of &#039;&#039;attributes&#039;&#039;. There are attributes that describe the application&#039;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 &amp;quot;static&amp;quot; application features in one place.&lt;br /&gt;
&lt;br /&gt;
=== Description ===&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The application description cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Uniqueness ===&lt;br /&gt;
A program can register as a &#039;&#039;unique application&#039;&#039;, 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.&lt;br /&gt;
&lt;br /&gt;
The following function call will register Audio Monster as a unique application:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_UniqueApplication, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Quite logically, uniqueness is an attribute that cannot be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Feature Set / Control Scope ===&lt;br /&gt;
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):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasIconifyFeature&lt;br /&gt;
|The application supports iconification and uniconification.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_Hide and APPLIBMT_Unhide messages.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_HasPrefsWindow&lt;br /&gt;
|The application has a dedicated Preferences (Settings) window.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_OpenPrefs message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanCreateNewDocs&lt;br /&gt;
|The application can create new documents (projects).&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_NewBlankDoc message.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_CanPrintDocs&lt;br /&gt;
|The application can print documents.&lt;br /&gt;
|Set to TRUE if your application responds to the APPLIBMT_PrintDoc message.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These four attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Receiving Notifications ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_AppNotifications&lt;br /&gt;
|Receive notifications about application registration/unregistration, icon type changes, GUI state changes (hidden/unhidden), and changes in the last used applications/documents list.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to application managers and taskbar-like programs.&lt;br /&gt;
|-&lt;br /&gt;
|REGAPP_BlankerNotifications&lt;br /&gt;
|Receive notifications concerning blanker activity.&lt;br /&gt;
|Set to TRUE if you want to receive such notifications. These messages will only be useful to screen blankers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Other types of application should not normally ask to receive these notifications: they would only increase traffic along their input stream.&lt;br /&gt;
&lt;br /&gt;
These two attributes can be changed after registration.&lt;br /&gt;
&lt;br /&gt;
=== Changing Application Attributes ===&lt;br /&gt;
&lt;br /&gt;
Certain attributes describe &amp;quot;dynamic&amp;quot; application properties and, as such, can be set or changed after registration. Some of these are &#039;&#039;state attributes&#039;&#039; 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.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Tag&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AllowsBlanker&lt;br /&gt;
|Same as REGAPP_AllowsBlanker above.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppOpenedDocument&lt;br /&gt;
|Adds a new entry to the application&#039;s Last Used Documents list.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_AppNotifications&amp;lt;br /&amp;gt;APPATTR_BlankerNotifications&lt;br /&gt;
|Same as the two corresponding [[#Receiving Notifications|registration tags]] above.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_CanCreateNewDocs&amp;lt;br /&amp;gt;APPATTR_CanPrintDocs&amp;lt;br /&amp;gt;APPATTR_HasIconifyFeature&amp;lt;br /&amp;gt;APPATTR_HasPrefsWindow&lt;br /&gt;
|Same as the four corresponding [[#Feature Set / Control Scope|registration tags]] above.&lt;br /&gt;
|Prefer setting these properties at registration time.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_ClearLastUsedDocs&lt;br /&gt;
|Clears the application&#039;s list of last used documents.&lt;br /&gt;
|Set this tag to TRUE to clear the list. (Note that this is not really an attribute but, rather, a command.)&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_FlushPrefs&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_Hidden&lt;br /&gt;
|A boolean program-state attribute indicating whether the application is currently iconified (hidden) or not.&lt;br /&gt;
|Update this attribute each time your application GUI changes state, as application managers may query about (and rely on) this information.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_IconType&lt;br /&gt;
|Changes the application icon type.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_MainPrefsDict&lt;br /&gt;
|Allows changing the application&#039;s Prefs dictionary.&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_NeedsGameMode&lt;br /&gt;
|A boolean program-state attribute informing the system (and other applications) that the program is about to enter, or has left, the game mode.&lt;br /&gt;
|By the &amp;quot;game mode&amp;quot; we understand a mode in which an application doesn&#039;t want to be &amp;quot;disturbed&amp;quot; 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.&lt;br /&gt;
|-&lt;br /&gt;
|APPATTR_SavePrefs&lt;br /&gt;
|Same as REGAPP_SavePrefs above.&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BOOL result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;SetApplicationAttrs(appID,&lt;br /&gt;
            APPATTR_Hidden, TRUE,&lt;br /&gt;
            APPATTR_AllowsBlanker, TRUE,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finding Applications ==&lt;br /&gt;
&lt;br /&gt;
The Application Library maintains a public list of all registered applications. Certain special-purpose programs – &#039;&#039;application managers&#039;&#039; – 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?&lt;br /&gt;
&lt;br /&gt;
Finding an application basically means obtaining its appID: it is an identifier as well as a “contact address” for the library&#039;s messaging system. To do this you need to know at least one of the following:&lt;br /&gt;
&lt;br /&gt;
* the application name, ie. the one under which it was registered via RegisterApplication();&lt;br /&gt;
* 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;&lt;br /&gt;
* the pathname pointing to the program file on disk, e.g. “Work:Utils/AudioMonster”.&lt;br /&gt;
&lt;br /&gt;
Based on this information, the respective piece of code that will find our application in the system might look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 appID;&lt;br /&gt;
&lt;br /&gt;
/* if you only know the application name */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the application name identifier */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you specifically want to talk to the second running instance */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_AppIdentifier, &amp;quot;AudioMonster_1.supercoders.com&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* if you know the pathname to the program file */&lt;br /&gt;
appID = IApplication-&amp;gt;FindApplication(FINDAPP_FileName, &amp;quot;Work:Utils/AudioMonster&amp;quot;,&lt;br /&gt;
                                      TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have obtained the appID you can start communicating with the respective application.&lt;br /&gt;
&lt;br /&gt;
== Messaging ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
{{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 &#039;&#039;AppMessages&#039;&#039;. 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).&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Data Structures ===&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
The following three structures are defined for carrying Application Library message data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg&lt;br /&gt;
{&lt;br /&gt;
	struct Message msg;&lt;br /&gt;
	uint32 senderAppID;  // the appID of the sender application&lt;br /&gt;
	uint32 type;         // identifies the type of message&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationOpenPrintDocMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR fileName;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct ApplicationCustomMsg&lt;br /&gt;
{&lt;br /&gt;
	struct ApplicationMsg almsg;&lt;br /&gt;
	STRPTR customMsg;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, structure &#039;&#039;ApplicationMsg&#039;&#039; contains a standard &#039;&#039;[[Exec_Messages_and_Ports#Messages|struct Message]]&#039;&#039;, the other fields are used for Application Library-specific data. The other two structures are mere extensions of the basic one: &#039;&#039;ApplicationOpenPrintDocMsg&#039;&#039; is used by certain [[#Control Messages|control messages]] that require a pointer to a filename; the &#039;&#039;ApplicationCustomMsg&#039;&#039; structure is used for [[#Custom Messages|custom messages]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Control Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Message name&lt;br /&gt;
!Description&lt;br /&gt;
!Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Quit&lt;br /&gt;
|The application is asked to shut down itself and quit.&lt;br /&gt;
|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).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ForceQuit&lt;br /&gt;
|Same as before but this time the application shall quit immediately, without asking for saving documents etc.&lt;br /&gt;
|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&#039;t implement it at all. The official AmigaOS application manager will never try to send APPLIBMT_ForceQuit in order to shut down running applications.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Hide&lt;br /&gt;
|The application shall hide its interface and iconify on the Workbench screen.&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_Unhide&lt;br /&gt;
|The application shall come back from the iconified (hidden) state.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ToFront&lt;br /&gt;
|The application window shall come to front.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenPrefs&lt;br /&gt;
|The application shall open its preferences window.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_ReloadPrefs&lt;br /&gt;
|The application shall reload its preferences.&lt;br /&gt;
|Whatever this command is useful for.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_NewBlankDoc&lt;br /&gt;
|The application shall open a new, blank document or project.&lt;br /&gt;
|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.&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_OpenDoc&lt;br /&gt;
|The application shall try to open a specific document or project.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above).&lt;br /&gt;
|-&lt;br /&gt;
|APPLIBMT_PrintDoc&lt;br /&gt;
|The application shall try to print a specific document.&lt;br /&gt;
|The name of the document is passed as part of the message data structure (&#039;&#039;struct ApplicationOpenPrintDocMsg&#039;&#039;: see [[#Data Structures|Data Structures]] above). An application that supports APPLIBMT_PrintDoc is expected to register itself with the REGAPP_CanPrintDocs set to TRUE.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Custom Messages ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
The pointer to the message text string is contained in a special [[#Data Structures|data structure]], &#039;&#039;struct ApplicationCustomMsg&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Special Messages ===&lt;br /&gt;
&lt;br /&gt;
There are also some special messages that an application can receive from the library or another running application:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Message name !! Description !! Implementation notes&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Message Handling ===&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;notification port&#039;&#039;. 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.&lt;br /&gt;
&lt;br /&gt;
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&#039;ll also want to handle other message types, such as input from the user interface (IDCMP events) or ARexx commands.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void event_loop(uint32 appID)&lt;br /&gt;
{&lt;br /&gt;
 struct MsgPort              *notificationPort = NULL;&lt;br /&gt;
 struct ApplicationMsg       *appLibMsg = NULL;&lt;br /&gt;
 struct ApplicationCustomMsg *customMsg = NULL;&lt;br /&gt;
 uint32 appLibSignal = 0, sigGot = 0;&lt;br /&gt;
 BOOL   done = FALSE;&lt;br /&gt;
&lt;br /&gt;
 /* Obtain pointer to Application Library&#039;s notification port and set the signal bit. */&lt;br /&gt;
 IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_Port, &amp;amp;notificationPort, TAG_END);&lt;br /&gt;
 appLibSignal = (1L &amp;lt;&amp;lt; notificationPort-&amp;gt;mp_SigBit);&lt;br /&gt;
 &lt;br /&gt;
 /* Go into the event loop. */&lt;br /&gt;
 while (!done)&lt;br /&gt;
  {&lt;br /&gt;
   /* Wait for a signal to arrive. */&lt;br /&gt;
   sigGot = IExec-&amp;gt;Wait(appLibSignal);&lt;br /&gt;
&lt;br /&gt;
   /* Process all Application Library messages. */&lt;br /&gt;
   if ( sigGot &amp;amp; appLibSignal )&lt;br /&gt;
    {&lt;br /&gt;
     /* Obtain pointer to the message. */&lt;br /&gt;
     while ( (appLibMsg = (struct ApplicationMsg *) IExec-&amp;gt;GetMsg(notificationPort)) )&lt;br /&gt;
      {&lt;br /&gt;
       /* Identify the type of message. */&lt;br /&gt;
       switch (appLibMsg-&amp;gt;type)&lt;br /&gt;
        {&lt;br /&gt;
         case APPLIBMT_Quit:&lt;br /&gt;
         case APPLIBMT_ForceQuit:&lt;br /&gt;
           done = TRUE;&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_ToFront:&lt;br /&gt;
           /* Make the program window come to front. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Hide:&lt;br /&gt;
           /* Iconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         case APPLIBMT_Unhide:&lt;br /&gt;
           /* Uniconify the program. */&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*  Process the custom message as you like.&lt;br /&gt;
             Here we just use printf() to output the message text. */&lt;br /&gt;
         case APPLIBMT_CustomMsg:&lt;br /&gt;
           customMsg = (struct ApplicationCustomMsg *) appLibMsg;&lt;br /&gt;
           printf(&amp;quot;The message text is: %s\n&amp;quot;, customMsg-&amp;gt; customMsg);&lt;br /&gt;
         break;&lt;br /&gt;
&lt;br /&gt;
         /*&lt;br /&gt;
             Process any other Application Library messages.&lt;br /&gt;
          */&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
       /* Return the processed message to the sender so that it can be freed. */&lt;br /&gt;
       IExec-&amp;gt;ReplyMsg( (struct Message *) appLibMsg);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Also remember that the notifications are addressed to an abstract &#039;&#039;application&#039;&#039; – this makes them rather different from IDCMP messages, which are always sent to a particular &#039;&#039;window&#039;&#039; (Intuition is unaware of the application concept). 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, if an APPLIBMT_ToFront command is sent to your iconified application, there is no window to come to front; you need to uniconify it first 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!&lt;br /&gt;
&lt;br /&gt;
=== Sending Messages ===&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
# Prepare the respective [[#Data Structures|data structure]]: specify the type of message and supply the sender&#039;s [[#Application Identifiers|identifier]] (appID).&lt;br /&gt;
# [[#Finding Applications|Find the receiver]] application.&lt;br /&gt;
# Send a message to it via the SendApplicationMsg() function.&lt;br /&gt;
&lt;br /&gt;
For example, if you want to tell the Audio Monster application to quit, you send it an APPLIBMT_Quit message like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationMsg appMsg;    // message data structure&lt;br /&gt;
uint32 audioMonsterID;           // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Step 1: Prepare the message data structure. */&lt;br /&gt;
appMsg.senderAppID = appID;      // identifier of the sender&lt;br /&gt;
appMsg.type = APPLIBMT_Quit;     // type of message&lt;br /&gt;
&lt;br /&gt;
/* Step 2: Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Step 3: Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 &amp;amp;appMsg,&lt;br /&gt;
                 APPLIBMT_Quit);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should you want to send your message to all currently registered applications at once, just use a receiver appID of 0.&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;struct ApplicationMsg&#039;&#039;. 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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct ApplicationCustomMsg customMsg;      // custom message data structure&lt;br /&gt;
uint32 audioMonsterID;                      // identifier of the receiver&lt;br /&gt;
&lt;br /&gt;
/* Prepare the message data structure. */&lt;br /&gt;
customMsg.almsg.senderAppID = appID;        // identifier of the sender&lt;br /&gt;
customMsg.almsg.type = APPLIBMT_CustomMsg;  // type of message&lt;br /&gt;
customMsg.customMsg = &amp;quot;Start playback&amp;quot;;     // message text&lt;br /&gt;
&lt;br /&gt;
/* Find the receiver application. */&lt;br /&gt;
if ( (audioMonsterID = IApplication-&amp;gt;FindApplication(FINDAPP_Name, &amp;quot;AudioMonster&amp;quot;, TAG_END)) )&lt;br /&gt;
 {&lt;br /&gt;
   /* Send the message. */&lt;br /&gt;
   IApplication-&amp;gt;SendApplicationMsg(appID,&lt;br /&gt;
                 audioMonsterID,&lt;br /&gt;
                 (struct ApplicationMsg *) &amp;amp;customMsg,&lt;br /&gt;
                 APPLIBMT_CustomMsg);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pop-up Notifications (Ringhio Messages) ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;Ringhio messages&#039;&#039; 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).&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 result;&lt;br /&gt;
&lt;br /&gt;
result = IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;All right, ma!&amp;quot;,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&#039;t mean “Yes” or “No”.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IApplication-&amp;gt;Notify(appID,&lt;br /&gt;
               APPNOTIFY_Title, &amp;quot;Important Message&amp;quot;,&lt;br /&gt;
               APPNOTIFY_Text, &amp;quot;Your socks need changing!&amp;quot;,&lt;br /&gt;
               APPNOTIFY_PubScreenName, &amp;quot;FRONT&amp;quot;,&lt;br /&gt;
               APPNOTIFY_ImageFile, &amp;quot;PROGDIR:Images/AudioMonster.jpg&amp;quot;,&lt;br /&gt;
               APPNOTIFY_BackMsg, &amp;quot;URL:http://www.supercoders.com&amp;quot;,&lt;br /&gt;
               APPNOTIFY_CloseOnDC, TRUE,&lt;br /&gt;
               TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pop-up Message Style Guide ===&lt;br /&gt;
&lt;br /&gt;
* Keep the message text short. Give the user chance to read the entire message before it disappears.&lt;br /&gt;
* 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).&lt;br /&gt;
* Use the pop-up message facility with moderation. Displaying the messages too frequently might hamper the workflow, and would most probably annoy the user.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Application Library functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| FindApplication()&lt;br /&gt;
| Searches for a previously registered application.&lt;br /&gt;
|-&lt;br /&gt;
| FreeApplicationList()&lt;br /&gt;
| Frees the list of applications generated by GetApplicationList().&lt;br /&gt;
|-&lt;br /&gt;
| GetAppLibAttrs()&lt;br /&gt;
| Obtains global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationAttrs()&lt;br /&gt;
| Obtains attributes of a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| GetApplicationList()&lt;br /&gt;
| Obtains the list of all currently registered applications.&lt;br /&gt;
|-&lt;br /&gt;
| LockApplicationIcon()&lt;br /&gt;
| Attempts to lock an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| Notify()&lt;br /&gt;
| Displays a pop-up message box.&lt;br /&gt;
|-&lt;br /&gt;
| RegisterApplication()&lt;br /&gt;
| Registers an application.&lt;br /&gt;
|-&lt;br /&gt;
| SendApplicationMsg()&lt;br /&gt;
| Sends a message to a registered application.&lt;br /&gt;
|-&lt;br /&gt;
| SetAppLibAttrs()&lt;br /&gt;
| Sets or changes global Application Library attributes.&lt;br /&gt;
|-&lt;br /&gt;
| SetApplicationAttrs()&lt;br /&gt;
| Sets or changes application attributes.&lt;br /&gt;
|-&lt;br /&gt;
| UnlockApplicationIcon()&lt;br /&gt;
| Unlocks an application icon.&lt;br /&gt;
|-&lt;br /&gt;
| UnregisterApplication()&lt;br /&gt;
| Unregisters an application.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=PrefsObjects&amp;diff=8074</id>
		<title>PrefsObjects</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=PrefsObjects&amp;diff=8074"/>
		<updated>2015-09-18T12:27:32Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Made this a separate wiki page.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Many applications are user-configurable so they need a way to store their settings; we traditionally use the term &#039;&#039;preferences&#039;&#039; or simply &#039;&#039;prefs&#039;&#039; in the AmigaOS context. It may come as a surprise that before version 4.x, AmigaOS had no real standard for storing preference data. Developers used various, often proprietary solutions: [[Icon_Library#The_Tool_Types_Array|icon tooltypes]], IFF-based formats, text files mimicking the Windows &#039;&#039;.ini&#039;&#039; format, or binary formats. Some of them are still very popular (tooltypes) or even used by the OS itself (some [[UI_Style_Guide_Preferences#Preferences|system preferences]] are stored as IFF-PREF files). While this is not a place to go into detail and discuss their particular advantages and disadvantages, let’s mention two common drawbacks that ultimately led to the development of the Application Library PrefsObjects system:&lt;br /&gt;
&lt;br /&gt;
# All of the solutions above require you to implement your own preferences handling code (that is, routines for parsing, verification, and saving).&lt;br /&gt;
# None of the various formats used have ever provided a smart enough way to administer complex, structured settings.&lt;br /&gt;
&lt;br /&gt;
Since its introduction in AmigaOS, PrefsObjects has largely been promoted as being &#039;&#039;XML-based&#039;&#039; and therefore human-readable and easy to edit. Yet using an industry standard format that is platform-independent and supports Unicode is not the biggest “selling point” of PrefsObjects. What makes it different from (and superior to) previous solutions is not the fact that it is XML-based but, rather, that it is &#039;&#039;object-oriented&#039;&#039;. Among other things, this property also helps address the two drawbacks mentioned above.&lt;br /&gt;
&lt;br /&gt;
Imagine, for example, a program that uses a plug-in system. Both the main program and its plug-in modules are configurable. Instead of having ten or more individual preference files, you’ll likely want a single file containing settings for the program as well as for the plug-ins. This naturally introduces a certain hierarchy. Plus, as some plug-ins may be delivered by third parties, the particular structure of the prefs file is somewhat beyond the control of the main program: the amount, contents and sequencing of data in the file depends on which plug-ins are installed and on how (and when, if ever) they store their settings. You would have to take all of this into account when implementing your own prefs-handling routines, which means a good deal of work.&lt;br /&gt;
&lt;br /&gt;
== The Interface and its Functions ==&lt;br /&gt;
&lt;br /&gt;
The [[Application_Library#Library Opening Chores|Library Opening Chores]] section of the Application Library documentation says that the library has two interfaces, called “application” and “prefsobjects”, respectively. If you want to implement your program preferences using the PrefsObjects system, you must obtain the “prefsobjects” interface: all the necessary functions and methods are contained therein. Naturally, your application must be registered before you can make use of PrefsObjects.&lt;br /&gt;
&lt;br /&gt;
All operations related to settings – retrieving, adding and changing –, as well as all operations related to the prefs file itself – creation, loading and saving – are performed solely through the library functions. Some operations can even be automated, sparing the programmer from unnecessary hassle.&lt;br /&gt;
&lt;br /&gt;
== Object Types ==&lt;br /&gt;
&lt;br /&gt;
PrefsObjects tackles the task by seeing data as &#039;&#039;objects&#039;&#039;, and by providing functions and methods for object manipulation. Loading, adding, updating or removing preference objects (be they single items or entire clusters of settings) then becomes a matter of calling the respective function – “invoking the method on the object”, as we say in object-oriented programming. If you are acquainted with this philosophy (as used, for example, in Intuition’s [[BOOPSI]] framework), you will find working with PrefsObjects very easy and straightforward.&lt;br /&gt;
&lt;br /&gt;
PrefsObjects distinguishes between six object types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Object Type&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| Dictionary || A container to encapsulate (embed) other objects. The prefs file usually has a Dictionary object at the top level.&lt;br /&gt;
|-&lt;br /&gt;
| Array || A container to encapsulate other objects. The difference between a Dictionary and an Array is in object referencing: in Arrays encapsulated objects are referenced by an index, while in a Dictionary they are referenced by a name (key string).&lt;br /&gt;
|-&lt;br /&gt;
| Number || An object to carry a long integer, double, or a boolean value.&lt;br /&gt;
|-&lt;br /&gt;
| String || An object to carry a text string.&lt;br /&gt;
|-&lt;br /&gt;
| Date || An object to carry date and time information.&lt;br /&gt;
|-&lt;br /&gt;
| Binary || An object to carry arbitrary binary data. However, binary data should be avoided as much as possible because it&#039;s restrictive in its form and is not future-proof, as it does not enable addition or removal of data without compromising compatibility.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== The Preferences File ==&lt;br /&gt;
&lt;br /&gt;
The files generated by the Application Library&#039;s PrefsObjects system are standard XML documents. They are encoded in UTF-8 (which AmigaOS doesn&#039;t support directly) but that doesn&#039;t necessarily pose a problem. First, you rarely need to edit prefs files manually – nor are you encouraged to do so, unless the file has become corrupted or contains a setting that prevents the application from starting up or running properly. Second, characters beyond the ASCII range are rarely used in preferences, so the prefs files will likely be readable and editable even in a text editor that has no notion of UTF-8. And third, you can always use the dedicated [[PrefsObjectsEditor]], located in the Utilities drawer on your system disk, which allows easy and safe editing of PrefsObjects files (the usage of this tool is, however, out of the scope of this page).&lt;br /&gt;
&lt;br /&gt;
As already mentioned above, all data access is handled by the Application Library. On the part of the programmer (or the application user), no actual knowledge of XML is required.&lt;br /&gt;
&lt;br /&gt;
=== File Structure ===&lt;br /&gt;
&lt;br /&gt;
The preferences file normally starts with a Dictionary object at the top level. The advantage of using a Dictionary is that objects stored in it are identified by a name (called a “key”). Using named objects is very practical. Unlike in Arrays, where objects are indexed, the order of data is quite irrelevant as long as you deal with objects on the same level. Your application can retrieve, add, modify, re-sequence or remove individual objects in an arbitrary order without having to worry about breaking anything: a named object will always be addressed properly if it exists.&lt;br /&gt;
&lt;br /&gt;
Object names (keys) must be unique at each particular level. In other words, you cannot have two settings items (objects) with the same key in the same dictionary. However, identical object names can be used in different dictionaries. The following example shows a prefs file structure that embeds two separate dictionaries named “Screen settings” and “Window settings”. As you can see, identical keys (“Name”, “Width” and “Height”) are then used without collision:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;classdiagram type=&amp;quot;orderly&amp;quot; scale=&amp;quot;80&amp;quot; dir=&amp;quot;prf&amp;quot;&amp;gt;&lt;br /&gt;
[“Root”\n(Dictionary)]-&amp;gt;[“Screen settings”\n(Dictionary)]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Height”\n(Number) ]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Width”\n(Number) ]&lt;br /&gt;
[“Window settings”\n(Dictionary)]-&amp;gt;[“Name”\n(String) ]&lt;br /&gt;
[“Root”\n(Dictionary)]-&amp;gt;[“Window settings”\n(Dictionary)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Height”\n(Number)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Width”\n(Number)]&lt;br /&gt;
[“Screen settings”\n(Dictionary)]-&amp;gt;[“Name”\n(String)]&lt;br /&gt;
&amp;lt;/classdiagram&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The diagram also illustrates the hierarchical nature of the PrefsObjects system: the fact that objects can be embedded inside other objects.&lt;br /&gt;
&lt;br /&gt;
== Reading Preferences ==&lt;br /&gt;
&lt;br /&gt;
In most cases, there will be just one preferences file associated with your application. If all you need is simply read settings from the file (and save them upon user request), it is recommended that you let the Application Library handle the file access. Registering your application like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
appID = IApplication-&amp;gt;RegisterApplication(&amp;quot;AudioMonster&amp;quot;,&lt;br /&gt;
           REGAPP_URLIdentifier, &amp;quot;supercoders.com&amp;quot;,&lt;br /&gt;
           REGAPP_Description, &amp;quot;A media player&amp;quot;,&lt;br /&gt;
           REGAPP_LoadPrefs, TRUE,&lt;br /&gt;
           TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will tell the library to access the prefs file automatically, under its default name and from the default location. The default naming scheme for prefs files uses the application name combined with the [[Application_Library#Application Identifiers|URL identifier]]. Also, the &#039;&#039;.xml&#039;&#039; extension is appended at the end of the name to identify the document format. So with this particular registration call, the default file name will be “AudioMonster.supercoders.com.xml” and the library will try to access it from the ENV: directory. (Don&#039;t worry, both the file name and location can be changed.)&lt;br /&gt;
&lt;br /&gt;
If the file is found, the library will internally allocate a Dictionary object and read the settings into it. If the file is not found, the library will allocate an empty Dictionary for you. In either case the said Dictionary object will become the application&#039;s &#039;&#039;Main Prefs Dictionary&#039;&#039; and will be associated with the application as one of its attributes. (The application may use multiple prefs Dictionaries but at any given point in time, only one of them is main.)&lt;br /&gt;
&lt;br /&gt;
After start-up and registration, the application will typically want to access the settings in order to configure itself. Having used &#039;&#039;REGAPP_LoadPrefs, TRUE&#039;&#039; in the registration call above, we should now have the application&#039;s Main Prefs Dictionary somewhere in memory, to which we need to obtain a pointer. This is done through the function GetApplicationAttrs() because as explained above, the Main Prefs Dictionary is an application attribute. After that we can use dedicated functions to access the individual objects (i.e. settings items) in the Dictionary.&lt;br /&gt;
&lt;br /&gt;
The code fragment below assumes that our prefs file – already loaded by the registration routine and transformed into an object – contains three settings items:&lt;br /&gt;
&lt;br /&gt;
* a boolean value stored in the file under the key of “Switch”;&lt;br /&gt;
* a numeric value of type uint32 stored under the key of “Number”;&lt;br /&gt;
* a text string stored under the key of “String”.&lt;br /&gt;
&lt;br /&gt;
We first obtain the pointer to the prefs Dictionary object, retrieve the three values, and store them in a dedicated data structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Default settings */&lt;br /&gt;
#define DEF_SWITCH   TRUE&lt;br /&gt;
#define DEF_NUMBER   100&lt;br /&gt;
#define DEF_STRING   &amp;quot;Play it again, Sam!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
struct Settings&lt;br /&gt;
{&lt;br /&gt;
  BOOL   switch;&lt;br /&gt;
  uint32 number;&lt;br /&gt;
  STRPTR string;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
PrefsObject    *myPrefsDict = NULL;          /* Main Prefs Dictionary          */&lt;br /&gt;
struct Settings mySettings;                  /* Structure to hold the settings */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Obtain the Dictionary object pointer */&lt;br /&gt;
IApplication-&amp;gt;GetApplicationAttrs(appID, APPATTR_MainPrefsDict, &amp;amp;myPrefsDict, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if ( myPrefsDict )&lt;br /&gt;
 {&lt;br /&gt;
&lt;br /&gt;
   /*   Retrieve the settings values.&lt;br /&gt;
&lt;br /&gt;
        Note: here we need to cast the type of the number and of the string value&lt;br /&gt;
        to avoid compiler warnings. This is because DictGetIntegerForKey()&lt;br /&gt;
        and DictGetStringForKey() return int32 and CONST_STRPTR, respectively,&lt;br /&gt;
        whereas our program declares them as uint32 and STRPTR.&lt;br /&gt;
    */&lt;br /&gt;
   mySettings.switch = IPrefsObjects-&amp;gt;DictGetBoolForKey(myPrefsDict, &amp;quot;Switch&amp;quot;, DEF_SWITCH);&lt;br /&gt;
   mySettings.number = (uint32) IPrefsObjects-&amp;gt;DictGetIntegerForKey(myPrefsDict, &amp;quot;Number&amp;quot;, DEF_NUMBER);&lt;br /&gt;
   mySettings.string = (STRPTR) IPrefsObjects-&amp;gt;DictGetStringForKey(myPrefsDict, &amp;quot;String&amp;quot;, DEF_STRING);&lt;br /&gt;
   &lt;br /&gt;
   /* Test out the string value. */&lt;br /&gt;
   IDOS-&amp;gt;Printf(&amp;quot;All I want to say is: %s\n&amp;quot;, mySettings.string);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should any of the settings items be missing, the respective default value is used. This will be the case, for example, when a physical XML prefs file is not present and the PrefsObjects system supplies an empty Dictionary.&lt;br /&gt;
&lt;br /&gt;
(to be continued)&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the PrefsObjects-related functions. See the SDK/Autodocs for details on each function call.&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| BeginDeserialization()&lt;br /&gt;
| Begins the deserialization of a prefs object.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetBoolForKey()&lt;br /&gt;
| Obtains a boolean value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetIntegerForKey()&lt;br /&gt;
| Obtains an integer value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetObjectForKey()&lt;br /&gt;
| Obtains an object for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetOptionForKey()&lt;br /&gt;
| Obtains a string from an option table.&lt;br /&gt;
|-&lt;br /&gt;
| DictGetStringForKey()&lt;br /&gt;
| Obtains a string value for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| DictSetObjectForKey()&lt;br /&gt;
| Sets an object for a dictionary key.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsArray()&lt;br /&gt;
| PrefsObjects array object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsBaseObject()&lt;br /&gt;
| PrefsObjects base object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsBinary()&lt;br /&gt;
| PrefsObjects binary object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsDate()&lt;br /&gt;
| PrefsObjects date object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsDictionary()&lt;br /&gt;
| PrefsObjects dictionary object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsNumber()&lt;br /&gt;
| PrefsObjects number object access function.&lt;br /&gt;
|-&lt;br /&gt;
| PrefsString()&lt;br /&gt;
| PrefsObjects string object access function.&lt;br /&gt;
|-&lt;br /&gt;
| ReadPrefs()&lt;br /&gt;
| Reads a prefs dictionary from a file.&lt;br /&gt;
|-&lt;br /&gt;
| WritePrefs()&lt;br /&gt;
| Writes a prefs dictionary to a file.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Libraries&amp;diff=8073</id>
		<title>Libraries</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Libraries&amp;diff=8073"/>
		<updated>2015-09-18T11:11:39Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Created a separate page for PrefsObjects because the Application Library documentation was getting too long.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== AmigaDOS ==&lt;br /&gt;
&lt;br /&gt;
[[AmigaDOS Introduction]]&lt;br /&gt;
&lt;br /&gt;
[[DOS Library]]&lt;br /&gt;
:[[AmigaDOS Data Structures|Data Structures]]&lt;br /&gt;
:[[Program Startup]]&lt;br /&gt;
:[[Basic Input and Output Programming]]&lt;br /&gt;
:[[Executing External Programs]]&lt;br /&gt;
:[[Cooperative Record Locking]]&lt;br /&gt;
:[[Notification]]&lt;br /&gt;
:[[Path Name Handling]]&lt;br /&gt;
:[[Pattern Matching]]&lt;br /&gt;
:[[Multiple Assigns]]&lt;br /&gt;
:[[AmigaDOS Packets|Packets]]&lt;br /&gt;
:[[AmigaDOS Vector-Port|Vector-Port]]&lt;br /&gt;
:[[Hard and Soft Links]]&lt;br /&gt;
:[[Writing a UserShell]]&lt;br /&gt;
:[[AmigaDOS Device Input and Output|Device Input and Output]]&lt;br /&gt;
&lt;br /&gt;
== User Interface Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[Intuition Library]]&lt;br /&gt;
:[[Intuition Screens|Screens]]&lt;br /&gt;
::[[Custom_Screen_Type|Custom Screen Type]]&lt;br /&gt;
::[[Public_Screen_Type|Public Screen Type]]&lt;br /&gt;
:[[Intuition Windows|Windows]]&lt;br /&gt;
::[[Window Types|Types]]&lt;br /&gt;
::[[Window Structures and Functions|Structures and Functions]]&lt;br /&gt;
::[[Window Communication|Communicating with Intuition]]&lt;br /&gt;
::[[Window Display Preservation|Display Preservation]]&lt;br /&gt;
:[[Intuition Gadgets|Gadgets]]&lt;br /&gt;
::[[Boolean_Gadget_Type|Boolean Gadget Type]]&lt;br /&gt;
::[[Proportional_Gadget_Type|Proportional Gadget Type]]&lt;br /&gt;
::[[String_Gadget_Type|String Gadget Type]]&lt;br /&gt;
:[[Intuition Menus|Menus]]&lt;br /&gt;
::[[Intuition Classic Menus|Classic Menus]]&lt;br /&gt;
::[[Intuition Menu Class|Menu Class]]&lt;br /&gt;
::[[Intuition Context Menus|Context Menus]]&lt;br /&gt;
:[[Intuition Requesters|Requesters]]&lt;br /&gt;
:[[Intuition Alerts|Alerts]]&lt;br /&gt;
:[[Intuition Images, Line Drawing and Text|Images, Line Drawing and Text]]&lt;br /&gt;
::[[Intuition Images|Images]]&lt;br /&gt;
::[[Intuition Borders|Borders]]&lt;br /&gt;
::[[Intuition Text|Text]]&lt;br /&gt;
:[[Intuition Input and Output Methods|Input and Output Methods]]&lt;br /&gt;
:[[Intuition Mouse|Mouse]]&lt;br /&gt;
:[[Intuition Keyboard|Keyboard]]&lt;br /&gt;
:[[Intuition Pointer|Pointer]]&lt;br /&gt;
:[[Intuition Special Functions|Special Functions]]&lt;br /&gt;
:[[BOOPSI - Object Oriented Intuition]]&lt;br /&gt;
::[[BOOPSI Gadgets]]&lt;br /&gt;
::[[BOOPSI Images]]&lt;br /&gt;
::[[BOOPSI Class Reference]]&lt;br /&gt;
&lt;br /&gt;
[[Workbench Library]]&lt;br /&gt;
&lt;br /&gt;
[[Icon Library]]&lt;br /&gt;
&lt;br /&gt;
[[Locale Library]]&lt;br /&gt;
&lt;br /&gt;
[[GadTools Library]]&lt;br /&gt;
:[[GadTools Menus]]&lt;br /&gt;
:[[GadTools Gadgets]]&lt;br /&gt;
&lt;br /&gt;
[[ASL Library]]&lt;br /&gt;
:[[ASL File Requester|File Requester]]&lt;br /&gt;
:[[ASL Font Requester|Font Requester]]&lt;br /&gt;
:[[ASL Screen Mode Requester|Screen Mode Requester]]&lt;br /&gt;
&lt;br /&gt;
[[Application Library]]&lt;br /&gt;
:[[PrefsObjects]]&lt;br /&gt;
&lt;br /&gt;
[[Preferences]]&lt;br /&gt;
&lt;br /&gt;
== Graphics Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[Graphics Library]]&lt;br /&gt;
:[[Display Database]]&lt;br /&gt;
:[[Graphics Primitives|Primitives]]&lt;br /&gt;
::[[Classic Graphics Primitives|Classic Primitives]]&lt;br /&gt;
:[[Graphics Sprites, Bobs and Animation|Sprites, Box and Animation]]&lt;br /&gt;
::[[Hardware Sprites]]&lt;br /&gt;
::[[Virtual Sprites|Virtual Sprites (VSprites)]]&lt;br /&gt;
::[[Blitter Objects|Blitter Objects (Bobs)]]&lt;br /&gt;
:[[Graphics Library and Text|Text]]&lt;br /&gt;
:[[Graphics Regions|Regions]]&lt;br /&gt;
&lt;br /&gt;
[[Layers Library]]&lt;br /&gt;
&lt;br /&gt;
== Additional Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[AmigaGuide Library]]&lt;br /&gt;
&lt;br /&gt;
[[Camd Library]]&lt;br /&gt;
&lt;br /&gt;
[[Commodities Exchange Library]]&lt;br /&gt;
&lt;br /&gt;
[[Datatypes Library]]&lt;br /&gt;
:[[Writing Datatype Classes]]&lt;br /&gt;
&lt;br /&gt;
[[IFFParse Library]]&lt;br /&gt;
:[[Parsing IFF]]&lt;br /&gt;
:[[Writing IFF]]&lt;br /&gt;
&lt;br /&gt;
[[Keymap Library]]&lt;br /&gt;
&lt;br /&gt;
[[Newlib Library]]&lt;br /&gt;
&lt;br /&gt;
[[PThreads Library]]&lt;br /&gt;
&lt;br /&gt;
[[RealTime Library]]&lt;br /&gt;
&lt;br /&gt;
[[Translator Library]]&lt;br /&gt;
&lt;br /&gt;
== Third-Party Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[Expat Library]]&lt;br /&gt;
&lt;br /&gt;
[[Filesysbox Library]]&lt;br /&gt;
&lt;br /&gt;
== Appendices ==&lt;br /&gt;
&lt;br /&gt;
[[Linker Libraries]]&lt;br /&gt;
&lt;br /&gt;
== 68k Libraries ==&lt;br /&gt;
&lt;br /&gt;
[[Math Libraries]]&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=GadTools_Library&amp;diff=8072</id>
		<title>GadTools Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=GadTools_Library&amp;diff=8072"/>
		<updated>2015-09-13T16:13:56Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Reworded the chapter introduction to reflect the current situation in AmigaOS.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GadTools Library ==&lt;br /&gt;
&lt;br /&gt;
Originally, programming even straightforward user interfaces in [[Intuition_Library|Intuition]] could be rather complicated, and certainly difficult for first-time programmers. The GadTools toolkit was introduced in AmigaOS 2.x to simplify the task. It provided relatively easy-to-use, higher-level chunks to build [http://en.wikipedia.org/wiki/Graphical_user_interface GUIs] from, and to help programmers through what used to be a difficult chore.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Note|text=As of AmigaOS 4.1 Final Edition, GadTools is basically a legacy toolkit retained for compatibility. Due to its inherent limitations, using GadTools in modern applications can no longer be recommended. Prefer using the object-oriented GUI programming framework based on [[BOOPSI]].}}&lt;br /&gt;
&lt;br /&gt;
== Elements of GadTools ==&lt;br /&gt;
&lt;br /&gt;
GadTools is the easy way to program gadgets and menus. With GadTools, the system handles the detail work required to control gadgets and menus so the application uses less code and simpler data structures.&lt;br /&gt;
&lt;br /&gt;
Another key benefit of GadTools is its standardized and elegant look. All applications that use GadTools will share a similar appearance and behavior. Users will appreciate a sense of instant familiarity even the first time they use a product.&lt;br /&gt;
&lt;br /&gt;
GadTools provides a significant degree of visual consistency across multiple applications that use it. There is also internal consistency between different elements of GadTools; the look is clean and orderly. Depth is used not just for visual embellishment, but as an important cue. For instance, the user is free to select symbols that appear inside a &amp;quot;raised&amp;quot; area, but &amp;quot;recessed&amp;quot; areas are informational only, and clicking in them has no effect.&lt;br /&gt;
&lt;br /&gt;
GadTools is not amenable to creative post-processing or hacking by programmers looking to achieve a result other than what GadTools currently offers. Software developers whose needs extend beyond the standard features of GadTools should create custom gadgets that share the look and feel of GadTools by using either BOOPSI or by directly programming gadgets at a lower level. See [[Intuition_Gadgets|Intuition Gadgets]] and [[BOOPSI_-_Object_Oriented_Intuition|BOOPSI]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== GadTools Tags ===&lt;br /&gt;
&lt;br /&gt;
Many of the GadTools functions use TagItem arrays or &#039;&#039;tag lists&#039;&#039; to pass information across the function interface. These tag-based functions come in two types, one that takes a pointer to an array of tag items and one that takes a variable number of tag item arguments directly in the function call. In general, the second form, often called the &#039;&#039;varargs&#039;&#039; form because the call takes a variable number of arguments, is provided for convenience and is internally converted to the first form. When looking through the Autodocs or other Amiga reference material, the documentation for both forms is usually available in the array-based function description.&lt;br /&gt;
&lt;br /&gt;
All GadTools tags begin with a leading &amp;quot;GT&amp;quot;. In general, they also have a two-letter mnemonic for the kind of gadget in question. For example, slider gadgets recognize tags such as &amp;quot;GTSL_Level&amp;quot;. The GadTools tags are defined in &amp;amp;lt;libraries/gadtools.h&amp;amp;gt;. Certain GadTools gadgets also recognize other Intuition tags such as GA_Disabled and PGA_Freedom, which can be found in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For more information on tags and tag-based functions, be sure to see the [[Utility_Library|Utility Library]].&lt;br /&gt;
&lt;br /&gt;
== GadTools Menus ==&lt;br /&gt;
&lt;br /&gt;
GadTools menus are the preferred way to manage menus on AmigaOS.&lt;br /&gt;
&lt;br /&gt;
See [[GadTools Menus]] for more information on how to use them.&lt;br /&gt;
&lt;br /&gt;
== GadTools Gadgets ==&lt;br /&gt;
&lt;br /&gt;
GadTools gadgets are largely superseded by BOOPSI-based GUI systems are to be avoided.&lt;br /&gt;
&lt;br /&gt;
See [[GadTools Gadgets]] for more information about this obsolete GUI system.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the Intuition functions discussed in this chapter. See the SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CreateGadgetA()&amp;lt;br/&amp;gt;CreateGadget()&lt;br /&gt;
| Allocate GadTools gadget.&lt;br /&gt;
|-&lt;br /&gt;
| FreeGadgets()&lt;br /&gt;
| Free all GadTools gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| GT_SetGadgetAttrsA()&amp;lt;br/&amp;gt;GT_SetGadgetAttrs()&lt;br /&gt;
| Update gadget.&lt;br /&gt;
|-&lt;br /&gt;
| CreateContext()&lt;br /&gt;
| Create a base for adding GadTools gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| CreateMenusA()&amp;lt;br/&amp;gt;CreateMenus()&lt;br /&gt;
| Allocate GadTools menu structures.&lt;br /&gt;
|-&lt;br /&gt;
| FreeMenus()&lt;br /&gt;
| Free menus allocated with CreateMenus().&lt;br /&gt;
|-&lt;br /&gt;
| LayoutMenuItemsA()&amp;lt;br/&amp;gt;LayoutMenuItems()&lt;br /&gt;
| Format GadTools menu items.&lt;br /&gt;
|-&lt;br /&gt;
| LayoutMenusA()&amp;lt;br/&amp;gt;LayoutMenus()&lt;br /&gt;
| Format GadTools menus.&lt;br /&gt;
|-&lt;br /&gt;
| GT_GetIMsg()&lt;br /&gt;
| GadTools gadget compatible version of GetMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_ReplyIMsg()&lt;br /&gt;
| GadTools gadget compatible version of ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_FilterIMsg()&lt;br /&gt;
| Process GadTools gadgets with GetMsg()/ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_PostFilterIMsg()&lt;br /&gt;
| Process GadTools gadgets with GetMsg()/ReplyMsg().&lt;br /&gt;
|-&lt;br /&gt;
| GT_RefreshWindow()&lt;br /&gt;
| Display GadTools gadget imagery after creation.&lt;br /&gt;
|-&lt;br /&gt;
| GT_BeginRefresh()&lt;br /&gt;
| GadTools gadget compatible version of BeginRefresh().&lt;br /&gt;
|-&lt;br /&gt;
| GT_EndRefresh()&lt;br /&gt;
| GadTools gadget compatible version of EndRefresh().&lt;br /&gt;
|-&lt;br /&gt;
| DrawBevelBoxA()&amp;lt;br/&amp;gt;DrawBevelBox()&lt;br /&gt;
| Draw a 3D box.&lt;br /&gt;
|-&lt;br /&gt;
| GetVisualInfoA()&amp;lt;br/&amp;gt;GetVisualInfo()&lt;br /&gt;
| Get drawing information for GadTools.&lt;br /&gt;
|-&lt;br /&gt;
| FreeVisualInfo()&lt;br /&gt;
| Free drawing information for GadTools.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=GUI_Programming&amp;diff=8071</id>
		<title>GUI Programming</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=GUI_Programming&amp;diff=8071"/>
		<updated>2015-09-13T15:39:40Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Added some notes about GadTools.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
In AmigaOS, the face a program shows you – its [http://en.wikipedia.org/wiki/Graphical_user_interface graphical user interface] (GUI) – is created through a subsystem called [[Intuition_Library|Intuition]]. Older literature sometimes used to refer to Intuition as “the Amiga user interface” but this is really not the case. Despite being responsible for much of what you can &#039;&#039;see&#039;&#039; in the OS (including its windowing desktop environment, the [[UserDoc:Workbench|Workbench]]), Intuition itself remains &#039;&#039;invisible&#039;&#039; to the user. It is merely a system component providing ready-made [[#GUI elements|elements]] to build GUIs from, and a set of functions through which these elements are manipulated. Intuition also interconnects GUIs with the operating system and handles various communications that underlie application usage and control.&lt;br /&gt;
&lt;br /&gt;
Intuition’s functionality is further extended by a number of auxiliary system components, or [[#GUI toolkits|toolkits]]. Like most AmigaOS subsystems, Intuition and its extensions are implemented as libraries of functions that can be accessed from a higher-level programming language like C, C++, Modula-2 or AmigaE.&lt;br /&gt;
&lt;br /&gt;
== GUI Elements ==&lt;br /&gt;
&lt;br /&gt;
Amiga programs do not look or behave very differently from, say, Mac or Windows applications because their user interface is based on shared metaphors and familiar elements. There may be differences in naming conventions or programming techniques but the building blocks are similar. The following table summarizes the building blocks (GUI elements) that are provided by Intuition and its various extensions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! GUI element types&lt;br /&gt;
! Description&lt;br /&gt;
! System component or toolkit providing this functionality&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Screens|screens]] || Virtual desktops on which windows are opened. || Intuition Library&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Windows|windows]] || Rectangular areas containing an interface through which a program communicates with the user, and vice versa. || Intuition Library, ReAction, MUI&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Menus|menus]] || Programmable sets of commands displayed as a pull-down list of options. || Intuition Library, GadTools Library&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Gadgets|gadgets]] || Various-purpose GUI controls (buttons, toolbars, gauges, text fields...) with a standardized look and behaviour. Often called “widgets” in other operating systems. || Intuition Library, GadTools Library, ReAction, MUI&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Images,_Line_Drawing_and_Text#Creating_Images|images]] || Non-selectable elements showing graphics or text. || Intuition Library, ReAction, MUI&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Requesters|requesters]] || Means for displaying information and for requesting input from the user. They would be called “dialogs”, “dialog boxes” or “dialog windows” in other OSes. || Intuition Library, ASL Library, ReAction, MUI&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Alerts|alerts]] || A method of emergency communication (such as system errors). || Exec Library, Intuition Library&lt;br /&gt;
|-&lt;br /&gt;
| [[Application_Library#Pop-up_notifications_(Ringhio_messages)|pop-up notifications]] || Automatic messages informing the user when things happen. Unlike requesters, notifications are “unobtrusive” and do not require any input from the user. || [[Application Library]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Images,_Line_Drawing_and_Text#Creating_Text|IntuiText]] || Formatted text to be placed at a specific position inside an Intuition element (screen, window, menu, gadget or requester). || Intuition Library&lt;br /&gt;
|-&lt;br /&gt;
| [[Intuition_Images,_Line_Drawing_and_Text#Creating_Borders|borders]] || Graphical structures made of lines that connect a series of defined points.. || Intuition Library&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== The Two Frameworks ==&lt;br /&gt;
&lt;br /&gt;
GUI programming in AmigaOS does not have to be done within a single framework or [http://en.wikipedia.org/wiki/Application_programming_interface API]. This fact can be confusing to newcomers. There are two frameworks in Intuition representing fundamentally different approaches to GUI programming:&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;data structure-oriented framework&#039;&#039;&#039; is the original Intuition API. Within this framework, if you want to create a GUI element, you have to provide dedicated data structures for it; if you want to manipulate the element, you have to use a function designed for the particular action and type of element.&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;object-oriented framework&#039;&#039;&#039; (called [[BOOPSI]]: Basic Object-Oriented Programming System for Intuition) is the extendable API. Within this framework, if you want to create a GUI element, you have to instantiate an &#039;&#039;object&#039;&#039; based upon a particular &#039;&#039;class&#039;&#039;. Objects are manipulated through a small set of &#039;&#039;methods&#039;&#039; which are really just functions.&lt;br /&gt;
&lt;br /&gt;
Modern AmigaOS programmers generally do not directly use either of these frameworks. Although it is still possible to do so it also requires a lot more code to achieve even a simple GUI.&lt;br /&gt;
&lt;br /&gt;
Instead, programmers are encouraged to use one or more of the various toolkits provided.&lt;br /&gt;
&lt;br /&gt;
== GUI toolkits ==&lt;br /&gt;
&lt;br /&gt;
While the Intuition Library originally provided a datastructure-based set of basic GUI elements (screens, windows, menus, buttons &amp;amp; string gadgets, etc.) they have been supplanted by more modern tools for user interface programming. Most of the GUI-related functionality is now provided through various extensions to Intuition, or &#039;&#039;toolkits&#039;&#039;. The following toolkits are installed by default in AmigaOS:&lt;br /&gt;
&lt;br /&gt;
=== GadTools ===&lt;br /&gt;
&lt;br /&gt;
[[GadTools_Library|GadTools]] is a datastructure-based toolkit. It is implemented as a single library and is not easily extensible. What is most useful about GadTools is the [[GadTools_Library#GadTools_Menus|menu building functionality]] (most of the other toolkits still use the menu build aspect of GadTools).&lt;br /&gt;
&lt;br /&gt;
GadTools was introduced in AmigaOS 2.x as a ready-made toolkit for easier, faster and more consistent GUI design. GadTools extended the original Intuition set with fancy new controls such as the [[GadTools_Library#Cycle_Gadgets|cycle gadget]], the [[GadTools_Library#Mutually-Exclusive_Gadgets|radiobutton]], or the [[GadTools_Library#Listview_Gadgets|listview]]. The gadgets shared similar imagery, thus giving Amiga GUIs a more uniform, standardized look. Apart from improving the gadget set, GadTools also greatly simplified the creation of menus (GadTools uses standard Intuition menus but provides its own build and layout system for them). On the other hand, there is a very limited support for images in GadTools.&lt;br /&gt;
&lt;br /&gt;
As of AmigaOS 4.1 Final Edition, GadTools is basically a legacy toolkit retained for compatibility.&lt;br /&gt;
&lt;br /&gt;
=== ASL ===&lt;br /&gt;
&lt;br /&gt;
[[ASL_Library|ASL]] is a datastructure-based toolkit. It is implemented as a single library and is not extensible.&lt;br /&gt;
&lt;br /&gt;
The Amiga Standard Library (ASL) is a toolkit designed to ease the programming of common requesters. There are currently three types of requester available: file requester, font requester and screenmode requester.&lt;br /&gt;
&lt;br /&gt;
=== ReAction ===&lt;br /&gt;
&lt;br /&gt;
[[ReAction]] is an object-oriented toolkit based on BOOPSI which is highly extensible.&lt;br /&gt;
&lt;br /&gt;
ReAction is a more modern toolkit which hides much of the complexity. It covers all the functionality of GadTools and ASL and uses and extends the original BOOPSI class set built into Intuition Library. ReAction is considered to be the standard Amiga GUI toolkit. Over the years it has been integrated into the Intuition/BOOPSI framework, and is no longer considered a separate component.&lt;br /&gt;
&lt;br /&gt;
=== MUI ===&lt;br /&gt;
&lt;br /&gt;
[http://en.wikipedia.org/wiki/Magic_User_Interface Magic User Interface] is an object-oriented toolkit based on BOOPSI which is highly extensible.&lt;br /&gt;
&lt;br /&gt;
MUI is a comprehensive third-party toolkit for application GUI design which can also incorporate GadTools menus and ASL. MUI is also available on older AmigaOS systems and various clone systems although the implementation differs so compatibility is not guaranteed.&lt;br /&gt;
&lt;br /&gt;
=== Qt, REBOL/View and Others ===&lt;br /&gt;
&lt;br /&gt;
Third party toolkits may also be used on AmigaOS just as well as the built-in toolkits listed above.&lt;br /&gt;
&lt;br /&gt;
Such toolkits generally work by opening an Intuition Screen and then rendering directly into that screen to create the GUI environment. Toolkits may also incorporate Intuition Windows and use them as a basic element. Native Intuition Menus may or may not be used as well.&lt;br /&gt;
&lt;br /&gt;
With the use of skinning, 3rd party GUIs can look and feel a lot like Amiga native GUIs. However, they can suffer from reduced speed due to the more intensive use of the CPU. Much of the speed problem can be alleviated through the use of hardware acceleration (e.g. compositing).&lt;br /&gt;
&lt;br /&gt;
== Choosing Toolkits ==&lt;br /&gt;
&lt;br /&gt;
* Choose the toolkit that works best for your application and your end users/customers.&lt;br /&gt;
&lt;br /&gt;
* GadTools gadgets are incompatible with all others and have their own [[GadTools_Library#Function_Reference|set of functions]] to be used with. There is no automatic layout system: GadTools gadgets are not scalable and are at fixed positions and dimensions. Due to its inherent limitations, using GadTools in modern applications can no longer be recommended.&lt;br /&gt;
&lt;br /&gt;
* If you require modern features like scalability, font sensitivity, automatic (re)layouting, or window iconification (the ability to collapse the program window into an icon on the Workbench screen), then use one of the object-oriented toolkits: ReAction or MUI.&lt;br /&gt;
&lt;br /&gt;
* The ASL toolkit is utilized by ReAction and MUI through dedicated classes. There is little need to use ASL directly in object-oriented GUIs.&lt;br /&gt;
&lt;br /&gt;
* The original BOOPSI class set built in Intuition is usable but somewhat limited. Some BOOPSI classes are now actually wrappers for ReAction classes.&lt;br /&gt;
&lt;br /&gt;
* Prefer not to mix datastructure-oriented and object-oriented GUI programming. Functions designed for Intuition windows, gadgets or images are not meant to be used on GUI objects created via ReAction. BOOPSI objects must solely be manipulated using [[BOOPSI_-_Object_Oriented_Intuition#Function_Reference|BOOPSI functions]] (methods).&lt;br /&gt;
&lt;br /&gt;
* Many new programmers seem to be confused about window programming. It&#039;s fairly easy:&lt;br /&gt;
** ReAction elements (objects) must reside in a &#039;&#039;ReAction window&#039;&#039;, that is, a window created through the Window Class, which is part of the toolkit. Such a window is manipulated as any other BOOPSI object.&lt;br /&gt;
** Intuition or GadTools elements must reside in an &#039;&#039;Intuition window&#039;&#039;, that is, a window created through functions OpenWindow() or OpenWindowTags(), which are both part of the Intuition Library. Such windows are manipulated using dedicated functions.&lt;br /&gt;
&lt;br /&gt;
* Despite both being based on BOOPSI, MUI and ReAction classes are not interchangeable. You cannot extend the functionality of ReAction with MUI classes and vice versa.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8070</id>
		<title>UI Style Guide Keyboard</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=UI_Style_Guide_Keyboard&amp;diff=8070"/>
		<updated>2015-09-13T07:56:56Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Updated for OS4, added a note on localizing menu shortcuts.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:User Interface Style Guide]]&lt;br /&gt;
== The Keyboard ==&lt;br /&gt;
&lt;br /&gt;
Your application should make gadget and menu items selectable with the keyboard as well as with the mouse. Often these keyboard equivalents (&amp;quot;hotkeys&amp;quot;) will provide shortcuts and a smoother workflow that is appreciated by the user. Also, allowing the choice conforms to the Amiga credo: &amp;quot;Power to the User&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The set of conventions you should follow to implement this style rule are presented in this section.&lt;br /&gt;
&lt;br /&gt;
== Keyboard Conventions ==&lt;br /&gt;
&lt;br /&gt;
The arrangement of the Amiga&#039;s keyboard varies from model to model, but in general there are always three sets of keys:&lt;br /&gt;
* the standard keyboard&lt;br /&gt;
* special keys&lt;br /&gt;
* modifier keys&lt;br /&gt;
&lt;br /&gt;
=== The Standard Keyboard ===&lt;br /&gt;
&lt;br /&gt;
The standard keys consist of the familiar alphanumeric keys found on any standard typewriter. (In English, this is referred to as the QWERTY keyboard, but in German, since the standard keys are arranged differently, it&#039;s known as the QWERTZ keyboard. It&#039;s called other names in other languages.)&lt;br /&gt;
&lt;br /&gt;
The actual arrangement of the standard keys can vary. To handle this, AmigaOS uses a special facility known as a keymap that allows the user to change the way the keyboard input is mapped so it will correspond to his country&#039;s keyboard layout and characters. For example, to access all the standard German ASCII characters the user can type &amp;quot;Setmap d&amp;quot; in the Shell. Then German characters such as ü and ß will have the same key designations as they do on a standard German keyboard.&lt;br /&gt;
&lt;br /&gt;
Use the keymap facility to handle key assignments on the standard keyboard.&lt;br /&gt;
&lt;br /&gt;
=== Special Keys ===&lt;br /&gt;
&lt;br /&gt;
The special keys include the function keys on the top row of your computer&#039;s keyboard, the Help key, the cursor (arrow) keys, the Del key, the backspace key and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Modifier Keys ===&lt;br /&gt;
&lt;br /&gt;
The modifier keys are the Ctrl, Shift, Alt and Amiga keys found on either side of the space bar. Modifier keys don&#039;t do anything by themselves; they alter the meaning of a key that is pressed at the same time. They are also used to modify the meaning of a mouse selection. For example, holding down the Shift key while clicking with the mouse is used for multiple object selection.&lt;br /&gt;
&lt;br /&gt;
==== Dead Keys ====&lt;br /&gt;
&lt;br /&gt;
A &amp;quot;dead&amp;quot; key is a key, or combination, that does nothing immediately but modifies the output of the next key. For example, on an American keyboard, Alt-H will superimpose a caret (^) symbol over the next appropriate character.&lt;br /&gt;
&lt;br /&gt;
Although relatively unimportant on the American keyboard, dead keys are important in many languages. Keep in mind that you need to work these in manually if you&#039;re doing your own raw key mapping.&lt;br /&gt;
&lt;br /&gt;
== System Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
To provide mouse functions from the keyboard, only three features a needed: a way to press the left mouse button, a way to press the right mouse button, and a way to move the mouse pointer. These are provided by the system. They are listed here so you are aware of them and can avoid using these key combinations for any other purpose.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Keyboard Shortcut&lt;br /&gt;
! Equivalent Mouse Activity&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Left-Alt&lt;br /&gt;
| Same as clicking left mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Right-Alt&lt;br /&gt;
| Same as clicking right mouse button&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Cursor Key&lt;br /&gt;
| Moves mouse pointer&lt;br /&gt;
|-&lt;br /&gt;
| Either Amiga + Shift + Cursor Key&lt;br /&gt;
| Moves mouse pointer in larger steps&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Five additional Amiga system functions have keyboard shortcuts:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Default Keyboard Shortcut&lt;br /&gt;
! Equivalent System Function&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + N&lt;br /&gt;
| Workbench screen to front&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + M&lt;br /&gt;
| Front screen to back&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + B&lt;br /&gt;
| Requester OK (leftmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + V&lt;br /&gt;
| Requester Cancel (rightmost gadget)&lt;br /&gt;
|-&lt;br /&gt;
| Left-Amiga + selection button&lt;br /&gt;
| Drags screen whether the pointer is on the title bar or not&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The first four functions listed above always use Left-Amiga in combination with another key. The second key in the combination can be changed by the user with the IControl preferences editor in your AmigaOS Prefs drawer. For the screen dragging shortcut, the user can change the modifier or combination of modifiers (Ctrl, Amiga, Shift, Alt) that need to be combined with the selection button.&lt;br /&gt;
&lt;br /&gt;
===  The Left-Amiga Key ===&lt;br /&gt;
&lt;br /&gt;
Keep in mind that the Left-Amiga key is reserved at all times for system operations and should never be used as a qualifier for an application keyboard shortcut!&lt;br /&gt;
&lt;br /&gt;
== Application Keyboard Shortcuts ==&lt;br /&gt;
&lt;br /&gt;
Your application should provide a way for the user to bind an application function or ARexx macro to a key or combination of keys. Keyboard access to program features and common functions can speed up workflow significantly.&lt;br /&gt;
&lt;br /&gt;
On the other hand, it’s not necessary to provide hotkeys for every single function in your program: the user wouldn’t be able to remember them anyway. Plus, keyboard shortcuts work best if they are mnemonic and logical: &amp;quot;S&amp;quot; for &amp;quot;Save&amp;quot;, &amp;quot;P&amp;quot; for &amp;quot;Print&amp;quot;, etc. Therefore, save handy mnemonics for common and really important functions, don&#039;t waste them on secondary features.&lt;br /&gt;
&lt;br /&gt;
If you provide a hotkey for an operation that can be dangerous (such as format disk or delete-type operations), make sure the user will get the opportunity to confirm or cancel the action should he/she make an inadvertent key press. The general rule of application design is: never put the user&#039;s data at stake!&lt;br /&gt;
&lt;br /&gt;
=== Gadgets ===&lt;br /&gt;
&lt;br /&gt;
Place an underscore under the letter in the gadget label that activates the gadget. (Note: although the letter is capitalized on the label, the&lt;br /&gt;
default action should react to the lower-case letter. Some Intuition gadgets have a different action for the shifted version of the letter. See the list in the table below.)&lt;br /&gt;
&lt;br /&gt;
The following three rules should apply:&lt;br /&gt;
&lt;br /&gt;
* All action should occur on the down press of the key.&lt;br /&gt;
* The same visual feedback should be given for keyboard activation as is given for mouse activation.&lt;br /&gt;
* Avoid assigning the Enter key to a gadget.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Feedback from Keystroke-Activated Gadgets&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Action button&lt;br /&gt;
| On the down press of the key, the gadget should appear to be pressed in. On release of the key, the gadget should come back out. (Note: at the time this manual was published, GadTools did not support this function.)&lt;br /&gt;
|-&lt;br /&gt;
| Check box&lt;br /&gt;
| Toggle the state of the check mark.&lt;br /&gt;
|-&lt;br /&gt;
| Scrolling list&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Radio button&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Cycle gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Selection gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Scroll gadget&lt;br /&gt;
| Unshifted would cycle forwards through the choices. Shifted would cycle backwards through the choices.&lt;br /&gt;
|-&lt;br /&gt;
| Slider&lt;br /&gt;
| Unshifted would increase the level by one unit. Shifted would decrease the level by one unit.&lt;br /&gt;
|-&lt;br /&gt;
| Text box&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|-&lt;br /&gt;
| Numeric entry&lt;br /&gt;
| Activate the gadget for entry.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Menus ===&lt;br /&gt;
&lt;br /&gt;
Use a Right-Amiga combination as the default keyboard shortcut for a menu item. Here is a list of common hotkeys for an application that has standard menus:&lt;br /&gt;
&lt;br /&gt;
 Project Menu&lt;br /&gt;
  Right-Amiga + N  New&lt;br /&gt;
  Right-Amiga + O  Open...&lt;br /&gt;
  Right-Amiga + S  Save&lt;br /&gt;
  Right-Amiga + A  Save As...&lt;br /&gt;
  Right-Amiga + P  Print&lt;br /&gt;
  Right-Amiga + ?  About...&lt;br /&gt;
  Right-Amiga + Q  Quit Program&lt;br /&gt;
&lt;br /&gt;
 Edit Menu&lt;br /&gt;
  Right-Amiga + X  Cut&lt;br /&gt;
  Right-Amiga + C  Copy&lt;br /&gt;
  Right-Amiga + V  Paste&lt;br /&gt;
  Right-Amiga + Z  Undo&lt;br /&gt;
&lt;br /&gt;
The original User Interface Style Guide suggested in this section that menu shortcuts should be [[Locale_Library|localized]] to reflect the user&#039;s preferred language as set in the system. This recommendation has turned out to be rather short-sighted and impractical: certain hotkeys like the ones listed above are so common, universal and &amp;quot;dyed in the wool&amp;quot; that changing them based on the currently selected locale would only be confusing. Also remember that unlike in gadgets, where the user can see the actual shortcut (it is underscored in the gadget label), you get no direct visual cue when using menu hotkeys because they are hidden in the menu. Thus, a menu hotkey could potentially be dangerous if the user pressed a known key combination that has suddenly changed meaning due to locale change.&lt;br /&gt;
&lt;br /&gt;
=== Requesters ===&lt;br /&gt;
&lt;br /&gt;
Hotkeys for requester buttons should also be provided. Requesters require user interaction: they &amp;quot;get in the way&amp;quot; and can slow down workflow. The user would therefore appreciate requester buttons having keyboard shortcuts (unless there’s a good reason not to provide them, such as safety of operation).&lt;br /&gt;
&lt;br /&gt;
== Use of the Special Keys ==&lt;br /&gt;
&lt;br /&gt;
As stated previously, the special keys are the function keys, the Del key, the Help key, the cursor (arrow) keys and the Esc key.&lt;br /&gt;
&lt;br /&gt;
=== Cursor Keys ===&lt;br /&gt;
&lt;br /&gt;
Cursor keys are a convenient way to control the movement of the cursor inside an application. Here are some standard ways to use them:&lt;br /&gt;
&lt;br /&gt;
==== Unmodified Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move a small amount in the specified direction. Often this is one unit such as a character in a word processor or a pixel in a paint package, but it could be several pixels in an application where navigation is more important than fine control.&lt;br /&gt;
&lt;br /&gt;
==== Shift + Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the window, or shift the view by one window full if you&#039;re already at that extreme.&lt;br /&gt;
&lt;br /&gt;
In applications such as a word processor where the two directions are not symmetrical, shifted cursor keys could take on different meanings. Shifted up and down cursors would page through windowfuls while the shifted left and right cursors would show more on the left and right if there is more to show. Of course it is often the case that the width of the document fits in the window, so the shifted left and right cursors could act as beginning-and end-of-line commands (or edge of window if the cursor isn&#039;t constrained to the form of the text).&lt;br /&gt;
&lt;br /&gt;
==== Alt-Cursor ====&lt;br /&gt;
&lt;br /&gt;
This is used for application-specific functions. It&#039;s usually semantic units such as words in a text processor or fields in a spreadsheet.&lt;br /&gt;
&lt;br /&gt;
==== Ctrl-Cursor ====&lt;br /&gt;
&lt;br /&gt;
Move to the appropriate extreme of the project (beginning, end, extreme left, extreme right).&lt;br /&gt;
&lt;br /&gt;
In the word processor example, the up and down cursor keys would take the cursor to the beginning and end of the file, respectively. But again, if the file fits within the width of the window, the left and right cursor keys combined with Alt would act like their shifted cousins.&lt;br /&gt;
&lt;br /&gt;
==== Function Keys ====&lt;br /&gt;
&lt;br /&gt;
Function keys should generally be reserved for the user to define. If your application does make use of them, then it should at least allow the user to redefine or modify them.&lt;br /&gt;
&lt;br /&gt;
==== Help Key ====&lt;br /&gt;
&lt;br /&gt;
If your application has built-in help, use the Help key to trigger it from the keyboard. Avoid giving the user that helpless feeling he gets when he presses the Help key and nothing happens.&lt;br /&gt;
&lt;br /&gt;
Note that PC keyboards connected to an AmigaOS-compatible computer will not feature the Help key. On such keyboards the Help key will be mapped to the ScrollLock key.&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Memory_Pools&amp;diff=8042</id>
		<title>Exec Memory Pools</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Memory_Pools&amp;diff=8042"/>
		<updated>2015-08-23T18:24:48Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Memory Pool Organization: fixed a typo.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
A memory pool is a block of memory that an application allocates for all its memory needs. The application draws from this pool rather than the system&#039;s free memory list whenever it requires memory.&lt;br /&gt;
&lt;br /&gt;
Memory pools add to the efficiency of the system and applications in two ways. The first is a potential decrease in memory fragmentation. Memory fragmentation is a condition where system memory is made up of blocks of allocated memory interspersed with blocks of free memory. The second benefit of memory pools is faster memory allocations and deallocations.&lt;br /&gt;
&lt;br /&gt;
By having an application take a pool of memory and allocate from it, fragmentation of system memory is decreased. An application may require six allocations ranging from 20 to 678 bytes in size which can be scattered throughout system memory. However, if those same six allocations are taken from a pool, the fragmentation occurs within the pool, but as far as the system is concerned, it&#039;s missing one large block instead of six small ones.&lt;br /&gt;
&lt;br /&gt;
An application using a memory pool will have faster memory allocations because the memory list of a pool is smaller, and therefore easier to traverse than the memory list of the system memory. For deallocations, it&#039;s even faster because deleting the pool itself deletes all the individual allocations made from it instead of having to deallocate each piece that was allocated.&lt;br /&gt;
&lt;br /&gt;
If you know that your memory pool allocations will always be of the same size, prefer using an [[Exec_Item_Pools|item pool]].&lt;br /&gt;
&lt;br /&gt;
== Memory Pool Organization ==&lt;br /&gt;
&lt;br /&gt;
A memory pool consists of units called puddles. Other than that, it has no formal organization. There is no pool structure defined anywhere. All you know about a pool is its address. Exec&#039;s pool manager handles the rest of the details.&lt;br /&gt;
&lt;br /&gt;
When you create a pool, you specify the size of its puddles and a threshold value, not the size of the pool. This is because memory pools are dynamic, they have no fixed size. The pool manager takes the memory for your application from the puddles. As you require memory, the pool manager expands the pool by adding puddles, and as you free memory, the pool manager shrinks the pool by deleting puddles.&lt;br /&gt;
&lt;br /&gt;
The size of the puddles is important because the pool manager will try to use as much of a puddle as possible before adding a new puddle. Each time you allocate memory from the pool, the pool manager takes the memory from a puddle unless an allocation exceeds the amount of memory remaining in a puddle. If the allocation request exceeds the memory remaining in a puddle, the pool manager adds a new puddle. If the allocation exceeds the pool&#039;s puddle size, the pool manager will create a special over-sized puddle to accommodate the allocation.&lt;br /&gt;
&lt;br /&gt;
The key is to use all the drops in a puddle. If you create a pool with puddles of 200 bytes and allocate 150 bytes at a time, you&#039;ll waste 50 bytes in every puddle. Set the puddle size in accordance with your memory requirements.&lt;br /&gt;
&lt;br /&gt;
The threshold value is the size of the largest allocation to use within a single puddle. An allocation request larger than the threshold value causes the pool manager to add a new puddle. Ideally, the threshold value should be the size of the largest allocation you will make.&lt;br /&gt;
&lt;br /&gt;
The relationship between puddle size and threshold can be tuned for optimal utilization of a pool. Threshold sizes cannot exceed puddle sizes and are recommended to be one half the puddle size, so that you can fit two of your largest allocations in a single puddle. However, if you are going to have a mix of large and small allocations, you might want to set a threshold value that allocates a majority of a puddle for a large allocation, and then uses up the remainder of the puddle with the smaller allocations.&lt;br /&gt;
&lt;br /&gt;
== Creating a Memory Pool ==&lt;br /&gt;
&lt;br /&gt;
You create a memory pool by calling AllocSysObject() with the ASOT_MEMPOOL type and specifying the puddle size, threshold size and memory type. The memory type must be of type MEMF_PRIVATE or MEMF_SHARED. The MEMF_ANY type may be used to indicate that either of these memory types is suitable. The MEMF_CLEARED flag may also be specified to automatically clear memory allocated from the pool.&lt;br /&gt;
&lt;br /&gt;
If successful, AllocSysObject() returns the address of the memory pool.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
APTR mem_pool = IExec-&amp;gt;AllocSysObjectTags(ASOT_MEMPOOL,&lt;br /&gt;
  ASOPOOL_Puddle, 4096,&lt;br /&gt;
  ASOPOOL_Threshold, 2048,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (mem_pool != NULL)&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Pool could not be created.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example above attempts to create a pool of memory with a puddle size of 4096 bytes and a threshold size of 2048 bytes.&lt;br /&gt;
&lt;br /&gt;
If your application requires memory of different types (for example, private memory and shared memory), it must create a pool for each type.&lt;br /&gt;
&lt;br /&gt;
Take note, again, that all you know about a pool is its address. Do not poke around it trying to figure out its organization. It&#039;s private for a reason!&lt;br /&gt;
&lt;br /&gt;
== Allocating Memory from a Pool&#039;s Puddles ==&lt;br /&gt;
&lt;br /&gt;
Memory is obtained from a pool&#039;s puddles by calling AllocPooled(). AllocPooled() requires a pointer to the memory pool and the size of the memory block needed. If successful, AllocPooled() returns a pointer to the memory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Rectangle *rect = IExec-&amp;gt;AllocPooled(mem_pool, sizeof(struct Rectangle);&lt;br /&gt;
&lt;br /&gt;
if (rect != NULL)&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
  IDOS-&amp;gt;Printf(&amp;quot;Memory could not be allocated.\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Freeing Memory from a Pool&#039;s Puddles ==&lt;br /&gt;
&lt;br /&gt;
An application can free a block of memory it allocated from a pool by calling FreePooled():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;FreePooled(mem_pool, mem_drop, mem_size);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function requires a pointer to the memory pool (mem_pool), a pointer to the block of memory being freed (mem_drop), and the size of the memory being freed (mem_size).&lt;br /&gt;
&lt;br /&gt;
== Deleting a Memory Pool ==&lt;br /&gt;
&lt;br /&gt;
A memory pool is deleted by calling FreeSysObject() with the ASOT_MEMPOOL type and a pointer to the memory pool you wish to delete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;FreeSysObject(ASOT_MEMPOOL, mem_pool);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deleting a pool also frees the puddles in it. This is quicker than doing individual FreePooled() calls. For example, a text editor will allocate memory for each line of the file being edited. When the editor is exited, each of the allocations has to be freed. With FreeSysObject(), it would take only one call instead of many.&lt;br /&gt;
&lt;br /&gt;
== Concurrent Access ==&lt;br /&gt;
&lt;br /&gt;
Memory pools may be protected from concurrent access by specifying the ASOPOOL_Protected tag when creating the pool.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
APTR mem_pool = IExec-&amp;gt;AllocSysObjectTags(ASOT_MEMPOOL,&lt;br /&gt;
  ASOPOOL_MFlags, MEMF_SHARED,&lt;br /&gt;
  ASOPOOL_Puddle, 4096,&lt;br /&gt;
  ASOPOOL_Threshold, 2048,&lt;br /&gt;
  ASOPOOL_Protected, TRUE,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, the memory is being shared between two Tasks or Processes so it must be of type MEMF_SHARED. By specifying that the memory pool is protected we are guaranteed all operations performed on the memory pool are thread safe.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following table gives a brief description of the Exec functions that control memory pools. See the SDK/Autodocs for more details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocPooled()&lt;br /&gt;
| Allocate memory with the pool manager.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_MEMPOOL)&lt;br /&gt;
| Allocate and initialize a new memory pool.&lt;br /&gt;
|-&lt;br /&gt;
| AllocVecPooled()&lt;br /&gt;
| Allocate memory with the pool manager and track size.&lt;br /&gt;
|-&lt;br /&gt;
| FreePooled()&lt;br /&gt;
| Free pooled memory.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_MEMPOOL)&lt;br /&gt;
| Free a memory pool.&lt;br /&gt;
|-&lt;br /&gt;
| FreeVecPooled()&lt;br /&gt;
| Free pooled memory allocated with AllocVecPooled().&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These Exec functions are now practically obsolete and not recommended for use in new code:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| CreatePool()&lt;br /&gt;
| Use AllocSysObject(ASOT_MEMPOOL) instead.&lt;br /&gt;
|-&lt;br /&gt;
| DeletePool()&lt;br /&gt;
| Use FreeSysObject(ASOT_MEMPOOL) instead.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Datatypes_Library&amp;diff=8041</id>
		<title>Datatypes Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Datatypes_Library&amp;diff=8041"/>
		<updated>2015-08-23T16:03:37Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: Unified spelling of system components. Slightly reworked the Introduction section.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
The purpose of the DataTypes Library is to provide tools for handling data in an object-oriented way. The object-oriented approach means that your application can work with numerous data file standards without having to worry about the complex details of each one. Instead you only need to understand the simple conventions of the library.&lt;br /&gt;
&lt;br /&gt;
The DataTypes Library is built on Intuition&#039;s BOOPSI facility (BOOPSI is an acronym for Basic Object-Oriented Programming System for Intuition). Although not required, it is very helpful to know a little about [[BOOPSI_-_Object_Oriented_Intuition|how BOOPSI works]] before trying to use the DataTypes Library. Some familiarity with object-oriented theory and practice is also helpful, though not required.&lt;br /&gt;
&lt;br /&gt;
Since the library uses the TagItem structure for passing parameters to functions, you will have to understand how TagItems work before you can call the library functions. For more information on TagItems refer to the [[Utility_Library|Utility Library]].&lt;br /&gt;
&lt;br /&gt;
== Spelling Conventions ==&lt;br /&gt;
&lt;br /&gt;
In software development, the term “datatype” or “data type” is often used in the general sense, whereas the AmigaOS DataTypes framework establishes a rather special context. In order to avoid possible confusion, this documentation uses capitalized spelling in reference to components and programming entities that are part of the said framework, such as: the DataTypes Library, a DataType subclass, a DataType object, Picture DataType, etc.&lt;br /&gt;
&lt;br /&gt;
== Why Use DataTypes? ==&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a summary of main properties and features:&lt;br /&gt;
&lt;br /&gt;
* Support for multiple file formats across various types of data (text, sound, image, animation etc.). Load data the easy way without having to write dedicated loader code!&lt;br /&gt;
&lt;br /&gt;
* Simple and consistent handling of multiple data standards. Once you have learned how to manipulate one type of data with the DataTypes Library, you will find that the other types are handled in much the same way.&lt;br /&gt;
&lt;br /&gt;
* Extensible. New data types can be added to those already supported.&lt;br /&gt;
&lt;br /&gt;
* Clipboard support. The DataTypes Library provides a consistent and easy-to-use interface to the Amiga&#039;s [[Clipboard_Device|clipboard device]] to encourage data sharing between applications.&lt;br /&gt;
&lt;br /&gt;
* Intuition gadget support. Because the DataTypes Library is implemented with BOOPSI, the data objects it handles can also be treated as gadgets. Gadget operations can be performed on data objects within Intuition&#039;s task context, the same as other BOOPSI gadgets.&lt;br /&gt;
&lt;br /&gt;
* Automatic conversion from one format to another. Future versions of the DataTypes Library will support other types of data objects. Conversion from one format to another will be automatically handled by the library.&lt;br /&gt;
&lt;br /&gt;
* Validation. For example, you can easily check to see if a file is a valid JPEG (AIFF, AmigaGuide etc.) or not.&lt;br /&gt;
&lt;br /&gt;
= Classes, Objects and Methods =&lt;br /&gt;
&lt;br /&gt;
The jargon used to describe the DataTypes Library may be a little confusing if you have never worked with object-oriented systems before. For instance, the kinds of data supported by the library are divided into &#039;&#039;classes&#039;&#039; and &#039;&#039;subclasses&#039;&#039;.  The term &#039;&#039;class&#039;&#039; is used here in a familiar way; the members of a class simply have a common set of properties. The members of a subclass have all the properties of the parent class (superclass) and additional properties specific to the subclass. (Each subclass can be further broken down into sub-subclasses and so on.)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Class || Ungulate || Has hooves, can run.&lt;br /&gt;
|-&lt;br /&gt;
| Subclass || Cow || Has udder, can be milked (also has hooves and can run).&lt;br /&gt;
|-&lt;br /&gt;
| Object || Daisy || An instance of class Cow; can run and can be milked.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
An actual instance of a class or subclass is referred to as an &#039;&#039;object&#039;&#039;. The term &#039;&#039;object&#039;&#039; is appropriate because in general we want to ignore the details of each individual case and concentrate instead on what we can do with an object based on its class. In the example above the Daisy object can run and can be milked. The operations that can be performed with an object are referred to as &#039;&#039;methods&#039;&#039; and the object is said to &#039;&#039;inherit&#039;&#039; the methods and other attributes of its parent class (which in turn inherits the methods and attributes of its parent class, if it has one).&lt;br /&gt;
&lt;br /&gt;
The datatypes.library implements &#039;&#039;datatypesclass&#039;&#039; from which all other DataType classes inherit from. The following BOOPSI class diagram illustrates how the various DataTypes classes relate to each other.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;classdiagram type=&amp;quot;orderly&amp;quot; scale=&amp;quot;60&amp;quot; dir=&amp;quot;rl&amp;quot;&amp;gt;&lt;br /&gt;
[gadgetclass]-&amp;gt;[rootclass]&lt;br /&gt;
[datatypesclass]-&amp;gt;[gadgetclass]&lt;br /&gt;
[sound.datatype]-&amp;gt;[datatypesclass]&lt;br /&gt;
[picture.datatype]-&amp;gt;[datatypesclass]&lt;br /&gt;
[text.datatype]-&amp;gt;[datatypesclass]&lt;br /&gt;
[amigaguide.datatype]-&amp;gt;[datatypesclass]&lt;br /&gt;
[animation.datatype]-&amp;gt;[datatypesclass]&lt;br /&gt;
[8svx.datatype]-&amp;gt;[sound.datatype]&lt;br /&gt;
[aiff.datatype]-&amp;gt;[sound.datatype]&lt;br /&gt;
[anim.datatype]-&amp;gt;[animation.datatype]&lt;br /&gt;
[ascii.datatype]-&amp;gt;[text.datatype]&lt;br /&gt;
[bmp.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[cdxl.datatype]-&amp;gt;[animation.datatype]&lt;br /&gt;
[gif.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[ilbm.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[jpeg.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[png.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[tiff.datatype]-&amp;gt;[picture.datatype]&lt;br /&gt;
[wav.datatype]-&amp;gt;[sound.datatype]&lt;br /&gt;
&amp;lt;/classdiagram&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ DataTypes Library Object Classes&lt;br /&gt;
! Object Classes&lt;br /&gt;
! Autodoc File Showing the Methods Supported&lt;br /&gt;
! Type of Data Object&lt;br /&gt;
|-&lt;br /&gt;
| Picture class&lt;br /&gt;
| picture_dtc.doc&lt;br /&gt;
| IFF graphic image file&lt;br /&gt;
|-&lt;br /&gt;
| Sound class&lt;br /&gt;
| sound_dtc.doc&lt;br /&gt;
| IFF audio sample file&lt;br /&gt;
|-&lt;br /&gt;
| Text class&lt;br /&gt;
| text_dtc.doc&lt;br /&gt;
| ASCII characters&lt;br /&gt;
|-&lt;br /&gt;
| AmigaGuide class&lt;br /&gt;
| amigaguide_dtc.doc&lt;br /&gt;
| Hypertext databases&lt;br /&gt;
|-&lt;br /&gt;
| Animation class&lt;br /&gt;
| animation_dtc.doc&lt;br /&gt;
| Animations containing graphics and sound&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The examples programs listed below demonstrate how to perform some basic &#039;&#039;methods&#039;&#039; on ILBM and 8SVX class objects.&lt;br /&gt;
&lt;br /&gt;
= DataTypes Class Attributes =&lt;br /&gt;
&lt;br /&gt;
DataType classes have other attributes in addition to the methods (operations) that they support. For each attribute, there is a corresponding TagItem defined in the DataTypes Library that you can use to examine or set that attribute in a particular object. For example, picture objects have a display mode attribute. The tag that controls this attribute is named PDTA_ModeID and is described in the Autodoc file picture_dtc.doc. See the Autodoc files for each class (as shown in Table 1) for a complete list of all class attributes.&lt;br /&gt;
&lt;br /&gt;
The class attribute descriptions in the include files also have a set of codes that indicate the &#039;&#039;applicability&#039;&#039; of the attribute. The codes are as follows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| I - Initialize&lt;br /&gt;
| You can initialize the attribute when the object is created&lt;br /&gt;
|-&lt;br /&gt;
| S - Set&lt;br /&gt;
| You can set the attribute to a new value after the object is created&lt;br /&gt;
|-&lt;br /&gt;
| G - Get&lt;br /&gt;
| You can get the value of the attribute after the object is created&lt;br /&gt;
|-&lt;br /&gt;
| N - Notify&lt;br /&gt;
| Changing the attribute triggers the object to send notification&lt;br /&gt;
|-&lt;br /&gt;
| U - Update&lt;br /&gt;
| Attribute can be set using the object&#039;s OM_UPDATE method&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These codes may seem a little mysterious until you have actually tried using the DataTypes Library. The N and U codes in particular are for special applications that want to implement their own object classes, an advanced topic beyond the scope of this article.&lt;br /&gt;
&lt;br /&gt;
= Basic Functions of the DataTypes Library =&lt;br /&gt;
&lt;br /&gt;
If all these new concepts seem a little daunting, rest assured; the DataTypes Library uses conventional C language function calls to get the job done. The calls you will be using most often are listed below. Notice that for each of these basic functions of DataTypes Library there is an equivalent BOOPSI call in the Intuition Library.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! datatypes.library&lt;br /&gt;
! intuition.library&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| NewDTObject()&lt;br /&gt;
| NewObject()&lt;br /&gt;
| Create a DataType object in memory from a file or clip&lt;br /&gt;
|-&lt;br /&gt;
| DisposeDTObject()&lt;br /&gt;
| DisposeObject()&lt;br /&gt;
| Free an object created earlier with NewDTObject() (or NewObject() )&lt;br /&gt;
|-&lt;br /&gt;
| GetDTAttrs()&lt;br /&gt;
| GetAttr()&lt;br /&gt;
| Get attributes of a DataType object&lt;br /&gt;
|-&lt;br /&gt;
| SetDTAttrs()&lt;br /&gt;
| SetAttrs()&lt;br /&gt;
| Set attributes for a DataType object&lt;br /&gt;
|-&lt;br /&gt;
| DoDTMethod()&lt;br /&gt;
| IDoMethod()&lt;br /&gt;
| Perform the given method (operation) with a DataType object&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are also additional functions used to access DataType objects.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function Name&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| AddDTObject() || Add a DataType object to a window.&lt;br /&gt;
|-&lt;br /&gt;
| RefreshDTObjectA() || Refresh the rendering of a DataType object.&lt;br /&gt;
|-&lt;br /&gt;
| RemoveDTObject() || Remove a DataType object from a window.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTMethods() || Get a list of the methods that a DataTypes object supports. Write, Copy, and Select are examples of methods that an object may support.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTTriggerMethods() || Get a list of the trigger methods that a DataType object supports. An action like Play, Pause, and Resume are examples of trigger methods that an object may support.&lt;br /&gt;
|-&lt;br /&gt;
| PrintDTObject() || Asynchronously print a DataType object.&lt;br /&gt;
|-&lt;br /&gt;
| GetDTString() || Get the localized text string for a DataTypes text id. Useful for obtaining localized error messages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In a typical application the sequence of calls might be performed&lt;br /&gt;
like this:&lt;br /&gt;
&lt;br /&gt;
# Use NewDTObject() to create an object in memory from given data.&lt;br /&gt;
# Get (or perhaps set) attributes of the object using GetDTAttr() (or SetDTAttrs() ).&lt;br /&gt;
# Perform &#039;&#039;methods&#039;&#039; (operations) with the object using DoDTMethod().&lt;br /&gt;
# Free the object and any memory or other resources it was using with the DisposeDTObject() call.&lt;br /&gt;
&lt;br /&gt;
= Basic Structures of the DataTypes Library =&lt;br /&gt;
&lt;br /&gt;
There are a lot of structures used with DataTypes Library function calls; too many to summarize in this article. However, here&#039;s a listing of the relevant include files that contain the structure definitions of interest to class users.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| &amp;lt;datatypes/datatypes.h&amp;gt;&lt;br /&gt;
| Group IDs, error numbers plus library overhead&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/datatypesclass.h&amp;gt;&lt;br /&gt;
| Defines DataType methods and associated structures&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/picture.h&amp;gt;&lt;br /&gt;
| Structures specific to the picture class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/sound.h&amp;gt;&lt;br /&gt;
| Structures specific to the sound class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;datatypes/text.h&amp;gt;&lt;br /&gt;
| Structures specific to the text class&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;libraries/amigaguide.h&amp;gt;&lt;br /&gt;
| Structures and methods for AmigaGuide databases&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
| Defines general BOOPSI object methods&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
| Defines gadget methods and associated structures&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The two most important definitions in these include files appear in &amp;lt;intuition/classusr.h&amp;gt;. The objects used with DataTypes Library functions (and the BOOPSI functions in Intuition) are defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef ULONG   Object;     /* abstract handle */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since we want to treat objects as black boxes and don&#039;t really care how they are implemented, this definition is very appropriate. When a method is performed with an object, the parameter used to identify the method is a Msg structure defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
    ULONG MethodID;&lt;br /&gt;
    /* method-specific data goes here */&lt;br /&gt;
} *Msg;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some methods require more information than just the method identifier. Such methods have a custom structure defined in the include files.  All method structures, however, begin with a field that contains the method ID.&lt;br /&gt;
&lt;br /&gt;
= Creating a DataType Object =&lt;br /&gt;
&lt;br /&gt;
The DataTypes function NewDTObjectA() must be used to create a new DataType object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Object *dto = IDataTypes-&amp;gt;NewDTObjectA (APTR name, struct TagItem *attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pointer that NewDTObjectA() returns is a pointer to a BOOPSI object. Like other BOOPSI objects, DataType objects are &amp;quot;black boxes&amp;quot; and are not to be peeked and poked without using the provided interface.&lt;br /&gt;
&lt;br /&gt;
To create a DataType object, NewDTObjectA() needs to know the where to obtain the data used to create the object. By default the name is treated as file name.&lt;br /&gt;
&lt;br /&gt;
The attrs tag list is a list of tag/value pairs, each of which contains an initial value for an attribute. There are a number of attributes defined in &amp;lt;datatypes/datatypesclass.h&amp;gt; that can be used at creation time.&lt;br /&gt;
&lt;br /&gt;
; DTA_SourceType&lt;br /&gt;
: Specify the type of the source data. The default is DTST_FILE.&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| DTST_RAM || Source data is in RAM.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_FILE || Source data is a file. Name is the name of the file.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_CLIPBOARD || Source data is in the clipboard. Name is the unit number. For example, (APTR)0, for clipboard unit zero.&lt;br /&gt;
|-&lt;br /&gt;
| DTST_HOTLINK || Reserved for future use.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; DTA_Handle&lt;br /&gt;
: Can be used instead of the name field. If the source type is DTST_FILE then handle must be a valid BPTR file handle. If the source type is DTST_CLIPBOARD then handle must be a valid IFFHandle.&lt;br /&gt;
&lt;br /&gt;
; DTA_DataType&lt;br /&gt;
: Can be used to specify the class for handling the data. Data must be a pointer to a valid DataType. This should only be used when attempting to create a new object that doesn&#039;t have source data, or could be handled by multiple classes (for example, could be used to force an object to be handled by the AmigaGuide class).&lt;br /&gt;
&lt;br /&gt;
; DTA_GroupID&lt;br /&gt;
: If this tag is present, then the data must be of the specified type, or the object creation will fail with ERROR_OBJECT_WRONG_TYPE. This can be used by a Sound editor to ensure that only sounds can be loaded, for example.&lt;br /&gt;
&lt;br /&gt;
There are additional attributes that can specified at creation time, but are dependant on the data type of the object being created. See the header files &amp;lt;datatypes/#?class.h&amp;gt; for more attributes.&lt;br /&gt;
&lt;br /&gt;
The following attributes, which are defined in &amp;lt;intuition/gadgetclass.h&amp;gt;, are also valid.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Tag !! Description&lt;br /&gt;
|-&lt;br /&gt;
| GA_Left || Specify the left edge of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelRight || Specify the left edge of the gadget being relative to the right edge of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Top || Specify the top edge of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelBottom || Specify the top edge of the gadget being relative to the bottom edge of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Width || Specify the width of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelWidth || Specify the width of the gadget being relative to the width of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Height || Specify the heigh of the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelHeight || Specify the heigh of the gadget being relative to the height of the containing window.&lt;br /&gt;
|-&lt;br /&gt;
| GA_ID || Specify a ID associated with the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_UserData || Attach application data to the gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Immediate || Indicate that the application should be notified of gadget down events for this gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_RelVerify || Indicate that the application should be notified of gadget up events for this gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GA_Previous || For adding the gadget to a list of gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| GA_DrawInfo || A pointer to a struct DrawInfo.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order for the application to receive information from the DataType object, it must set up a target for the notification attributes that the object sends out.&lt;br /&gt;
&lt;br /&gt;
; ICA_TARGET&lt;br /&gt;
: Specify a target for the notification attributes that the DataType object sends out.&lt;br /&gt;
&lt;br /&gt;
; ICA_MAP&lt;br /&gt;
: Specify an attribute mapping for the notification attributes that the DataType object sends out.&lt;br /&gt;
&lt;br /&gt;
The usual method to obtain notification is to set up an ICA_TARGET of ICTARGET_IDCMP so that the application will receive the attributes via the IDCMP_IDCMPUPDATE Intuition message class. But it is also possible to set up another BOOPSI object as the receiver.&lt;br /&gt;
&lt;br /&gt;
If NewDTObjectA() is successful, it returns a pointer to a DataType object. Otherwise it returns NULL and the reason for failure can be obtained using IoErr(). See the Autodocs for datatypes.library for more information.&lt;br /&gt;
&lt;br /&gt;
= Obtaining Environment Information for a DataType =&lt;br /&gt;
&lt;br /&gt;
In order to embed a DataType object in a window, it is neccessary to ask the object what its minimum environment is. For example, since the remap code doesn&#039;t handle remapping HAM pictures, they must be shown on a HAM screen, and therefore can&#039;t be added to a window that is on a non-HAM screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG modeid = INVALID_ID;&lt;br /&gt;
LONG nomwidth, nomheight;&lt;br /&gt;
BOOL useScreen = FALSE;&lt;br /&gt;
struct dtFrameBox dtf;&lt;br /&gt;
struct FrameInfo fri;&lt;br /&gt;
&lt;br /&gt;
/* Get the attributes that we are interested in */&lt;br /&gt;
IDataTypes-&amp;gt;GetDTAttrs(dto,&lt;br /&gt;
    /* Get the mode ID */&lt;br /&gt;
    PDTA_ModeID,    &amp;amp;modeid,&lt;br /&gt;
&lt;br /&gt;
    /* Get the desired size */&lt;br /&gt;
    DTA_NominalHoriz, &amp;amp;nomwidth,&lt;br /&gt;
    DTA_NominalVert,  &amp;amp;nomheight,&lt;br /&gt;
&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Clear the structures */&lt;br /&gt;
IUtility-&amp;gt;ClearMem(&amp;amp;dtf, sizeof (struct dtFrameBox));&lt;br /&gt;
IUtility-&amp;gt;ClearMem(&amp;amp;fri, sizeof (struct FrameInfo));&lt;br /&gt;
&lt;br /&gt;
/* Fill in the message */&lt;br /&gt;
dtf.MethodID          = DTM_FRAMEBOX;&lt;br /&gt;
dtf.dtf_FrameInfo     = &amp;amp;fri;&lt;br /&gt;
dtf.dtf_ContentsInfo  = &amp;amp;fri;&lt;br /&gt;
dtf.dtf_SizeFrameInfo = sizeof (struct FrameInfo);&lt;br /&gt;
&lt;br /&gt;
/* Perform the frame method */&lt;br /&gt;
if (IDataTypes-&amp;gt;DoDTMethodA (dto, NULL, NULL, (Msg) &amp;amp;dtf))&lt;br /&gt;
{&lt;br /&gt;
  /* Check to see if the object requires a HAM screen */&lt;br /&gt;
  if (fri.fri_PropertyFlags &amp;amp; DIPF_IS_HAM)&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;HAM\n&amp;quot;);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
  /* Check to see if the object requires an ExtraHalfBrite screen */&lt;br /&gt;
  else if (fri.fri_PropertyFlags &amp;amp; DIPF_IS_EXTRAHALFBRITE)&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;ExtraHalfBrite\n&amp;quot;);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
  /* A safety check to see if a screen is required */&lt;br /&gt;
  else if ((fri.fri_PropertyFlags == 0) &amp;amp;&amp;amp; (modeid &amp;amp; 0x800) &amp;amp;&amp;amp; (modeid != INVALID_ID))&lt;br /&gt;
  {&lt;br /&gt;
      IDOS-&amp;gt;Printf (&amp;quot;ModeID=0x%08lx\n&amp;quot;, modeid);&lt;br /&gt;
      useScreen = TRUE;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
  /* No special environment required, can be attached to any screen mode */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Adding a DataType Object to a Window =&lt;br /&gt;
&lt;br /&gt;
A DataType object must be added to a window using the AddDTObject() function of DataTypes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG AddDTObject (struct Window *w, struct Requester *r, Object *dto, LONG pos)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function will add a DataTypes object to the existing gadget list for the specified window. The recommended value for pos is -1 which will cause the DataType object to be added to the end of the list.&lt;br /&gt;
&lt;br /&gt;
DataType objects should not be added using the WA_Gadgets attribute to OpenWindowTagList() or by using the AddGList() function. There is special information that DataTypes requires that will not obtained if any method other than AddDTObject() is used.&lt;br /&gt;
&lt;br /&gt;
When the DataType object is added to the window, the layout method for the object will be invoked. It is possible that the layout will take a while to perform, in that case the object will spawn a process to handle the layout asynchronously. In order to refresh the object&#039;s visual information, it is necessary to obtain IDCMP_IDCMPUPDATE messages from the object and refresh the object when a DTA_Sync attribute is received.&lt;br /&gt;
&lt;br /&gt;
The following code fragment illustrates adding a DataType object to a window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Object *dto;&lt;br /&gt;
&lt;br /&gt;
struct IntuiMessage *imsg;&lt;br /&gt;
struct Window *win;&lt;br /&gt;
ULONG sigr;&lt;br /&gt;
&lt;br /&gt;
struct TagItem *tstate, *tags;&lt;br /&gt;
ULONG tidata;&lt;br /&gt;
ULONG errnum;&lt;br /&gt;
&lt;br /&gt;
BOOL going = TRUE;&lt;br /&gt;
&lt;br /&gt;
/* Set the pertinent attributes of the DataType object */&lt;br /&gt;
IDataTypes-&amp;gt;SetDTAttrs (dto, NULL, NULL,&lt;br /&gt;
&lt;br /&gt;
    /* Set the dimensions of the object */&lt;br /&gt;
    GA_Left,    win-&amp;gt;BorderLeft,&lt;br /&gt;
    GA_Top,     win-&amp;gt;BorderTop,&lt;br /&gt;
    GA_Width,   win-&amp;gt;Width - win-&amp;gt;BorderLeft - win-&amp;gt;BorderRight,&lt;br /&gt;
    GA_Height,  win-&amp;gt;Height - win-&amp;gt;BorderTop - win-&amp;gt;BorderBottom,&lt;br /&gt;
&lt;br /&gt;
    /* Make sure we receive IDCMP_IDCMPUPDATE messages from&lt;br /&gt;
     * the object */&lt;br /&gt;
    ICA_TARGET, ICTARGET_IDCMP,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
/* Add the object to the window */&lt;br /&gt;
IDataTypes-&amp;gt;AddDTObject (win, NULL, dto, -1);&lt;br /&gt;
&lt;br /&gt;
/* Refresh the DataType object */&lt;br /&gt;
IDataTypes-&amp;gt;RefreshDTObjects (dto, win, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
/* Keep going until we&#039;re told to stop */&lt;br /&gt;
while (going)&lt;br /&gt;
{&lt;br /&gt;
    /* Wait for an event */&lt;br /&gt;
    sigr = IExec-&amp;gt;Wait ((1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit) | SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
    /* Did we get a break signal */&lt;br /&gt;
    if (sigr &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        going = FALSE;&lt;br /&gt;
&lt;br /&gt;
    /* Pull Intuition messages */&lt;br /&gt;
    while (imsg = (struct IntuiMessage *) IExec-&amp;gt;GetMsg (win-&amp;gt;UserPort))&lt;br /&gt;
    {&lt;br /&gt;
        /* Handle each message */&lt;br /&gt;
        switch (imsg-&amp;gt;Class)&lt;br /&gt;
        {&lt;br /&gt;
            case IDCMP_IDCMPUPDATE:&lt;br /&gt;
                /* Get a pointer to the attribute list */&lt;br /&gt;
                tstate = tags = (struct TagItem *) imsg-&amp;gt;IAddress;&lt;br /&gt;
&lt;br /&gt;
                /* Step through the attribute list */&lt;br /&gt;
                while (tag = IUtility-&amp;gt;NextTagItem (&amp;amp;tstate))&lt;br /&gt;
                {&lt;br /&gt;
                    tidata = tag-&amp;gt;ti_Data;&lt;br /&gt;
                    switch (tag-&amp;gt;ti_Tag)&lt;br /&gt;
                    {&lt;br /&gt;
                        /* Change in busy state */&lt;br /&gt;
                        case DTA_Busy:&lt;br /&gt;
                            if (tidata)&lt;br /&gt;
                                IIntuition-&amp;gt;SetWindowPointer (win, WA_BusyPointer, TRUE, TAG_END);&lt;br /&gt;
                            else&lt;br /&gt;
                                IIntuition-&amp;gt;SetWindowPointer (win, WA_Pointer, NULL, TAG_END);&lt;br /&gt;
                            break;&lt;br /&gt;
&lt;br /&gt;
                        /* Error message */&lt;br /&gt;
                        case DTA_ErrorLevel:&lt;br /&gt;
                            if (tidata)&lt;br /&gt;
                            {&lt;br /&gt;
                                errnum = IUtility-&amp;gt;GetTagData (DTA_ErrorNumber, NULL, tags);&lt;br /&gt;
                                IDOS-&amp;gt;PrintErrorMsg (errnum, (STRPTR) options[OPT_NAME]);&lt;br /&gt;
                            }&lt;br /&gt;
                            break;&lt;br /&gt;
&lt;br /&gt;
                        /* Time to refresh */&lt;br /&gt;
                        case DTA_Sync:&lt;br /&gt;
                            /* Refresh the DataType object */&lt;br /&gt;
                            IDataTypes-&amp;gt;RefreshDTObjects (dto, win, NULL, NULL);&lt;br /&gt;
                            break;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Done with the message, so reply to it */&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg ((struct Message *) imsg);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Removing a DataType Object from a Window =&lt;br /&gt;
&lt;br /&gt;
A DataType object must be removed from the window using the RemoveDTObject() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG RemoveDTObject (struct Window *w, Object *dto)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function removes the DataType object from the window&#039;s gadget list.&lt;br /&gt;
&lt;br /&gt;
This is the only way that a DataType object should be removed from the window list. Using RemoveGList() is not supported, nor is removing the object manually.&lt;br /&gt;
&lt;br /&gt;
= Setting an Existing DataType Object&#039;s Attributes =&lt;br /&gt;
&lt;br /&gt;
An objects attributes are not necessarily static. An application can ask an object to set certain attributes using the SetDTAttrs() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG SetDTAttrsA (Object *dto, struct Window *w, struct Requester *r, struct TagItem *attrs)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The return value is DataType object specific, but generally a non-zero value means that the object needs to be visually refreshed.&lt;br /&gt;
&lt;br /&gt;
The following fragment illustrates how to set the current top values for a DataType object, using the VarArgs version of SetDTAttrsA().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IDataTypes-&amp;gt;SetDTAttrs(dto, window, NULL,&lt;br /&gt;
    DTA_TopVert,  0,&lt;br /&gt;
    DTA_TopHoriz, 0,&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will cause the DataType object to update its vertical and horizontal top values. If the object has been added to a window, then the display will be updated accordingly.&lt;br /&gt;
&lt;br /&gt;
Note that it is not OK to call SetGadgetAttrs() or SetAttrs() on a DataType object.&lt;br /&gt;
&lt;br /&gt;
= Getting a DataType Object&#039;s Attributes =&lt;br /&gt;
&lt;br /&gt;
The DataTypes function GetDTAttrsA() is used to obtain the values for a list of attributes from a DataType object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG GetDTAttrsA (Object *dto, struct TagItem *attrs)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where dto is a pointer to a DataType object returned by NewDTObjectA().&lt;br /&gt;
&lt;br /&gt;
And attrs is a TAG_END terminated array of attributes. Where the data element of each pair contains the address of the storage variable for that attribute.&lt;br /&gt;
&lt;br /&gt;
This function will return a number that indicates that number of attributes that it was able to obtain. For example if four attributes asked for and GetDTAttrs returns a four, then all the attributes were obtained.&lt;br /&gt;
&lt;br /&gt;
The following code fragment illustrates how to get the current top values&lt;br /&gt;
for a DataTypes object using the VarArgs form of GetDTAttrsA().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG topv, toph;&lt;br /&gt;
&lt;br /&gt;
if (IDataTypes-&amp;gt;GetDTAttrs (dto, DTA_TopVert, &amp;amp;topv, DTA_TopHoriz, &amp;amp;toph, TAG_END) == 2)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf (&amp;quot;Top: vertical=%ld, horizontal=%ld\n&amp;quot;, topv, toph);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf (&amp;quot;couldn&#039;t obtain the top values\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= A Simple DataTypes Example =&lt;br /&gt;
&lt;br /&gt;
The example program listed here should clarify some of the concepts discussed so far. Suppose you have a communications program and want to add the capability of playing back a user-specified 8SVX sample file for the bell sound (Ctrl-G). The program below shows how to play a sound with the DataTypes Library.&lt;br /&gt;
&lt;br /&gt;
In this program, objects are of class 8SVX (a subclass of the Sound DataType). The method performed with the object is named DTM_TRIGGER (described in the Autodoc file sound_dtc.doc). The DTM_TRIGGER method (with type set to STM_PLAY) causes a sampled sound to be played on the Amiga&#039;s audio hardware. Since the DTM_TRIGGER method requires other information in addition to the method ID, a dtTrigger structure is used. This structure is defined in &amp;lt;datatypes/datatypesclass.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Note that if the Sound DataType is enhanced to support other types of sound files in a future version of AmigaOS, the code given here will automatically support the new type. This example expects the file name and path to a sound file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Run from CLI only. */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypesclass.h&amp;gt;  /* This includes other files we need */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;          /* Prototypes for system functions */&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/datatypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
struct DataTypesIFace *IDataTypes = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  APTR dtobject = NULL       /* Pointer to a DataTypes object */&lt;br /&gt;
  struct dtTrigger mydtt;    /* A trigger structure for the DTM_TRIGGER method */&lt;br /&gt;
&lt;br /&gt;
  if(argc &amp;lt;= 1 ) /* CLI only, at least one argument please. */&lt;br /&gt;
  {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Give a file name too.\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  struct Library *DataTypesBase = IExec-&amp;gt;OpenLibrary(&amp;quot;datatypes.library&amp;quot;, 50);&lt;br /&gt;
  IDataTypes = (struct DataTypesIFace*)IExec-&amp;gt;GetInterface(DataTypesBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
 &lt;br /&gt;
  if (IIntuition != NULL &amp;amp;&amp;amp; IDataTypes != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    /* Attempt to make an 8svx sound object from the file name the user */&lt;br /&gt;
    /* specified in the command line.  For a list of possible error     */&lt;br /&gt;
    /* returns, see the Autodocs for NewDTObjectA().  The group ID tag  */&lt;br /&gt;
    /* will allow only Sound DataType files to be accepted for the call.*/&lt;br /&gt;
    if (dtobject = IDataTypes-&amp;gt;NewDTObject(argv[1], DTA_GroupID, GID_SOUND,&lt;br /&gt;
                                                    TAG_END) )&lt;br /&gt;
    {&lt;br /&gt;
      mydtt.MethodID     = DTM_TRIGGER; /* Fill in the dtTrigger struct */&lt;br /&gt;
      mydtt.dtt_GInfo    = NULL;&lt;br /&gt;
      mydtt.dtt_Function = STM_PLAY;&lt;br /&gt;
      mydtt.dtt_Data     = NULL;&lt;br /&gt;
&lt;br /&gt;
      /* The return value of the DTM_TRIGGER method used with the 8svx */&lt;br /&gt;
      /* Sound DataType is undefined in V39.  This is likely to change */&lt;br /&gt;
      /* in future versions of the Amiga operating system.             */&lt;br /&gt;
      uint32 dores = IDataTypes-&amp;gt;DoDTMethodA(dtobject, NULL, NULL, &amp;amp;mydtt);&lt;br /&gt;
&lt;br /&gt;
      // Let the 8svx sound finish playing. Another way is to use&lt;br /&gt;
      // SDTA_SignalTask and SDTA_SignalBit to find out when it is&lt;br /&gt;
      // finished playing.&lt;br /&gt;
      IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
      IDataTypes-&amp;gt;DisposeDTObject(dtobject);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create new object or not a sound data file\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t open interfaces\n&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IDataTypes);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(DataTypesBase);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to playing back a sampled sound, the DataTypes Library allows sound objects to become gadgets (the library includes default imagery for a sound gadget). Since all DataTypes are implemented as a subclass of the BOOPSI &#039;&#039;gadgetclass&#039;&#039;, they all support the methods of gadget objects as described in the [[BOOPSI_-_Object_Oriented_Intuition|BOOPSI chapter]] in the Libraries section.&lt;br /&gt;
&lt;br /&gt;
= Embedding DataTypes =&lt;br /&gt;
&lt;br /&gt;
Since DataTypes are a subclass of the Intuition &#039;&#039;gadgetclass&#039;&#039;, DataType objects can be attached to an Intuition window in a similiar way that gadgets can be added to a window. DataTypes use a parallel set of functions because it requires additional information that the Intuition functions weren&#039;t able to provide.&lt;br /&gt;
&lt;br /&gt;
Currently the handling of the data is limited to reading, writing, printing, viewing (audio or visual), and clipboard access. Utilities like MultiView or MultiViewer are examples of applications that can embed DataType objects.&lt;br /&gt;
&lt;br /&gt;
= Determining Data Type =&lt;br /&gt;
&lt;br /&gt;
One of the main features of the DataTypes system is its ability to determine the type of a block of data. This data block can reside in a file or the clipboard.&lt;br /&gt;
&lt;br /&gt;
The following functions are used to determine the DataType of a data block:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| ObtainDataTypeA() || Obtain the DataType descriptor for a data block.&lt;br /&gt;
|-&lt;br /&gt;
| ReleaseDataType() || Release the DataType descriptor for a data block.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The data type detection functions use the DataType structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DataType&lt;br /&gt;
{&lt;br /&gt;
    struct Node            dtn_Node1;&lt;br /&gt;
    struct Node            dtn_Node2;&lt;br /&gt;
    struct DataTypeHeader *dtn_Header;&lt;br /&gt;
    struct List            dtn_ToolList;&lt;br /&gt;
    STRPTR                 dtn_FunctionName;&lt;br /&gt;
    struct TagItem        *dtn_AttrList;&lt;br /&gt;
    ULONG                  dtn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The DataType structure is read-only. The only pertinent field is the dtn_Header field, which points to a DataTypeHeader structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DataTypeHeader&lt;br /&gt;
{&lt;br /&gt;
    STRPTR   dth_Name;&lt;br /&gt;
    STRPTR   dth_BaseName;&lt;br /&gt;
    STRPTR   dth_Pattern;&lt;br /&gt;
    WORD    *dth_Mask;&lt;br /&gt;
    ULONG    dth_GroupID;&lt;br /&gt;
    ULONG    dth_ID;&lt;br /&gt;
    WORD     dth_MaskLen;&lt;br /&gt;
    WORD     dth_Pad;&lt;br /&gt;
    UWORD    dth_Flags;&lt;br /&gt;
    WORD     dth_Priority;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The DataTypeHeader structure fields are as follows:&lt;br /&gt;
&lt;br /&gt;
; dth_Name&lt;br /&gt;
: Descriptive name of the data type. For example, the description for an ILBM data type could possibly be &amp;quot;Amiga BitMap Picture&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
; dth_BaseName&lt;br /&gt;
: This is the base name for the data type and is used to obtain the class that handles this data type.&lt;br /&gt;
&lt;br /&gt;
; dth_GroupID&lt;br /&gt;
: This identifies the general type of data that the object contains. Following are the possible values:&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| GID_SYSTEM || Fonts, Executables, Libraries, Devices, etc...&lt;br /&gt;
|-&lt;br /&gt;
| GID_TEXT || Formatted or unformatted text.&lt;br /&gt;
|-&lt;br /&gt;
| GID_DOCUMENT || Formatted text with embedded DataTypes (such as pictures).&lt;br /&gt;
|-&lt;br /&gt;
| GID_SOUND || Audio samples.&lt;br /&gt;
|-&lt;br /&gt;
| GID_INSTRUMENT || Audio samples used for playing music.&lt;br /&gt;
|-&lt;br /&gt;
| GID_MUSIC || Musical scores.&lt;br /&gt;
|-&lt;br /&gt;
| GID_PICTURE || Graphic picture or brush.&lt;br /&gt;
|-&lt;br /&gt;
| GID_ANIMATION || Moving picture or cartoon.&lt;br /&gt;
|-&lt;br /&gt;
| GID_MOVIE || Moving picture or cartoon with sound.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; dth_ID&lt;br /&gt;
: This is an individual indentifier for the DataType. For IFF files it is the same as the FORM type, for example ILBM for an Amiga BitMap picture. For non-IFF files, it is the first four characters of dth_Name.&lt;br /&gt;
&lt;br /&gt;
; dth_Flags&lt;br /&gt;
: The flags field contains, among other information, the coarse type of data. The type can be obtained by ANDing DTF_TYPE_MASK with this field.&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| DTF_IFF || Interchange File Format&lt;br /&gt;
|-&lt;br /&gt;
| DTF_BINARY || Non-readable characters&lt;br /&gt;
|-&lt;br /&gt;
| DTF_ASCII || Readable characters&lt;br /&gt;
|-&lt;br /&gt;
| DTF_MISC || Disks and drawers&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; dth_Pattern&lt;br /&gt;
; dth_Mask&lt;br /&gt;
; dth_MaskLen&lt;br /&gt;
; dth_Priority&lt;br /&gt;
: These fields are used by the detection code in datatypes.library for determining the data type. See the &amp;quot;Defining a DataType Descriptor&amp;quot; section for more information.&lt;br /&gt;
&lt;br /&gt;
Following is a code fragment that shows how to determine the data type of a file. This fragment uses functions from the DataTypes Library, DOS Library, and IFFParse Library.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
    STRPTR name = &amp;quot;somefilename&amp;quot;;&lt;br /&gt;
    BPTR lock;&lt;br /&gt;
&lt;br /&gt;
    struct DataTypeHeader *dth;&lt;br /&gt;
    struct DataType *dtn;&lt;br /&gt;
    UBYTE idesc[5];&lt;br /&gt;
    STRPTR tdesc;&lt;br /&gt;
    STRPTR gdesc;&lt;br /&gt;
    UWORD ttype;&lt;br /&gt;
&lt;br /&gt;
    /* Obtain a lock on the file that we want information on */&lt;br /&gt;
    if (lock = IDOS-&amp;gt;Lock (name, ACCESS_READ))&lt;br /&gt;
    {&lt;br /&gt;
	/* Get a pointer to the appropriate DataType structure */&lt;br /&gt;
	if (dtn = IDataTypes-&amp;gt;ObtainDataTypeA (DTST_FILE, (APTR)lock, NULL))&lt;br /&gt;
	{&lt;br /&gt;
	    /* Get a pointer to the DataTypeHeader structure */&lt;br /&gt;
	    dth = dtn-&amp;gt;dtn_Header;&lt;br /&gt;
&lt;br /&gt;
	    /* Get the coarse type */&lt;br /&gt;
	    ttype = dth-&amp;gt;dth_Flags &amp;amp; DTF_TYPE_MASK;&lt;br /&gt;
&lt;br /&gt;
	    /* Get a pointer to the text strings */&lt;br /&gt;
	    tdesc = IDataTypes-&amp;gt;GetDTString (ttype + DTMSG_TYPE_OFFSET);&lt;br /&gt;
	    gdesc = IDataTypes-&amp;gt;GetDTString (dth-&amp;gt;dth_GroupID);&lt;br /&gt;
&lt;br /&gt;
	    /* Convert the ID to a string. */&lt;br /&gt;
	    IIFFParse-&amp;gt;IDtoStr (dth-&amp;gt;dth_ID, idesc);&lt;br /&gt;
&lt;br /&gt;
	    /* Display the information */&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;   Description: %s\n&amp;quot;, dth-&amp;gt;dth_Name);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;     Base Name: %s\n&amp;quot;, dth-&amp;gt;dth_BaseName);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;          Type: %d - %s\n&amp;quot;, ttype, tdesc);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;         Group: %s\n&amp;quot;, gdesc);&lt;br /&gt;
	    IDOS-&amp;gt;Printf (&amp;quot;            ID: %s\n&amp;quot;, idesc);&lt;br /&gt;
&lt;br /&gt;
	    /* Release the DataType structure now that we are done with it */&lt;br /&gt;
	    IDataTypes-&amp;gt;ReleaseDataType (dtn);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Release the DOS lock on the file */&lt;br /&gt;
	IDOS-&amp;gt;UnLock (lock);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= A Picture Class Example =&lt;br /&gt;
&lt;br /&gt;
Here is a second, more complex example showing how to use all the DataTypes Library functions described so far. In this example the objects used are of class ILBM, a subclass of the Picture DataType.&lt;br /&gt;
&lt;br /&gt;
Two methods will be performed with the object, DTM_PROCLAYOUT and DTM_FRAMEBOX. Both these methods have associated structures (gpLayout and dtFrameBox respectively). DTM_PROCLAYOUT makes the object available within the context of your application task (as opposed to Intuition&#039;s). DTM_FRAMEBOX queries the display environment required by the picture.&lt;br /&gt;
&lt;br /&gt;
Other attributes of the picture are obtained with a call to GetDTAttrs() and then a matching Intuition screen is created and the ILBM object is displayed. This example expects the file and path name of a picture file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
; /* Compiled with SAS/C 6.56.  Run from CLI only.&lt;br /&gt;
sc DATA=NEAR NMINC STRMERGE NOSTKCHK IGNORE=73 dtpic.c&lt;br /&gt;
slink from lib:c.o dtpic.o TO dtpic LIBRARY lib:sc.lib lib:amiga.lib&lt;br /&gt;
quit ; */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;datatypes/datatypes.h&amp;gt;      /* DataTypes definitions we need */&lt;br /&gt;
#include &amp;lt;datatypes/pictureclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;clib/exec_protos.h&amp;gt;         /* Prototypes for system functions */&lt;br /&gt;
#include &amp;lt;clib/intuition_protos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/alib_protos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/datatypes_protos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;clib/graphics_protos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifdef LATTICE&lt;br /&gt;
int CXBRK(void)    { return(0); }     /* Disable SAS/C CTRL-C handling */&lt;br /&gt;
int chkabort(void) { return(0); }&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase=NULL;   /* System library bases */&lt;br /&gt;
struct Library *GfxBase=NULL;&lt;br /&gt;
struct Library *DataTypesBase=NULL;&lt;br /&gt;
&lt;br /&gt;
VOID main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
APTR dtobject=NULL;                   /* Pointer to a DataTypes object       */&lt;br /&gt;
ULONG res;                            /* Variable for function return values */&lt;br /&gt;
struct dtFrameBox mydtFrameBox;       /* Use this with DTM_FRAMEBOX method   */&lt;br /&gt;
struct FrameInfo myFrameInfo;         /* For info returned from DTM_FRAMEBOX */&lt;br /&gt;
struct gpLayout mygpLayout;           /* Use this with DTM_PROCLAYOUT method */&lt;br /&gt;
&lt;br /&gt;
ULONG modeID = INVALID_ID;            /* Variables for storing the display */&lt;br /&gt;
struct Screen *myScreen=NULL;         /* environment information obtained  */&lt;br /&gt;
struct BitMap *bm = NULL;             /* the DataType object.              */&lt;br /&gt;
ULONG *cregs = NULL;&lt;br /&gt;
ULONG i,r,g,b,numcolors;&lt;br /&gt;
&lt;br /&gt;
if (IntuitionBase=OpenLibrary(&amp;quot;intuition.library&amp;quot;,39L))&lt;br /&gt;
  {&lt;br /&gt;
  if (GfxBase=OpenLibrary(&amp;quot;graphics.library&amp;quot;,39L))&lt;br /&gt;
    {&lt;br /&gt;
    if(DataTypesBase=OpenLibrary(&amp;quot;datatypes.library&amp;quot;,0L))&lt;br /&gt;
        {&lt;br /&gt;
        if(argc &amp;gt; 1 ) /* CLI only, at least one argument please.  */&lt;br /&gt;
            {&lt;br /&gt;
            /* Attempt to create a picture object in memory from the file    */&lt;br /&gt;
            /* name given by the user in the command line.  If we wanted to  */&lt;br /&gt;
            /* show the picture in a screen set up ahead of time, we could   */&lt;br /&gt;
            /* set PDTA_Remap to TRUE and provide a pointer to the screen    */&lt;br /&gt;
            /* with the PDTA_Screen tag (datatypes.library handles the rest).*/&lt;br /&gt;
            /* However in this case we want to first find out the attributes */&lt;br /&gt;
            /* of the picture object and set up a matching screen and do the */&lt;br /&gt;
            /* remapping later.  Therefore PDTA_Remap is set to false.       */&lt;br /&gt;
            /* The group ID tag ensures that we get only a picture file type.*/&lt;br /&gt;
            if (dtobject = NewDTObject(argv[1], PDTA_Remap,  FALSE,&lt;br /&gt;
                                                DTA_GroupID, GID_PICTURE,&lt;br /&gt;
                                                TAG_END) )&lt;br /&gt;
                {&lt;br /&gt;
                /* Here we want to find the display environment required by  */&lt;br /&gt;
                /* this picture.  To do that, perform the DTM_FRAMEBOX method */&lt;br /&gt;
                /* on the object.  The DataTypes Library fills in the struct */&lt;br /&gt;
                /* FrameBox you give it with the info on the display needed.  */&lt;br /&gt;
                mydtFrameBox.MethodID         = DTM_FRAMEBOX;&lt;br /&gt;
                mydtFrameBox.dtf_GInfo        = NULL;&lt;br /&gt;
                mydtFrameBox.dtf_ContentsInfo = NULL;&lt;br /&gt;
                mydtFrameBox.dtf_FrameInfo    = &amp;amp;myFrameInfo;&lt;br /&gt;
                mydtFrameBox.dtf_SizeFrameInfo= sizeof (struct FrameInfo);&lt;br /&gt;
                mydtFrameBox.dtf_FrameFlags   = 0L;&lt;br /&gt;
&lt;br /&gt;
                /* The return value from DTM_FRAMEBOX is currently undefined */&lt;br /&gt;
                res = DoMethodA(dtobject, &amp;amp;mydtFrameBox);&lt;br /&gt;
&lt;br /&gt;
                /* OK, now do the layout (remap) of the object on our process */&lt;br /&gt;
                mygpLayout.MethodID   = DTM_PROCLAYOUT;&lt;br /&gt;
                mygpLayout.gpl_GInfo  = NULL;&lt;br /&gt;
                mygpLayout.gpl_Initial= 1L;&lt;br /&gt;
&lt;br /&gt;
                /* The return value of DTM_PROCLAYOUT is non-zero for success */&lt;br /&gt;
                if( res = DoMethodA(dtobject, &amp;amp;mygpLayout) )&lt;br /&gt;
                   {&lt;br /&gt;
                   /* Get the attributes of this picture object.  You could  */&lt;br /&gt;
                   /* use a series of GetAttr() function calls here instead.  */&lt;br /&gt;
                   res = GetDTAttrs(dtobject, PDTA_ModeID, &amp;amp;modeID,&lt;br /&gt;
                                              PDTA_CRegs, &amp;amp;cregs,&lt;br /&gt;
                                              PDTA_BitMap, &amp;amp;bm,&lt;br /&gt;
                                              TAG_END);&lt;br /&gt;
&lt;br /&gt;
                   /* Did we get all threee attributes? */&lt;br /&gt;
                   if( (modeID!=INVALID_ID) &amp;amp;&amp;amp; (cregs) &amp;amp;&amp;amp; (bm) )&lt;br /&gt;
                       {&lt;br /&gt;
                       /* Open a screen that matches the picture object */&lt;br /&gt;
                       if( myScreen = OpenScreenTags( NULL,&lt;br /&gt;
                           SA_Width,     myFrameInfo.fri_Dimensions.Width,&lt;br /&gt;
                           SA_Height,    myFrameInfo.fri_Dimensions.Height,&lt;br /&gt;
                           SA_Depth,     myFrameInfo.fri_Dimensions.Depth,&lt;br /&gt;
                           SA_DisplayID, modeID,&lt;br /&gt;
                           SA_BitMap,    bm,&lt;br /&gt;
                           TAG_END) )&lt;br /&gt;
                           {&lt;br /&gt;
                           /* Now fill in the color registers for this screen */&lt;br /&gt;
                           numcolors = 2&amp;lt;&amp;lt;(myFrameInfo.fri_Dimensions.Depth-1);&lt;br /&gt;
                           for( i=0; i &amp;lt; numcolors; i++ )&lt;br /&gt;
                              {&lt;br /&gt;
                              r = cregs[i * 3 + 0];&lt;br /&gt;
                              g = cregs[i * 3 + 1];&lt;br /&gt;
                              b = cregs[i * 3 + 2];&lt;br /&gt;
                              SetRGB32(&amp;amp;myScreen-&amp;gt;ViewPort, i, r, g, b);&lt;br /&gt;
                              }&lt;br /&gt;
&lt;br /&gt;
                           printf(&amp;quot;Ctrl-C in this window to quit\n&amp;quot;);&lt;br /&gt;
                           /* Wait for the user to have a look...  */&lt;br /&gt;
                           Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                           CloseScreen(myScreen);&lt;br /&gt;
                           }&lt;br /&gt;
                       else printf(&amp;quot;Couldn&#039;t open required screen\n&amp;quot;);&lt;br /&gt;
                       }&lt;br /&gt;
                   else printf(&amp;quot;Couldn&#039;t get picture attributes\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                  DisposeDTObject(dtobject);&lt;br /&gt;
                  }&lt;br /&gt;
               else printf(&amp;quot;Couldn&#039;t perform PROC_LAYOUT\n&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
            else printf(&amp;quot;Couldn&#039;t create new object or not a picture file\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        else printf(&amp;quot;Give a file name too.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        CloseLibrary(DataTypesBase);&lt;br /&gt;
        }&lt;br /&gt;
    else printf(&amp;quot;Can&#039;t open DataTypes Library\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CloseLibrary(GfxBase);&lt;br /&gt;
    }&lt;br /&gt;
  else printf(&amp;quot;Can&#039;t open V39 graphics\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  CloseLibrary(IntuitionBase);&lt;br /&gt;
  }&lt;br /&gt;
else printf(&amp;quot;Can&#039;t open V39 Intuition\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with 8SVX objects, the DataTypes Library allows ILBM objects to be treated as gadgets. Remember that all DataTypes are a subclass of the BOOPSI &#039;&#039;gadgetclass&#039;&#039; and therefore support the gadget methods described in [[BOOPSI_-_Object_Oriented_Intuition|the BOOPSI chapter]].&lt;br /&gt;
&lt;br /&gt;
= Function Reference =&lt;br /&gt;
&lt;br /&gt;
(to be provided)&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Input_Device&amp;diff=8040</id>
		<title>Input Device</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Input_Device&amp;diff=8040"/>
		<updated>2015-08-22T19:46:55Z</updated>

		<summary type="html">&lt;p&gt;Daniel Jedlicka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Devices|Input]]{{CodeReview}}&lt;br /&gt;
== Input Device ==&lt;br /&gt;
&lt;br /&gt;
The input device is the central collection point for input events disseminated throughout the system. The best way to describe the input device is a manager of a stream with feeders. The input device itself and other modules such as the file system add events to the stream; so do input device “users”—programs or other devices that use parts of the stream or change it in some way. Feeders of the input device include the keyboard, timer and gameport devices. The keyboard, gameport, and timer devices are special cases in that the input device opens them and asks them for input. Users of the input device include Intuition and the console device.&lt;br /&gt;
&lt;br /&gt;
== Input Device Commands and Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command&lt;br /&gt;
! Command Operation&lt;br /&gt;
|-&lt;br /&gt;
| CMD_FLUSH || Purge all active and queued requests for the input device.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_RESET || Reset the input port to its initialized state. All active and queued I/O requests will be aborted. Restarts the device if it has been stopped.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_START || Restart the currently active input (if any) and resume queued I/O requests.&lt;br /&gt;
|-&lt;br /&gt;
| CMD_STOP || Stop any currently active input and prevent queued I/O requests from starting.&lt;br /&gt;
|-&lt;br /&gt;
| IND_ADDEVENT || Propagate an input event to all handlers, updating event qualifiers.&lt;br /&gt;
|-&lt;br /&gt;
| IND_ADDHANDLER || Add an input-stream handler into the handler chain.&lt;br /&gt;
|-&lt;br /&gt;
| IND_ADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes.&lt;br /&gt;
|-&lt;br /&gt;
| IND_GETHANDLERLIST || Obtain a pointer to the list of input handlers.&lt;br /&gt;
|-&lt;br /&gt;
| IND_GETKDEVICE || Query the name and unit number of the device keyboard input events are collected from.&lt;br /&gt;
|-&lt;br /&gt;
| IND_GETMDEVICE || Query the name and unit number of the device mouse input events are collected from.&lt;br /&gt;
|-&lt;br /&gt;
| IND_GETPERIOD || Get the key repeat period.&lt;br /&gt;
|-&lt;br /&gt;
| IND_GETTHRESH || Get the key repeat threshold.&lt;br /&gt;
|-&lt;br /&gt;
| IND_IMMEDIATEADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes. The hook will be invoked on the current configuration first. &lt;br /&gt;
|-&lt;br /&gt;
| IND_REMHANDLER || Remove an input-stream handler from the handler chain.&lt;br /&gt;
|-&lt;br /&gt;
| IND_REMOVENOTIFY || Remove a hook previously installed with the IND_ADDNOTIFY command.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETKDEVICE || Choose the device to collect keyboard input events from.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETMDEVICE || Choose the device to collect mouse input events from.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETMPORT || Set the controller port to which the mouse is connected.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETMTRIG || Set conditions that must be met by a mouse before a pending read request will be satisfied.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETMTYPE || Set the type of device at the mouse port.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETPERIOD || Set the period at which a repeating key repeats.&lt;br /&gt;
|-&lt;br /&gt;
| IND_SETTHRESH || Set the repeating key hold-down time before repeat starts.&lt;br /&gt;
|-&lt;br /&gt;
| IND_WRITEEVENT || Propagate an input event stream to all devices.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Input Device Functions&lt;br /&gt;
| PeekQualifier() || Return the input device’s current qualifiers.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Device Interface ==&lt;br /&gt;
&lt;br /&gt;
The input device operates like the other Amiga devices. To use it, you must first open the input device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.&lt;br /&gt;
&lt;br /&gt;
A number of structures are used by the input device to do its processing. Some are used to pass commands and data to the device, some are used to describe input events like mouse movements and key depressions, and one structure is used to describe the environment for input event handlers.&lt;br /&gt;
&lt;br /&gt;
The I/O request used by the input device for most commands is IOStdReq.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct IOStdReq&lt;br /&gt;
{&lt;br /&gt;
    struct Message io_Message; /* message reply port */&lt;br /&gt;
    struct Device  *io_Device; /* device node pointer */&lt;br /&gt;
    struct Unit    *io_Unit;   /* unit */&lt;br /&gt;
    UWORD  io_Command;         /* input device command */&lt;br /&gt;
    UBYTE  io_Flags;           /* input device flags */&lt;br /&gt;
    BYTE   io_Error;           /* error code */&lt;br /&gt;
    ULONG  io_Length;          /* number of bytes to transfer */&lt;br /&gt;
    APTR   io_Data;            /* pointer to data area */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file exec/io.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
Two of the input device commands—IND_SETTHRESH and IND_SETPERIOD—require a time specification and &#039;&#039;must&#039;&#039; use a TimeRequest structure instead of an IOStdReq.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TimeRequest&lt;br /&gt;
{&lt;br /&gt;
    struct IORequest Request;&lt;br /&gt;
    struct TimeVal Time;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, the TimeRequest structure includes an IORequest structure. The io_Command field of the IORequest indicates the command to the input device and the TimeVal structure sets the time values. See the include file devices/timer.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=In Case You Feel Like Reinventing the Wheel...|text=You could define a “super-IORequest” structure for the input device which would combine the IOStdReq fields with the TimeVal structure of the TimeRequest structure.}}&lt;br /&gt;
&lt;br /&gt;
=== Opening the Input Device ===&lt;br /&gt;
&lt;br /&gt;
Three primary steps are required to open the input device:&lt;br /&gt;
&lt;br /&gt;
* Create a message port using AllocSysObject() and ASOT_PORT type. Reply messages from the device must be directed to a message port.&lt;br /&gt;
* Create an extended I/O request structure of type IOStdReq or TimeRequest using AllocSysObject() and ASOT_IOREQUEST type. The I/O request created by the AllocSysObject() function will be used to pass commands and data to the input device.&lt;br /&gt;
* Open the Input device. Call OpenDevice(), passing the I/O request.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort *InputMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (InputMP != NULL)&lt;br /&gt;
{&lt;br /&gt;
    struct IOStdReq *InputIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
        ASOIOR_ReplyPort, InputMP,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (ClipIO != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        if (IExec-&amp;gt;OpenDevice(&amp;quot;input.device&amp;quot;, 0, (struct IORequest *)InputIO, 0))&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;input.device did not open\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above code will work for all the input device commands except for the ones which require a time specification. For those, the code would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *InputMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (InputMP != NULL)&lt;br /&gt;
{&lt;br /&gt;
    struct TimeRequest *InputIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
        ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
        ASOIOR_ReplyPort, InputMP,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (ClipIO != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        if (IExec-&amp;gt;OpenDevice(&amp;quot;input.device&amp;quot;, 0, (struct IORequest *)InputIO, 0))&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;input.device did not open\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Input Device Event Types ===&lt;br /&gt;
&lt;br /&gt;
The input device is automatically opened by the console device when the system boots. When the input device is opened, a task named “input.device” is started. The input device task communicates directly with the keyboard device to obtain raw key events. It also communicates with the gameport device to obtain mouse button and mouse movement events and with the timer device to obtain time events. In addition to these events, you can add your own input events to the input device, to be fed to the handler chain (see below).&lt;br /&gt;
&lt;br /&gt;
The keyboard device is accessible directly (see [[Keyboard_Device|Keyboard Device]]). However, once the input.device task has started, you should not read events from the keyboard device directly, since doing so will deprive the input device of the events and confuse key repeating.&lt;br /&gt;
&lt;br /&gt;
The gameport device has two units. As you view the Amiga, looking at the gameport connectors, the left connector is assigned as the primary mouse input for Intuition and contributes gameport input events to the input event stream.&lt;br /&gt;
&lt;br /&gt;
The right connector is handled by the other gameport unit and is currently unassigned. While the input device task is running, that task expects to read the input from the left connector. Direct use of the gameport device is covered in [[Gameport_Device|Gameport Device]].&lt;br /&gt;
&lt;br /&gt;
The timer device is used to generate time events for the input device. It is also used to control key repeat rate and key repeat threshold. The timer device is a shared-access device and is described in [[Timer_Device|Timer Device]].&lt;br /&gt;
&lt;br /&gt;
The device-specific commands are described below. First though, it may be helpful to consider the types of input events that the input device deals with. An input event is a data structure that describes the following:&lt;br /&gt;
&lt;br /&gt;
* The class of the event — often describes the device that generated the event.&lt;br /&gt;
* The subclass of the event — space for more information if needed.&lt;br /&gt;
* The code — keycode if keyboard, button information if mouse, others.&lt;br /&gt;
* A qualifier such as “Alt key also down,”or “key repeat active”.&lt;br /&gt;
* A position field that contains a data address or a mouse position count.&lt;br /&gt;
* A time stamp, to determine the sequence in which the events occurred.&lt;br /&gt;
* A link-field by which input events are linked together.&lt;br /&gt;
* The class, subclass, code and qualifier of the previous down key.&lt;br /&gt;
&lt;br /&gt;
The full definitions for each field can be found in the include file devices/inputevent.h. You can find more information about input events in [[Gameport_Device|Gameport Device]] and [[Console_Device|Console Device]].&lt;br /&gt;
&lt;br /&gt;
The various types of input events are listed below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Input Device Event Types&lt;br /&gt;
| IECLASS_NULL || A NOP input event&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_RAWKEY || A raw keycode from the keyboard device&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_RAWMOUSE || The raw mouse report from the gameport device&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_EVENT || A private console event&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_POINTERPOS || A pointer position report&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_TIMER || A timer event&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_GADGETDOWN || Select button pressed down over a gadget (address in ie_EventAddress)&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_GADGETUP || Select button released over the same gadget (address in ie_EventAddress)&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_REQUESTER || Some requester activity has taken place.&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_MENULIST || This is a menu number transmission (menu number is in ie_Code)&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_CLOSEWINDOW || User has selected the active window’s Close Gadget&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_SIZEWINDOW || This window has a new size&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_REFRESHWINDOW || The window pointed to by ie_EventAddress needs to be refreshed&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_NEWPREFS || New preferences are available&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_DISKREMOVED || The disk has been removed&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_DISKINSERTED || The disk has been inserted&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_ACTIVEWINDOW || The window is about to be been made active&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_INACTIVEWINDOW || The window is about to be made inactive&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_NEWPOINTERPOS || Extended-function pointer position report&lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_MENUHELP || Help key report during Menu session &lt;br /&gt;
|-&lt;br /&gt;
| IECLASS_CHANGEWINDOW || The Window has been modified with move, size, zoom, or change&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There is a difference between simply receiving an input event from a device and actually becoming a handler of an input event stream. A handler is a routine that is passed an input event list. It is up to the handler to decide if it can process the input events. If the handler does not recognize an event, it leaves it undisturbed in the event list.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=It All Flows Downhill|text=Handlers can themselves generate new linked lists of events which can be passed down to lower priority handlers.}}&lt;br /&gt;
&lt;br /&gt;
The InputEvent structure is used by the input device to describe an input event such as a keypress or a mouse movement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct InputEvent&lt;br /&gt;
{&lt;br /&gt;
    struct  InputEvent *ie_NextEvent;   /* the chronologically next event */&lt;br /&gt;
    UBYTE   ie_Class;                   /* the input event class */&lt;br /&gt;
    UBYTE   ie_SubClass;                /* optional subclass of the class */&lt;br /&gt;
    UWORD   ie_Code;                    /* the input event code */&lt;br /&gt;
    UWORD   ie_Qualifier;               /* qualifiers in effect for the event*/&lt;br /&gt;
    union&lt;br /&gt;
    {&lt;br /&gt;
        struct&lt;br /&gt;
        {&lt;br /&gt;
            WORD    ie_x;               /* the pointer position for the event*/&lt;br /&gt;
            WORD    ie_y;&lt;br /&gt;
        } ie_xy;&lt;br /&gt;
        APTR    ie_addr;                /* the event address */&lt;br /&gt;
        struct&lt;br /&gt;
        {&lt;br /&gt;
            UBYTE   ie_prev1DownCode;   /* previous down keys for dead */&lt;br /&gt;
            UBYTE   ie_prev1DownQual;   /*   key translation: the ie_Code */&lt;br /&gt;
            UBYTE   ie_prev2DownCode;   /*   &amp;amp; low byte of ie_Qualifier for */&lt;br /&gt;
            UBYTE   ie_prev2DownQual;   /*   last &amp;amp; second last down keys */&lt;br /&gt;
        } ie_dead;&lt;br /&gt;
    } ie_position;&lt;br /&gt;
    struct timeval ie_TimeStamp;        /* the system tick at the event */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IEPointerPixel and IEPointerTablet structures are used to set the mouse position with the IECLASS_NEWPOINTERPOS input event class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct IEPointerPixel&lt;br /&gt;
{&lt;br /&gt;
    struct Screen       *iepp_Screen;   /* pointer to an open screen */&lt;br /&gt;
    struct&lt;br /&gt;
    {                           /* pixel coordinates in iepp_Screen */&lt;br /&gt;
        WORD    X;&lt;br /&gt;
        WORD    Y;&lt;br /&gt;
    } iepp_Position;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct IEPointerTablet&lt;br /&gt;
{&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        UWORD   X;&lt;br /&gt;
        UWORD   Y;&lt;br /&gt;
    } iept_Range;       /* 0 is min, these are max      */&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        UWORD   X;&lt;br /&gt;
        UWORD   Y;&lt;br /&gt;
    } iept_Value;       /* between 0 and iept_Range     */&lt;br /&gt;
&lt;br /&gt;
    WORD iept_Pressure; /* -128 to 127 (unused, set to 0)  */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file devices/inputevent.h for the complete structure definitions.&lt;br /&gt;
&lt;br /&gt;
For input device handler installation, the Interrupt structure is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Interrupt&lt;br /&gt;
{&lt;br /&gt;
    struct Node is_Node;&lt;br /&gt;
    APTR   is_Data;         /* server data segment */&lt;br /&gt;
    VOID   (*is_Code)();    /* server code entry   */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the include file exec/interrupts.h for the complete structure definition.&lt;br /&gt;
&lt;br /&gt;
=== Closing the Input Device ===&lt;br /&gt;
&lt;br /&gt;
Each OpenDevice() must eventually be matched by a call to CloseDevice(). All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if (!(IExec-&amp;gt;CheckIO(InputIO)))&lt;br /&gt;
    {&lt;br /&gt;
    IExec-&amp;gt;AbortIO(InputIO);  /* Ask device to abort request, if pending */&lt;br /&gt;
    }&lt;br /&gt;
IExec-&amp;gt;WaitIO(InputIO);   /* Wait for abort, then clean up */&lt;br /&gt;
IExec-&amp;gt;CloseDevice((struct IORequest *)InputIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Using the Mouse Port With the Input Device ==&lt;br /&gt;
&lt;br /&gt;
To get mouse port information you must first set the current mouse port by passing an IOStdReq to the device with IND_SETMPORT set in io_Command and a pointer to a byte set in io_Data. If the byte is set to 0 the left controller port will be used as the current mouse port; if it is set to 1, the right controller port will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int8 port = 1;      /* set mouse port to right controller */&lt;br /&gt;
&lt;br /&gt;
InputIO-&amp;gt;io_Data    = &amp;amp;port;&lt;br /&gt;
InputIO-&amp;gt;io_Flags   = IOF_QUICK;&lt;br /&gt;
InputIO-&amp;gt;io_Length  = 1;&lt;br /&gt;
InputIO-&amp;gt;io_Command = IND_SETMPORT;&lt;br /&gt;
IExec-&amp;gt;BeginIO((struct IORequest *)InputIO);&lt;br /&gt;
if (InputIO-&amp;gt;io_Error)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nSETMPORT failed %ld\n&amp;quot;, InputIO-&amp;gt;io_Error);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Put That Back!|text=The default mouse port is the left controller. Don’t forget to set the mouse port back to the left controller before exiting if you change it to the right controller during your application.}}&lt;br /&gt;
&lt;br /&gt;
=== Setting the Conditions for a Mouse Port Report ===&lt;br /&gt;
&lt;br /&gt;
You set the conditions for a mouse port report by passing an IOStdReq to the device with IND_SETMTRIG set in io_Command, the address of a GamePortTrigger structure set in io_Data and the length of the structure set in io_Length.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct GamePortTrigger InputTR;&lt;br /&gt;
&lt;br /&gt;
InputIO-&amp;gt;io_Data    = (APTR)InputTR;  /* set trigger conditions */&lt;br /&gt;
InputIO-&amp;gt;io_Command = IND_SETMTRIG;   /* from InputTR */&lt;br /&gt;
InputIO-&amp;gt;io_Length  = sizeof(struct GamePortTrigger);&lt;br /&gt;
IExec-&amp;gt;DoIO(InputIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The information needed for mouse port report setting is contained in a GamePortTrigger data structure which is defined in the include file devices/gameport.h.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct GamePortTrigger&lt;br /&gt;
{&lt;br /&gt;
    UWORD    gpt_Keys;      /* key transition triggers */&lt;br /&gt;
    UWORD    gpt_Timeout;   /* time trigger (vertical blank units) */&lt;br /&gt;
    UWORD    gpt_XDelta;    /* X distance trigger */&lt;br /&gt;
    UWORD    gpt_YDelta;    /* Y distance trigger */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See the [[Gameport Device]] chapter of this manual for a full description of setting mouse port trigger conditions.&lt;br /&gt;
&lt;br /&gt;
== Adding an Input Handler ==&lt;br /&gt;
&lt;br /&gt;
You add an input-stream handler to the input chain by passing an IOStdReq to the device with IND_ADDHANDLER set in io_Command and a pointer to an Interrupt structure set in io_Data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Interrupt *InputHandler;&lt;br /&gt;
struct IOStdReq  *InputIO&lt;br /&gt;
&lt;br /&gt;
InputHandler-&amp;gt;is_Code         = ButtonSwap; /* Address of code */&lt;br /&gt;
InputHandler-&amp;gt;is_Data         = NULL;       /* User Value passed in A1 */&lt;br /&gt;
InputHandler-&amp;gt;is_Node.ln_Pri  = 100;        /* Priority in food chain */&lt;br /&gt;
InputHandler-&amp;gt;is_Node.ln_Name = NameString; /* Name of handler */&lt;br /&gt;
&lt;br /&gt;
InputIO-&amp;gt;io_Data    = (APTR)inputHandler;   /* Point to the structure */&lt;br /&gt;
InputIO-&amp;gt;io_Command = IND_ADDHANDLER;       /* Set command ... */&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)InputIO);   /* DoIO( ) the command */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Intuition is one of the input device handlers and normally distributes most of the input events.&lt;br /&gt;
&lt;br /&gt;
Intuition inserts itself at priority position 50. The console device sits at priority position 0. You can choose the position in the chain at which your handler will be inserted by setting the priority field in the list-node part of the interrupt data structure you pass to this routine.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Speed Saves|text=&#039;&#039;Any&#039;&#039; processing time expended by a handler subtracts from the time available before the next event happens. Therefore, handlers for the input stream &#039;&#039;must&#039;&#039; be fast.}}&lt;br /&gt;
&lt;br /&gt;
=== Rules for Input Device Handlers ===&lt;br /&gt;
&lt;br /&gt;
The following rules should be followed when you are designing an input handler:&lt;br /&gt;
&lt;br /&gt;
* If an input handler is capable of processing a specific kind of an input event and that event has no links (ie_NextEvent = 0), the handler can end the handler chain by returning a NULL (0) value.&lt;br /&gt;
&lt;br /&gt;
* If there are multiple events linked together, the handler is free to unlink an event from the input event chain, thereby passing a shorter list of events to subsequent handlers. The starting address of the modified list is the return value.&lt;br /&gt;
&lt;br /&gt;
* If a handler wishes to add new events to the chain that it passes to a lower-priority handler, it may initialize memory to contain the new event or event chain. The handler, when it again gets control on the next round of event handling, should assume nothing about the current contents of the memory blocks attached to the event chain. Lower priority handlers may have modified the memory as they handled their part of the event. The handler that allocates the memory for this purpose should keep track of the starting address and the size of this memory chunk so that the memory can be returned to the free memory list when it is no longer needed.&lt;br /&gt;
&lt;br /&gt;
Your handler routine should be structured similar to the following pseudo-language statement:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
newEventChain = yourHandlerCode(oldEventChain, yourHandlerData);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where:&lt;br /&gt;
&lt;br /&gt;
; yourHandlerCode&lt;br /&gt;
: is the entry point to your routine.&lt;br /&gt;
; oldEventChain&lt;br /&gt;
: is the starting address for the current chain of input events.&lt;br /&gt;
; yourHandlerData&lt;br /&gt;
: is a user-definable value, usually a pointer to some data structure your handler requires.&lt;br /&gt;
; newEventChain&lt;br /&gt;
: is the starting address of an event chain which you are passing to the next handler, if any.&lt;br /&gt;
&lt;br /&gt;
When your handler code is called, the event chain and the handler data is passed in. When your code returns, it should return the pointer to the event chain. If all of the events were removed by the routine, return NULL. A NULL (0) value terminates the handling thus freeing more CPU resources.&lt;br /&gt;
&lt;br /&gt;
Memory that you use to describe a new input event that you have added to the event chain is available for reuse or deallocation when the handler is called again or after the IND_REMHANDLER command for the handler is complete. There is no guarantee that any field in the event is unchanged since a handler may change any field of an event that comes through the food chain.&lt;br /&gt;
&lt;br /&gt;
Because IND_ADDHANDLER installs a handler in any position in the handler chain, it can, for example, ignore specific types of input events as well as act upon and modify existing streams of input. It can even create new input events for Intuition or other programs to interpret.&lt;br /&gt;
&lt;br /&gt;
=== Removing an Input Handler ===&lt;br /&gt;
&lt;br /&gt;
You remove a handler from the handler chain by passing an IOStdReq to the device IND_REMHANDLER set in io_Command and a pointer to the Interrupt structure used to add the handler.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Interrupt *InputHandler;&lt;br /&gt;
struct IOStdReq  *InputIO;&lt;br /&gt;
&lt;br /&gt;
InputIO-&amp;gt;io_Data    = (APTR)InputHandler;   /* Which handler to REM */&lt;br /&gt;
InputIO-&amp;gt;io_Command = IND_REMHANDLER;       /* The REM command */&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)InputIO);   /* Send the command */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Writing Events to the Input Device Stream ==&lt;br /&gt;
&lt;br /&gt;
Typically, input events are internally generated by the timer device, keyboard device, and input device.&lt;br /&gt;
&lt;br /&gt;
An application can also generate an input event by setting the appropriate fields for the event in an InputEvent structure and sending it to the input device. It will then be treated as any other event and passed through to the input handler chain. However, I/O requests for IND_WRITEVENT cannot be made from interrupt code.&lt;br /&gt;
&lt;br /&gt;
You generate an input event by passing an IOStdReq to the device with IND_WRITEEVENT set in io_Command, a pointer to an InputEvent structure set in io_Data and the length of the structure set in io_Length.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct InputEvent *FakeEvent;&lt;br /&gt;
struct IOStdReq   *InputIO;&lt;br /&gt;
&lt;br /&gt;
InputIO-&amp;gt;io_Data    = (APTR)FakeEvent;&lt;br /&gt;
InputIO-&amp;gt;io_Length  = sizeof(struct InputEvent);&lt;br /&gt;
InputIO-&amp;gt;io_Command = IND_WRITEEVENT;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)InputIO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|title=You Know What Happens When You Assume|text=This command propagates the input event through the handler chain. The handlers may link other events onto the end of this event or modify the contents of the data structure you constructed in any way they wish. Therefore, do not assume any of the data will be the same from event to event.}}&lt;br /&gt;
&lt;br /&gt;
=== Setting the Position of the Mouse ===&lt;br /&gt;
&lt;br /&gt;
One use of writing input events to the input device is to set the position of the mouse pointer. The mouse pointer can be positioned by using the input classes IECLASS_POINTERPOS and IECLASS_NEWPOINTERPOS.&lt;br /&gt;
&lt;br /&gt;
There are two ways to set the position of the mouse pointer using the input class IECLASS_POINTERPOS:&lt;br /&gt;
&lt;br /&gt;
* At an absolute position on the current screen.&lt;br /&gt;
* At a position relative to the current mouse pointer position on the current screen.&lt;br /&gt;
&lt;br /&gt;
In both cases, you set the Class field of the InputEvent structure to IECLASS_POINTERPOS, ie_X with the new x-coordinate and ie_Y with the new y-coordinate. Absolute positioning is done by setting ie_Qualifier to 0 and relative positioning is done by setting ie_Qualifier to IEQUALIFIER_RELATIVEMOUSE.&lt;br /&gt;
&lt;br /&gt;
Once the proper values are set, pass an IOStdReq to the input device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT.&lt;br /&gt;
&lt;br /&gt;
There are three ways to set the mouse pointer position using IECLASS_NEWPOINTERPOS:&lt;br /&gt;
&lt;br /&gt;
* At an absolute x-y coordinate on a screen—you specify the exact location of the pointer and which screen.&lt;br /&gt;
* At an relative x-y coordinate—you specify where it will go in relation to the current pointer position and which screen.&lt;br /&gt;
* At a normalized position on a tablet device—you specify the maximum x-value and y-value of the tablet and an x-y coordinate between them and the input device will normalize it to fit.&lt;br /&gt;
&lt;br /&gt;
The basic steps required are the same for all three methods.&lt;br /&gt;
&lt;br /&gt;
* Get a pointer to the screen where you want to position the pointer. This is not necessary for the tablet device.&lt;br /&gt;
* Set up a structure to indicate the new position of the pointer.&lt;br /&gt;
&lt;br /&gt;
For absolute and relative positioning, you set up an IEPointerPixel structure with iepp_Position.X set to the new x-coordinate, iepp_Position.Y set to the new y-coordinate and iepp_Screen set to the screen pointer. You set up an InputEvent structure with ie_SubClass set to IESUBCLASS_PIXEL, a pointer to the IEPointerPixel structure set in ie_EventAddress, IECLASS_NEWPOINTERPOS set in Class, and ie_Qualifier set to either IEQUALIFIER_RELATIVEMOUSE for relative positioning or 0 for absolute positioning.&lt;br /&gt;
&lt;br /&gt;
For tablet positioning, you set up an IEPointerTablet structure with iept_Range.X set to the maximum x-coordinate and iept_Range.Y set to the maximum y-coordinate, and iept_Value.X set to the new x-coordinate and iept_Value.Y set to the new y-coordinate. You set up an InputEvent structure with a pointer to the IEPointerTablet structure set in ie_EventAddress, ie_SubClass to IESUBCLASS_TABLET and Class set to IECLASS_NEWPOINTERPOS.&lt;br /&gt;
&lt;br /&gt;
Finally, for all three methods, pass an IOStdReq to the device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT.&lt;br /&gt;
&lt;br /&gt;
The following example sets the mouse pointer at an absolute position on a public screen using IECLASS_NEWPOINTERPOS.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Set_Mouse.c&lt;br /&gt;
 *&lt;br /&gt;
 * This example sets the mouse at x=100 and y=200&lt;br /&gt;
 *&lt;br /&gt;
 * Run from CLI only&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/input.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/inputevent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/screens.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
struct MsgPort *InputMP = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
if (InputMP != NULL)&lt;br /&gt;
    {&lt;br /&gt;
    struct InputEvent *FakeEvent = IExec-&amp;gt;AllocVecTags(sizeof(struct InputEvent),&lt;br /&gt;
        AVT_Type, MEMF_SHARED,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
        &lt;br /&gt;
    if (FakeEvent != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        struct IEPointerPixel *NeoPix = IExec-&amp;gt;AllocVecTags(sizeof(struct IEPointerPixel),&lt;br /&gt;
            AVT_Type, MEMF_SHARED,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
            &lt;br /&gt;
        if (NeoPix != NULL)&lt;br /&gt;
            {&lt;br /&gt;
            struct IOStdReq *InputIO = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
                ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
                ASOIOR_ReplyPort, InputMP,&lt;br /&gt;
                TAG_END);&lt;br /&gt;
                &lt;br /&gt;
            if (InputIO != NULL)&lt;br /&gt;
                {&lt;br /&gt;
                if (!IExec-&amp;gt;OpenDevice(&amp;quot;input.device&amp;quot;, NULL, (struct IORequest *)InputIO, NULL))&lt;br /&gt;
                    {&lt;br /&gt;
                    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
                    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
                    if (IIntuition != NULL)&lt;br /&gt;
                        {&lt;br /&gt;
                        struct Screen *PubScreen;&lt;br /&gt;
                        /* Get pointer to screen and lock screen */&lt;br /&gt;
                        if (PubScreen = (struct Screen *)IIntuition-&amp;gt;LockPubScreen(NULL))&lt;br /&gt;
                            {&lt;br /&gt;
                            /* Set up IEPointerPixel fields */&lt;br /&gt;
                            NeoPix-&amp;gt;iepp_Screen     = (struct Screen *)PubScreen; /* WB screen */&lt;br /&gt;
                            NeoPix-&amp;gt;iepp_Position.X = 100;  /* put pointer at x = 100 */&lt;br /&gt;
                            NeoPix-&amp;gt;iepp_Position.Y = 200;  /* put pointer at y = 200 */&lt;br /&gt;
&lt;br /&gt;
                            /* Set up InputEvent fields */&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_EventAddress = (APTR)NeoPix;          /* IEPointerPixel */&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_NextEvent    = NULL;&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_Class        = IECLASS_NEWPOINTERPOS; /* new mouse pos */&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_SubClass     = IESUBCLASS_PIXEL;      /* on pixel */&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_Code         = IECODE_NOBUTTON;&lt;br /&gt;
                            FakeEvent-&amp;gt;ie_Qualifier    = 0;                     /* absolute positioning */&lt;br /&gt;
&lt;br /&gt;
                            InputIO-&amp;gt;io_Data    = (APTR)FakeEvent;    /* InputEvent */&lt;br /&gt;
                            InputIO-&amp;gt;io_Length  = sizeof(struct InputEvent);&lt;br /&gt;
                            InputIO-&amp;gt;io_Command = IND_WRITEEVENT;&lt;br /&gt;
                            IExec-&amp;gt;DoIO((struct IORequest *)InputIO);&lt;br /&gt;
&lt;br /&gt;
                            IIntuition-&amp;gt;UnlockPubScreen(NULL, PubScreen);  /* Unlock screen */&lt;br /&gt;
                            }&lt;br /&gt;
                        else&lt;br /&gt;
                            IDOS-&amp;gt;Printf(&amp;quot;Could not get pointer to screen\n&amp;quot;);&lt;br /&gt;
                        }&lt;br /&gt;
                    else&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;Error: Could not open intuition.library\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                    IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
                    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
&lt;br /&gt;
                    IExec-&amp;gt;CloseDevice((struct IORequest *)InputIO);&lt;br /&gt;
                    }&lt;br /&gt;
                else&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Error: Could not open input.device\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, InputIO);&lt;br /&gt;
                }&lt;br /&gt;
            else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create IORequest\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeVec(NeoPix);&lt;br /&gt;
            }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Error: Could not allocate memory for NeoPix\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeVec(FakeEvent);&lt;br /&gt;
        }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Error: Could not allocate memory for FakeEvent\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, InputMP);&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Setting the Key Repeat Threshold ==&lt;br /&gt;
&lt;br /&gt;
The key repeat threshold is the number of seconds and microseconds a user must hold down a key before it begins to repeat. This delay is normally set by the Preferences tool or by Intuition when it notices that the Preferences have been changed, but you can also do it directly through the input device.&lt;br /&gt;
&lt;br /&gt;
You set the key repeat threshold by passing a TimeRequest with IND_SETTHRESH set in io_Command and the number of seconds to delay set in Seconds and the number of microseconds to delay set in Microseconds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/timer.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct TimeRequest *InputTime;  /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */&lt;br /&gt;
&lt;br /&gt;
InputTime-&amp;gt;Time.Seconds       = 1;        /* 1 second */&lt;br /&gt;
InputTime-&amp;gt;Time.Microseconds  = 500000;   /* 500,000 microseconds */&lt;br /&gt;
InputTime-&amp;gt;Request.io_Command = IND_SETTHRESH;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)InputTime);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code above will set the key repeat threshold to 1.5 seconds.&lt;br /&gt;
&lt;br /&gt;
== Setting the Key Repeat Interval ==&lt;br /&gt;
&lt;br /&gt;
The key repeat interval is the time period, in seconds and microseconds, between key repeat events once the initial key repeat threshold has elapsed. (See &amp;quot;Setting the Key Repeat Threshold&amp;quot; above.) Like the key repeat threshold, this is normally issued by Intuition and preset by the Preferences tool.&lt;br /&gt;
&lt;br /&gt;
You set the key repeat interval by passing a TimeRequest with IND_SETPERIOD set in io_Command and the number of seconds set in Seconds and the number of microseconds set in Microseconds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */&lt;br /&gt;
&lt;br /&gt;
InputTime-&amp;gt;Time.Seconds       = 0;&lt;br /&gt;
InputTime-&amp;gt;Time.Microseconds  = 12000;  /* 0.012 seconds */&lt;br /&gt;
InputTime-&amp;gt;Request.io_Command = IND_SETPERIOD;&lt;br /&gt;
IExec-&amp;gt;DoIO((struct IORequest *)InputTime);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code above sets the key repeat interval to 0.012 seconds.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The Right Tool For The Right Job|text=As previously stated, you &#039;&#039;must&#039;&#039; use a TimeRequest structure with IND_SETTHRESH and IND_SETPERIOD.}}&lt;br /&gt;
&lt;br /&gt;
== Determining the Current Qualifiers ==&lt;br /&gt;
&lt;br /&gt;
Some applications need to know whether the user is holding down a qualifier key or a mouse button during an operation. To determine the current qualifiers, you call the input device function PeekQualifier().&lt;br /&gt;
&lt;br /&gt;
PeekQualifier() returns what the input device &#039;&#039;considers&#039;&#039; to be the current qualifiers at the time PeekQualifier() is called (e.g., keyboard qualifiers and mouse buttons). This does not include any qualifiers which have been added, removed or otherwise modified by input handlers.&lt;br /&gt;
&lt;br /&gt;
In order to call the function, you must set a pointer to the input device base address and get the interface. Once you set the interface pointer, you can call the function. &#039;&#039;You must open the device in order to access the device base address and interface.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
PeekQualifier() returns an unsigned word with bits set according to the qualifiers in effect at the &#039;&#039;time&#039;&#039; the function is called. It takes no parameters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
struct IOStdReq   *InputIO;           /* I/O request block */&lt;br /&gt;
uint16 Quals;                         /* qualifiers */&lt;br /&gt;
  .&lt;br /&gt;
  .&lt;br /&gt;
  .&lt;br /&gt;
if (!IExec-&amp;gt;OpenDevice(&amp;quot;input.device&amp;quot;, NULL, (struct IORequest *)InputIO, NULL))&lt;br /&gt;
     {&lt;br /&gt;
     /* Set input device base address in InputBase */&lt;br /&gt;
     struct Library *InputBase = (struct Library *)InputIO-&amp;gt;io_Device;&lt;br /&gt;
     struct InputIFace *IInput = (struct InputIFace *)IExec-&amp;gt;GetInterface(InputBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
     /* Call the function */&lt;br /&gt;
     Quals = IInput-&amp;gt;PeekQualifier();&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     .&lt;br /&gt;
     IExec-&amp;gt;DropInterface((struct Interface *)IInput);&lt;br /&gt;
     IExec-&amp;gt;CloseDevice(InputIO);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The qualifiers returned are listed in the table below.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit&lt;br /&gt;
! Qualifier&lt;br /&gt;
! Key or Button&lt;br /&gt;
|-&lt;br /&gt;
| 0 || IEQUALIFIER_LSHIFT || Left Shift&lt;br /&gt;
|-&lt;br /&gt;
| 1 || IEQUALIFIER_RSHIFT || Right Shift&lt;br /&gt;
|-&lt;br /&gt;
| 2 || IEQUALIFIER_CAPSLOCK || Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
| 3 || IEQUALIFIER_CONTROL || Control&lt;br /&gt;
|-&lt;br /&gt;
| 4 || IEQUALIFIER_LALT || Left Alt&lt;br /&gt;
|-&lt;br /&gt;
| 5 || IEQUALIFIER_RALT || Right Alt&lt;br /&gt;
|-&lt;br /&gt;
| 6 || IEQUALIFIER_LCOMMAND || Left-Amiga&lt;br /&gt;
|-&lt;br /&gt;
| 7 || IEQUALIFIER_RCOMMAND || Right-Amiga&lt;br /&gt;
|-&lt;br /&gt;
| 12 || IEQUALIFIER_MIDBUTTON || Middle Mouse&lt;br /&gt;
|-&lt;br /&gt;
| 13 || IEQUALIFIER_RBUTTON || Right Mouse&lt;br /&gt;
|-&lt;br /&gt;
| 14 || IEQUALIFIER_LEFTBUTTON || Left Mouse&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Input Device and Intuition ==&lt;br /&gt;
&lt;br /&gt;
There are several ways to receive information from the various devices that are part of the input device. The first way is to communicate directly with the device. This method is not recommended while the input device task is running – which is most of the time. The second way is to become a handler for the stream of events which the input device produces. That method is shown above.&lt;br /&gt;
&lt;br /&gt;
The third method of getting input from the input device is to retrieve the data from the console device or from the IDCMP (Intuition Direct Communications Message Port). These are the preferred methods for applications in a multitasking environment because each application can receive juts its own input (i.e., only the input which occurs when one of its window is active). See the [[Intuition_Input_and_Output_Methods|Intuition Input and Output Methods]] for more information on IDCMP messages. See [[Console_Device|Console Device]] for more information on console device I/O.&lt;br /&gt;
&lt;br /&gt;
== Example Input Device Program ==&lt;br /&gt;
&lt;br /&gt;
=== Swap_Buttons.c ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/memory.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/interrupts.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/input.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
UBYTE NameString[] = &amp;quot;Swap Buttons&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW,&lt;br /&gt;
                        WINDOWDRAG|WINDOWCLOSE|SIMPLE_REFRESH|NOCAREREFRESH,&lt;br /&gt;
                        NULL,NULL,NameString,NULL,NULL,0,0,0,0,WBENCHSCREEN};&lt;br /&gt;
&lt;br /&gt;
extern VOID ButtonSwap();&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * This routine opens a window and waits for the one event that&lt;br /&gt;
 * can happen (CLOSEWINDOW)  This is just to let the user play with&lt;br /&gt;
 * the swapped buttons and then close the program...&lt;br /&gt;
 */&lt;br /&gt;
VOID WaitForUser(VOID)&lt;br /&gt;
{&lt;br /&gt;
struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
IIntuition = (struct IntuitionIFace *)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
if (IIntuition != NULL)&lt;br /&gt;
    {&lt;br /&gt;
    struct Window *win = IIntuition-&amp;gt;OpenWindow(&amp;amp;mywin);&lt;br /&gt;
    if (win != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        IExec-&amp;gt;WaitPort(win-&amp;gt;UserPort);&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg(IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort));&lt;br /&gt;
&lt;br /&gt;
        IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)IIntuition);&lt;br /&gt;
IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
struct MsgPort *inputPort = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (inputPort != NULL)&lt;br /&gt;
    {&lt;br /&gt;
    struct Interrupt *inputHandler = IExec-&amp;gt;AllocSysObjectTags(ASOT_INTERRUPT,&lt;br /&gt;
        ASOINTR_Code, ButtonSwap,&lt;br /&gt;
        TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (inputHandler != NULL)&lt;br /&gt;
        {&lt;br /&gt;
        struct IOStdReq *inputReqBlk = IExec-&amp;gt;AllocSysObjectTags(ASOT_IOREQUEST,&lt;br /&gt;
            ASOIOR_Size, sizeof(struct IOStdReq),&lt;br /&gt;
            ASOIOR_ReplyPort, inputPort,&lt;br /&gt;
            TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (inputReqBlk != NULL)&lt;br /&gt;
            {&lt;br /&gt;
            if (!IExec-&amp;gt;OpenDevice(&amp;quot;input.device&amp;quot;, NULL,&lt;br /&gt;
                             (struct IORequest *)inputReqBlk, NULL))&lt;br /&gt;
                {&lt;br /&gt;
                inputHandler-&amp;gt;is_Node.ln_Pri  = 100;&lt;br /&gt;
                inputHandler-&amp;gt;is_Node.ln_Name = NameString;&lt;br /&gt;
                &lt;br /&gt;
                inputReqBlk-&amp;gt;io_Data    = (APTR)inputHandler;&lt;br /&gt;
                inputReqBlk-&amp;gt;io_Command = IND_ADDHANDLER;&lt;br /&gt;
                IExec-&amp;gt;DoIO((struct IORequest *)inputReqBlk);&lt;br /&gt;
&lt;br /&gt;
                WaitForUser();&lt;br /&gt;
&lt;br /&gt;
                inputReqBlk-&amp;gt;io_Data    = (APTR)inputHandler;&lt;br /&gt;
                inputReqBlk-&amp;gt;io_Command = IND_REMHANDLER;&lt;br /&gt;
                IExec-&amp;gt;DoIO((struct IORequest *)inputReqBlk);&lt;br /&gt;
&lt;br /&gt;
                IExec-&amp;gt;CloseDevice((struct IORequest *)inputReqBlk);&lt;br /&gt;
                }&lt;br /&gt;
            else&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;Error: Could not open input.device\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, inputReqBlk);&lt;br /&gt;
            }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create I/O request\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_INTERRUPT, inputHandler);&lt;br /&gt;
        }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Error: Could not allocate interrupt\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, inputPort);&lt;br /&gt;
    }&lt;br /&gt;
else&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Error: Could not create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== InputHandler.a ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
****************************************************************************&lt;br /&gt;
*       InputHandler.a&lt;br /&gt;
*&lt;br /&gt;
* InputHandler that does a Left/Right mouse button swap...&lt;br /&gt;
*&lt;br /&gt;
* See Swap_Buttons.c for details on how to compile/assemble/link...&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Required includes...&lt;br /&gt;
*&lt;br /&gt;
        INCDIR  &amp;amp;quot;include:&amp;amp;quot;&lt;br /&gt;
        INCLUDE &amp;amp;quot;exec/types.i&amp;amp;quot;&lt;br /&gt;
        INCLUDE &amp;amp;quot;exec/io.i&amp;amp;quot;&lt;br /&gt;
        INCLUDE &amp;amp;quot;devices/inputevent.i&amp;amp;quot;&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Make the entry point external...&lt;br /&gt;
*&lt;br /&gt;
        xdef    _ButtonSwap&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* This is the input handler that will swap the&lt;br /&gt;
* mouse buttons for left handed use.&lt;br /&gt;
*&lt;br /&gt;
* The event list gets passed to you in  a0.&lt;br /&gt;
* The is_Data field is passed to you in a1.&lt;br /&gt;
* This example does not use the is_Data field...&lt;br /&gt;
*&lt;br /&gt;
* On exit you must return the event list in d0.  In this way&lt;br /&gt;
* you could add or remove items from the event list.&lt;br /&gt;
*&lt;br /&gt;
* The handler gets called here...&lt;br /&gt;
*&lt;br /&gt;
*&lt;br /&gt;
_ButtonSwap:    move.l  a0,-(sp)        ; Save the event list&lt;br /&gt;
*&lt;br /&gt;
* Since the event list could be a linked list, we start a loop&lt;br /&gt;
* here to handle all of the events passed to us.&lt;br /&gt;
*&lt;br /&gt;
CheckLoop:      move.w  ie_Qualifier(a0),d1             ; Get qualifiers...&lt;br /&gt;
                move.w  d1,d0                           ; Two places...&lt;br /&gt;
*&lt;br /&gt;
* Since we are changing left and right mouse buttons, we need to make&lt;br /&gt;
* sure that we change the qualifiers on all of the messages.  The&lt;br /&gt;
* left and right mouse buttons are tracked in the message qualifiers&lt;br /&gt;
* for use in such things as dragging.  To make sure that we continue&lt;br /&gt;
* to drag correctly, we change the qualifiers.&lt;br /&gt;
*&lt;br /&gt;
CheckRight:     btst    #IEQUALIFIERB_RBUTTON,d1        ; Check for right&lt;br /&gt;
                beq.s   NoRight&lt;br /&gt;
                bset    #IEQUALIFIERB_LEFTBUTTON,d0     ; Set the left...&lt;br /&gt;
                beq.s   CheckLeft&lt;br /&gt;
NoRight:        bclr    #IEQUALIFIERB_LEFTBUTTON,d0     ; Clear the left...&lt;br /&gt;
*&lt;br /&gt;
CheckLeft:      btst    #IEQUALIFIERB_LEFTBUTTON,d1     ; Check for left&lt;br /&gt;
                beq.s   NoLeft&lt;br /&gt;
                bset    #IEQUALIFIERB_RBUTTON,d0        ; Set the right...&lt;br /&gt;
                beq.s   SaveQual&lt;br /&gt;
NoLeft:         bclr    #IEQUALIFIERB_RBUTTON,d0        ; Clear the right...&lt;br /&gt;
*&lt;br /&gt;
SaveQual:       move.w  d0,ie_Qualifier(a0)             ; Save back...&lt;br /&gt;
*&lt;br /&gt;
* The actual button up/down events are transmitted as the&lt;br /&gt;
* code field in RAWMOUSE events.  The code field must the be&lt;br /&gt;
* checked and modified when needed on RAWMOUSE events.  If the&lt;br /&gt;
* event is not a RAWMOUSE, we are done with it.&lt;br /&gt;
*&lt;br /&gt;
                cmp.b   #IECLASS_RAWMOUSE,ie_Class(a0)  ; Check for mouse&lt;br /&gt;
                bne.s   NextEvent                       ; If not, next...&lt;br /&gt;
*&lt;br /&gt;
                move.w  ie_Code(a0),d0                  ; Get code...&lt;br /&gt;
                move.w  d0,d1                           ; Save...&lt;br /&gt;
                and.w   #$7F,d0                         ; Mask UP_PREFIX&lt;br /&gt;
                cmp.w   #IECODE_LBUTTON,d0              ; Check for Left...&lt;br /&gt;
                beq.s   SwapThem                        ; If so, swap...&lt;br /&gt;
                cmp.w   #IECODE_RBUTTON,d0              ; Check for Right...&lt;br /&gt;
                bne.s   NextEvent                       ; If not, next...&lt;br /&gt;
*&lt;br /&gt;
SwapThem:       eor.w   #1,d1                           ; Flip bottom bit&lt;br /&gt;
                move.w  d1,ie_Code(a0)                  ; Save it...&lt;br /&gt;
*&lt;br /&gt;
* The event list is linked via a pointer to the next event&lt;br /&gt;
* in the first element of the structure.  That is why it is not&lt;br /&gt;
* nessesary to use:  move.l ie_NextEvent(a0),d0&lt;br /&gt;
*&lt;br /&gt;
* The reason I move to d0 first is that this also checks for zero.&lt;br /&gt;
* The last event in the list will have a NULL ie_NextEvent field.&lt;br /&gt;
* This is NOT as standard EXEC list where the node after the last&lt;br /&gt;
* node is NULL.  Input events are single-linked for performance.&lt;br /&gt;
*&lt;br /&gt;
NextEvent:      move.l  (a0),d0                         ; Get next event&lt;br /&gt;
                move.l  d0,a0                           ; into a0...&lt;br /&gt;
                bne.s   CheckLoop                       ; Do some more.&lt;br /&gt;
*&lt;br /&gt;
* All done, just return the event list...  (in d0)&lt;br /&gt;
*&lt;br /&gt;
                move.l  (sp)+,d0        ; Get event list back...&lt;br /&gt;
                rts                     ; return from handler...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional Information on the Input Device ==&lt;br /&gt;
&lt;br /&gt;
Additional programming information on the input device can be found in the include files and the autodocs for the input device. Both are contained in the SDK.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Includes&lt;br /&gt;
|-&lt;br /&gt;
| devices/input.h&lt;br /&gt;
|-&lt;br /&gt;
| devices/inputevent.h&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! AutoDocs&lt;br /&gt;
|-&lt;br /&gt;
| input.doc&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Daniel Jedlicka</name></author>
	</entry>
</feed>