

<?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=Fr%C3%A9d%C3%A9ric+Khannouf</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=Fr%C3%A9d%C3%A9ric+Khannouf"/>
	<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/wiki/Special:Contributions/Fr%C3%A9d%C3%A9ric_Khannouf"/>
	<updated>2026-05-29T22:38:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BOOPSI_Images&amp;diff=12394</id>
		<title>BOOPSI Images</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BOOPSI_Images&amp;diff=12394"/>
		<updated>2024-09-10T12:10:45Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* mytextlabelclass.c */ Removed some useless definitions. Some cleaning and type castings&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
BOOPSI&#039;s imageclass is one of the standard classes built into Intuition. As its name implies, it is a class of Intuition Images. These BOOPSI images can be used in place of traditional Image structure (as they contain an Intuition Image structure), but they are much more powerful. By using BOOPSI methods, an application or Intuition can tell an imageclass object to render itself. Because it renders itself (rather than Intuition rendering it), the imageclass object is free to render whatever it wants (well, within reason). For example, a BOOPSI image object can render itself according to the current display resolution, or to scale itself to any size an application requests.&lt;br /&gt;
&lt;br /&gt;
== BOOPSI Image Methods ==&lt;br /&gt;
&lt;br /&gt;
Imageclass defines several methods of its own which subclasses of imageclass either have to implement or pass on to their superclass. The method IDs for imageclass are defined in &amp;lt;intuition/imageclass.h&amp;gt;. Each method requires some parameters.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Method !! Description&lt;br /&gt;
|-&lt;br /&gt;
| IM_DRAW || Draw image with state.&lt;br /&gt;
|-&lt;br /&gt;
| IM_DRAWFRAME || Draw image within frame limits.&lt;br /&gt;
|-&lt;br /&gt;
| IM_ERASE || Erase image with state.&lt;br /&gt;
|-&lt;br /&gt;
| IM_ERASEFRAME || Erase image within frame.&lt;br /&gt;
|-&lt;br /&gt;
| IM_HITFRAME || Determine if image was hit within frame.&lt;br /&gt;
|-&lt;br /&gt;
| IM_HITTEST || Determine if image was hit.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The following methods are described at the imageclass level although it&#039;s up to the subclasses to actually implement them. If a class does not implement these methods it should either return zero, indicating that this class does not support the method, or defer processing on to its superclass.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Method !! Description&lt;br /&gt;
|-&lt;br /&gt;
| IM_FRAMEBOX || Get recommended frame around some box.&lt;br /&gt;
|-&lt;br /&gt;
| IM_EXTENT || Inquire about rendering extent.&lt;br /&gt;
|-&lt;br /&gt;
| IM_EXTENTFRAME || Inquire about rendering extent with dimensions.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The formats of each of these BOOPSI messages all differ. The MethodID is the only parameter common to each method.&lt;br /&gt;
&lt;br /&gt;
=== IM_DRAW ===&lt;br /&gt;
&lt;br /&gt;
The IM_DRAW method is used to tell the image to render itself. The Intuition function DrawImageState() uses this method. IM_DRAW receives the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impDraw&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct RastPort *imp_RPort;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Offset;&lt;br /&gt;
&lt;br /&gt;
    uint32 imp_State;&lt;br /&gt;
    struct DrawInfo *imp_DrInfo;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The imp_State field contains the visual state to render the image. The visual states (defined in &amp;lt;intuition/imageclass.h&amp;gt;) are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| IDS_NORMAL || idle state&lt;br /&gt;
|-&lt;br /&gt;
| IDS_SELECTED || for selected gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| IDS_DISABLED || for disabled gadgets.&lt;br /&gt;
|-&lt;br /&gt;
| IDS_BUSY || for future functionality&lt;br /&gt;
|-&lt;br /&gt;
| IDS_INDETERMINATE || for future functionality&lt;br /&gt;
|-&lt;br /&gt;
| IDS_INACTIVENORMAL || normal, in inactive window border.&lt;br /&gt;
|-&lt;br /&gt;
| IDS_INACTIVESELECTED || selected, in inactive border.&lt;br /&gt;
|-&lt;br /&gt;
| IDS_INACTIVEDISABLED || disabled, in inactive border.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
When setting the pens to render an image, use the values from the imp_DrInfo-&amp;gt;dri_Pens pen array (Note that it is possible that imp_DrInfo will be NULL). The possible pen values are defined in &amp;lt;intuition/screens.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following code fragment shows how to use the shadow color for rendering.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint16 *pens = (imp-&amp;gt;imp_DrInfo) ? imp-&amp;gt;imp_DrInfo-&amp;gt;dri_Pens : NULL;&lt;br /&gt;
&lt;br /&gt;
if (pens)&lt;br /&gt;
{&lt;br /&gt;
    IIntuition-&amp;gt;SetAPen (imp-&amp;gt;imp_RPort, pens[SHADOWPEN]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IM_DRAWFRAME ===&lt;br /&gt;
&lt;br /&gt;
The IM_DRAWFRAME method instructs the image to render itself within the confines of the given rectangle. It receives the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impDraw&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct RastPort *imp_RPort;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Offset;&lt;br /&gt;
&lt;br /&gt;
    uint32 imp_State;&lt;br /&gt;
    struct DrawInfo *imp_DrInfo;&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 Width;&lt;br /&gt;
        int16 Height;&lt;br /&gt;
    } imp_Dimensions;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Width and Height fields provide the object&#039;s rectangular bounds. How the image object deals with the frame is implementation specific. Typically, a scaleable image will scale itself as best it can to fit into the rectangle. The mytextlabelclass.c example does not actually implement this method, instead mytextlabelclass treats IM_DRAWFRAME like the IM_DRAW method.&lt;br /&gt;
&lt;br /&gt;
In general, applications that use this method to draw an object should use the IM_ERASEFRAME method (see below) to erase it. This will ensure that the image was erased at the proper scale.&lt;br /&gt;
&lt;br /&gt;
=== IM_ERASE ===&lt;br /&gt;
&lt;br /&gt;
The IM_ERASE method tells an image to erase itself. The Intuition function EraseImage() uses this method. IM_ERASE receives the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impErase&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct RastPort *imp_RPort;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Offset;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mytextlabelclass example doesn&#039;t know anything about this method, so it blindly passes this message on to the superclass. The superclass, imageclass, will call the graphics.library function EraseRect() using the dimensions found in the imageclass object&#039;s Image structure.&lt;br /&gt;
&lt;br /&gt;
=== IM_ERASEFRAME ===&lt;br /&gt;
&lt;br /&gt;
The IM_ERASEFRAME method instructs an image confined to a given rectangle&lt;br /&gt;
to erase itself.  Normally this method is used to erase an image drawn&lt;br /&gt;
using the IM_DRAWFRAME method.  This method expects the following&lt;br /&gt;
parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impEraseFrame&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct RastPort *imp_RPort;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Offset;&lt;br /&gt;
&lt;br /&gt;
    /* these parameters only valid for IM_ERASEFRAME */&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 Width;&lt;br /&gt;
        int16 Height;&lt;br /&gt;
    } imp_Dimensions;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mytextlabelclass example blindly passes this method on to its superclass. The superclass treats IM_ERASEFRAME just like IM_ERASE.&lt;br /&gt;
&lt;br /&gt;
=== IM_HITFRAME ===&lt;br /&gt;
&lt;br /&gt;
The IM_HITFRAME method is used to determine if a point is within an image that is contained within (or scaled to) the given rectangle. This method is intended to test images that were rendered using IM_DRAWFRAME. This method receives the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impHitFrame&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Point;&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 Width;&lt;br /&gt;
        int16 Height;&lt;br /&gt;
    } imp_Dimensions;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mytextlabelclass example blindly passes this method on to its superclass. The superclass treat this meothd just like the IM_HITTEST method.&lt;br /&gt;
&lt;br /&gt;
=== IM_HITTEST ===&lt;br /&gt;
&lt;br /&gt;
IM_HITTEST returns true if a point is within the image. The Intuition function PointInImage() uses this method. IM_HITTEST requires the following parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impHitTest&lt;br /&gt;
{&lt;br /&gt;
    uint16 MethodID;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;&lt;br /&gt;
        int16 Y;&lt;br /&gt;
    } imp_Point;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mytextlabelclass example blindly passes this method on to its superclass. The superclass, imageclass, will return TRUE if the point is within the old Image structure box.&lt;br /&gt;
&lt;br /&gt;
=== IM_FRAMEBOX ===&lt;br /&gt;
&lt;br /&gt;
The IM_FRAMEBOX method returns size information for an image (usually some sort of frame image). The following parameters are associated with the IM_FRAMEBOX method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct impFrameBox&lt;br /&gt;
{&lt;br /&gt;
    uint32 MethodID;&lt;br /&gt;
    struct IBox *imp_ContentsBox;  /* Application supplied IBox for the result */&lt;br /&gt;
    struct IBox *imp_FrameBox;     /* Rectangle to frame */&lt;br /&gt;
    struct DrawInfo *imp_DrInfo;&lt;br /&gt;
    uint32 imp_FrameFlags;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is used to ask the image what size it would like to be, if it had to frame the rectangle in the imp_FrameBox field. This method normally applies only to image classes that put a frame around some object (like frameiclass). By passing the dimensions and position of a rectangle, the framing image determines the position and size it should be to properly &amp;quot;frame&amp;quot; the object bounded by the imp_FrameBox rectangle. IM_FRAMEBOX stores the result in the IBox structure pointed to by&lt;br /&gt;
imp_ContentsBox. This method allows an application to use a framing image without having to worry about image specific details such as accounting for the thickness of the frame or centering the frame around the object.&lt;br /&gt;
&lt;br /&gt;
The imp_FrameFlags field is a bit field used to specify certain options for the IM_FRAMEBOX method. Currently, there is only one defined for it, FRAMEF_SPECIFY. If this bit is set, IM_FRAMEBOX has to use the width and height supplied to it in the imp_FrameBox field (even if these are too small!) as the frame dimensions. It can only adjust its position, typically to center the object as best as possible.&lt;br /&gt;
&lt;br /&gt;
This method is not supported by the mytextlabelclass example. It passes this message to its superclass which does not support this method either. When the message returns from the superclass, the return value will be zero, indicating to the application that this method is not supported.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== usemyIC.c ===&lt;br /&gt;
&lt;br /&gt;
The example code initializes and uses a custom imageclass object. Notice that usemyIC.c directly manipulates fields within the Image structure embedded within the BOOPSI imageclass object. This is legal for image classes whose immediate superclass is imageclass (for the LeftEdge, TopEdge, Width, Height, ImageData, PlanePick, and PlaneOnOff Image structure fields only; the other Image structure fields are off limits). Indirect subclasses of imageclass may not alter the values in the embedded Image structure as future direct subclasses of imageclass may need to know about changes to values in the Image structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * usemyIC.c&lt;br /&gt;
 *&lt;br /&gt;
 * Originally written by David N. Junod&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/cghooks.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/imageclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfx.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfxmacros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.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/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
Class *initmyTextLabelClass(VOID);&lt;br /&gt;
ULONG  freemyTextLabelClass(Class * cl);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    Class          *cl;&lt;br /&gt;
    struct Image   *im;&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;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
    &lt;br /&gt;
    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL)    &lt;br /&gt;
    {&lt;br /&gt;
        /* Open a window, without system gadgets or IDCMP events */&lt;br /&gt;
        if (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                                 WA_Left, 10,&lt;br /&gt;
                                 WA_Top, 10,&lt;br /&gt;
                                 WA_Width, 320,&lt;br /&gt;
                                 WA_Height, 100,&lt;br /&gt;
                                 TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            /* Cache the pointer to the RastPort */&lt;br /&gt;
            struct RastPort *rp = win-&amp;gt;RPort;&lt;br /&gt;
&lt;br /&gt;
            /* Cache the upper-left coordinates of the window */&lt;br /&gt;
            uint16 top = win-&amp;gt;BorderTop + INTERHEIGHT;&lt;br /&gt;
            uint16 left = win-&amp;gt;BorderRight + INTERWIDTH;&lt;br /&gt;
&lt;br /&gt;
            /* Cache the height of the font */&lt;br /&gt;
            uint16 height = rp-&amp;gt;TxHeight + INTERHEIGHT;&lt;br /&gt;
&lt;br /&gt;
            /* Initialize the custom image class. */&lt;br /&gt;
            if (cl = initmyTextLabelClass())&lt;br /&gt;
            {&lt;br /&gt;
                /* Create a new image structure, using the given string. */&lt;br /&gt;
                if (im = IIntuition-&amp;gt;NewObject(cl, NULL,&lt;br /&gt;
                                   IA_Data, (ULONG) &amp;quot;Line _1&amp;quot;,&lt;br /&gt;
                                   TAG_END))&lt;br /&gt;
                {&lt;br /&gt;
                    /* Paint using the provided text string. */&lt;br /&gt;
                    IIntuition-&amp;gt;DrawImageState(rp, im, left, top,&lt;br /&gt;
                                   IDS_NORMAL, NULL);&lt;br /&gt;
&lt;br /&gt;
                    /* Replace the text string, and paint it. */&lt;br /&gt;
                    im-&amp;gt;ImageData = (USHORT *) &amp;quot;Line _2&amp;quot;;&lt;br /&gt;
                    IIntuition-&amp;gt;DrawImageState(rp, im, left, top + height,&lt;br /&gt;
                                   IDS_NORMAL, NULL);&lt;br /&gt;
&lt;br /&gt;
                    /* Replace the text string, and paint it. */&lt;br /&gt;
                    im-&amp;gt;ImageData = (USHORT *) &amp;quot;Line _3&amp;quot;;&lt;br /&gt;
                    IIntuition-&amp;gt;DrawImageState(rp, im, left, top + (height * 2),&lt;br /&gt;
                                   IDS_NORMAL, NULL);&lt;br /&gt;
&lt;br /&gt;
                    /* Free the image. */&lt;br /&gt;
                    IIntuition-&amp;gt;DisposeObject(im);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                /* Free the image class. */&lt;br /&gt;
                freemyTextLabelClass(cl);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Delay(250);&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*)IGraphics);&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&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;
=== mytextlabelclass.c ===&lt;br /&gt;
&lt;br /&gt;
The image class example code, mytextlabelclass.c, illustrates a complete custom image class. This image class provides an application with textual labels that have a particular character underlined. This is useful for indicating which key controls a gadget (although the example provided only utilizes imageclass objects; there are no gadgets involved).&lt;br /&gt;
&lt;br /&gt;
A custom image can be used in the place of any standard Intuition Image structure. For example, an application can attach an imageclass object to: the GadgetRender and SelectRender fields of a Gadget structure (defined in &amp;lt;intuition/intuition.h&amp;gt;), the ReqImage field of a Requester structure, or even the ItemFill field of the MenuItem structure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Original code written by David N. Junod&lt;br /&gt;
 *&lt;br /&gt;
 * The Image structure as used by this class:&lt;br /&gt;
 *&lt;br /&gt;
 * struct Image {&lt;br /&gt;
 *&lt;br /&gt;
 * int16    LeftEdge;        &amp;lt;----Offset relative to the container&lt;br /&gt;
 * int16    TopEdge;&lt;br /&gt;
 *&lt;br /&gt;
 * int16    Width;           &amp;lt;----Contains the text extent of the string&lt;br /&gt;
 * int16    Height;&lt;br /&gt;
 *&lt;br /&gt;
 * int16    Depth;           &amp;lt;----Maintained by BOOPSI (must be set to CUSTOMIMAGEDEPTH).&lt;br /&gt;
 *&lt;br /&gt;
 * Uint16   *ImageData;      &amp;lt;----Pointer to a NULL terminated text string&lt;br /&gt;
 *&lt;br /&gt;
 * uint8    PlanePick;       &amp;lt;----We use this for the foreground color&lt;br /&gt;
 *&lt;br /&gt;
 * uint8    PlaneOnOff;      &amp;lt;----We use this for the background color&lt;br /&gt;
 *&lt;br /&gt;
 * struct Image *NextImage;  &amp;lt;----Pointer to the next image.  Handled by DrawImage(). };&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;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/cghooks.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/imageclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/screens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfx.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/gfxmacros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;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;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
extern struct IntuitionIFace *IIntuition;&lt;br /&gt;
extern struct GraphicsIFace *IGraphics;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Because we are dealing with imageclass objects, the data structure that makes up the&lt;br /&gt;
 * object is an intuition Image structure.&lt;br /&gt;
 */&lt;br /&gt;
#define IM(o) ((struct Image *)(o))&lt;br /&gt;
&lt;br /&gt;
#define MYCLASSID    NULL&lt;br /&gt;
#define SUPERCLASSID (IMAGECLASS)&lt;br /&gt;
&lt;br /&gt;
Class          *initmyTextLabelClass(VOID);&lt;br /&gt;
uint32           freemyTextLabelClass(Class * cl);&lt;br /&gt;
uint32           dispatchmyTextLabel(Class * cl, Object * o, Msg msg);&lt;br /&gt;
uint32           setmyTextLabelAttrs(Class * cl, Object * o, struct opSet * msg);&lt;br /&gt;
uint32           getmyTextLabelAttr(Class * cl, Object * o, struct opGet * msg);&lt;br /&gt;
uint32           drawmyTextLabel(Class * cl, Object * o, struct impDraw * msg);&lt;br /&gt;
int16            aTextExtent(struct RastPort *, STRPTR, LONG, struct TextExtent *);&lt;br /&gt;
uint16           GetLabelKeystroke(STRPTR label);&lt;br /&gt;
static VOID     getContentsExtent(Class * cl, Object * o, struct DrawInfo * drinfo);&lt;br /&gt;
&lt;br /&gt;
struct localObjData&lt;br /&gt;
{&lt;br /&gt;
    /* Font to use */&lt;br /&gt;
    struct TextFont *lod_Font;&lt;br /&gt;
&lt;br /&gt;
    /* The key that is underlined */&lt;br /&gt;
    uint16           lod_Key;&lt;br /&gt;
&lt;br /&gt;
    /* DrawMode */&lt;br /&gt;
    uint8            lod_Mode;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Class *initmyTextLabelClass(VOID)&lt;br /&gt;
{&lt;br /&gt;
    Class          *cl;&lt;br /&gt;
&lt;br /&gt;
    if ((cl = IIntuition-&amp;gt;MakeClass(MYCLASSID,&lt;br /&gt;
        SUPERCLASSID, NULL,&lt;br /&gt;
        sizeof(struct localObjData), 0)))&lt;br /&gt;
    {&lt;br /&gt;
        /* Fill in the callback hook */&lt;br /&gt;
        cl-&amp;gt;cl_Dispatcher.h_Entry = (HOOKFUNC)dispatchmyTextLabel;&lt;br /&gt;
    }&lt;br /&gt;
    /* Return a pointer to the class */&lt;br /&gt;
    return (cl);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint32 freemyTextLabelClass(Class * cl)&lt;br /&gt;
{&lt;br /&gt;
    /* Try to free the public class */&lt;br /&gt;
    return ((uint32) IIntuition-&amp;gt;FreeClass(cl));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint32 dispatchmyTextLabel(Class * cl, Object * o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
    struct localObjData *lod;&lt;br /&gt;
    Object         *newobj;&lt;br /&gt;
    uint32           retval;&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;MethodID)&lt;br /&gt;
    {&lt;br /&gt;
        case OM_NEW:&lt;br /&gt;
            /* Pass up to the superclass... */&lt;br /&gt;
            if ((newobj = (Object *) IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg)))&lt;br /&gt;
            {&lt;br /&gt;
                struct TagItem *attrs = ((struct opSet *) msg)-&amp;gt;ops_AttrList;&lt;br /&gt;
                struct DrawInfo *drinfo;&lt;br /&gt;
&lt;br /&gt;
                /* Get the DrawInfo */&lt;br /&gt;
                drinfo = (struct DrawInfo *) IUtility-&amp;gt;GetTagData(SYSIA_DrawInfo, NULL, attrs);&lt;br /&gt;
&lt;br /&gt;
                /* Get the instance data */&lt;br /&gt;
                lod = (struct localObjData *)INST_DATA(cl, newobj);&lt;br /&gt;
&lt;br /&gt;
                /* Establish defaults */&lt;br /&gt;
                IM(newobj)-&amp;gt;PlanePick = 1;&lt;br /&gt;
                lod-&amp;gt;lod_Mode = JAM1;&lt;br /&gt;
&lt;br /&gt;
                /* Set the attributes */&lt;br /&gt;
                setmyTextLabelAttrs(cl, newobj, (struct opSet *) msg);&lt;br /&gt;
&lt;br /&gt;
                /* Get the bounding rectangle of the label */&lt;br /&gt;
                getContentsExtent(cl, newobj, drinfo);&lt;br /&gt;
            }&lt;br /&gt;
            retval = (uint32) newobj;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case OM_GET:&lt;br /&gt;
            retval = getmyTextLabelAttr(cl, o, (struct opGet *) msg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case OM_UPDATE:&lt;br /&gt;
        case OM_SET:&lt;br /&gt;
            /* Do the superclass first */&lt;br /&gt;
            retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
&lt;br /&gt;
            /* Call our set routines */&lt;br /&gt;
            retval += setmyTextLabelAttrs(cl, o, (struct opSet *) msg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case IM_DRAW:               /* draw the label */&lt;br /&gt;
        case IM_DRAWFRAME:          /* drawmyTextLabel() will take care of extra framing info */&lt;br /&gt;
            retval = drawmyTextLabel(cl, o, (struct impDraw *) msg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* Let the superclass handle everything else */&lt;br /&gt;
        default:&lt;br /&gt;
            retval = (uint32) IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (retval);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Set attributes of an object */&lt;br /&gt;
uint32 setmyTextLabelAttrs(Class * cl, Object * o, struct opSet * msg)&lt;br /&gt;
{&lt;br /&gt;
    struct localObjData *lod = (struct localObjData *)INST_DATA(cl, o);&lt;br /&gt;
    struct TagItem *tags = msg-&amp;gt;ops_AttrList;&lt;br /&gt;
    struct TagItem *tstate;&lt;br /&gt;
    struct TagItem *tag;&lt;br /&gt;
    uint32           tidata;&lt;br /&gt;
&lt;br /&gt;
    /* process rest */&lt;br /&gt;
    tstate = tags;&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;
        case IA_FGPen:&lt;br /&gt;
            IM(o)-&amp;gt;PlanePick = (uint8) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case IA_BGPen:&lt;br /&gt;
            IM(o)-&amp;gt;PlaneOnOff = (uint8) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* Must be a TextFont pointer. */&lt;br /&gt;
        case IA_Font:&lt;br /&gt;
        /* Set the font */&lt;br /&gt;
            lod-&amp;gt;lod_Font = (struct TextFont *) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* Drawing mode to use */&lt;br /&gt;
        case IA_Mode:&lt;br /&gt;
            lod-&amp;gt;lod_Mode = (uint8) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case IA_Data:&lt;br /&gt;
            IM(o)-&amp;gt;ImageData = (uint16 *) tidata;&lt;br /&gt;
            lod-&amp;gt;lod_Key = GetLabelKeystroke((STRPTR) tidata);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (1L);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Inquire attributes of an object */&lt;br /&gt;
uint32 getmyTextLabelAttr(Class * cl, Object * o, struct opGet * msg)&lt;br /&gt;
{&lt;br /&gt;
    struct localObjData *lod = (struct localObjData *)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;opg_AttrID)&lt;br /&gt;
    {&lt;br /&gt;
        case IA_Font:&lt;br /&gt;
            *msg-&amp;gt;opg_Storage = (uint32) lod-&amp;gt;lod_Font;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case IA_Mode:&lt;br /&gt;
            *msg-&amp;gt;opg_Storage = (uint32) lod-&amp;gt;lod_Mode;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* Let the superclass try */&lt;br /&gt;
        default:&lt;br /&gt;
            return ((uint32) IIntuition-&amp;gt;IDoSuperMethodA(cl, o, (Msg)msg));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (1L);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint32 drawmyTextLabel(Class * cl, Object * o, struct impDraw * msg)&lt;br /&gt;
{&lt;br /&gt;
    struct localObjData *lod = (struct localObjData *)INST_DATA(cl, o);&lt;br /&gt;
    STRPTR          label = (STRPTR) IM(o)-&amp;gt;ImageData;&lt;br /&gt;
    struct DrawInfo *di = msg-&amp;gt;imp_DrInfo;&lt;br /&gt;
    struct RastPort *rp = msg-&amp;gt;imp_RPort;&lt;br /&gt;
    struct TextFont *tf = NULL;&lt;br /&gt;
    int16            len = strlen(label);&lt;br /&gt;
    int16            left, top;&lt;br /&gt;
    int16            height = 0;&lt;br /&gt;
    int16            width = 0;&lt;br /&gt;
    int16            i;&lt;br /&gt;
&lt;br /&gt;
    /* Clear the key */&lt;br /&gt;
    lod-&amp;gt;lod_Key = NULL;&lt;br /&gt;
&lt;br /&gt;
    /* Get a pointer to the font to use */&lt;br /&gt;
    if (!(tf = lod-&amp;gt;lod_Font) &amp;amp;&amp;amp; di)&lt;br /&gt;
    {&lt;br /&gt;
        tf = di-&amp;gt;dri_Font;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Make sure we have font pointer */&lt;br /&gt;
    if (tf)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set the font */&lt;br /&gt;
        IGraphics-&amp;gt;SetFont(rp, tf);&lt;br /&gt;
    }&lt;br /&gt;
    /* Figure out our coordinates */&lt;br /&gt;
    top = msg-&amp;gt;imp_Offset.Y + IM(o)-&amp;gt;TopEdge + rp-&amp;gt;TxBaseline;&lt;br /&gt;
    left = msg-&amp;gt;imp_Offset.X + IM(o)-&amp;gt;LeftEdge;&lt;br /&gt;
&lt;br /&gt;
    /* See if we have frame information. */&lt;br /&gt;
    if (msg-&amp;gt;MethodID == IM_DRAWFRAME)&lt;br /&gt;
    {&lt;br /&gt;
        /* Center the text inside the frame. */&lt;br /&gt;
        width = msg-&amp;gt;imp_Dimensions.Width;&lt;br /&gt;
        height = msg-&amp;gt;imp_Dimensions.Height;&lt;br /&gt;
        top += ((height - IM(o)-&amp;gt;Height) &amp;gt; 0) ? ((height - IM(o)-&amp;gt;Height) / 2) : 0;&lt;br /&gt;
        left += ((width - IM(o)-&amp;gt;Width) &amp;gt; 0) ? ((width - IM(o)-&amp;gt;Width) / 2) : 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Set the colors */&lt;br /&gt;
    IGraphics-&amp;gt;SetAPen(rp, IM(o)-&amp;gt;PlanePick);&lt;br /&gt;
    IGraphics-&amp;gt;SetBPen(rp, IM(o)-&amp;gt;PlaneOnOff);&lt;br /&gt;
&lt;br /&gt;
    /* Set the drawing mode */&lt;br /&gt;
    IGraphics-&amp;gt;SetDrMd(rp, lod-&amp;gt;lod_Mode);&lt;br /&gt;
&lt;br /&gt;
    /* Move to the start */&lt;br /&gt;
    IGraphics-&amp;gt;Move(rp, left, top);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    /* Step through string */&lt;br /&gt;
    for (i = 0; i &amp;lt; (len - 1); i++)&lt;br /&gt;
    {&lt;br /&gt;
        /* Is this an &#039;_&#039; ? */&lt;br /&gt;
        if (label[i] == &#039;_&#039;)&lt;br /&gt;
        {&lt;br /&gt;
            int16            bot = (top + rp-&amp;gt;TxHeight - rp-&amp;gt;TxBaseline);&lt;br /&gt;
            int16            mark;&lt;br /&gt;
&lt;br /&gt;
            /* Draw the first part of the string */&lt;br /&gt;
            IGraphics-&amp;gt;Text(rp, label, i);&lt;br /&gt;
&lt;br /&gt;
            /* Remember where we are in the string */&lt;br /&gt;
            mark = rp-&amp;gt;cp_x;&lt;br /&gt;
&lt;br /&gt;
            /* Draw the underscore */&lt;br /&gt;
            IGraphics-&amp;gt;Move(rp, mark, bot);&lt;br /&gt;
            IGraphics-&amp;gt;Draw(rp, (mark + IGraphics-&amp;gt;TextLength(rp, &amp;amp;label[(i + 1)], 1L) - 2), bot);&lt;br /&gt;
&lt;br /&gt;
            /* Return to where we were */&lt;br /&gt;
            IGraphics-&amp;gt;Move(rp, mark, top);&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
             * Draw the rest of the string.  This one is done last so that the cursor&lt;br /&gt;
             * could be positioned after the text.&lt;br /&gt;
             */&lt;br /&gt;
            IGraphics-&amp;gt;Text(rp, &amp;amp;label[(i + 1)], (len - i - 1));&lt;br /&gt;
&lt;br /&gt;
            /* Return the underlined character */&lt;br /&gt;
            lod-&amp;gt;lod_Key = (uint16) label[i];&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Do we have an underscore? */&lt;br /&gt;
    if (!lod-&amp;gt;lod_Key)&lt;br /&gt;
    {&lt;br /&gt;
    /* Didn&#039;t find an &#039;_&#039; sign */&lt;br /&gt;
        IGraphics-&amp;gt;Text(rp, label, len);&lt;br /&gt;
    }&lt;br /&gt;
    return (1L);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint16 GetLabelKeystroke(STRPTR label)&lt;br /&gt;
{&lt;br /&gt;
    LONG            count = (label) ? strlen(label) : 0L;&lt;br /&gt;
    LONG            i;&lt;br /&gt;
&lt;br /&gt;
    /* Search for an _ sign */&lt;br /&gt;
    for (i = 0; i &amp;lt; (count - 1); i++)&lt;br /&gt;
    {&lt;br /&gt;
        /* Did we find an _ sign? */&lt;br /&gt;
        if (label[i] == &#039;_&#039;)&lt;br /&gt;
        {&lt;br /&gt;
            return ((uint16) label[(i + 1)]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return (0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* TextExtent that honors the &#039;_&#039; as being a non-printable character (once) */&lt;br /&gt;
int16 aTextExtent(struct RastPort * rp, STRPTR string, LONG count, struct TextExtent * te)&lt;br /&gt;
{&lt;br /&gt;
    int16 retval = FALSE;&lt;br /&gt;
    STRPTR buffer;&lt;br /&gt;
    LONG i;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate a temporary buffer */&lt;br /&gt;
    if ((buffer = (STRPTR)IExec-&amp;gt;AllocVecTags((count + 1), AVT_ClearWithValue, 0, TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
        /* Step through string */&lt;br /&gt;
        for (i = 0; i &amp;lt; count; i++)&lt;br /&gt;
        {&lt;br /&gt;
            /* Is this an &#039;_&#039; sign? */&lt;br /&gt;
            if (string[i] == &#039;_&#039;)&lt;br /&gt;
            {&lt;br /&gt;
                /* Add the rest of the label to the buffer */&lt;br /&gt;
                strcat (buffer, &amp;amp;string[(i + 1)]);&lt;br /&gt;
&lt;br /&gt;
                /* Adjust the length of the string. */&lt;br /&gt;
                count--;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                /* Copy each character over, until we reach the _ mark */&lt;br /&gt;
                buffer[i] = string[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Get the extent */&lt;br /&gt;
        IGraphics-&amp;gt;TextExtent(rp, buffer, count, te);&lt;br /&gt;
&lt;br /&gt;
        /* Free the temporary buffer */&lt;br /&gt;
        IExec-&amp;gt;FreeVec (buffer);&lt;br /&gt;
&lt;br /&gt;
        /* Show that we were successful */&lt;br /&gt;
        retval = TRUE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Return whatever textextent returned */&lt;br /&gt;
    return (retval);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static VOID getContentsExtent(Class * cl, Object * o, struct DrawInfo * drinfo)&lt;br /&gt;
{&lt;br /&gt;
    struct localObjData *lod = (localObjData *)INST_DATA(cl, o);&lt;br /&gt;
    struct TextExtent te = {0, 0, {0, 0, 0, 0}};&lt;br /&gt;
    struct RastPort rp;&lt;br /&gt;
    STRPTR          label;&lt;br /&gt;
&lt;br /&gt;
    /* maybe look at some flags to handle other types of text someday */&lt;br /&gt;
    if ((label = (STRPTR) IM(o)-&amp;gt;ImageData))&lt;br /&gt;
    {&lt;br /&gt;
        /* Initialize the RastPort */&lt;br /&gt;
        IGraphics-&amp;gt;InitRastPort(&amp;amp;rp);&lt;br /&gt;
&lt;br /&gt;
        if (lod-&amp;gt;lod_Font)&lt;br /&gt;
        {&lt;br /&gt;
            IGraphics-&amp;gt;SetFont(&amp;amp;rp, lod-&amp;gt;lod_Font);&lt;br /&gt;
        }&lt;br /&gt;
        else if (drinfo &amp;amp;&amp;amp; drinfo-&amp;gt;dri_Font)&lt;br /&gt;
        {&lt;br /&gt;
            IGraphics-&amp;gt;SetFont(&amp;amp;rp, drinfo-&amp;gt;dri_Font);&lt;br /&gt;
        }&lt;br /&gt;
        /* Get the rectangle for the label */&lt;br /&gt;
        aTextExtent(&amp;amp;rp, label, strlen(label), &amp;amp;te);&lt;br /&gt;
&lt;br /&gt;
        /* Set the image structure */&lt;br /&gt;
        IM(o)-&amp;gt;Width = te.te_Width;&lt;br /&gt;
        IM(o)-&amp;gt;Height = te.te_Height;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        IM(o)-&amp;gt;Width = IM(o)-&amp;gt;Height = 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BOOPSI_Gadgets&amp;diff=12393</id>
		<title>BOOPSI Gadgets</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BOOPSI_Gadgets&amp;diff=12393"/>
		<updated>2024-09-10T09:06:10Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* RKMButtonclass.c */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
One of the major enhancements to Intuition is the implementation of customizable BOOPSI gadgets. BOOPSI gadgets are not limited by dependencies upon Intuition Image and Gadget structures. Unlike ordinary gadgets, which were handled exclusively by Intuition, BOOPSI gadgets handle their own rendering and their own user input.&lt;br /&gt;
&lt;br /&gt;
Since BOOPSI gadgets draw themselves, there is almost no restriction on what they can look like. A BOOPSI gadget can use graphics.library RastPort drawing functions to draw vector-based imagery which the gadget can scale to any dimension. Instead of just a two-state Boolean gadget, a BOOPSI gadget can have any number of states, each of which has its own imagery. If a programmer wanted to he could even make a BOOPSI gadget that uses the animation system to render itself.&lt;br /&gt;
&lt;br /&gt;
Because BOOPSI gadgets handle their own input, they see all the user&#039;s input, which the gadget is free to interpret. While the user has a BOOPSI gadget selected, the gadget can track mouse moves, process mouse and keyboard key presses, or watch the timer events.&lt;br /&gt;
&lt;br /&gt;
The power of a BOOPSI gadget is not limited to its ability to handle its own rendering and user input. BOOPSI gadgets are also BOOPSI objects so the gain all the benefits BOOPSI provides. This means all BOOPSI gadgets inherit the methods and attributes from their superclasses. BOOPSI gadgets can use BOOPSI images to take care of rendering their imagery. A BOOPSI gadget could be a &amp;quot;composite&amp;quot; gadget that is composed of several BOOPSI gadgets, images, and models.&lt;br /&gt;
&lt;br /&gt;
=== The BOOPSI Gadget Methods ===&lt;br /&gt;
&lt;br /&gt;
Intuition drives a BOOPSI gadget by sending it BOOPSI messages. Intuition uses the following BOOPSI methods:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Method !! Description&lt;br /&gt;
|-&lt;br /&gt;
| GM_DOMAIN || Obtain gadget sizing requirements.&lt;br /&gt;
|-&lt;br /&gt;
| GM_EXTENT || Inquire about rendering extent. (V51)&lt;br /&gt;
|-&lt;br /&gt;
| GM_GOACTIVE || This method asks a gadget if it wants to be the active gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GM_GOINACTIVE || This method tells a gadget that it is no longer active.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HANDLEINPUT || This method passes a gadget an input event.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HELPTEST || Determine if gadget help was hit.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HITTEST || This method asks a gadget whether it has been &amp;quot;hit&amp;quot; by a mouse click.&lt;br /&gt;
|-&lt;br /&gt;
| GM_LAYOUT || Calculate relative gadget coordinates.&lt;br /&gt;
|-&lt;br /&gt;
| GM_RENDER || This method tells the gadget to render itself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The formats of each of these BOOPSI messages differ, but they all have two things in common. Like all BOOPSI messages, each starts with their respective method ID. For each of these methods, the method ID field is followed by a pointer to a GadgetInfo structure (defined in &amp;lt;intuition/cghooks.h&amp;gt;). The GadgetInfo structure contains information about the display on which the gadget needs to render itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct GadgetInfo {&lt;br /&gt;
    struct Screen               *gi_Screen;&lt;br /&gt;
    struct Window               *gi_Window;     /* null for screen gadgets */&lt;br /&gt;
    struct Requester            *gi_Requester;  /* null if not GTYP_REQGADGET */&lt;br /&gt;
&lt;br /&gt;
    /* rendering information: don&#039;t use these without cloning/locking.&lt;br /&gt;
     * Official way is to call ObtainGIRPort()&lt;br /&gt;
     */&lt;br /&gt;
    struct RastPort             *gi_RastPort;&lt;br /&gt;
    struct Layer                *gi_Layer;&lt;br /&gt;
&lt;br /&gt;
    /* copy of dimensions of screen/window/g00/req(/group)&lt;br /&gt;
     * that gadget resides in.  Left/Top of this box is&lt;br /&gt;
     * offset from window mouse coordinates to gadget coordinates&lt;br /&gt;
     *  screen gadgets:                 0,0 (from screen coords)&lt;br /&gt;
     *  window gadgets (no g00):        0,0&lt;br /&gt;
     *  GTYP_GZZGADGETs (borderlayer):  0,0&lt;br /&gt;
     *  GZZ innerlayer gadget:          borderleft, bordertop&lt;br /&gt;
     *  Requester gadgets:              reqleft, reqtop&lt;br /&gt;
     */&lt;br /&gt;
    struct IBox                 gi_Domain;&lt;br /&gt;
&lt;br /&gt;
    /* these are the pens for the window or screen      */&lt;br /&gt;
    struct {&lt;br /&gt;
        UBYTE   DetailPen;&lt;br /&gt;
        UBYTE   BlockPen;&lt;br /&gt;
    }                           gi_Pens;&lt;br /&gt;
&lt;br /&gt;
    /* the Detail and Block pens in gi_DrInfo-&amp;amp;gt;dri_Pens[] are&lt;br /&gt;
     * for the screen.  Use the above for window-sensitive colors.&lt;br /&gt;
     */&lt;br /&gt;
    struct DrawInfo             *gi_DrInfo;&lt;br /&gt;
&lt;br /&gt;
    /* reserved space: this structure is extensible&lt;br /&gt;
     * anyway, but using these saves some recompilation&lt;br /&gt;
     */&lt;br /&gt;
    ULONG                       gi_Reserved[6];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All the fields in this structure are read only.&lt;br /&gt;
&lt;br /&gt;
Although this structure contains a pointer to the gadget&#039;s RastPort structure, applications should not use it for rendering. Instead, use the intuition.library function ObtainGIRPort() to obtain a copy of the GadgetInfo&#039;s RastPort. When the gadget is finished with this RastPort, it should call ReleaseGIRPort() to relinquish the RastPort.&lt;br /&gt;
&lt;br /&gt;
==== GM_DOMAIN ====&lt;br /&gt;
&lt;br /&gt;
Intuition uses the GM_DOMAIN method to determine the basic sizing requirements of an object. The domain is most often used by layout gadgets to aid in distributing the gadgets within the layout via GM_LAYOUT. The GM_DOMAIN method uses the gpDomain structure (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) as its message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpDomain&lt;br /&gt;
{&lt;br /&gt;
    uint32		 MethodID;&lt;br /&gt;
    struct GadgetInfo	*gpd_GInfo;&lt;br /&gt;
    struct RastPort	*gpd_RPort;	/* RastPort to layout for */&lt;br /&gt;
    int32		 gpd_Which;&lt;br /&gt;
    struct IBox		 gpd_Domain;	/* Resulting domain */&lt;br /&gt;
    struct TagItem	*gpd_Attrs;	/* Additional attributes */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gpd_Which field is used to determine which domain the caller is interested in: GDOMAIN_MINIMUM, GDOMAIN_NOMINAL or GDOMAIN_MAXIMUM. The object is expected to fill in the provided gpd_Domain with the width and height dimensions. The location coordinates are not used. The gpd_Attrs pointer, if not NULL, may contain additional tags which are defined on a per-class basis.&lt;br /&gt;
&lt;br /&gt;
==== GM_EXTENT (V51) ====&lt;br /&gt;
&lt;br /&gt;
The GM_EXTENT method is used to ask the gadget what pixels (at least) it will fully redraw when its GM_RENDER method is invoked in the same context. By &amp;quot;fully redraw&amp;quot; we mean changing the pixel&#039;s color in a way that is totally unrelated to its previous value -- so this doesn&#039;t apply to alpha-blended pixels for example.&lt;br /&gt;
&lt;br /&gt;
Intuition uses that information for optimization purposes. During GUI refreshes, it will skip filling or erasing those pixels that the gadget would then completely re-render anyway. Supporting this method in your gadgets will help Intuition improve the smoothness of user interface refresh by preventing redundant graphic calls and minimizing &amp;quot;flicker&amp;quot; effects caused by background clearing especially during window resizes.&lt;br /&gt;
&lt;br /&gt;
===== Simple Support =====&lt;br /&gt;
&lt;br /&gt;
The easiest way to support GM_EXTENT is to make sure your gadget always fills every pixel within its dimensions and just return the relevant result value (GMR_FULLHBOX or GMR_FULLBBOX). In this case, you may safely ignore the actual message contents and the following text.&lt;br /&gt;
&lt;br /&gt;
If the above solution is not feasible (for instance because the gadget has an irregular or not fully connected shape) then you should check the gpe_Region and gpe_RPort message fields: if any of them is non-NULL you may decide to employ a more detailed way to tell the caller exactly what pixels your GM_RENDER method does fill.&lt;br /&gt;
&lt;br /&gt;
===== Region and RastPort Support =====&lt;br /&gt;
&lt;br /&gt;
If gpe_Region is non-NULL you can compose in it your gadget&#039;s shape by using graphics.library&#039;s XxxxRectRegion() functions. Remember to look at the gpe_Action field to determine what function to use. For example, if gpe_Action is GEXTENT_ADD you should use OrRectRegion(). Note you must NOT pre-clear or alter the initial region&#039;s contents in any way other than to compose your own gadget&#039;s shape. Once finished composing your gadget&#039;s shape in the region, return GMR_CLIPDONE to let the caller know you updated the region&#039;s contents.&lt;br /&gt;
&lt;br /&gt;
If gpe_RPort is non-NULL you can draw your gadget&#039;s shape into it just like you would do for a GM_RENDER message -- however, you&#039;re only allowed to use colors 0 or 1 because you&#039;re actually drawing into a single-bitplane mask. Again, check the gpe_Action field to find out whether you should actually set, clear or invert the pixels making up your gadget&#039;s shape in the mask. You are not allowed to alter any pixels other than those belonging to your gadget&#039;s shape nor to clear the mask&#039;s background before rendering. Once finished drawing your gadget&#039;s shape in the mask plane return GMR_MASKDONE to let the caller know you updated the mask&#039;s contents.&lt;br /&gt;
&lt;br /&gt;
If both gpe_Region and gpe_RPort are non-NULL you may simply choose the method which is most suited for your purposes. You don&#039;t have to support both for the same message. If you do, however, you can let the caller know by ORing together the appropriate return values. The region solution is best suited for gadgets made up of a few rectangular parts whereas the mask method is better in the case of more complex gadget shapes.&lt;br /&gt;
&lt;br /&gt;
===== Images =====&lt;br /&gt;
&lt;br /&gt;
If all or part of your gadget&#039;s rendering is performed by some BOOPSI image you could also try asking the image for its extent information by way of an IM_EXTENT or IM_EXTENTFRAME message (see [[BOOPSI_Images|BOOPSI Images]]).&lt;br /&gt;
&lt;br /&gt;
If for whatever reasons your GM_EXTENT method finds itself unable to support any of the described solutions it should return GMR_INVALID. This particular return value will tell Intuition not to clip away the gadget&#039;s shape at all. While this is usually slower and more prone to flickering it will still produce correct graphic results. This is the same fallback applied for any gadgets not recognizing the GM_EXTENT method.&lt;br /&gt;
&lt;br /&gt;
===== Superclass Extent Handling =====&lt;br /&gt;
&lt;br /&gt;
Since it is very important that a gadget&#039;s GM_RENDER and GM_EXTENT methods remain synchronized the default behavior of a gadget class should be to only handle GM_EXTENT if it is the &amp;quot;true class&amp;quot; of the object the method is invoked on and just return GMR_INVALID otherwise. This is because a subclass might override the behavior of GM_RENDER in such a way that your class&#039; GM_EXTENT results are no longer correct.&lt;br /&gt;
&lt;br /&gt;
If a gadget subclass needs and knows it&#039;s ok to let its superclass handle GM_EXTENT it must set the GPEF_ALLOWSUPER flag in gpe_Flags before calling IDoSuperMethodA() and clear it as soon as the call returns. If GPEF_ALLOWSUPER is set in gpe_Flags your gadget should accept and handle GM_EXTENT regardless of whether it is the true class or not.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=As of Intuition V51 the mask method is not yet implemented.}}&lt;br /&gt;
&lt;br /&gt;
==== GM_GOACTIVE/GM_HANDLEINPUT ====&lt;br /&gt;
&lt;br /&gt;
If a gadget returns GMR_GADGETHIT, Intuition will send it a GM_GOACTIVE message (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpInput                          /* Used by GM_GOACTIVE and GM_HANDLEINPUT */&lt;br /&gt;
{&lt;br /&gt;
    ULONG             MethodID;&lt;br /&gt;
    struct GadgetInfo *gpi_GInfo;&lt;br /&gt;
    struct InputEvent *gpi_IEvent;      /* The input event that triggered this method&lt;br /&gt;
                                         * (for GM_GOACTIVE, this can be NULL) */&lt;br /&gt;
    LONG              *gpi_Termination; /* For GADGETUP IntuiMessage.Code */&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        WORD X;                         /* Mouse position relative to upper          */&lt;br /&gt;
        WORD Y;                         /* left corner of gadget (LeftEdge, TopEdge) */&lt;br /&gt;
    } gpi_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The GM_GOACTIVE message gives a gadget the opportunity to become the active gadget. The active gadget is the gadget that is currently receiving user input. Under normal conditions, only one gadget can be the active gadget (it is possible to have more than one active gadget using a groupgclass object (See the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] for more details).&lt;br /&gt;
&lt;br /&gt;
While a gadget is active, Intuition sends it GM_HANDLEINPUT messages. Each GM_HANDLEINPUT message corresponds to a single InputEvent structure. These InputEvents can be keyboard presses, timer events, mouse moves, or mouse button presses. The message&#039;s gpi_IEvent field points to this InputEvent structure. It&#039;s up to the GM_HANDLEINPUT method to interpret the meaning of these events and update the visual state of the gadget as the user manipulates the gadget. For example, the GM_HANDLEINPUT method of a prop gadget has to track mouse events to see where the user has moved the prop gadget&#039;s knob and update the gadget&#039;s imagery to reflect the new position of the knob.&lt;br /&gt;
&lt;br /&gt;
For the GM_GOACTIVE method, the gpi_IEvent field points to the struct InputEvent that triggered the GM_GOACTIVE message. Unlike the GM_HANDLEINPUT message, GM_GOACTIVE&#039;s gpi_IEvent can be NULL. If the GM_GOACTIVE message was triggered by a function like intuition.library&#039;s ActivateGadget() and not by a real InputEvent (like the user clicking the gadget), the gpi_IEvent field will be NULL.&lt;br /&gt;
&lt;br /&gt;
For gadgets that only want to become active as a direct result of a mouse click, this difference is important. For example, the prop gadget becomes active only when the user clicks on its knob. Because the only way the user can control the prop gadget is via the mouse, it does not make sense for anything but the mouse to activate the gadget. On the other hand, a string gadget doesn&#039;t care how it is activated because, as soon as it&#039;s active, it gets user input from the keyboard rather than the mouse. Not all gadgets can become active. Some gadgets cannot become active because they have been temporarily disabled (their Gadget.Flags GFLG_DISABLED bit is set). Other gadgets will not become active because they don&#039;t need to process input. For example, a toggle gadget won&#039;t become active because it only needs to process one input event, the mouse click that toggles the gadget (which it gets from the GM_GOACTIVE message). If a toggle gadget gets a GM_GOACTIVE message and its gpi_IEvent field is not NULL, it will toggle its state and refuse to &amp;quot;go active&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The GM_GOACTIVE method has to take care of any visual state changes to a gadget that a GM_GOACTIVE message might trigger. For example, the toggle gadget in the previous paragraph has to take care of toggling its visual state from selected imagery to unselected imagery. If the gadget goes through a state change when it becomes the active gadget, (like when a string gadget positions its cursor) GM_GOACTIVE has to take care of this.&lt;br /&gt;
&lt;br /&gt;
===== Return Values =====&lt;br /&gt;
&lt;br /&gt;
The return values of both GM_GOACTIVE and GM_HANDLEINPUT tell Intuition whether or not the gadget wants to be active. A gadget&#039;s GM_GOACTIVE method returns GMR_MEACTIVE (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;) if it wants to become the active gadget. A gadget&#039;s GM_HANDLEINPUT method returns GMR_MEACTIVE if it wants to remain the active gadget. If a gadget either does not want to become or remain the active gadget, it returns one of the &amp;quot;go inactive&amp;quot; return values:&lt;br /&gt;
&lt;br /&gt;
; GMR_NOREUSE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent.&lt;br /&gt;
&lt;br /&gt;
; GMR_REUSE&lt;br /&gt;
: Tells Intuition to process the gpInput.gpi_IEvent InputEvent.&lt;br /&gt;
&lt;br /&gt;
; GMR_NEXTACTIVE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent and activate the next GFLG_TagCycle gadget.&lt;br /&gt;
&lt;br /&gt;
; GMR_PREVACTIVE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent and activate the previous GFLG_TagCycle gadget.&lt;br /&gt;
&lt;br /&gt;
GMR_NOREUSE tells Intuition that the gadget does not want to be active and to throw away the InputEvent that triggered the message. For example, an active prop gadget returns GMR_NOREUSE when the user lets go of the left mouse button (thus letting go of the prop gadget&#039;s knob).&lt;br /&gt;
&lt;br /&gt;
For the GM_HANDLEINPUT method, a gadget can also return GMR_REUSE, which tells Intuition to reuse the InputEvent. For example, if the user clicks outside the active string gadget, that string gadget returns GMR_REUSE. Intuition can now process that mouse click, which can be over another gadget. Another case where a string gadget returns GMR_REUSE is when the user pushes the right mouse button (the menu button). The string gadget becomes inactive and the menu button InputEvent gets reused. Intuition sees this event and tries to pop up the menu bar.&lt;br /&gt;
&lt;br /&gt;
For the GM_GOACTIVE method, a gadget must not return GMR_REUSE. If a gadget gets a GM_GOACTIVE message from Intuition and the message has an gpi_IEvent, the message was triggered by the user clicking on the gadget. In this case, Intuition knows that the user is trying to select the gadget. Intuition doesn&#039;t know if the gadget can be activated, but if it can be activated, the event that triggered the activation has just taken place. If the gadget cannot become active for any reason, it must not let Intuition reuse that InputEvent as the gadget has already taken care of the the event&#039;s purpose (clicking on the gadget). In essence, the user tried to activate the gadget and the gadget refused to become active.&lt;br /&gt;
&lt;br /&gt;
The other two possible return values are GMR_NEXTACTIVE and GMR_PREVACTIVE. These tell Intuition that a gadget does not want to be active and that the InputEvent should be discarded. Intuition then looks for the next (GMR_NEXTACTIVE) or previous (GMR_PREVACTIVE) gadget that has its GFLG_TABCYCLE flag set in its Gadget.Activation field (see the gadgetclass GA_TabCycle attribute in the [[BOOPSI_Class_Reference|BOOPSI Class Reference]]).&lt;br /&gt;
&lt;br /&gt;
For both GM_GOACTIVE and GM_HANDLEINPUT, the gadget can bitwise-OR any of these &amp;quot;go inactive&amp;quot; return values with GMR_VERIFY. The GMR_VERIFY flag tells Intuition to send a GADGETUP IntuiMessage to the gadget&#039;s window. If the gadget uses GMR_VERIFY, it has to supply a value for the IntuiMessage.Code field. It does this by passing a value in the gpInput.gpi_Termination field. This field points to a long word, the lower 16-bits of which Intuition copies into the Code field. The upper 16-bits are for future enhancements, so clear these bits.&lt;br /&gt;
&lt;br /&gt;
==== GM_GOINACTIVE ====&lt;br /&gt;
&lt;br /&gt;
After an active gadget deactivates, Intuition sends it a GM_GOINACTIVE message (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpGoInactive&lt;br /&gt;
{&lt;br /&gt;
    ULONG             MethodID;    /* GM_GOINACTIVE */&lt;br /&gt;
    struct GadgetInfo *gpgi_GInfo;&lt;br /&gt;
    ULONG             gpgi_Abort; /* gpgi_Abort=1 if gadget was aborted by Intuition   */&lt;br /&gt;
                                  /* and 0 if gadget went inactive at its own request. */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gpgi_Abort field contains either a 0 or 1. If 0, the gadget became inactive on its own power (because the GM_GOACTIVE or GM_HANDLEINPUT method returned something besides GMR_MEACTIVE). If gpgi_Abort is 1, Intuition aborted this active gadget. Some instances where Intuition aborts a gadget include: the user clicked in another window or screen, an application removed the active gadget with RemoveGList(), and an application called ActiveWindow() on a window other than the gadget&#039;s window.&lt;br /&gt;
&lt;br /&gt;
==== GM_HELPTEST ====&lt;br /&gt;
&lt;br /&gt;
This method uses the same message structure as GM_HITTEST, struct gpHitTest, and operates similarly to GM_HITTEST. While a window is in help mode, when the user positions the pointer within the bounding box of a BOOPSI gadget that supports gadget help, Intuition sends the gadget a GM_HELPTEST message. After an active gadget deactivates, Intuition sends it a GM_GOINACTIVE message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpHitTest&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;    // GM_HELPTEST&lt;br /&gt;
    struct GadgetInfo *gpht_GInfo;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;                    // Is this point inside&lt;br /&gt;
        int16 Y;                    // gadget?&lt;br /&gt;
    } gpht_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like the GM_HITTEST method, the GM_HELPTEST method asks a gadget if a point is within the gadget&#039;s bounds. Like the GM_HITTEST method, the GM_HELPTEST method allows a BOOPSI gadget to have a non-rectangular hit area (or in this case, a help area). If the point is within the gadget, the gadget uses one of two return codes. If the gadget returns a value of GMR_HELPHIT, Intuition places a value of 0xFFFF in the Code field of the IDCMP_GADGETHELP message. The gadget also has the option of using GMR_HELPCODE instead of GMR_HELPHIT. GMR_HELPCODE is a little peculiar as a return value. Although GMR_HELPCODE is 32 bits long, Intuition identifies GMR_HELPCODE using only its upper 16 bits. If the upper 16 bits of GM_HELPTEST&#039;s return value matches the upper 16 bits of GMR_HELPCODE, Intuition copies the lower word of the return value into the Code field of the IDCMP_GADGETHELP message. The BOOPSI gadget is free to set the return value&#039;s lower word to any 16-bit value.&lt;br /&gt;
&lt;br /&gt;
If the point is not within the gadget&#039;s &amp;quot;help spot&amp;quot;, the gadget returns GMR_NOHELPHIT. Intuition will then look under that gadget for more gadgets. If a gadget&#039;s dispatcher passes the GM_HELPTEST method on to the gadgetclass dispatcher, the gadgetclass dispatcher&lt;br /&gt;
will always return GMR_HELPHIT.&lt;br /&gt;
&lt;br /&gt;
An application can put several windows in the same help group. This feature groups several windows so that as long as one of those windows is active, the application can receive IDCMP_GADGETHELP messages about all of those windows. For more information, See the HelpControl() Autodoc and the WA_HelpGroup section of the OpenWindow() Autodoc (both are in intuition.doc).&lt;br /&gt;
&lt;br /&gt;
==== GM_HITTEST ====&lt;br /&gt;
&lt;br /&gt;
When Intuition gets a left mouse button click in a window, one of the things it does is check through the window&#039;s list of gadgets to see if that click was inside the bounds of a gadget&#039;s Gadget structure (using the LeftEdge, TopEdge, Width, and Height fields). If it was (and that gadget is a BOOPSI gadget), Intuition sends that gadget a GM_HITTEST message (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpHitTest&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;    // GM_HITTEST&lt;br /&gt;
    struct GadgetInfo *gpht_GInfo;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;                    // Is this point inside&lt;br /&gt;
        int16 Y;                    // gadget?&lt;br /&gt;
    } gpht_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This message contains the coordinates of the mouse click. These coordinates are relative to the upper-left of the gadget (LeftEdge, TopEdge).&lt;br /&gt;
&lt;br /&gt;
Because Intuition can only tell if the user clicked inside gadget&#039;s &amp;quot;bounding box&amp;quot;, Intuition only knows that the click was close to the gadget. Intuition uses the GM_HITTEST to ask the gadget if the click was really inside the gadget. The gadget returns GMR_GADGETHIT (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;) to tell Intuition that the user hit it, otherwise it returns zero. This method allows a gadget to be any shape or pattern, rather than just rectangular.&lt;br /&gt;
&lt;br /&gt;
==== GM_LAYOUT ====&lt;br /&gt;
&lt;br /&gt;
Intuition uses the GM_LAYOUT method to tell a GREL BOOPSI gadget that its window&#039;s dimensions have changed. The GM_LAYOUT method uses the gpLayout structure (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) as its message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpLayout&lt;br /&gt;
{&lt;br /&gt;
    uint32              MethodID;&lt;br /&gt;
    struct GadgetInfo  *gpl_GInfo;&lt;br /&gt;
    uint32              gpl_Initial; /* This field is non-zero if this method was invoked&lt;br /&gt;
                                      * during AddGList() or OpenWindow().  zero if this&lt;br /&gt;
                                      * method was invoked during window resizing.&lt;br /&gt;
                                      */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For GREL gadgets, Intuition sends a GM_LAYOUT message just after erasing the gadget&#039;s bounding box. Intuition does not touch the values of the gadget&#039;s bounding box or hit box, it lets the gadget handle recalculating these values. The gadget can lay itself out based on the new window (or requester) dimensions found in the GadgetInfo structure passed in the GM_LAYOUT message (gpl_GInfo from the gpLayout structure). Intuition also adds the old and new bounding box of the gadget to the window&#039;s damaged regions so that the gadget will appear is its new position. The gadget must not perform any rendering inside the GM_LAYOUT method. Intuition will send a GM_RENDER message to the gadget when its time to redraw the gadget in its new position.&lt;br /&gt;
&lt;br /&gt;
There are two cases where Intuition sends a GM_LAYOUT message:&lt;br /&gt;
# When the gadget&#039;s window is resized&lt;br /&gt;
# When Intuition adds the gadget to a window&lt;br /&gt;
&lt;br /&gt;
For most GREL BOOPSI gadgets, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to follow the existing convention for regular GREL gadgets. Each of these fields has a flag in the Gadget.Flags field (GFLG_RELRIGHT, GFLG_RELBOTTOM, GFLG_RELWIDTH, and GFLG_RELHEIGHT, respectively). If that field&#039;s flag is set, Intuition expects the value in that field to be relative to either the window border or the window dimensions. For example, if GFLG_RELRIGHT is set, Intuition expects the value in the gadget&#039;s LeftEdge field to be relative to the right window border.&lt;br /&gt;
&lt;br /&gt;
There is a special kind of GREL BOOPSI gadget called a custom relativity gadget. For this type of gadget, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to be absolute measurements, just like the values Intuition expects in these fields for non-GREL gadgets.&lt;br /&gt;
&lt;br /&gt;
Setting the GFLG_RELSPECIAL bit in the Gadget.Flags field marks the gadget as a custom relativity gadget. The best way to set this bit is by setting the GA_RelSpecial attribute to TRUE when creating the gadget.&lt;br /&gt;
&lt;br /&gt;
==== GM_RENDER ====&lt;br /&gt;
&lt;br /&gt;
Every time Intuition feels it is necessary to redraw a BOOPSI gadget, it sends a gadget a GM_RENDER message. The GM_RENDER message (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) tells a gadget to render itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpRender&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;   /* GM_RENDER */&lt;br /&gt;
    struct GadgetInfo *gpr_GInfo;&lt;br /&gt;
    struct RastPort   *gpr_RPort;  /* all ready for use */&lt;br /&gt;
    int32              gpr_Redraw; /* might be a &amp;quot;highlight pass&amp;quot; */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some events that cause Intuition to send a GM_RENDER are: an application passed the gadget to OpenWindow(), the user moved or resized a gadget&#039;s window, or an application explicitly asked Intuition to refresh some gadgets.&lt;br /&gt;
&lt;br /&gt;
The GM_RENDER message contains a pointer to the gadget&#039;s RastPort so the GM_RENDER method does not have to extract it from the gpr_GInfo GadgetInfo structure using ObtainGIRPort()). The gadget renders itself according to how much imagery it needs to replace. The gpr_Redraw field contains one of three values:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| GREDRAW_REDRAW&lt;br /&gt;
| Redraw the entire gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GREDRAW_UPDATE&lt;br /&gt;
| The user has manipulated the gadget, causing a change to its imagery. Update only that part of the gadget&#039;s imagery that is effected by the user manipulating the gadget (for example, the knob and scrolling field of the prop gadget).&lt;br /&gt;
|-&lt;br /&gt;
| GREDRAW_TOGGLE&lt;br /&gt;
| If this gadget supports it, toggle to or from the highlighting imagery.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Intuition is not the only entity that calls this method. The gadget&#039;s other methods may call this method to render the gadget when it goes through state changes. For example, as a prop gadget is following the mouse from the gadget&#039;s GM_HANDLEINPUT method, the gadget could send itself GM_RENDER messages, telling itself to update its imagery according to where the mouse has moved.&lt;br /&gt;
&lt;br /&gt;
The DoRender() method should always be used by class implementers to invoke a GM_RENDER method on a gadget object. Applications use the RefreshGList() function which will invoke GM_RENDER for the caller in the proper context.&lt;br /&gt;
&lt;br /&gt;
=== The Active Gadget ===&lt;br /&gt;
&lt;br /&gt;
While a gadget is active, Intuition sends it a GM_HANDLEINPUT message for every timer pulse, mouse move, mouse click, and key press that takes place. A timer event pulse arrives about every tenth of a second. Mouse move events can arrive at a much higher rate than the timer pulses. Without even considering the keyboard, a gadget can get a lot of GM_HANDLEINPUT messages in a short amount of time. Because the active gadget has to handle a large volume of GM_HANDLEINPUT messages, the overhead of this method should be kept to a minimum.&lt;br /&gt;
&lt;br /&gt;
Because the gadget will always receive a GM_GOACTIVE message before it is active and a GM_GOINACTIVE message after it is no longer active, the gadget can use these methods to allocate, initialize, and deallocate temporary resources it needs for the GM_HANDLEINPUT method. This can significantly reduce the overhead of GM_HANDLEINPUT because it eliminates the need to allocate, initialize, and deallocate resources for every GM_HANDLEINPUT message.&lt;br /&gt;
&lt;br /&gt;
Note that the RastPort from ObtainGIRPort() is not cachable using this method. If the GM_HANDLEINPUT method needs to use a RastPort, it has to obtain and release the RastPort for every GM_HANDLEINPUT message using ObtainGIRPort() and ReleaseGIRPort().&lt;br /&gt;
&lt;br /&gt;
=== RKMButtonclass.c ===&lt;br /&gt;
&lt;br /&gt;
The following example is a sample BOOPSI gadget, RKMButClass.c.&lt;br /&gt;
&lt;br /&gt;
While the user has the RKMButton selected, the gadget sends an OM_UPDATE message to its ICA_TARGET for every timer event the button sees.&lt;br /&gt;
&lt;br /&gt;
The gadget sends notification about its RKMBUT_Pulse attribute, which is the horizontal distance in screen pixels the mouse is from the center of the button.&lt;br /&gt;
The gadget takes care of rendering all of its imagery (as opposed to using a BOOPSI image to do it).&lt;br /&gt;
The gadget&#039;s imagery is scalable to any dimensions and can be set (using SetGadgetAttrs()) while the gadget is in place.&lt;br /&gt;
&lt;br /&gt;
One possible use for such a gadget is as buttons for a prop gadget. &lt;br /&gt;
&lt;br /&gt;
If the user has the prop gadget&#039;s RKMButton selected, while the mouse is to the left of the button&#039;s center, the knob on the prop gadget moves left.&lt;br /&gt;
While the mouse is to the right of the button&#039;s center, the knob on the prop gadget moves right.&lt;br /&gt;
The speed at which the knob moves is proportional to the horizontal distance from the mouse to the active RKMButton.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
/* RKMButClass.c - Example BOOPSI gadget */&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/classes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/imageclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/cghooks.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/hooks.h&amp;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;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;graphics/gfxmacros.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR vers = &amp;quot;$VER: TestBut 50.1&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/****************      Class specifics      ****************/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
#define RKMBUT_Pulse   (TAG_USER + 1)&lt;br /&gt;
&lt;br /&gt;
struct ButINST&lt;br /&gt;
{&lt;br /&gt;
    LONG midX, midY; /* Coordinates of middle of gadget */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* ButINST has one flag:  */&lt;br /&gt;
#define ERASE_ONLY   0x00000001 /* Tells rendering routine to */&lt;br /&gt;
/* only erase the gadget, not */&lt;br /&gt;
/* rerender a new one.  This  */&lt;br /&gt;
/* lets the gadget erase it-  */&lt;br /&gt;
/* self before it rescales.   */&lt;br /&gt;
&lt;br /&gt;
/* The functions in this module */&lt;br /&gt;
Class *initRKMButGadClass(void);&lt;br /&gt;
BOOL   freeRKMButGadClass(Class *);&lt;br /&gt;
ULONG  dispatchRKMButGad(Class *, Object *, Msg);&lt;br /&gt;
void   NotifyPulse(Class *, Object *, ULONG, LONG, struct gpInput *);&lt;br /&gt;
ULONG  RenderRKMBut(Class *, struct Gadget *, struct gpRender *);&lt;br /&gt;
void   MainLoop(ULONG, ULONG);&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/* The main() function connects an RKMButClass object to a BOOPSI integer gadget, which displays */&lt;br /&gt;
/* the RKMButClass gadget&#039;s RKMBUT_Pulse value.  The code scales and move the gadget while it is */&lt;br /&gt;
/* in place.                                                                                     */&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct TagItem pulse2int[] =&lt;br /&gt;
    {&lt;br /&gt;
        {RKMBUT_Pulse, STRINGA_LongVal},&lt;br /&gt;
        {TAG_END,}&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
#define INTWIDTH  40&lt;br /&gt;
#define INTHEIGHT 20&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct Window *w;&lt;br /&gt;
Class *rkmbutcl;&lt;br /&gt;
struct Gadget *integer, *but;&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
&lt;br /&gt;
    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        if (w = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
            WA_Flags,       WFLG_DEPTHGADGET | WFLG_DRAGBAR |&lt;br /&gt;
                            WFLG_CLOSEGADGET | WFLG_SIZEGADGET,&lt;br /&gt;
            WA_IDCMP,       IDCMP_CLOSEWINDOW,&lt;br /&gt;
            WA_Width,       640,&lt;br /&gt;
            WA_Height,      200,&lt;br /&gt;
            TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            IIntuition-&amp;gt;WindowLimits(w, 450, 200, 640, 200);&lt;br /&gt;
&lt;br /&gt;
            if (rkmbutcl = initRKMButGadClass())&lt;br /&gt;
            {&lt;br /&gt;
                if (integer = (struct Gadget *)IIntuition-&amp;gt;NewObject(NULL,&lt;br /&gt;
                    &amp;quot;strgclass&amp;quot;,&lt;br /&gt;
                    GA_ID,            1L,&lt;br /&gt;
                    GA_Top,           (w-&amp;gt;BorderTop) + 5L,&lt;br /&gt;
                    GA_Left,          (w-&amp;gt;BorderLeft) + 5L,&lt;br /&gt;
                    GA_Width,         INTWIDTH,&lt;br /&gt;
                    GA_Height,        INTHEIGHT,&lt;br /&gt;
                    STRINGA_LongVal,  0L,&lt;br /&gt;
                    STRINGA_MaxChars, 5L,&lt;br /&gt;
                    TAG_END))&lt;br /&gt;
                {&lt;br /&gt;
                    if (but = (struct Gadget *)IIntuition-&amp;gt;NewObject(rkmbutcl,&lt;br /&gt;
                        NULL,&lt;br /&gt;
                        GA_ID,       2L,&lt;br /&gt;
                        GA_Top,      (w-&amp;gt;BorderTop) + 5L,&lt;br /&gt;
                        GA_Left,     integer-&amp;gt;LeftEdge +&lt;br /&gt;
                                     integer-&amp;gt;Width + 5L,&lt;br /&gt;
                        GA_Width,    40L,&lt;br /&gt;
                        GA_Height,   INTHEIGHT,&lt;br /&gt;
                        GA_Previous, integer,&lt;br /&gt;
                        ICA_MAP,     pulse2int,&lt;br /&gt;
                        ICA_TARGET,  integer,&lt;br /&gt;
                        TAG_END))&lt;br /&gt;
                    {&lt;br /&gt;
                        IIntuition-&amp;gt;AddGList(w, integer, -1, -1, NULL);&lt;br /&gt;
                        IIntuition-&amp;gt;RefreshGList(integer, w, NULL, -1);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Height&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(TAG_END, 0L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Width&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Height, 100L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Y position&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Width, 100L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget X position&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Top, but-&amp;gt;TopEdge + 20);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to quit&amp;quot;, NULL);&lt;br /&gt;
                        MainLoop(GA_Left, but-&amp;gt;LeftEdge + 20);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;RemoveGList(w, integer, -1);&lt;br /&gt;
                        IIntuition-&amp;gt;DisposeObject((Object *)but);&lt;br /&gt;
                    }&lt;br /&gt;
                    IIntuition-&amp;gt;DisposeObject((Object *)integer);&lt;br /&gt;
                }&lt;br /&gt;
                freeRKMButGadClass(rkmbutcl);&lt;br /&gt;
            }&lt;br /&gt;
            IIntuition-&amp;gt;CloseWindow(w);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&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;
void MainLoop(ULONG attr, ULONG value)&lt;br /&gt;
{&lt;br /&gt;
    ULONG done = FALSE;&lt;br /&gt;
&lt;br /&gt;
    IIntuition-&amp;gt;SetGadgetAttrs(but, w, NULL, attr, value, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    while (done == FALSE)&lt;br /&gt;
    {&lt;br /&gt;
        IExec-&amp;gt;WaitPort((struct MsgPort *)w-&amp;gt;UserPort);&lt;br /&gt;
        while (msg = (struct IntuiMessage *)&lt;br /&gt;
            IExec-&amp;gt;GetMsg((struct MsgPort *)w-&amp;gt;UserPort))&lt;br /&gt;
        {&lt;br /&gt;
            if (msg-&amp;gt;Class == IDCMP_CLOSEWINDOW)&lt;br /&gt;
            {&lt;br /&gt;
                done = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/**    Make the class and set up the dispatcher&#039;s hook    **/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
Class *initRKMButGadClass(void)&lt;br /&gt;
{&lt;br /&gt;
    Class *cl = NULL;&lt;br /&gt;
    extern ULONG HookEntry();     /* defined in amiga.lib */&lt;br /&gt;
&lt;br /&gt;
    if ( cl =  IIntuition-&amp;gt;MakeClass( NULL,&lt;br /&gt;
        &amp;quot;gadgetclass&amp;quot;, NULL,&lt;br /&gt;
        sizeof ( struct ButINST ),&lt;br /&gt;
        0 ))&lt;br /&gt;
    {&lt;br /&gt;
        /* initialize the cl_Dispatcher Hook    */&lt;br /&gt;
        cl-&amp;gt;cl_Dispatcher.h_Entry = (HOOKFUNC)dispatchRKMButGad;&lt;br /&gt;
    }&lt;br /&gt;
    return ( cl );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/******************     Free the class      ****************/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
BOOL freeRKMButGadClass(Class *cl)&lt;br /&gt;
{&lt;br /&gt;
    return IIntuition-&amp;gt;FreeClass(cl);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/**********       The RKMBut class dispatcher      *********/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
ULONG dispatchRKMButGad(Class *cl, Object *o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ButINST *inst;&lt;br /&gt;
    ULONG retval = FALSE;&lt;br /&gt;
    Object *object;&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;MethodID)&lt;br /&gt;
    {&lt;br /&gt;
    case OM_NEW:       /* First, pass up to superclass */&lt;br /&gt;
        if (object = (Object *)IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg))&lt;br /&gt;
        {&lt;br /&gt;
            struct Gadget *g = (struct Gadget *)object;&lt;br /&gt;
&lt;br /&gt;
            /* Initial local instance data */&lt;br /&gt;
            inst = (ButINST *)INST_DATA(cl, object);&lt;br /&gt;
            inst-&amp;gt;midX   = g-&amp;gt;LeftEdge + ( (g-&amp;gt;Width) / 2);&lt;br /&gt;
            inst-&amp;gt;midY   = g-&amp;gt;TopEdge + ( (g-&amp;gt;Height) / 2);&lt;br /&gt;
&lt;br /&gt;
            retval = (ULONG)object;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_HITTEST:&lt;br /&gt;
        /* Since this is a rectangular gadget this  */&lt;br /&gt;
        /* method always returns GMR_GADGETHIT.     */&lt;br /&gt;
        retval = GMR_GADGETHIT;&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_GOACTIVE:&lt;br /&gt;
        inst = (ButINST*)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
        /* Only become active if the GM_GOACTIVE   */&lt;br /&gt;
        /* was triggered by direct user input.     */&lt;br /&gt;
        if (((struct gpInput *)msg)-&amp;gt;gpi_IEvent)&lt;br /&gt;
        {&lt;br /&gt;
            /* This gadget is now active, change    */&lt;br /&gt;
            /* visual state to selected and render. */&lt;br /&gt;
            ((struct Gadget *)o)-&amp;gt;Flags |= GFLG_SELECTED;&lt;br /&gt;
            RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
            retval = GMR_MEACTIVE;&lt;br /&gt;
        }&lt;br /&gt;
        else            /* The GM_GOACTIVE was not         */&lt;br /&gt;
            /* triggered by direct user input. */&lt;br /&gt;
            retval = GMR_NOREUSE;&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_RENDER:&lt;br /&gt;
        retval = RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_HANDLEINPUT:   /* While it is active, this gadget sends its superclass an        */&lt;br /&gt;
        /* OM_NOTIFY pulse for every IECLASS_TIMER event that goes by     */&lt;br /&gt;
        /* (about one every 10th of a second).  Any object that is        */&lt;br /&gt;
        /* connected to this gadget will get A LOT of OM_UPDATE messages. */&lt;br /&gt;
    {&lt;br /&gt;
        struct Gadget *g = (struct Gadget *)o;&lt;br /&gt;
        struct gpInput *gpi = (struct gpInput *)msg;&lt;br /&gt;
        struct InputEvent *ie = gpi-&amp;gt;gpi_IEvent;&lt;br /&gt;
&lt;br /&gt;
        inst = (ButINST*)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
        retval = GMR_MEACTIVE;&lt;br /&gt;
&lt;br /&gt;
        if (ie-&amp;gt;ie_Class == IECLASS_RAWMOUSE)&lt;br /&gt;
        {&lt;br /&gt;
            switch (ie-&amp;gt;ie_Code)&lt;br /&gt;
            {&lt;br /&gt;
            case SELECTUP: /* The user let go of the gadget so return GMR_NOREUSE    */&lt;br /&gt;
                /* to deactivate and to tell Intuition not to reuse       */&lt;br /&gt;
                /* this Input Event as we have already processed it.      */&lt;br /&gt;
&lt;br /&gt;
                /*If the user let go of the gadget while the mouse was    */&lt;br /&gt;
                /*over it, mask GMR_VERIFY into the return value so       */&lt;br /&gt;
                /*Intuition will send a Release Verify (GADGETUP).        */&lt;br /&gt;
                if ( ((gpi-&amp;gt;gpi_Mouse).X &amp;lt; g-&amp;gt;LeftEdge) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).X &amp;gt; g-&amp;gt;LeftEdge + g-&amp;gt;Width) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).Y &amp;lt; g-&amp;gt;TopEdge) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).Y &amp;gt; g-&amp;gt;TopEdge + g-&amp;gt;Height) )&lt;br /&gt;
                    retval = GMR_NOREUSE | GMR_VERIFY;&lt;br /&gt;
                else&lt;br /&gt;
                    retval = GMR_NOREUSE;&lt;br /&gt;
&lt;br /&gt;
                /* Since the gadget is going inactive, send a final   */&lt;br /&gt;
                /* notification to the ICA_TARGET.                    */&lt;br /&gt;
                NotifyPulse(cl , o, 0L, inst-&amp;gt;midX, (struct gpInput *)msg);&lt;br /&gt;
                break;&lt;br /&gt;
            case MENUDOWN: /* The user hit the menu button. Go inactive and let      */&lt;br /&gt;
                /* Intuition reuse the menu button event so Intuition can */&lt;br /&gt;
                /* pop up the menu bar.                                   */&lt;br /&gt;
                retval = GMR_REUSE;&lt;br /&gt;
&lt;br /&gt;
                /* Since the gadget is going inactive, send a final   */&lt;br /&gt;
                /* notification to the ICA_TARGET.                    */&lt;br /&gt;
                NotifyPulse(cl , o, 0L, inst-&amp;gt;midX, (struct gpInput *)msg);&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                retval = GMR_MEACTIVE;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
        else if (ie-&amp;gt;ie_Class == IECLASS_TIMER)&lt;br /&gt;
            /* If the gadget gets a timer event, it sends an interim OM_NOTIFY */&lt;br /&gt;
            NotifyPulse(cl, o, OPUF_INTERIM, inst-&amp;gt;midX, gpi); /*     to its superclass. */&lt;br /&gt;
    }&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
    case GM_GOINACTIVE:           /* Intuition said to go inactive.  Clear the GFLG_SELECTED */&lt;br /&gt;
        /* bit and render using unselected imagery.                */&lt;br /&gt;
        ((struct Gadget *)o)-&amp;gt;Flags &amp;amp;= ~GFLG_SELECTED;&lt;br /&gt;
        RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
        break;&lt;br /&gt;
    case OM_SET:/* Although this class doesn&#039;t have settable attributes, this gadget class   */&lt;br /&gt;
        /* does have scaleable imagery, so it needs to find out when its size and/or */&lt;br /&gt;
        /* position has changed so it can erase itself, THEN scale, and rerender.    */&lt;br /&gt;
        if ( IUtility-&amp;gt;FindTagItem(GA_Width,  ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Height, ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Top,    ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Left,   ((struct opSet *)msg)-&amp;gt;ops_AttrList) )&lt;br /&gt;
        {&lt;br /&gt;
            struct RastPort *rp;&lt;br /&gt;
            struct Gadget *g = (struct Gadget *)o;&lt;br /&gt;
&lt;br /&gt;
            WORD x,y,w,h;&lt;br /&gt;
&lt;br /&gt;
            x = g-&amp;gt;LeftEdge;&lt;br /&gt;
            y = g-&amp;gt;TopEdge;&lt;br /&gt;
            w = g-&amp;gt;Width;&lt;br /&gt;
            h = g-&amp;gt;Height;&lt;br /&gt;
&lt;br /&gt;
            inst = (ButINST *)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
            retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
&lt;br /&gt;
            /* Get pointer to RastPort for gadget. */&lt;br /&gt;
            if (rp = IIntuition-&amp;gt;ObtainGIRPort( ((struct opSet *)msg)-&amp;gt;ops_GInfo) )&lt;br /&gt;
            {&lt;br /&gt;
                UWORD *pens = ((struct opSet *)msg)-&amp;gt;ops_GInfo-&amp;gt;gi_DrInfo-&amp;gt;dri_Pens;&lt;br /&gt;
&lt;br /&gt;
                IGraphics-&amp;gt;SetAPen(rp, pens[BACKGROUNDPEN]);&lt;br /&gt;
                IGraphics-&amp;gt;SetDrMd(rp, JAM1);                            /* Erase the old gadget.       */&lt;br /&gt;
                IGraphics-&amp;gt;RectFill(rp, x, y, x+w, y+h);&lt;br /&gt;
&lt;br /&gt;
                inst-&amp;gt;midX = g-&amp;gt;LeftEdge + ( (g-&amp;gt;Width) / 2); /* Recalculate where the       */&lt;br /&gt;
                inst-&amp;gt;midY = g-&amp;gt;TopEdge + ( (g-&amp;gt;Height) / 2); /* center of the gadget is.    */&lt;br /&gt;
&lt;br /&gt;
                /* Rerender the gadget.        */&lt;br /&gt;
                IIntuition-&amp;gt;IDoMethod(o, GM_RENDER, ((struct opSet *)msg)-&amp;gt;ops_GInfo, rp, GREDRAW_REDRAW);&lt;br /&gt;
                IIntuition-&amp;gt;ReleaseGIRPort(rp);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
        break;&lt;br /&gt;
    default:          /* rkmmodelclass does not recognize the methodID, let the superclass&#039;s */&lt;br /&gt;
        /* dispatcher take a look at it.                                       */&lt;br /&gt;
        retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    return(retval);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/************** Build an OM_NOTIFY message for RKMBUT_Pulse and send it to the superclass. *******/&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
void NotifyPulse(Class *cl, Object *o, ULONG flags, LONG mid, struct gpInput *gpi)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem tt[3];&lt;br /&gt;
&lt;br /&gt;
    tt[0].ti_Tag = RKMBUT_Pulse;&lt;br /&gt;
    tt[0].ti_Data = mid - ((gpi-&amp;gt;gpi_Mouse).X + ((struct Gadget *)o)-&amp;gt;LeftEdge);&lt;br /&gt;
&lt;br /&gt;
    tt[1].ti_Tag = GA_ID;&lt;br /&gt;
    tt[1].ti_Data = ((struct Gadget *)o)-&amp;gt;GadgetID;&lt;br /&gt;
&lt;br /&gt;
    tt[2].ti_Tag = TAG_END;&lt;br /&gt;
&lt;br /&gt;
    IIntuition-&amp;gt;IDoSuperMethod(cl, o, OM_NOTIFY, tt, gpi-&amp;gt;gpi_GInfo, flags);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/*******************************   Erase and rerender the gadget.   ******************************/&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
ULONG RenderRKMBut(Class *cl, struct Gadget *g, struct gpRender *msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ButINST *inst = (ButINST *)INST_DATA(cl, (Object *)g);&lt;br /&gt;
    struct RastPort *rp;&lt;br /&gt;
    ULONG retval = TRUE;&lt;br /&gt;
    UWORD *pens = msg-&amp;gt;gpr_GInfo-&amp;gt;gi_DrInfo-&amp;gt;dri_Pens;&lt;br /&gt;
&lt;br /&gt;
    if (msg-&amp;gt;MethodID == GM_RENDER)   /* If msg is truly a GM_RENDER message (not a gpInput that */&lt;br /&gt;
        /* looks like a gpRender), use the rastport within it...   */&lt;br /&gt;
        rp = msg-&amp;gt;gpr_RPort;&lt;br /&gt;
    else                              /* ...Otherwise, get a rastport using ObtainGIRPort().     */&lt;br /&gt;
        rp = IIntuition-&amp;gt;ObtainGIRPort(msg-&amp;gt;gpr_GInfo);&lt;br /&gt;
&lt;br /&gt;
    if (rp)&lt;br /&gt;
    {&lt;br /&gt;
        UWORD back, shine, shadow, w, h, x, y;&lt;br /&gt;
&lt;br /&gt;
        if (g-&amp;gt;Flags &amp;amp; GFLG_SELECTED) /* If the gadget is selected, reverse the meanings of the  */&lt;br /&gt;
        {                             /* pens.                                                   */&lt;br /&gt;
            back   = pens[FILLPEN];&lt;br /&gt;
            shine  = pens[SHADOWPEN];&lt;br /&gt;
            shadow = pens[SHINEPEN];&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            back   = pens[BACKGROUNDPEN];&lt;br /&gt;
            shine  = pens[SHINEPEN];&lt;br /&gt;
            shadow = pens[SHADOWPEN];&lt;br /&gt;
        }&lt;br /&gt;
        IGraphics-&amp;gt;SetDrMd(rp, JAM1);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, back);          /* Erase the old gadget.       */&lt;br /&gt;
        IGraphics-&amp;gt;RectFill(rp, g-&amp;gt;LeftEdge,&lt;br /&gt;
            g-&amp;gt;TopEdge,&lt;br /&gt;
            g-&amp;gt;LeftEdge + g-&amp;gt;Width,&lt;br /&gt;
            g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, shadow);     /* Draw shadow edge.            */&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, g-&amp;gt;LeftEdge + 1, g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width, g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width, g-&amp;gt;TopEdge + 1);&lt;br /&gt;
&lt;br /&gt;
        w = g-&amp;gt;Width / 4;       /* Draw Arrows - Sorry, no frills imagery */&lt;br /&gt;
        h = g-&amp;gt;Height / 2;&lt;br /&gt;
        x = g-&amp;gt;LeftEdge + (w/2);&lt;br /&gt;
        y = g-&amp;gt;TopEdge + (h/2);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, x, inst-&amp;gt;midY);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, y);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, y + (g-&amp;gt;Height) - h);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, inst-&amp;gt;midY);&lt;br /&gt;
&lt;br /&gt;
        x = g-&amp;gt;LeftEdge + (w/2) + g-&amp;gt;Width / 2;&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, x + w, inst-&amp;gt;midY);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, y);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, y  + (g-&amp;gt;Height) - h);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, inst-&amp;gt;midY);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, shine);    /* Draw shine edge.           */&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, g-&amp;gt;LeftEdge, g-&amp;gt;TopEdge + g-&amp;gt;Height - 1);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge, g-&amp;gt;TopEdge);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width - 1, g-&amp;gt;TopEdge);&lt;br /&gt;
&lt;br /&gt;
        if (msg-&amp;gt;MethodID != GM_RENDER) /* If we allocated a rastport, give it back.             */&lt;br /&gt;
            IIntuition-&amp;gt;ReleaseGIRPort(rp);&lt;br /&gt;
    }&lt;br /&gt;
    else retval = FALSE;&lt;br /&gt;
    return(retval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=BOOPSI_Gadgets&amp;diff=12392</id>
		<title>BOOPSI Gadgets</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=BOOPSI_Gadgets&amp;diff=12392"/>
		<updated>2024-09-10T09:04:27Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* RKMButtonclass.c */ Little fixes and type castings to make g++ happy&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
One of the major enhancements to Intuition is the implementation of customizable BOOPSI gadgets. BOOPSI gadgets are not limited by dependencies upon Intuition Image and Gadget structures. Unlike ordinary gadgets, which were handled exclusively by Intuition, BOOPSI gadgets handle their own rendering and their own user input.&lt;br /&gt;
&lt;br /&gt;
Since BOOPSI gadgets draw themselves, there is almost no restriction on what they can look like. A BOOPSI gadget can use graphics.library RastPort drawing functions to draw vector-based imagery which the gadget can scale to any dimension. Instead of just a two-state Boolean gadget, a BOOPSI gadget can have any number of states, each of which has its own imagery. If a programmer wanted to he could even make a BOOPSI gadget that uses the animation system to render itself.&lt;br /&gt;
&lt;br /&gt;
Because BOOPSI gadgets handle their own input, they see all the user&#039;s input, which the gadget is free to interpret. While the user has a BOOPSI gadget selected, the gadget can track mouse moves, process mouse and keyboard key presses, or watch the timer events.&lt;br /&gt;
&lt;br /&gt;
The power of a BOOPSI gadget is not limited to its ability to handle its own rendering and user input. BOOPSI gadgets are also BOOPSI objects so the gain all the benefits BOOPSI provides. This means all BOOPSI gadgets inherit the methods and attributes from their superclasses. BOOPSI gadgets can use BOOPSI images to take care of rendering their imagery. A BOOPSI gadget could be a &amp;quot;composite&amp;quot; gadget that is composed of several BOOPSI gadgets, images, and models.&lt;br /&gt;
&lt;br /&gt;
=== The BOOPSI Gadget Methods ===&lt;br /&gt;
&lt;br /&gt;
Intuition drives a BOOPSI gadget by sending it BOOPSI messages. Intuition uses the following BOOPSI methods:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Method !! Description&lt;br /&gt;
|-&lt;br /&gt;
| GM_DOMAIN || Obtain gadget sizing requirements.&lt;br /&gt;
|-&lt;br /&gt;
| GM_EXTENT || Inquire about rendering extent. (V51)&lt;br /&gt;
|-&lt;br /&gt;
| GM_GOACTIVE || This method asks a gadget if it wants to be the active gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GM_GOINACTIVE || This method tells a gadget that it is no longer active.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HANDLEINPUT || This method passes a gadget an input event.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HELPTEST || Determine if gadget help was hit.&lt;br /&gt;
|-&lt;br /&gt;
| GM_HITTEST || This method asks a gadget whether it has been &amp;quot;hit&amp;quot; by a mouse click.&lt;br /&gt;
|-&lt;br /&gt;
| GM_LAYOUT || Calculate relative gadget coordinates.&lt;br /&gt;
|-&lt;br /&gt;
| GM_RENDER || This method tells the gadget to render itself.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The formats of each of these BOOPSI messages differ, but they all have two things in common. Like all BOOPSI messages, each starts with their respective method ID. For each of these methods, the method ID field is followed by a pointer to a GadgetInfo structure (defined in &amp;lt;intuition/cghooks.h&amp;gt;). The GadgetInfo structure contains information about the display on which the gadget needs to render itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct GadgetInfo {&lt;br /&gt;
    struct Screen               *gi_Screen;&lt;br /&gt;
    struct Window               *gi_Window;     /* null for screen gadgets */&lt;br /&gt;
    struct Requester            *gi_Requester;  /* null if not GTYP_REQGADGET */&lt;br /&gt;
&lt;br /&gt;
    /* rendering information: don&#039;t use these without cloning/locking.&lt;br /&gt;
     * Official way is to call ObtainGIRPort()&lt;br /&gt;
     */&lt;br /&gt;
    struct RastPort             *gi_RastPort;&lt;br /&gt;
    struct Layer                *gi_Layer;&lt;br /&gt;
&lt;br /&gt;
    /* copy of dimensions of screen/window/g00/req(/group)&lt;br /&gt;
     * that gadget resides in.  Left/Top of this box is&lt;br /&gt;
     * offset from window mouse coordinates to gadget coordinates&lt;br /&gt;
     *  screen gadgets:                 0,0 (from screen coords)&lt;br /&gt;
     *  window gadgets (no g00):        0,0&lt;br /&gt;
     *  GTYP_GZZGADGETs (borderlayer):  0,0&lt;br /&gt;
     *  GZZ innerlayer gadget:          borderleft, bordertop&lt;br /&gt;
     *  Requester gadgets:              reqleft, reqtop&lt;br /&gt;
     */&lt;br /&gt;
    struct IBox                 gi_Domain;&lt;br /&gt;
&lt;br /&gt;
    /* these are the pens for the window or screen      */&lt;br /&gt;
    struct {&lt;br /&gt;
        UBYTE   DetailPen;&lt;br /&gt;
        UBYTE   BlockPen;&lt;br /&gt;
    }                           gi_Pens;&lt;br /&gt;
&lt;br /&gt;
    /* the Detail and Block pens in gi_DrInfo-&amp;amp;gt;dri_Pens[] are&lt;br /&gt;
     * for the screen.  Use the above for window-sensitive colors.&lt;br /&gt;
     */&lt;br /&gt;
    struct DrawInfo             *gi_DrInfo;&lt;br /&gt;
&lt;br /&gt;
    /* reserved space: this structure is extensible&lt;br /&gt;
     * anyway, but using these saves some recompilation&lt;br /&gt;
     */&lt;br /&gt;
    ULONG                       gi_Reserved[6];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All the fields in this structure are read only.&lt;br /&gt;
&lt;br /&gt;
Although this structure contains a pointer to the gadget&#039;s RastPort structure, applications should not use it for rendering. Instead, use the intuition.library function ObtainGIRPort() to obtain a copy of the GadgetInfo&#039;s RastPort. When the gadget is finished with this RastPort, it should call ReleaseGIRPort() to relinquish the RastPort.&lt;br /&gt;
&lt;br /&gt;
==== GM_DOMAIN ====&lt;br /&gt;
&lt;br /&gt;
Intuition uses the GM_DOMAIN method to determine the basic sizing requirements of an object. The domain is most often used by layout gadgets to aid in distributing the gadgets within the layout via GM_LAYOUT. The GM_DOMAIN method uses the gpDomain structure (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) as its message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpDomain&lt;br /&gt;
{&lt;br /&gt;
    uint32		 MethodID;&lt;br /&gt;
    struct GadgetInfo	*gpd_GInfo;&lt;br /&gt;
    struct RastPort	*gpd_RPort;	/* RastPort to layout for */&lt;br /&gt;
    int32		 gpd_Which;&lt;br /&gt;
    struct IBox		 gpd_Domain;	/* Resulting domain */&lt;br /&gt;
    struct TagItem	*gpd_Attrs;	/* Additional attributes */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gpd_Which field is used to determine which domain the caller is interested in: GDOMAIN_MINIMUM, GDOMAIN_NOMINAL or GDOMAIN_MAXIMUM. The object is expected to fill in the provided gpd_Domain with the width and height dimensions. The location coordinates are not used. The gpd_Attrs pointer, if not NULL, may contain additional tags which are defined on a per-class basis.&lt;br /&gt;
&lt;br /&gt;
==== GM_EXTENT (V51) ====&lt;br /&gt;
&lt;br /&gt;
The GM_EXTENT method is used to ask the gadget what pixels (at least) it will fully redraw when its GM_RENDER method is invoked in the same context. By &amp;quot;fully redraw&amp;quot; we mean changing the pixel&#039;s color in a way that is totally unrelated to its previous value -- so this doesn&#039;t apply to alpha-blended pixels for example.&lt;br /&gt;
&lt;br /&gt;
Intuition uses that information for optimization purposes. During GUI refreshes, it will skip filling or erasing those pixels that the gadget would then completely re-render anyway. Supporting this method in your gadgets will help Intuition improve the smoothness of user interface refresh by preventing redundant graphic calls and minimizing &amp;quot;flicker&amp;quot; effects caused by background clearing especially during window resizes.&lt;br /&gt;
&lt;br /&gt;
===== Simple Support =====&lt;br /&gt;
&lt;br /&gt;
The easiest way to support GM_EXTENT is to make sure your gadget always fills every pixel within its dimensions and just return the relevant result value (GMR_FULLHBOX or GMR_FULLBBOX). In this case, you may safely ignore the actual message contents and the following text.&lt;br /&gt;
&lt;br /&gt;
If the above solution is not feasible (for instance because the gadget has an irregular or not fully connected shape) then you should check the gpe_Region and gpe_RPort message fields: if any of them is non-NULL you may decide to employ a more detailed way to tell the caller exactly what pixels your GM_RENDER method does fill.&lt;br /&gt;
&lt;br /&gt;
===== Region and RastPort Support =====&lt;br /&gt;
&lt;br /&gt;
If gpe_Region is non-NULL you can compose in it your gadget&#039;s shape by using graphics.library&#039;s XxxxRectRegion() functions. Remember to look at the gpe_Action field to determine what function to use. For example, if gpe_Action is GEXTENT_ADD you should use OrRectRegion(). Note you must NOT pre-clear or alter the initial region&#039;s contents in any way other than to compose your own gadget&#039;s shape. Once finished composing your gadget&#039;s shape in the region, return GMR_CLIPDONE to let the caller know you updated the region&#039;s contents.&lt;br /&gt;
&lt;br /&gt;
If gpe_RPort is non-NULL you can draw your gadget&#039;s shape into it just like you would do for a GM_RENDER message -- however, you&#039;re only allowed to use colors 0 or 1 because you&#039;re actually drawing into a single-bitplane mask. Again, check the gpe_Action field to find out whether you should actually set, clear or invert the pixels making up your gadget&#039;s shape in the mask. You are not allowed to alter any pixels other than those belonging to your gadget&#039;s shape nor to clear the mask&#039;s background before rendering. Once finished drawing your gadget&#039;s shape in the mask plane return GMR_MASKDONE to let the caller know you updated the mask&#039;s contents.&lt;br /&gt;
&lt;br /&gt;
If both gpe_Region and gpe_RPort are non-NULL you may simply choose the method which is most suited for your purposes. You don&#039;t have to support both for the same message. If you do, however, you can let the caller know by ORing together the appropriate return values. The region solution is best suited for gadgets made up of a few rectangular parts whereas the mask method is better in the case of more complex gadget shapes.&lt;br /&gt;
&lt;br /&gt;
===== Images =====&lt;br /&gt;
&lt;br /&gt;
If all or part of your gadget&#039;s rendering is performed by some BOOPSI image you could also try asking the image for its extent information by way of an IM_EXTENT or IM_EXTENTFRAME message (see [[BOOPSI_Images|BOOPSI Images]]).&lt;br /&gt;
&lt;br /&gt;
If for whatever reasons your GM_EXTENT method finds itself unable to support any of the described solutions it should return GMR_INVALID. This particular return value will tell Intuition not to clip away the gadget&#039;s shape at all. While this is usually slower and more prone to flickering it will still produce correct graphic results. This is the same fallback applied for any gadgets not recognizing the GM_EXTENT method.&lt;br /&gt;
&lt;br /&gt;
===== Superclass Extent Handling =====&lt;br /&gt;
&lt;br /&gt;
Since it is very important that a gadget&#039;s GM_RENDER and GM_EXTENT methods remain synchronized the default behavior of a gadget class should be to only handle GM_EXTENT if it is the &amp;quot;true class&amp;quot; of the object the method is invoked on and just return GMR_INVALID otherwise. This is because a subclass might override the behavior of GM_RENDER in such a way that your class&#039; GM_EXTENT results are no longer correct.&lt;br /&gt;
&lt;br /&gt;
If a gadget subclass needs and knows it&#039;s ok to let its superclass handle GM_EXTENT it must set the GPEF_ALLOWSUPER flag in gpe_Flags before calling IDoSuperMethodA() and clear it as soon as the call returns. If GPEF_ALLOWSUPER is set in gpe_Flags your gadget should accept and handle GM_EXTENT regardless of whether it is the true class or not.&lt;br /&gt;
&lt;br /&gt;
{{Note|text=As of Intuition V51 the mask method is not yet implemented.}}&lt;br /&gt;
&lt;br /&gt;
==== GM_GOACTIVE/GM_HANDLEINPUT ====&lt;br /&gt;
&lt;br /&gt;
If a gadget returns GMR_GADGETHIT, Intuition will send it a GM_GOACTIVE message (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpInput                          /* Used by GM_GOACTIVE and GM_HANDLEINPUT */&lt;br /&gt;
{&lt;br /&gt;
    ULONG             MethodID;&lt;br /&gt;
    struct GadgetInfo *gpi_GInfo;&lt;br /&gt;
    struct InputEvent *gpi_IEvent;      /* The input event that triggered this method&lt;br /&gt;
                                         * (for GM_GOACTIVE, this can be NULL) */&lt;br /&gt;
    LONG              *gpi_Termination; /* For GADGETUP IntuiMessage.Code */&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        WORD X;                         /* Mouse position relative to upper          */&lt;br /&gt;
        WORD Y;                         /* left corner of gadget (LeftEdge, TopEdge) */&lt;br /&gt;
    } gpi_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The GM_GOACTIVE message gives a gadget the opportunity to become the active gadget. The active gadget is the gadget that is currently receiving user input. Under normal conditions, only one gadget can be the active gadget (it is possible to have more than one active gadget using a groupgclass object (See the [[BOOPSI_Class_Reference|BOOPSI Class Reference]] for more details).&lt;br /&gt;
&lt;br /&gt;
While a gadget is active, Intuition sends it GM_HANDLEINPUT messages. Each GM_HANDLEINPUT message corresponds to a single InputEvent structure. These InputEvents can be keyboard presses, timer events, mouse moves, or mouse button presses. The message&#039;s gpi_IEvent field points to this InputEvent structure. It&#039;s up to the GM_HANDLEINPUT method to interpret the meaning of these events and update the visual state of the gadget as the user manipulates the gadget. For example, the GM_HANDLEINPUT method of a prop gadget has to track mouse events to see where the user has moved the prop gadget&#039;s knob and update the gadget&#039;s imagery to reflect the new position of the knob.&lt;br /&gt;
&lt;br /&gt;
For the GM_GOACTIVE method, the gpi_IEvent field points to the struct InputEvent that triggered the GM_GOACTIVE message. Unlike the GM_HANDLEINPUT message, GM_GOACTIVE&#039;s gpi_IEvent can be NULL. If the GM_GOACTIVE message was triggered by a function like intuition.library&#039;s ActivateGadget() and not by a real InputEvent (like the user clicking the gadget), the gpi_IEvent field will be NULL.&lt;br /&gt;
&lt;br /&gt;
For gadgets that only want to become active as a direct result of a mouse click, this difference is important. For example, the prop gadget becomes active only when the user clicks on its knob. Because the only way the user can control the prop gadget is via the mouse, it does not make sense for anything but the mouse to activate the gadget. On the other hand, a string gadget doesn&#039;t care how it is activated because, as soon as it&#039;s active, it gets user input from the keyboard rather than the mouse. Not all gadgets can become active. Some gadgets cannot become active because they have been temporarily disabled (their Gadget.Flags GFLG_DISABLED bit is set). Other gadgets will not become active because they don&#039;t need to process input. For example, a toggle gadget won&#039;t become active because it only needs to process one input event, the mouse click that toggles the gadget (which it gets from the GM_GOACTIVE message). If a toggle gadget gets a GM_GOACTIVE message and its gpi_IEvent field is not NULL, it will toggle its state and refuse to &amp;quot;go active&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The GM_GOACTIVE method has to take care of any visual state changes to a gadget that a GM_GOACTIVE message might trigger. For example, the toggle gadget in the previous paragraph has to take care of toggling its visual state from selected imagery to unselected imagery. If the gadget goes through a state change when it becomes the active gadget, (like when a string gadget positions its cursor) GM_GOACTIVE has to take care of this.&lt;br /&gt;
&lt;br /&gt;
===== Return Values =====&lt;br /&gt;
&lt;br /&gt;
The return values of both GM_GOACTIVE and GM_HANDLEINPUT tell Intuition whether or not the gadget wants to be active. A gadget&#039;s GM_GOACTIVE method returns GMR_MEACTIVE (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;) if it wants to become the active gadget. A gadget&#039;s GM_HANDLEINPUT method returns GMR_MEACTIVE if it wants to remain the active gadget. If a gadget either does not want to become or remain the active gadget, it returns one of the &amp;quot;go inactive&amp;quot; return values:&lt;br /&gt;
&lt;br /&gt;
; GMR_NOREUSE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent.&lt;br /&gt;
&lt;br /&gt;
; GMR_REUSE&lt;br /&gt;
: Tells Intuition to process the gpInput.gpi_IEvent InputEvent.&lt;br /&gt;
&lt;br /&gt;
; GMR_NEXTACTIVE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent and activate the next GFLG_TagCycle gadget.&lt;br /&gt;
&lt;br /&gt;
; GMR_PREVACTIVE&lt;br /&gt;
: Tells Intuition to throw away the gpInput.gpi_IEvent InputEvent and activate the previous GFLG_TagCycle gadget.&lt;br /&gt;
&lt;br /&gt;
GMR_NOREUSE tells Intuition that the gadget does not want to be active and to throw away the InputEvent that triggered the message. For example, an active prop gadget returns GMR_NOREUSE when the user lets go of the left mouse button (thus letting go of the prop gadget&#039;s knob).&lt;br /&gt;
&lt;br /&gt;
For the GM_HANDLEINPUT method, a gadget can also return GMR_REUSE, which tells Intuition to reuse the InputEvent. For example, if the user clicks outside the active string gadget, that string gadget returns GMR_REUSE. Intuition can now process that mouse click, which can be over another gadget. Another case where a string gadget returns GMR_REUSE is when the user pushes the right mouse button (the menu button). The string gadget becomes inactive and the menu button InputEvent gets reused. Intuition sees this event and tries to pop up the menu bar.&lt;br /&gt;
&lt;br /&gt;
For the GM_GOACTIVE method, a gadget must not return GMR_REUSE. If a gadget gets a GM_GOACTIVE message from Intuition and the message has an gpi_IEvent, the message was triggered by the user clicking on the gadget. In this case, Intuition knows that the user is trying to select the gadget. Intuition doesn&#039;t know if the gadget can be activated, but if it can be activated, the event that triggered the activation has just taken place. If the gadget cannot become active for any reason, it must not let Intuition reuse that InputEvent as the gadget has already taken care of the the event&#039;s purpose (clicking on the gadget). In essence, the user tried to activate the gadget and the gadget refused to become active.&lt;br /&gt;
&lt;br /&gt;
The other two possible return values are GMR_NEXTACTIVE and GMR_PREVACTIVE. These tell Intuition that a gadget does not want to be active and that the InputEvent should be discarded. Intuition then looks for the next (GMR_NEXTACTIVE) or previous (GMR_PREVACTIVE) gadget that has its GFLG_TABCYCLE flag set in its Gadget.Activation field (see the gadgetclass GA_TabCycle attribute in the [[BOOPSI_Class_Reference|BOOPSI Class Reference]]).&lt;br /&gt;
&lt;br /&gt;
For both GM_GOACTIVE and GM_HANDLEINPUT, the gadget can bitwise-OR any of these &amp;quot;go inactive&amp;quot; return values with GMR_VERIFY. The GMR_VERIFY flag tells Intuition to send a GADGETUP IntuiMessage to the gadget&#039;s window. If the gadget uses GMR_VERIFY, it has to supply a value for the IntuiMessage.Code field. It does this by passing a value in the gpInput.gpi_Termination field. This field points to a long word, the lower 16-bits of which Intuition copies into the Code field. The upper 16-bits are for future enhancements, so clear these bits.&lt;br /&gt;
&lt;br /&gt;
==== GM_GOINACTIVE ====&lt;br /&gt;
&lt;br /&gt;
After an active gadget deactivates, Intuition sends it a GM_GOINACTIVE message (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpGoInactive&lt;br /&gt;
{&lt;br /&gt;
    ULONG             MethodID;    /* GM_GOINACTIVE */&lt;br /&gt;
    struct GadgetInfo *gpgi_GInfo;&lt;br /&gt;
    ULONG             gpgi_Abort; /* gpgi_Abort=1 if gadget was aborted by Intuition   */&lt;br /&gt;
                                  /* and 0 if gadget went inactive at its own request. */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The gpgi_Abort field contains either a 0 or 1. If 0, the gadget became inactive on its own power (because the GM_GOACTIVE or GM_HANDLEINPUT method returned something besides GMR_MEACTIVE). If gpgi_Abort is 1, Intuition aborted this active gadget. Some instances where Intuition aborts a gadget include: the user clicked in another window or screen, an application removed the active gadget with RemoveGList(), and an application called ActiveWindow() on a window other than the gadget&#039;s window.&lt;br /&gt;
&lt;br /&gt;
==== GM_HELPTEST ====&lt;br /&gt;
&lt;br /&gt;
This method uses the same message structure as GM_HITTEST, struct gpHitTest, and operates similarly to GM_HITTEST. While a window is in help mode, when the user positions the pointer within the bounding box of a BOOPSI gadget that supports gadget help, Intuition sends the gadget a GM_HELPTEST message. After an active gadget deactivates, Intuition sends it a GM_GOINACTIVE message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpHitTest&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;    // GM_HELPTEST&lt;br /&gt;
    struct GadgetInfo *gpht_GInfo;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;                    // Is this point inside&lt;br /&gt;
        int16 Y;                    // gadget?&lt;br /&gt;
    } gpht_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like the GM_HITTEST method, the GM_HELPTEST method asks a gadget if a point is within the gadget&#039;s bounds. Like the GM_HITTEST method, the GM_HELPTEST method allows a BOOPSI gadget to have a non-rectangular hit area (or in this case, a help area). If the point is within the gadget, the gadget uses one of two return codes. If the gadget returns a value of GMR_HELPHIT, Intuition places a value of 0xFFFF in the Code field of the IDCMP_GADGETHELP message. The gadget also has the option of using GMR_HELPCODE instead of GMR_HELPHIT. GMR_HELPCODE is a little peculiar as a return value. Although GMR_HELPCODE is 32 bits long, Intuition identifies GMR_HELPCODE using only its upper 16 bits. If the upper 16 bits of GM_HELPTEST&#039;s return value matches the upper 16 bits of GMR_HELPCODE, Intuition copies the lower word of the return value into the Code field of the IDCMP_GADGETHELP message. The BOOPSI gadget is free to set the return value&#039;s lower word to any 16-bit value.&lt;br /&gt;
&lt;br /&gt;
If the point is not within the gadget&#039;s &amp;quot;help spot&amp;quot;, the gadget returns GMR_NOHELPHIT. Intuition will then look under that gadget for more gadgets. If a gadget&#039;s dispatcher passes the GM_HELPTEST method on to the gadgetclass dispatcher, the gadgetclass dispatcher&lt;br /&gt;
will always return GMR_HELPHIT.&lt;br /&gt;
&lt;br /&gt;
An application can put several windows in the same help group. This feature groups several windows so that as long as one of those windows is active, the application can receive IDCMP_GADGETHELP messages about all of those windows. For more information, See the HelpControl() Autodoc and the WA_HelpGroup section of the OpenWindow() Autodoc (both are in intuition.doc).&lt;br /&gt;
&lt;br /&gt;
==== GM_HITTEST ====&lt;br /&gt;
&lt;br /&gt;
When Intuition gets a left mouse button click in a window, one of the things it does is check through the window&#039;s list of gadgets to see if that click was inside the bounds of a gadget&#039;s Gadget structure (using the LeftEdge, TopEdge, Width, and Height fields). If it was (and that gadget is a BOOPSI gadget), Intuition sends that gadget a GM_HITTEST message (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpHitTest&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;    // GM_HITTEST&lt;br /&gt;
    struct GadgetInfo *gpht_GInfo;&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        int16 X;                    // Is this point inside&lt;br /&gt;
        int16 Y;                    // gadget?&lt;br /&gt;
    } gpht_Mouse;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This message contains the coordinates of the mouse click. These coordinates are relative to the upper-left of the gadget (LeftEdge, TopEdge).&lt;br /&gt;
&lt;br /&gt;
Because Intuition can only tell if the user clicked inside gadget&#039;s &amp;quot;bounding box&amp;quot;, Intuition only knows that the click was close to the gadget. Intuition uses the GM_HITTEST to ask the gadget if the click was really inside the gadget. The gadget returns GMR_GADGETHIT (defined in &amp;amp;lt;intuition/gadgetclass.h&amp;amp;gt;) to tell Intuition that the user hit it, otherwise it returns zero. This method allows a gadget to be any shape or pattern, rather than just rectangular.&lt;br /&gt;
&lt;br /&gt;
==== GM_LAYOUT ====&lt;br /&gt;
&lt;br /&gt;
Intuition uses the GM_LAYOUT method to tell a GREL BOOPSI gadget that its window&#039;s dimensions have changed. The GM_LAYOUT method uses the gpLayout structure (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) as its message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpLayout&lt;br /&gt;
{&lt;br /&gt;
    uint32              MethodID;&lt;br /&gt;
    struct GadgetInfo  *gpl_GInfo;&lt;br /&gt;
    uint32              gpl_Initial; /* This field is non-zero if this method was invoked&lt;br /&gt;
                                      * during AddGList() or OpenWindow().  zero if this&lt;br /&gt;
                                      * method was invoked during window resizing.&lt;br /&gt;
                                      */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For GREL gadgets, Intuition sends a GM_LAYOUT message just after erasing the gadget&#039;s bounding box. Intuition does not touch the values of the gadget&#039;s bounding box or hit box, it lets the gadget handle recalculating these values. The gadget can lay itself out based on the new window (or requester) dimensions found in the GadgetInfo structure passed in the GM_LAYOUT message (gpl_GInfo from the gpLayout structure). Intuition also adds the old and new bounding box of the gadget to the window&#039;s damaged regions so that the gadget will appear is its new position. The gadget must not perform any rendering inside the GM_LAYOUT method. Intuition will send a GM_RENDER message to the gadget when its time to redraw the gadget in its new position.&lt;br /&gt;
&lt;br /&gt;
There are two cases where Intuition sends a GM_LAYOUT message:&lt;br /&gt;
# When the gadget&#039;s window is resized&lt;br /&gt;
# When Intuition adds the gadget to a window&lt;br /&gt;
&lt;br /&gt;
For most GREL BOOPSI gadgets, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to follow the existing convention for regular GREL gadgets. Each of these fields has a flag in the Gadget.Flags field (GFLG_RELRIGHT, GFLG_RELBOTTOM, GFLG_RELWIDTH, and GFLG_RELHEIGHT, respectively). If that field&#039;s flag is set, Intuition expects the value in that field to be relative to either the window border or the window dimensions. For example, if GFLG_RELRIGHT is set, Intuition expects the value in the gadget&#039;s LeftEdge field to be relative to the right window border.&lt;br /&gt;
&lt;br /&gt;
There is a special kind of GREL BOOPSI gadget called a custom relativity gadget. For this type of gadget, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to be absolute measurements, just like the values Intuition expects in these fields for non-GREL gadgets.&lt;br /&gt;
&lt;br /&gt;
Setting the GFLG_RELSPECIAL bit in the Gadget.Flags field marks the gadget as a custom relativity gadget. The best way to set this bit is by setting the GA_RelSpecial attribute to TRUE when creating the gadget.&lt;br /&gt;
&lt;br /&gt;
==== GM_RENDER ====&lt;br /&gt;
&lt;br /&gt;
Every time Intuition feels it is necessary to redraw a BOOPSI gadget, it sends a gadget a GM_RENDER message. The GM_RENDER message (defined in &amp;lt;intuition/gadgetclass.h&amp;gt;) tells a gadget to render itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct gpRender&lt;br /&gt;
{&lt;br /&gt;
    uint32             MethodID;   /* GM_RENDER */&lt;br /&gt;
    struct GadgetInfo *gpr_GInfo;&lt;br /&gt;
    struct RastPort   *gpr_RPort;  /* all ready for use */&lt;br /&gt;
    int32              gpr_Redraw; /* might be a &amp;quot;highlight pass&amp;quot; */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some events that cause Intuition to send a GM_RENDER are: an application passed the gadget to OpenWindow(), the user moved or resized a gadget&#039;s window, or an application explicitly asked Intuition to refresh some gadgets.&lt;br /&gt;
&lt;br /&gt;
The GM_RENDER message contains a pointer to the gadget&#039;s RastPort so the GM_RENDER method does not have to extract it from the gpr_GInfo GadgetInfo structure using ObtainGIRPort()). The gadget renders itself according to how much imagery it needs to replace. The gpr_Redraw field contains one of three values:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| GREDRAW_REDRAW&lt;br /&gt;
| Redraw the entire gadget.&lt;br /&gt;
|-&lt;br /&gt;
| GREDRAW_UPDATE&lt;br /&gt;
| The user has manipulated the gadget, causing a change to its imagery. Update only that part of the gadget&#039;s imagery that is effected by the user manipulating the gadget (for example, the knob and scrolling field of the prop gadget).&lt;br /&gt;
|-&lt;br /&gt;
| GREDRAW_TOGGLE&lt;br /&gt;
| If this gadget supports it, toggle to or from the highlighting imagery.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Intuition is not the only entity that calls this method. The gadget&#039;s other methods may call this method to render the gadget when it goes through state changes. For example, as a prop gadget is following the mouse from the gadget&#039;s GM_HANDLEINPUT method, the gadget could send itself GM_RENDER messages, telling itself to update its imagery according to where the mouse has moved.&lt;br /&gt;
&lt;br /&gt;
The DoRender() method should always be used by class implementers to invoke a GM_RENDER method on a gadget object. Applications use the RefreshGList() function which will invoke GM_RENDER for the caller in the proper context.&lt;br /&gt;
&lt;br /&gt;
=== The Active Gadget ===&lt;br /&gt;
&lt;br /&gt;
While a gadget is active, Intuition sends it a GM_HANDLEINPUT message for every timer pulse, mouse move, mouse click, and key press that takes place. A timer event pulse arrives about every tenth of a second. Mouse move events can arrive at a much higher rate than the timer pulses. Without even considering the keyboard, a gadget can get a lot of GM_HANDLEINPUT messages in a short amount of time. Because the active gadget has to handle a large volume of GM_HANDLEINPUT messages, the overhead of this method should be kept to a minimum.&lt;br /&gt;
&lt;br /&gt;
Because the gadget will always receive a GM_GOACTIVE message before it is active and a GM_GOINACTIVE message after it is no longer active, the gadget can use these methods to allocate, initialize, and deallocate temporary resources it needs for the GM_HANDLEINPUT method. This can significantly reduce the overhead of GM_HANDLEINPUT because it eliminates the need to allocate, initialize, and deallocate resources for every GM_HANDLEINPUT message.&lt;br /&gt;
&lt;br /&gt;
Note that the RastPort from ObtainGIRPort() is not cachable using this method. If the GM_HANDLEINPUT method needs to use a RastPort, it has to obtain and release the RastPort for every GM_HANDLEINPUT message using ObtainGIRPort() and ReleaseGIRPort().&lt;br /&gt;
&lt;br /&gt;
=== RKMButtonclass.c ===&lt;br /&gt;
&lt;br /&gt;
The following example is a sample BOOPSI gadget, RKMButClass.c.&lt;br /&gt;
&lt;br /&gt;
While the user has the RKMButton selected, the gadget sends an OM_UPDATE message to its ICA_TARGET for every timer event the button sees.&lt;br /&gt;
&lt;br /&gt;
The gadget sends notification about its RKMBUT_Pulse attribute, which is the horizontal distance in screen pixels the mouse is from the center of the button.&amp;lt;br&amp;gt;&lt;br /&gt;
The gadget takes care of rendering all of its imagery (as opposed to using a BOOPSI image to do it).&amp;lt;br&amp;gt;&lt;br /&gt;
The gadget&#039;s imagery is scalable to any dimensions and can be set (using SetGadgetAttrs()) while the gadget is in place.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One possible use for such a gadget is as buttons for a prop gadget. &lt;br /&gt;
&lt;br /&gt;
If the user has the prop gadget&#039;s RKMButton selected, while the mouse is to the left of the button&#039;s center, the knob on the prop gadget moves left.&amp;lt;br&amp;gt;&lt;br /&gt;
While the mouse is to the right of the button&#039;s center, the knob on the prop gadget moves right.&amp;lt;br&amp;gt;&lt;br /&gt;
The speed at which the knob moves is proportional to the horizontal distance from the mouse to the active RKMButton.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
/* RKMButClass.c - Example BOOPSI gadget */&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/classes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/classusr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/imageclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gadgetclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/cghooks.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/hooks.h&amp;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;
#include &amp;lt;proto/graphics.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;graphics/gfxmacros.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CONST_STRPTR vers = &amp;quot;$VER: TestBut 50.1&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/****************      Class specifics      ****************/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
#define RKMBUT_Pulse   (TAG_USER + 1)&lt;br /&gt;
&lt;br /&gt;
struct ButINST&lt;br /&gt;
{&lt;br /&gt;
    LONG midX, midY; /* Coordinates of middle of gadget */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* ButINST has one flag:  */&lt;br /&gt;
#define ERASE_ONLY   0x00000001 /* Tells rendering routine to */&lt;br /&gt;
/* only erase the gadget, not */&lt;br /&gt;
/* rerender a new one.  This  */&lt;br /&gt;
/* lets the gadget erase it-  */&lt;br /&gt;
/* self before it rescales.   */&lt;br /&gt;
&lt;br /&gt;
/* The functions in this module */&lt;br /&gt;
Class *initRKMButGadClass(void);&lt;br /&gt;
BOOL   freeRKMButGadClass(Class *);&lt;br /&gt;
ULONG  dispatchRKMButGad(Class *, Object *, Msg);&lt;br /&gt;
void   NotifyPulse(Class *, Object *, ULONG, LONG, struct gpInput *);&lt;br /&gt;
ULONG  RenderRKMBut(Class *, struct Gadget *, struct gpRender *);&lt;br /&gt;
void   MainLoop(ULONG, ULONG);&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/* The main() function connects an RKMButClass object to a BOOPSI integer gadget, which displays */&lt;br /&gt;
/* the RKMButClass gadget&#039;s RKMBUT_Pulse value.  The code scales and move the gadget while it is */&lt;br /&gt;
/* in place.                                                                                     */&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
struct TagItem pulse2int[] =&lt;br /&gt;
    {&lt;br /&gt;
        {RKMBUT_Pulse, STRINGA_LongVal},&lt;br /&gt;
        {TAG_END,}&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
#define INTWIDTH  40&lt;br /&gt;
#define INTHEIGHT 20&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct GraphicsIFace *IGraphics;&lt;br /&gt;
struct Window *w;&lt;br /&gt;
Class *rkmbutcl;&lt;br /&gt;
struct Gadget *integer, *but;&lt;br /&gt;
struct IntuiMessage *msg;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    struct Library *IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
    struct Library *GfxBase = IExec-&amp;gt;OpenLibrary(&amp;quot;graphics.library&amp;quot;, 50);&lt;br /&gt;
&lt;br /&gt;
    IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    IGraphics = (struct GraphicsIFace*)IExec-&amp;gt;GetInterface(GfxBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (IIntuition != NULL &amp;amp;&amp;amp; IGraphics != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        if (w = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
            WA_Flags,       WFLG_DEPTHGADGET | WFLG_DRAGBAR |&lt;br /&gt;
                            WFLG_CLOSEGADGET | WFLG_SIZEGADGET,&lt;br /&gt;
            WA_IDCMP,       IDCMP_CLOSEWINDOW,&lt;br /&gt;
            WA_Width,       640,&lt;br /&gt;
            WA_Height,      200,&lt;br /&gt;
            TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            IIntuition-&amp;gt;WindowLimits(w, 450, 200, 640, 200);&lt;br /&gt;
&lt;br /&gt;
            if (rkmbutcl = initRKMButGadClass())&lt;br /&gt;
            {&lt;br /&gt;
                if (integer = (struct Gadget *)IIntuition-&amp;gt;NewObject(NULL,&lt;br /&gt;
                    &amp;quot;strgclass&amp;quot;,&lt;br /&gt;
                    GA_ID,            1L,&lt;br /&gt;
                    GA_Top,           (w-&amp;gt;BorderTop) + 5L,&lt;br /&gt;
                    GA_Left,          (w-&amp;gt;BorderLeft) + 5L,&lt;br /&gt;
                    GA_Width,         INTWIDTH,&lt;br /&gt;
                    GA_Height,        INTHEIGHT,&lt;br /&gt;
                    STRINGA_LongVal,  0L,&lt;br /&gt;
                    STRINGA_MaxChars, 5L,&lt;br /&gt;
                    TAG_END))&lt;br /&gt;
                {&lt;br /&gt;
                    if (but = (struct Gadget *)IIntuition-&amp;gt;NewObject(rkmbutcl,&lt;br /&gt;
                        NULL,&lt;br /&gt;
                        GA_ID,       2L,&lt;br /&gt;
                        GA_Top,      (w-&amp;gt;BorderTop) + 5L,&lt;br /&gt;
                        GA_Left,     integer-&amp;gt;LeftEdge +&lt;br /&gt;
                                     integer-&amp;gt;Width + 5L,&lt;br /&gt;
                        GA_Width,    40L,&lt;br /&gt;
                        GA_Height,   INTHEIGHT,&lt;br /&gt;
                        GA_Previous, integer,&lt;br /&gt;
                        ICA_MAP,     pulse2int,&lt;br /&gt;
                        ICA_TARGET,  integer,&lt;br /&gt;
                        TAG_END))&lt;br /&gt;
                    {&lt;br /&gt;
                        IIntuition-&amp;gt;AddGList(w, integer, -1, -1, NULL);&lt;br /&gt;
                        IIntuition-&amp;gt;RefreshGList(integer, w, NULL, -1);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Height&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(TAG_END, 0L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Width&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Height, 100L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget Y position&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Width, 100L);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to resize gadget X position&amp;quot;,&lt;br /&gt;
                            NULL);&lt;br /&gt;
                        MainLoop(GA_Top, but-&amp;gt;TopEdge + 20);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;SetWindowTitles(w,&lt;br /&gt;
                            &amp;quot;&amp;lt;-- Click to quit&amp;quot;, NULL);&lt;br /&gt;
                        MainLoop(GA_Left, but-&amp;gt;LeftEdge + 20);&lt;br /&gt;
&lt;br /&gt;
                        IIntuition-&amp;gt;RemoveGList(w, integer, -1);&lt;br /&gt;
                        IIntuition-&amp;gt;DisposeObject((Object *)but);&lt;br /&gt;
                    }&lt;br /&gt;
                    IIntuition-&amp;gt;DisposeObject((Object *)integer);&lt;br /&gt;
                }&lt;br /&gt;
                freeRKMButGadClass(rkmbutcl);&lt;br /&gt;
            }&lt;br /&gt;
            IIntuition-&amp;gt;CloseWindow(w);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IGraphics);&lt;br /&gt;
    IExec-&amp;gt;DropInterface((struct Interface*)IIntuition);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(GfxBase);&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;
void MainLoop(ULONG attr, ULONG value)&lt;br /&gt;
{&lt;br /&gt;
    ULONG done = FALSE;&lt;br /&gt;
&lt;br /&gt;
    IIntuition-&amp;gt;SetGadgetAttrs(but, w, NULL, attr, value, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    while (done == FALSE)&lt;br /&gt;
    {&lt;br /&gt;
        IExec-&amp;gt;WaitPort((struct MsgPort *)w-&amp;gt;UserPort);&lt;br /&gt;
        while (msg = (struct IntuiMessage *)&lt;br /&gt;
            IExec-&amp;gt;GetMsg((struct MsgPort *)w-&amp;gt;UserPort))&lt;br /&gt;
        {&lt;br /&gt;
            if (msg-&amp;gt;Class == IDCMP_CLOSEWINDOW)&lt;br /&gt;
            {&lt;br /&gt;
                done = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((Message *)msg);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/**    Make the class and set up the dispatcher&#039;s hook    **/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
Class *initRKMButGadClass(void)&lt;br /&gt;
{&lt;br /&gt;
    Class *cl = NULL;&lt;br /&gt;
    extern ULONG HookEntry();     /* defined in amiga.lib */&lt;br /&gt;
&lt;br /&gt;
    if ( cl =  IIntuition-&amp;gt;MakeClass( NULL,&lt;br /&gt;
        &amp;quot;gadgetclass&amp;quot;, NULL,&lt;br /&gt;
        sizeof ( struct ButINST ),&lt;br /&gt;
        0 ))&lt;br /&gt;
    {&lt;br /&gt;
        /* initialize the cl_Dispatcher Hook    */&lt;br /&gt;
        cl-&amp;gt;cl_Dispatcher.h_Entry = (HOOKFUNC)dispatchRKMButGad;&lt;br /&gt;
    }&lt;br /&gt;
    return ( cl );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/******************     Free the class      ****************/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
BOOL freeRKMButGadClass(Class *cl)&lt;br /&gt;
{&lt;br /&gt;
    return IIntuition-&amp;gt;FreeClass(cl);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
/**********       The RKMBut class dispatcher      *********/&lt;br /&gt;
/***********************************************************/&lt;br /&gt;
ULONG dispatchRKMButGad(Class *cl, Object *o, Msg msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ButINST *inst;&lt;br /&gt;
    ULONG retval = FALSE;&lt;br /&gt;
    Object *object;&lt;br /&gt;
&lt;br /&gt;
    switch (msg-&amp;gt;MethodID)&lt;br /&gt;
    {&lt;br /&gt;
    case OM_NEW:       /* First, pass up to superclass */&lt;br /&gt;
        if (object = (Object *)IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg))&lt;br /&gt;
        {&lt;br /&gt;
            struct Gadget *g = (struct Gadget *)object;&lt;br /&gt;
&lt;br /&gt;
            /* Initial local instance data */&lt;br /&gt;
            inst = (ButINST *)INST_DATA(cl, object);&lt;br /&gt;
            inst-&amp;gt;midX   = g-&amp;gt;LeftEdge + ( (g-&amp;gt;Width) / 2);&lt;br /&gt;
            inst-&amp;gt;midY   = g-&amp;gt;TopEdge + ( (g-&amp;gt;Height) / 2);&lt;br /&gt;
&lt;br /&gt;
            retval = (ULONG)object;&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_HITTEST:&lt;br /&gt;
        /* Since this is a rectangular gadget this  */&lt;br /&gt;
        /* method always returns GMR_GADGETHIT.     */&lt;br /&gt;
        retval = GMR_GADGETHIT;&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_GOACTIVE:&lt;br /&gt;
        inst = (ButINST*)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
        /* Only become active if the GM_GOACTIVE   */&lt;br /&gt;
        /* was triggered by direct user input.     */&lt;br /&gt;
        if (((struct gpInput *)msg)-&amp;gt;gpi_IEvent)&lt;br /&gt;
        {&lt;br /&gt;
            /* This gadget is now active, change    */&lt;br /&gt;
            /* visual state to selected and render. */&lt;br /&gt;
            ((struct Gadget *)o)-&amp;gt;Flags |= GFLG_SELECTED;&lt;br /&gt;
            RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
            retval = GMR_MEACTIVE;&lt;br /&gt;
        }&lt;br /&gt;
        else            /* The GM_GOACTIVE was not         */&lt;br /&gt;
            /* triggered by direct user input. */&lt;br /&gt;
            retval = GMR_NOREUSE;&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_RENDER:&lt;br /&gt;
        retval = RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
        break;&lt;br /&gt;
    case GM_HANDLEINPUT:   /* While it is active, this gadget sends its superclass an        */&lt;br /&gt;
        /* OM_NOTIFY pulse for every IECLASS_TIMER event that goes by     */&lt;br /&gt;
        /* (about one every 10th of a second).  Any object that is        */&lt;br /&gt;
        /* connected to this gadget will get A LOT of OM_UPDATE messages. */&lt;br /&gt;
    {&lt;br /&gt;
        struct Gadget *g = (struct Gadget *)o;&lt;br /&gt;
        struct gpInput *gpi = (struct gpInput *)msg;&lt;br /&gt;
        struct InputEvent *ie = gpi-&amp;gt;gpi_IEvent;&lt;br /&gt;
&lt;br /&gt;
        inst = (ButINST*)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
        retval = GMR_MEACTIVE;&lt;br /&gt;
&lt;br /&gt;
        if (ie-&amp;gt;ie_Class == IECLASS_RAWMOUSE)&lt;br /&gt;
        {&lt;br /&gt;
            switch (ie-&amp;gt;ie_Code)&lt;br /&gt;
            {&lt;br /&gt;
            case SELECTUP: /* The user let go of the gadget so return GMR_NOREUSE    */&lt;br /&gt;
                /* to deactivate and to tell Intuition not to reuse       */&lt;br /&gt;
                /* this Input Event as we have already processed it.      */&lt;br /&gt;
&lt;br /&gt;
                /*If the user let go of the gadget while the mouse was    */&lt;br /&gt;
                /*over it, mask GMR_VERIFY into the return value so       */&lt;br /&gt;
                /*Intuition will send a Release Verify (GADGETUP).        */&lt;br /&gt;
                if ( ((gpi-&amp;gt;gpi_Mouse).X &amp;lt; g-&amp;gt;LeftEdge) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).X &amp;gt; g-&amp;gt;LeftEdge + g-&amp;gt;Width) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).Y &amp;lt; g-&amp;gt;TopEdge) ||&lt;br /&gt;
                    ((gpi-&amp;gt;gpi_Mouse).Y &amp;gt; g-&amp;gt;TopEdge + g-&amp;gt;Height) )&lt;br /&gt;
                    retval = GMR_NOREUSE | GMR_VERIFY;&lt;br /&gt;
                else&lt;br /&gt;
                    retval = GMR_NOREUSE;&lt;br /&gt;
&lt;br /&gt;
                /* Since the gadget is going inactive, send a final   */&lt;br /&gt;
                /* notification to the ICA_TARGET.                    */&lt;br /&gt;
                NotifyPulse(cl , o, 0L, inst-&amp;gt;midX, (struct gpInput *)msg);&lt;br /&gt;
                break;&lt;br /&gt;
            case MENUDOWN: /* The user hit the menu button. Go inactive and let      */&lt;br /&gt;
                /* Intuition reuse the menu button event so Intuition can */&lt;br /&gt;
                /* pop up the menu bar.                                   */&lt;br /&gt;
                retval = GMR_REUSE;&lt;br /&gt;
&lt;br /&gt;
                /* Since the gadget is going inactive, send a final   */&lt;br /&gt;
                /* notification to the ICA_TARGET.                    */&lt;br /&gt;
                NotifyPulse(cl , o, 0L, inst-&amp;gt;midX, (struct gpInput *)msg);&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                retval = GMR_MEACTIVE;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
        else if (ie-&amp;gt;ie_Class == IECLASS_TIMER)&lt;br /&gt;
            /* If the gadget gets a timer event, it sends an interim OM_NOTIFY */&lt;br /&gt;
            NotifyPulse(cl, o, OPUF_INTERIM, inst-&amp;gt;midX, gpi); /*     to its superclass. */&lt;br /&gt;
    }&lt;br /&gt;
        break;&lt;br /&gt;
&lt;br /&gt;
    case GM_GOINACTIVE:           /* Intuition said to go inactive.  Clear the GFLG_SELECTED */&lt;br /&gt;
        /* bit and render using unselected imagery.                */&lt;br /&gt;
        ((struct Gadget *)o)-&amp;gt;Flags &amp;amp;= ~GFLG_SELECTED;&lt;br /&gt;
        RenderRKMBut(cl, (struct Gadget *)o, (struct gpRender *)msg);&lt;br /&gt;
        break;&lt;br /&gt;
    case OM_SET:/* Although this class doesn&#039;t have settable attributes, this gadget class   */&lt;br /&gt;
        /* does have scaleable imagery, so it needs to find out when its size and/or */&lt;br /&gt;
        /* position has changed so it can erase itself, THEN scale, and rerender.    */&lt;br /&gt;
        if ( IUtility-&amp;gt;FindTagItem(GA_Width,  ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Height, ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Top,    ((struct opSet *)msg)-&amp;gt;ops_AttrList) ||&lt;br /&gt;
            IUtility-&amp;gt;FindTagItem(GA_Left,   ((struct opSet *)msg)-&amp;gt;ops_AttrList) )&lt;br /&gt;
        {&lt;br /&gt;
            struct RastPort *rp;&lt;br /&gt;
            struct Gadget *g = (struct Gadget *)o;&lt;br /&gt;
&lt;br /&gt;
            WORD x,y,w,h;&lt;br /&gt;
&lt;br /&gt;
            x = g-&amp;gt;LeftEdge;&lt;br /&gt;
            y = g-&amp;gt;TopEdge;&lt;br /&gt;
            w = g-&amp;gt;Width;&lt;br /&gt;
            h = g-&amp;gt;Height;&lt;br /&gt;
&lt;br /&gt;
            inst = (ButINST *)INST_DATA(cl, o);&lt;br /&gt;
&lt;br /&gt;
            retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
&lt;br /&gt;
            /* Get pointer to RastPort for gadget. */&lt;br /&gt;
            if (rp = IIntuition-&amp;gt;ObtainGIRPort( ((struct opSet *)msg)-&amp;gt;ops_GInfo) )&lt;br /&gt;
            {&lt;br /&gt;
                UWORD *pens = ((struct opSet *)msg)-&amp;gt;ops_GInfo-&amp;gt;gi_DrInfo-&amp;gt;dri_Pens;&lt;br /&gt;
&lt;br /&gt;
                IGraphics-&amp;gt;SetAPen(rp, pens[BACKGROUNDPEN]);&lt;br /&gt;
                IGraphics-&amp;gt;SetDrMd(rp, JAM1);                            /* Erase the old gadget.       */&lt;br /&gt;
                IGraphics-&amp;gt;RectFill(rp, x, y, x+w, y+h);&lt;br /&gt;
&lt;br /&gt;
                inst-&amp;gt;midX = g-&amp;gt;LeftEdge + ( (g-&amp;gt;Width) / 2); /* Recalculate where the       */&lt;br /&gt;
                inst-&amp;gt;midY = g-&amp;gt;TopEdge + ( (g-&amp;gt;Height) / 2); /* center of the gadget is.    */&lt;br /&gt;
&lt;br /&gt;
                /* Rerender the gadget.        */&lt;br /&gt;
                IIntuition-&amp;gt;IDoMethod(o, GM_RENDER, ((struct opSet *)msg)-&amp;gt;ops_GInfo, rp, GREDRAW_REDRAW);&lt;br /&gt;
                IIntuition-&amp;gt;ReleaseGIRPort(rp);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
        break;&lt;br /&gt;
    default:          /* rkmmodelclass does not recognize the methodID, let the superclass&#039;s */&lt;br /&gt;
        /* dispatcher take a look at it.                                       */&lt;br /&gt;
        retval = IIntuition-&amp;gt;IDoSuperMethodA(cl, o, msg);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    return(retval);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/************** Build an OM_NOTIFY message for RKMBUT_Pulse and send it to the superclass. *******/&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
void NotifyPulse(Class *cl, Object *o, ULONG flags, LONG mid, struct gpInput *gpi)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem tt[3];&lt;br /&gt;
&lt;br /&gt;
    tt[0].ti_Tag = RKMBUT_Pulse;&lt;br /&gt;
    tt[0].ti_Data = mid - ((gpi-&amp;gt;gpi_Mouse).X + ((struct Gadget *)o)-&amp;gt;LeftEdge);&lt;br /&gt;
&lt;br /&gt;
    tt[1].ti_Tag = GA_ID;&lt;br /&gt;
    tt[1].ti_Data = ((struct Gadget *)o)-&amp;gt;GadgetID;&lt;br /&gt;
&lt;br /&gt;
    tt[2].ti_Tag = TAG_END;&lt;br /&gt;
&lt;br /&gt;
    IIntuition-&amp;gt;IDoSuperMethod(cl, o, OM_NOTIFY, tt, gpi-&amp;gt;gpi_GInfo, flags);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
/*******************************   Erase and rerender the gadget.   ******************************/&lt;br /&gt;
/*************************************************************************************************/&lt;br /&gt;
ULONG RenderRKMBut(Class *cl, struct Gadget *g, struct gpRender *msg)&lt;br /&gt;
{&lt;br /&gt;
    struct ButINST *inst = (ButINST *)INST_DATA(cl, (Object *)g);&lt;br /&gt;
    struct RastPort *rp;&lt;br /&gt;
    ULONG retval = TRUE;&lt;br /&gt;
    UWORD *pens = msg-&amp;gt;gpr_GInfo-&amp;gt;gi_DrInfo-&amp;gt;dri_Pens;&lt;br /&gt;
&lt;br /&gt;
    if (msg-&amp;gt;MethodID == GM_RENDER)   /* If msg is truly a GM_RENDER message (not a gpInput that */&lt;br /&gt;
        /* looks like a gpRender), use the rastport within it...   */&lt;br /&gt;
        rp = msg-&amp;gt;gpr_RPort;&lt;br /&gt;
    else                              /* ...Otherwise, get a rastport using ObtainGIRPort().     */&lt;br /&gt;
        rp = IIntuition-&amp;gt;ObtainGIRPort(msg-&amp;gt;gpr_GInfo);&lt;br /&gt;
&lt;br /&gt;
    if (rp)&lt;br /&gt;
    {&lt;br /&gt;
        UWORD back, shine, shadow, w, h, x, y;&lt;br /&gt;
&lt;br /&gt;
        if (g-&amp;gt;Flags &amp;amp; GFLG_SELECTED) /* If the gadget is selected, reverse the meanings of the  */&lt;br /&gt;
        {                             /* pens.                                                   */&lt;br /&gt;
            back   = pens[FILLPEN];&lt;br /&gt;
            shine  = pens[SHADOWPEN];&lt;br /&gt;
            shadow = pens[SHINEPEN];&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            back   = pens[BACKGROUNDPEN];&lt;br /&gt;
            shine  = pens[SHINEPEN];&lt;br /&gt;
            shadow = pens[SHADOWPEN];&lt;br /&gt;
        }&lt;br /&gt;
        IGraphics-&amp;gt;SetDrMd(rp, JAM1);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, back);          /* Erase the old gadget.       */&lt;br /&gt;
        IGraphics-&amp;gt;RectFill(rp, g-&amp;gt;LeftEdge,&lt;br /&gt;
            g-&amp;gt;TopEdge,&lt;br /&gt;
            g-&amp;gt;LeftEdge + g-&amp;gt;Width,&lt;br /&gt;
            g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, shadow);     /* Draw shadow edge.            */&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, g-&amp;gt;LeftEdge + 1, g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width, g-&amp;gt;TopEdge + g-&amp;gt;Height);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width, g-&amp;gt;TopEdge + 1);&lt;br /&gt;
&lt;br /&gt;
        w = g-&amp;gt;Width / 4;       /* Draw Arrows - Sorry, no frills imagery */&lt;br /&gt;
        h = g-&amp;gt;Height / 2;&lt;br /&gt;
        x = g-&amp;gt;LeftEdge + (w/2);&lt;br /&gt;
        y = g-&amp;gt;TopEdge + (h/2);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, x, inst-&amp;gt;midY);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, y);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, y + (g-&amp;gt;Height) - h);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, inst-&amp;gt;midY);&lt;br /&gt;
&lt;br /&gt;
        x = g-&amp;gt;LeftEdge + (w/2) + g-&amp;gt;Width / 2;&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, x + w, inst-&amp;gt;midY);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, y);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x, y  + (g-&amp;gt;Height) - h);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, x + w, inst-&amp;gt;midY);&lt;br /&gt;
&lt;br /&gt;
        IGraphics-&amp;gt;SetAPen(rp, shine);    /* Draw shine edge.           */&lt;br /&gt;
        IGraphics-&amp;gt;Move(rp, g-&amp;gt;LeftEdge, g-&amp;gt;TopEdge + g-&amp;gt;Height - 1);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge, g-&amp;gt;TopEdge);&lt;br /&gt;
        IGraphics-&amp;gt;Draw(rp, g-&amp;gt;LeftEdge + g-&amp;gt;Width - 1, g-&amp;gt;TopEdge);&lt;br /&gt;
&lt;br /&gt;
        if (msg-&amp;gt;MethodID != GM_RENDER) /* If we allocated a rastport, give it back.             */&lt;br /&gt;
            IIntuition-&amp;gt;ReleaseGIRPort(rp);&lt;br /&gt;
    }&lt;br /&gt;
    else retval = FALSE;&lt;br /&gt;
    return(retval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Workbench_Library&amp;diff=12390</id>
		<title>Workbench Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Workbench_Library&amp;diff=12390"/>
		<updated>2024-02-19T10:00:51Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* An AppIcon Example */ Typo fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Workbench Library ==&lt;br /&gt;
&lt;br /&gt;
Workbench is the graphic user interface to the Amiga file system that uses symbols called icons to represent disks, directories and files. This article shows how to use workbench.library.&lt;br /&gt;
&lt;br /&gt;
Workbench is both a system program and a screen. Normally it is the first thing the user sees when the machine is booted providing a friendly operating environment for launching applications and performing other important system activities like navigating through the Amiga&#039;s hierarchical filing system.&lt;br /&gt;
&lt;br /&gt;
All application programs should be compatible with Workbench. There are only two things you need to know to do this: how to make icons for your application, data files and directories; and how to get arguments if your application is launched from Workbench.&lt;br /&gt;
&lt;br /&gt;
== The Info File ==&lt;br /&gt;
&lt;br /&gt;
The iconic representation of Amiga filing system objects is implemented through &#039;&#039;.info&#039;&#039; files. In general, for each file, disk or directory that is visible in the Workbench environment, there is an associated &#039;&#039;.info&#039;&#039; file which contains the icon imagery and other information needed by Workbench.&lt;br /&gt;
&lt;br /&gt;
Icons are associated with a particular file or directory by name. For example, the icon for a file named &#039;&#039;myapp&#039;&#039; would be stored in a &#039;&#039;.info&#039;&#039; file named &#039;&#039;myapp.info&#039;&#039; in the same directory.&lt;br /&gt;
&lt;br /&gt;
To make your application program accessible (and visible) in the Workbench environment, you need only supply a &#039;&#039;.info&#039;&#039; file with the appropriate name and type. The are four main types of icons (and &#039;&#039;.info&#039;&#039; files) used to represent Amiga filing system objects.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Basic Workbench Icon Types&lt;br /&gt;
! Workbench Icon Type&lt;br /&gt;
! Image&lt;br /&gt;
! Filing System Object&lt;br /&gt;
! Result When Icon Is Activated&lt;br /&gt;
|-&lt;br /&gt;
| Disk || [[File:LibDiskIcon.png]] || The root level directory || Window opens showing files and subdirectories&lt;br /&gt;
|-&lt;br /&gt;
| Drawer || [[File:LibDrawerIcon.png]] || A subdirectory || Window opens showing files and subdirectories&lt;br /&gt;
|-&lt;br /&gt;
| Tool || [[File:LibToolIcon.png]] || An executable file || Application runs (i.e., an application)&lt;br /&gt;
|-&lt;br /&gt;
| Project || [[File:LibProjectIcon.png]] || A data file || Typically, the application that created the data file runs and the data file is automatically loaded into it.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Icons can be created with the IconEdit program (in the Tools directory of the Extras disk), or by copying an existing &#039;&#039;.info&#039;&#039; file of the correct type. Icons can also be created under program control with PutDiskObject(). See [[Icon Library]] for a discussion of the icon library functions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The icon type can be set and the icon imagery edited with the IconEdit program.&#039;&#039;&#039;  For an executable file the icon type must be set to tool. For a data file the icon type must be set to project. Create icons for your application disk and directories too. For a directory, the icon is stored in a &#039;&#039;.info&#039;&#039; file at the same level where the directory name appears (not in the directory itself). The icon type should be set to drawer. The icon for a disk should always be stored in a file named &#039;&#039;disk.info&#039;&#039; at the root level directory of the disk. The icon type should be set to disk.&lt;br /&gt;
&lt;br /&gt;
== Workbench Environment ==&lt;br /&gt;
&lt;br /&gt;
On the Amiga there are at least two ways to start a program running:&lt;br /&gt;
&lt;br /&gt;
* By activating a tool or project icon in Workbench (an icon is activated by pointing to it with the mouse and double-clicking the mouse select button.)&lt;br /&gt;
* By typing the name of an executable file at the Shell (also known as the CLI or Command Line Interface)&lt;br /&gt;
&lt;br /&gt;
In the Workbench environment, a program is run as a separate process. A process is simply a task with additional information needed to use DOS library.&lt;br /&gt;
&lt;br /&gt;
By default, a Workbench program does not have a window to which its output will go. Therefore, &#039;&#039;stdin&#039;&#039;, &#039;&#039;stdout&#039;&#039; and &#039;&#039;stderr&#039;&#039; do not point to legal file handles. This means you cannot use &#039;&#039;stdio&#039;&#039; functions such as printf() if your program is started from Workbench unless you first set up a &#039;&#039;stdio&#039;&#039; window.&lt;br /&gt;
&lt;br /&gt;
Some compilers have options or defaults to provide a &#039;&#039;stdio&#039;&#039; window for programs started from Workbench. Applications can use an auto console window for &#039;&#039;stdio&#039;&#039; when started from Workbench by opening &amp;amp;quot;CON:0/0/640/200/auto/close/wait&amp;amp;quot; as a file. An auto console window will only open if &#039;&#039;stdio&#039;&#039; input or output occurs. This can also be handled in the startup code module that comes with your compiler.&lt;br /&gt;
&lt;br /&gt;
=== Argument Passing in Workbench ===&lt;br /&gt;
&lt;br /&gt;
Applications started from Workbench receive arguments in the form of a WBStartup structure. This is similar to obtaining arguments from a command line interface through argc and argv. The WBStartup message contains an argument count and a pointer to a list of file and directory names.&lt;br /&gt;
&lt;br /&gt;
==== One Argument ====&lt;br /&gt;
&lt;br /&gt;
A program started by activating its tool icon gets one argument in the WBStartup message: the name of the tool itself.&lt;br /&gt;
&lt;br /&gt;
==== Two Arguments ====&lt;br /&gt;
&lt;br /&gt;
All project icons (data files) have a &#039;&#039;default tool&#039;&#039; field associated with them that tells Workbench which application tool to run in order to operate on the data that the icon represents. When the user activates a project icon, Workbench runs the application specified in the default tool field passing it two arguments in the WBStartup message: the name of the tool and the project icon that the user activated.&lt;br /&gt;
&lt;br /&gt;
==== Multiple Arguments ====&lt;br /&gt;
&lt;br /&gt;
With &#039;&#039;extended select&#039;&#039;, the user can activate many icons at once. (Extended select means the user holds down the Shift key while clicking the mouse select button once on each icon in a group, double-clicking on the last icon.) If one of the icons in a group activated with extended select is an application tool, Workbench runs that application passing it the name of all the other icons in the group. This allows the user to start an application with multiple project files as arguments. If none of the icons in a group activated with extended select is a tool icon, then Workbench looks in the default tool field of each icon in the order they were selected and runs the first tool it finds.&lt;br /&gt;
&lt;br /&gt;
=== WBStartup Message ===&lt;br /&gt;
&lt;br /&gt;
When Workbench loads and starts a program, its sends the program a WBStartup message containing the arguments as summarized above. Normally, the startup code supplied with your compiler will place a pointer to WBStartup in argv for you, set argc to zero and call your program.&lt;br /&gt;
&lt;br /&gt;
The WBStartup message, whose structure is outlined in &amp;amp;lt;workbench/startup.h&amp;amp;gt;, has the following structure elements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct WBStartup&lt;br /&gt;
{&lt;br /&gt;
    struct Message      sm_Message;     /* a standard message structure */&lt;br /&gt;
    struct MsgPort *    sm_Process;     /* the process descriptor for you */&lt;br /&gt;
    BPTR                sm_Segment;     /* a descriptor for your code */&lt;br /&gt;
    LONG                sm_NumArgs;     /* the number of elements in ArgList */&lt;br /&gt;
    char *              sm_ToolWindow;  /* reserved for future use */&lt;br /&gt;
    struct WBArg *      sm_ArgList;     /* the arguments themselves */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fields of the WBStartup structure are used as follows.&lt;br /&gt;
&lt;br /&gt;
; sm_Message&lt;br /&gt;
: A standard Exec message. The reply port is set to the Workbench.&lt;br /&gt;
&lt;br /&gt;
; sm_Process&lt;br /&gt;
: The process descriptor for the tool (as returned by CreateProcess())&lt;br /&gt;
&lt;br /&gt;
; sm_Segment&lt;br /&gt;
: The loaded code for the tool (returned by LoadSeg())&lt;br /&gt;
&lt;br /&gt;
; sm_NumArgs&lt;br /&gt;
: The number of arguments in sm_ArgList&lt;br /&gt;
&lt;br /&gt;
; sm_ToolWindow&lt;br /&gt;
: Reserved (not currently passed in startup message)&lt;br /&gt;
&lt;br /&gt;
; sm_ArgList&lt;br /&gt;
: This is the argument list itself. It is a pointer to an array of WBArg structures with sm_NumArgs elements.&lt;br /&gt;
&lt;br /&gt;
Workbench arguments are passed as an array of WBArg structures in the sm_ArgList field of WBStartup. The first WBArg in the list is always the tool itself. If multiple icons have been selected when a tool is activated, the selected icons are passed to the tool as additional WBArgs. If the tool was derived from a default tool, the project will be the second WBArg. If extended select was used, arguments other than the tool are passed in the order of selection; the first icon selected will be first (after the tool), and so on.&lt;br /&gt;
&lt;br /&gt;
Each argument is a struct WBArg and has two parts: wa_Name and wa_Lock.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct WBArg&lt;br /&gt;
{&lt;br /&gt;
    BPTR   wa_Lock; /* a lock descriptor */&lt;br /&gt;
    BYTE * wa_Name; /* a string relative to that lock */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The wa_Name element is the name of an AmigaDOS filing system object. The wa_Name field of the first WBArg is always the name of your program and the wa_Lock field is an AmigaDOS Lock on the directory where your program is stored.&lt;br /&gt;
&lt;br /&gt;
If your program was started by activating a project icon, then you get a second WBarg with the wa_Name field containing the file name of the project and the wa_Lock containing an AmigaDOS Lock on the directory where the project file is stored.&lt;br /&gt;
&lt;br /&gt;
If your program was started through extended select, then you get one WBArg for each icon in the selected group in the order they were selected. The wa_Name field contains the file name corresponding to each icon unless the icon is for a directory, disk, or the Trashcan in which case the wa_Name is set to NULL. The wa_Lock field contains an AmigaDOS Lock on the directory where the file is stored. (For disk or drawer icons the wa_Lock is a lock on the directory represented by the icon. Or, wa_Lock may be NULL if the icon type does not support locks.)&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Workbench Locks Belong to Workbench|text=You must never call UnLock() on a wa_Lock. These locks belong to Workbench, and Workbench will UnLock() them when the WBStartup message is replied by your startup code. You must also never UnLock() your program&#039;s initial current directory lock (i.e., the lock returned by an initial CurrentDir() call). The classic symptom caused by unlocking Workbench locks is a system hang after your program exits, even though the same program exits with no problems when started from the Shell.}}&lt;br /&gt;
&lt;br /&gt;
You should save the lock returned from an initial CurrentDir(), and CurrentDir() back to it before exiting. In the Workbench environment, depending on your startup code, the current directory will generally be set to one of the wa_Locks. By using CurrentDir(wa_Lock) and then referencing wa_Name, you can find, read, and modify the files that have been passed to your program as WBArgs.&lt;br /&gt;
&lt;br /&gt;
=== Example of Parsing Workbench Arguments ===&lt;br /&gt;
&lt;br /&gt;
The following example will display all WBArgs if started from Workbench, and all Shell arguments if started from the Shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* prargs.c&lt;br /&gt;
**&lt;br /&gt;
** PrArgs.c - This program prints all Workbench or Shell (CLI) arguments.&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct WBStartup *argmsg;&lt;br /&gt;
    struct WBArg *wb_arg;&lt;br /&gt;
    int32 ktr;&lt;br /&gt;
    BPTR olddir;&lt;br /&gt;
    BPTR outFile;&lt;br /&gt;
&lt;br /&gt;
    /* argc is zero when run from the Workbench,&lt;br /&gt;
    **         positive when run from the CLI.&lt;br /&gt;
    */&lt;br /&gt;
    if (argc == 0)&lt;br /&gt;
    {&lt;br /&gt;
    /* AmigaDOS has a special facility that allows a window  */&lt;br /&gt;
    /* with a console and a file handle to be easily created. */&lt;br /&gt;
    /* CON: windows allow you to use fprintf() with no hassle */&lt;br /&gt;
    if (ZERO != (outFile = IDOS-&amp;gt;Open(&amp;quot;CON:0/0/640/200/PrArgs&amp;quot;, MODE_NEWFILE)))&lt;br /&gt;
        {&lt;br /&gt;
        /* In AmigaOS, argv is a pointer to the WBStartup message&lt;br /&gt;
        ** when argc is zero.  (run under the Workbench.)&lt;br /&gt;
        */&lt;br /&gt;
        argmsg = (struct WBStartup *)argv ;&lt;br /&gt;
        wb_arg = argmsg-&amp;gt;sm_ArgList ;         /* head of the arg list */&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;FPrintf(outFile, &amp;quot;Run from the workbench, %ld args.\n&amp;quot;,&lt;br /&gt;
                         argmsg-&amp;gt;sm_NumArgs);&lt;br /&gt;
&lt;br /&gt;
        for (ktr = 0; ktr &amp;lt; argmsg-&amp;gt;sm_NumArgs; ktr++, wb_arg++)&lt;br /&gt;
            {&lt;br /&gt;
            if (ZERO != wb_arg-&amp;gt;wa_Lock)&lt;br /&gt;
                {&lt;br /&gt;
                /* locks supported, change to the proper directory */&lt;br /&gt;
                olddir = IDOS-&amp;gt;CurrentDir(wb_arg-&amp;gt;wa_Lock) ;&lt;br /&gt;
&lt;br /&gt;
                /* process the file.&lt;br /&gt;
                ** If you have done the CurrentDir() above, then you can&lt;br /&gt;
                ** access the file by its name.  Otherwise, you have to&lt;br /&gt;
                ** examine the lock to get a complete path to the file.&lt;br /&gt;
                */&lt;br /&gt;
                IDOS-&amp;gt;FPrintf(outFile, &amp;quot;\tArg %2.2ld (w/ lock): &#039;%s&#039;.\n&amp;quot;,&lt;br /&gt;
                                 ktr, wb_arg-&amp;gt;wa_Name);&lt;br /&gt;
&lt;br /&gt;
                /* change back to the original directory when done.&lt;br /&gt;
                ** be sure to change back before you exit.&lt;br /&gt;
                */&lt;br /&gt;
                IDOS-&amp;gt;CurrentDir(olddir) ;&lt;br /&gt;
                }&lt;br /&gt;
            else&lt;br /&gt;
                {&lt;br /&gt;
                /* something that does not support locks */&lt;br /&gt;
                IDOS-&amp;gt;FPrintf(outFile, &amp;quot;\tArg %2.2ld (no lock): &#039;%s&#039;.\n&amp;quot;,&lt;br /&gt;
                                 ktr, wb_arg-&amp;gt;wa_Name);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        /* wait before closing down */&lt;br /&gt;
        IDOS-&amp;gt;Delay(500L);&lt;br /&gt;
        IDOS-&amp;gt;Close(outFile);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
    /* using &#039;tinymain&#039; from lattice c.&lt;br /&gt;
    ** define a place to send the output (originating CLI window = &amp;quot;*&amp;quot;)&lt;br /&gt;
    ** Note - if you open &amp;quot;CONSOLE:&amp;quot; and your program is RUN, the user will not&lt;br /&gt;
    ** be able to close the CLI window until you close the &amp;quot;CONSOLE:&amp;quot; file.&lt;br /&gt;
    */&lt;br /&gt;
    if (ZERO != (outFile = IDOS-&amp;gt;Open(&amp;quot;CONSOLE:&amp;quot;, MODE_NEWFILE)))&lt;br /&gt;
        {&lt;br /&gt;
        IDOS-&amp;gt;FPrintf(outFile, &amp;quot;Run from the CLI, %d args.\n&amp;quot;, argc);&lt;br /&gt;
&lt;br /&gt;
        for ( ktr = 0; ktr &amp;lt; argc; ktr++)&lt;br /&gt;
            {&lt;br /&gt;
            /* print an arg, and its number */&lt;br /&gt;
            IDOS-&amp;gt;FPrintf(outFile, &amp;quot;\tArg %2.2ld: &#039;%s&#039;.\n&amp;quot;, ktr, argv[ktr]);&lt;br /&gt;
            }&lt;br /&gt;
        IDOS-&amp;gt;Close(outFile);&lt;br /&gt;
        }&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;
== The Workbench Library ==&lt;br /&gt;
&lt;br /&gt;
Workbench arguments are sent to an application when it is started. There are also special facilities that allow an application that is already running to get additional arguments. These special facilities are known as AppWindow, AppIcon and AppMenuItem.&lt;br /&gt;
&lt;br /&gt;
An AppWindow is a special kind of window that allows the user to drag icons into it. Applications that set up an AppWindow will receive a message from Workbench whenever the user moves an icon into the AppWindow. The message contains the name of the file or directory that the icon represents.&lt;br /&gt;
&lt;br /&gt;
An AppIcon is similar to an AppWindow. It is a special type of icon that allows the user to drag other icons on top of it. Like AppWindows, an application that sets up an AppIcon will receive a message from Workbench whenever the user moves another icon on top of the AppIcon. The message contains the name of the file or directory that the moved icon represents.&lt;br /&gt;
&lt;br /&gt;
An AppMenuItem allows an application to add a custom menu item to the usual set of menu choices supported by Workbench. An application that sets up an AppMenuItem will receive a message from Workbench whenever the user picks that item from the Workbench menus.&lt;br /&gt;
&lt;br /&gt;
When an application receives the messages described above, the message will include struct WBArg *am_ArgList containing the names (wa_Name) and directory locks (wa_Lock) of all selected icons that were passed as arguments by the user. This am_ArgList has the same format as the sm_ArgList of a WBStartup message.&lt;br /&gt;
&lt;br /&gt;
=== The AppMessage Structure ===&lt;br /&gt;
&lt;br /&gt;
When Workbench notifies an application of AppWindow, AppIcon, or AppMenuItem activity, it sends an AppMessage to the application&#039;s message port (from &amp;lt;workbench/workbench.h&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
    #define      AM_VERSION     1&lt;br /&gt;
&lt;br /&gt;
    struct AppMessage {&lt;br /&gt;
        struct Message am_Message;    /* standard message structure */&lt;br /&gt;
        UWORD am_Type;                /* message type */&lt;br /&gt;
        ULONG am_UserData;            /* application specific */&lt;br /&gt;
        ULONG am_ID;                  /* application definable ID */&lt;br /&gt;
        LONG am_NumArgs;              /* # of elements in arglist */&lt;br /&gt;
        struct WBArg *am_ArgList;     /* the arguments themselves */&lt;br /&gt;
        UWORD am_Version;             /* will be AM_VERSION */&lt;br /&gt;
        UWORD am_Class;               /* message class */&lt;br /&gt;
        WORD am_MouseX;               /* mouse x position of event */&lt;br /&gt;
        WORD am_MouseY;               /* mouse y position of event */&lt;br /&gt;
        ULONG am_Seconds;             /* current system clock time */&lt;br /&gt;
        ULONG am_Micros;              /* current system clock time */&lt;br /&gt;
        ULONG am_Reserved[8];&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The AppMessage&#039;s am_Type field tells the application which type of AppObject the message is about.  The field will be:&lt;br /&gt;
* AMTYPE_APPWINDOW if the message is about an AppWindow,&lt;br /&gt;
* AMTYPE_APPICON if the message is about an AppIcon, or&lt;br /&gt;
* AMTYPE_APPMENUITEM if the message is about an AppMenuItem.&lt;br /&gt;
&lt;br /&gt;
When an application creates an AppObject, it can assign the AppObject application specific data (most likely a pointer) and an ID. Workbench will pass an AppObject&#039;s data and ID back to the application when it sends an AppMessage about the AppObject. The AppMessage&#039;s am_UserData and am_ID fields hold the user data and the ID.&lt;br /&gt;
&lt;br /&gt;
The am_NumArgs field tells how many icons were involved in the user&#039;s AppObject action. For an AppWindow or AppIcon, am_NumArgs is the number of icons the user dropped on the AppWindow or AppIcon. For an AppMenuItem, am_NumArgs represents the number of icons that were selected when the user selected this AppMenuItem. If no icons were selected during an AppMenuItem event or the user double-clicked on an AppIcon, am_NumArgs will be zero. Workbench does not send AppMessages if the user double-clicks an AppWindow.&lt;br /&gt;
&lt;br /&gt;
The am_ArgList field is a pointer to a list of WBArgs (from &amp;lt;workbench/startup.h&amp;gt;) corresponding to each icon dropped (or selected). If there were no icons dropped or selected, this field will be NULL.&lt;br /&gt;
&lt;br /&gt;
For future expansion possibilities, the AppMessage structure has a version number. The version number is #defined as AM_VERSION in &amp;lt;workbench/workbench.h&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The am_MouseX and am_MouseY fields apply only to AppWindows and contain the coordinates of the mouse pointer when the user dropped the icon(s). These coordinates are relative to the AppWindow&#039;s upper left corner.&lt;br /&gt;
&lt;br /&gt;
The am_Seconds and am_Micros fields represent the time that the event took place.&lt;br /&gt;
&lt;br /&gt;
Any remaining fields are undefined at present and should be set to NULL.&lt;br /&gt;
&lt;br /&gt;
=== Workbench Library Functions ===&lt;br /&gt;
&lt;br /&gt;
AppWindows, AppIcons and AppMenuItems extend the user&#039;s ability to perform operations with the Workbench iconic interface. They all provide graphical methods for passing arguments to a running application. In order to manage AppWindows, AppIcons and AppMenuItems, the Amiga OS includes these Workbench library functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct AppIcon *AddAppIconA( ULONG, ULONG, char *, struct MsgPort *, struct FileLock *,&lt;br /&gt;
  struct DiskObject *, struct *TagItem );&lt;br /&gt;
struct AppMenuItem *AddAppMenuItemA( ULONG, ULONG, char *, struct MsgPort *, struct *TagItem);&lt;br /&gt;
struct AppWindow *AddAppWindowA( ULONG, ULONG, struct Window *, struct MsgPort *, struct *TagItem);&lt;br /&gt;
&lt;br /&gt;
BOOL RemoveAppIcon(struct AppIcon *);&lt;br /&gt;
BOOL RemoveAppMenuItem(struct AppMenuItem *);&lt;br /&gt;
BOOL RemoveAppWindow(struct AppWindow  *);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The functions AddAppMenuItemA(), AddAppWindowA() and AddAppIconA() have alternate entry points using the same function name without the trailing A. The alternate functions accept any TagItem arguments on the stack instead of from an array. See the listings below for examples.&lt;br /&gt;
&lt;br /&gt;
=== An AppIcon Example ===&lt;br /&gt;
&lt;br /&gt;
The example listed here shows how to create an AppIcon and obtain arguments from Workbench when the user drops other icons on top of it. The AppIcon will appear as a disk icon named &amp;quot;TestAppIcon&amp;quot; on the Workbench screen. (All AppIcons appear on the Workbench screen or window.)&lt;br /&gt;
&lt;br /&gt;
For convenience, this example code uses GetDefDiskObject() to create the icon imagery for the AppIcon. Applications should never do this. Use your own custom imagery for AppIcons instead.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* appicon.c&lt;br /&gt;
/* Works from the Shell (CLI) only */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;          /* Need this for the Amiga variable types  */&lt;br /&gt;
#include &amp;lt;workbench/workbench.h&amp;gt; /* This has DiskObject and AppIcon structs */&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;   /* This has WBStartup and WBArg structs    */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;      /* Need this to check library versions     */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;proto/icon.h&amp;gt;          /* Icon (DiskObject) function prototypes   */&lt;br /&gt;
#include &amp;lt;proto/exec.h&amp;gt;          /* Exec message, port and library functions*/&lt;br /&gt;
#include &amp;lt;proto/workbench.h&amp;gt;     /* AppIcon function protos                 */&lt;br /&gt;
&lt;br /&gt;
struct Library *IconBase;&lt;br /&gt;
struct Library *WorkbenchBase;&lt;br /&gt;
struct IconIFace *IIcon;&lt;br /&gt;
struct WorkbenchIFace *IWorkbench;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct DiskObject   *dobj=NULL;&lt;br /&gt;
  struct MsgPort    *myport=NULL;&lt;br /&gt;
  struct AppIcon   *appicon=NULL;&lt;br /&gt;
  struct AppMessage *appmsg=NULL;&lt;br /&gt;
&lt;br /&gt;
  LONG dropcount=0L;&lt;br /&gt;
  ULONG x;&lt;br /&gt;
  BOOL success=0L;&lt;br /&gt;
&lt;br /&gt;
  IconBase = IExec-&amp;gt;OpenLibrary(&amp;quot;icon.library&amp;quot;, 50);&lt;br /&gt;
  WorkbenchBase = IExec-&amp;gt;OpenLibrary(&amp;quot;workbench.library&amp;quot;, 50);&lt;br /&gt;
  &lt;br /&gt;
  IIcon = (struct IconIFace*)IExec-&amp;gt;GetInterface(IconBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  IWorkbench = (struct WorkbenchIFace*)IExec-&amp;gt;GetInterface(WorkbenchBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  &lt;br /&gt;
  /* Get the the right version of the Icon Library, initialize IconBase */&lt;br /&gt;
  if(IIcon != NULL &amp;amp;&amp;amp; IWorkbench != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    /* This is the easy way to get some icon imagery */&lt;br /&gt;
    /* Real applications should use custom imagery   */&lt;br /&gt;
    dobj = IIcon-&amp;gt;GetDefDiskObject(WBDISK);&lt;br /&gt;
    if(dobj != 0)&lt;br /&gt;
      {&lt;br /&gt;
      /* The type must be set to NULL for a WBAPPICON */&lt;br /&gt;
      dobj-&amp;gt;do_Type = NULL;&lt;br /&gt;
&lt;br /&gt;
      myport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, NULL);&lt;br /&gt;
      if(myport)&lt;br /&gt;
        {&lt;br /&gt;
        /* Put the AppIcon up on the Workbench window */&lt;br /&gt;
        appicon = IWorkbench-&amp;gt;AddAppIconA(0L,0L,&amp;quot;TestAppIcon&amp;quot;,myport,NULL,dobj,NULL);&lt;br /&gt;
        if(appicon)&lt;br /&gt;
          {&lt;br /&gt;
          /* For the sake of this example, we allow the AppIcon */&lt;br /&gt;
          /* to be activated only five times.                   */&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Drop files on the Workbench AppIcon\n&amp;quot;);&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Example exits after 5 drops\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          while(dropcount&amp;lt;5)&lt;br /&gt;
            {&lt;br /&gt;
            /* Here&#039;s the main event loop where we wait for */&lt;br /&gt;
            /* messages to show up from the AppIcon         */&lt;br /&gt;
            IExec-&amp;gt;WaitPort(myport);&lt;br /&gt;
&lt;br /&gt;
            /* Might be more than one message at the port... */&lt;br /&gt;
            while(appmsg=(struct AppMessage *)IExec-&amp;gt;GetMsg(myport))&lt;br /&gt;
              {&lt;br /&gt;
              if(appmsg-&amp;gt;am_NumArgs==0L)&lt;br /&gt;
                {&lt;br /&gt;
                /* If NumArgs is 0 the AppIcon was activated directly */&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;User activated the AppIcon.\n&amp;quot;);&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;A Help window for the user would be good here\n&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
              else if(appmsg-&amp;gt;am_NumArgs&amp;gt;0L)&lt;br /&gt;
                {&lt;br /&gt;
                /* If NumArgs is &amp;gt;0 the AppIcon was activated by */&lt;br /&gt;
                /* having one or more icons dropped on top of it */&lt;br /&gt;
                IDOS-&amp;gt;Printf(&amp;quot;User dropped %ld icons on the AppIcon\n&amp;quot;,&lt;br /&gt;
                                              appmsg-&amp;gt;am_NumArgs);&lt;br /&gt;
                for(x=0;x&amp;lt;appmsg-&amp;gt;am_NumArgs;x++)&lt;br /&gt;
                  {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;#%ld name=&#039;%s&#039;\n&amp;quot;,x+1,appmsg-&amp;gt;am_ArgList[x].wa_Name);&lt;br /&gt;
                  }&lt;br /&gt;
                }&lt;br /&gt;
              /* Let Workbench know we&#039;re done with the message */&lt;br /&gt;
              IExec-&amp;gt;ReplyMsg((struct Message *)appmsg);&lt;br /&gt;
              }&lt;br /&gt;
            dropcount++;&lt;br /&gt;
            }&lt;br /&gt;
          success = IWorkbench-&amp;gt;RemoveAppIcon(appicon);&lt;br /&gt;
          }&lt;br /&gt;
        /* Clear away any messages that arrived at the last moment */&lt;br /&gt;
        while(appmsg = (struct AppMessage *)IExec-&amp;gt;GetMsg(myport))&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)appmsg);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, myport);&lt;br /&gt;
        }&lt;br /&gt;
      IIcon-&amp;gt;FreeDiskObject(dobj);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IWorkbench);&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IIcon);&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(WorkbenchBase);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(IconBase);&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;
=== An AppMenuItem Example ===&lt;br /&gt;
&lt;br /&gt;
This example shows how to create an AppMenuItem. The example adds a menu item named &amp;quot;Browse Files&amp;quot; to the Workbench Tools menu. (All AppMenuItems appear in the Workbench Tools menu.) When the menu item is activated, the example program receives a message from Workbench and then attempts to start up an instance of the More program. (The More program is in the Utilities directory of the Workbench volume.)&lt;br /&gt;
&lt;br /&gt;
The example starts up the More program as a separate, asynchronous process using the SystemTags() function. When the AppMenuItem has been activated five times, the program exits after freeing any system resources it has used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* appmenuitem.c&lt;br /&gt;
/* Works from the Shell (CLI) only */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;          /* Need this for the Amiga variable types  */&lt;br /&gt;
#include &amp;lt;workbench/workbench.h&amp;gt; /* This has DiskObject and AppIcon structs */&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;   /* This has WBStartup and WBArg structs    */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dostags.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;          /* Exec message, port and library functions*/&lt;br /&gt;
#include &amp;lt;proto/workbench.h&amp;gt;     /* AppMenuItem function protos             */&lt;br /&gt;
&lt;br /&gt;
struct Library *WorkbenchBase;&lt;br /&gt;
struct WorkbenchIFace *IWorkbench;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct MsgPort      *myport=NULL;&lt;br /&gt;
  struct AppMenuItem *appitem=NULL;&lt;br /&gt;
  struct AppMessage   *appmsg=NULL;&lt;br /&gt;
  LONG result, x, count=0L;&lt;br /&gt;
  BOOL success=0L;&lt;br /&gt;
  BPTR file;&lt;br /&gt;
&lt;br /&gt;
  WorkbenchBase = IExec-&amp;gt;OpenLibrary(&amp;quot;workbench.library&amp;quot;, 50);&lt;br /&gt;
  IWorkbench = (struct WorkbenchIFace*)IExec-&amp;gt;GetInterface(WorkbenchBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  &lt;br /&gt;
  if (IWorkbench != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if(myport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, NULL))&lt;br /&gt;
    {&lt;br /&gt;
    /* Add our own AppMenuItem to the Workbench Tools Menu */&lt;br /&gt;
    appitem = IWorkbench-&amp;gt;AddAppMenuItemA(0L,                   /* Our ID# for item */&lt;br /&gt;
                    (ULONG)&amp;quot;SYS:Utilities/More&amp;quot;,  /* Our UserData     */&lt;br /&gt;
                           &amp;quot;Browse Files&amp;quot;,        /* MenuItem Text    */&lt;br /&gt;
                            myport,NULL);         /* MsgPort, no tags */&lt;br /&gt;
    if(appitem)&lt;br /&gt;
      {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;Select Workbench Tools demo menuitem &#039;Browse Files&#039;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      /* For this example, we allow the AppMenuItem to be selected */&lt;br /&gt;
      /* only once, then we remove it and exit                     */&lt;br /&gt;
      IExec-&amp;gt;WaitPort(myport);&lt;br /&gt;
      while((appmsg = (struct AppMessage *)IExec-&amp;gt;GetMsg(myport)) &amp;amp;&amp;amp; (count&amp;lt;1))&lt;br /&gt;
        {&lt;br /&gt;
        /* Handle messages from the AppMenuItem - we have only one  */&lt;br /&gt;
        /* item so we don&#039;t have to check its appmsg-&amp;gt;am_ID number. */&lt;br /&gt;
        /* We&#039;ll System() the command string that we passed as      */&lt;br /&gt;
        /* userdata when we added the menu item.                    */&lt;br /&gt;
        /* We find our userdata pointer in appmsg-&amp;gt;am_UserData      */&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;User picked AppMenuItem with %ld icons selected\n&amp;quot;,&lt;br /&gt;
                                                appmsg-&amp;gt;am_NumArgs);&lt;br /&gt;
        for(x=0;x&amp;lt;appmsg-&amp;gt;am_NumArgs;x++)&lt;br /&gt;
           IDOS-&amp;gt;Printf(&amp;quot;  #%ld name=&#039;%s&#039;\n&amp;quot;,x+1,appmsg-&amp;gt;am_ArgList[x].wa_Name);&lt;br /&gt;
&lt;br /&gt;
        count++;&lt;br /&gt;
        if( file=Open(&amp;quot;CON:0/40/640/150/AppMenu Example/auto/close/wait&amp;quot;,&lt;br /&gt;
                         MODE_OLDFILE)  )     /* for any stdio output */&lt;br /&gt;
          {&lt;br /&gt;
          result=IDOS-&amp;gt;SystemTags((UBYTE *)appmsg-&amp;gt;am_UserData,SYS_Input,file,&lt;br /&gt;
                                                         SYS_Output,NULL,&lt;br /&gt;
                                                         SYS_Asynch,TRUE,&lt;br /&gt;
                                                         TAG_END);&lt;br /&gt;
          /* If Asynch System() itself fails, we must close file */&lt;br /&gt;
          if(result == -1) IDOS-&amp;gt;Close(file);&lt;br /&gt;
          }&lt;br /&gt;
        IExec-&amp;gt;ReplyMsg((struct Message *)appmsg);&lt;br /&gt;
        }&lt;br /&gt;
      success = IWorkbench-&amp;gt;RemoveAppMenuItem(appitem);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    /* Clear away any messages that arrived at the last moment */&lt;br /&gt;
    /* and let Workbench know we&#039;re done with the messages     */&lt;br /&gt;
    while(appmsg=(struct AppMessage *)IExec-&amp;gt;GetMsg(myport))&lt;br /&gt;
      {&lt;br /&gt;
      IExec-&amp;gt;ReplyMsg((struct Message *)appmsg);&lt;br /&gt;
      }&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, myport);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  IExec-&amp;gt;DropInterface((struct Interface*)IWorkbench);&lt;br /&gt;
  IExec-&amp;gt;CloseLibrary(WorkbenchBase);&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;
=== An AppWindow Example ===&lt;br /&gt;
&lt;br /&gt;
This example shows how to create an AppWindow and obtain arguments from Workbench when the user drops an icon into it. The AppWindow will appear on the Workbench screen with the name &amp;quot;AppWindow&amp;quot; and will run until the window&#039;s close gadget is selected. If any icons are dropped into the AppWindow, the program prints their arguments in the Shell window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* appwindow.c */&lt;br /&gt;
/* Works from the Shell (CLI) only */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;          /* Need this for the Amiga variable types  */&lt;br /&gt;
#include &amp;lt;workbench/workbench.h&amp;gt; /* This has DiskObject and AppWindow       */&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;   /* This has WBStartup and WBArg structs    */&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;      /* Need this to check library versions     */&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/workbench.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library        *IntuitionBase;&lt;br /&gt;
struct Library        *WorkbenchBase;&lt;br /&gt;
struct IntuitionIFace *IIntuition;&lt;br /&gt;
struct WorkbenchIFace *IWorkbench;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct MsgPort *awport;&lt;br /&gt;
  struct Window  *win;&lt;br /&gt;
  struct AppWindow *appwin;&lt;br /&gt;
  struct IntuiMessage *imsg;&lt;br /&gt;
  struct AppMessage *amsg;&lt;br /&gt;
  struct WBArg   *argptr;&lt;br /&gt;
&lt;br /&gt;
  ULONG           winsig, appwinsig, signals, id = 1, userdata = 0;&lt;br /&gt;
  BOOL            done = FALSE;&lt;br /&gt;
  int             i;&lt;br /&gt;
&lt;br /&gt;
  IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
  WorkbenchBase = IExec-&amp;gt;OpenLibrary(&amp;quot;workbench.library&amp;quot;, 50);&lt;br /&gt;
  &lt;br /&gt;
  IIntuition = (struct IntuitionIFace*)IExec-&amp;gt;GetInterface(IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
  IWorkbench = (struct WorkbenchIFace*)IExec-&amp;gt;GetInterface(WorkbenchBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
  if (IIntuition != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    if (IWorkbench != NULL)&lt;br /&gt;
    {&lt;br /&gt;
      if (awport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, NULL))&lt;br /&gt;
         {&lt;br /&gt;
         if (win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                                  WA_Width, 200,        WA_Height, 50,&lt;br /&gt;
                                  WA_IDCMP, CLOSEWINDOW,&lt;br /&gt;
                                  WA_Flags, WINDOWCLOSE | WINDOWDRAG,&lt;br /&gt;
                                  WA_Title, &amp;quot;AppWindow&amp;quot;,&lt;br /&gt;
                                  TAG_END))&lt;br /&gt;
          {&lt;br /&gt;
          if (appwin = IWorkbench-&amp;gt;AddAppWindow(id, userdata, win, awport, NULL))&lt;br /&gt;
            {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;AppWindow added... Drag files into AppWindow\n&amp;quot;);&lt;br /&gt;
            winsig    = 1L &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit;&lt;br /&gt;
            appwinsig = 1L &amp;lt;&amp;lt; awport-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
            while (! done)&lt;br /&gt;
              {&lt;br /&gt;
              /* Wait for IDCMP messages and AppMessages */&lt;br /&gt;
              signals = IExec-&amp;gt;Wait( winsig | appwinsig );&lt;br /&gt;
&lt;br /&gt;
              if(signals &amp;amp; winsig)      /* Got an IDCMP message */&lt;br /&gt;
                {&lt;br /&gt;
                while (imsg = (struct IntuiMessage *) IExec-&amp;gt;GetMsg(win-&amp;gt;UserPort))&lt;br /&gt;
                  {&lt;br /&gt;
                  if (imsg-&amp;gt;Class = CLOSEWINDOW)   done = TRUE;&lt;br /&gt;
                  IExec-&amp;gt;ReplyMsg((struct Message *) imsg);&lt;br /&gt;
                  }&lt;br /&gt;
                }&lt;br /&gt;
              if(signals &amp;amp; appwinsig)   /* Got an AppMessage */&lt;br /&gt;
                {&lt;br /&gt;
                while (amsg = (struct AppMessage *) IExec-&amp;gt;GetMsg(awport))&lt;br /&gt;
                  {&lt;br /&gt;
                  IDOS-&amp;gt;Printf(&amp;quot;AppMsg: Type=%ld, ID=%ld, NumArgs=%ld\n&amp;quot;,&lt;br /&gt;
                           amsg-&amp;gt;am_Type, amsg-&amp;gt;am_ID, amsg-&amp;gt;am_NumArgs);&lt;br /&gt;
                  argptr = amsg-&amp;gt;am_ArgList;&lt;br /&gt;
                  for (i = 0; i &amp;lt; amsg-&amp;gt;am_NumArgs; i++)&lt;br /&gt;
                    {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;   arg(%ld): Name=&#039;%s&#039;, Lock=%lx\n&amp;quot;,&lt;br /&gt;
                             i, argptr-&amp;gt;wa_Name, argptr-&amp;gt;wa_Lock);&lt;br /&gt;
                    argptr++;&lt;br /&gt;
                    }&lt;br /&gt;
                  IExec-&amp;gt;ReplyMsg((struct Message *) amsg);&lt;br /&gt;
                  }&lt;br /&gt;
                }&lt;br /&gt;
              }     /* done */&lt;br /&gt;
            IWorkbench-&amp;gt;RemoveAppWindow(appwin);&lt;br /&gt;
            }&lt;br /&gt;
          IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
          }&lt;br /&gt;
        /* Make sure there are no more outstanding messages */&lt;br /&gt;
        while(amsg = (struct AppMessage *)IExec-&amp;gt;GetMsg(awport))&lt;br /&gt;
              IExec-&amp;gt;ReplyMsg((struct Message *)amsg);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, awport);&lt;br /&gt;
        }&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;DropInterface((struct Interface*)IWorkbench);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(WorkbenchBase);&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;
== Workbench and the Startup Code Module ==&lt;br /&gt;
&lt;br /&gt;
Standard startup code handles the detail work of interfacing with the arguments and environment of Workbench and the Shell (or CLI). This section describes the behavior of standard startup modules such as the ones supplied with newlib and clib2.&lt;br /&gt;
&lt;br /&gt;
The environment for a program started from Workbench is quite different from the environment for a program started from the Shell. The Shell does not create a new process for a program; it jumps to the program&#039;s code and the program shares the process with the Shell. Programs run under the Shell have access to all the Shell&#039;s environment, including the ability to modify that environment. (Programs run from the Shell should be careful to restore all values that existed on startup.) Workbench starts a program as a new DOS process, explicitly passing the execution environment to the program.&lt;br /&gt;
&lt;br /&gt;
=== Workbench Startup ===&lt;br /&gt;
&lt;br /&gt;
When the user activates a project or tool icon, the program is run as a separate process asynchronous to Workbench. This allows the user to take full advantage of the multitasking features of the Amiga. A process is simply a task with additional information needed to use DOS library.&lt;br /&gt;
&lt;br /&gt;
When Workbench loads and starts a program, it sends the program a WBStartup message containing the arguments as described earlier. The WBStartup also contains a pointer to the new Process structure which describes the execution environment of the program. The WBStartup message is posted to the message port of the program&#039;s Process structure.&lt;br /&gt;
&lt;br /&gt;
The Process message port is for the exclusive use of DOS, so this message must be removed from the port before using any DOS library functions. Normally this is handled by the startup code module that comes with your compiler so you don&#039;t have to worry about this unless you are writing your own startup code.&lt;br /&gt;
&lt;br /&gt;
Standard startup code modules also set up SysBase, the pointer to the Exec master library, and open the DOS library setting up DOSBase. That is why Exec and AmigaDOS functions can be called by C applications without first opening a library; the startup code that applications are linked with handles this. Some special startups may also set up NIL: input and output streams, or may open a &#039;&#039;stdio&#039;&#039; window so that the Workbench applications can use stdio functions such as printf().&lt;br /&gt;
&lt;br /&gt;
The startup code can tell if it is running in the Workbench environment because the pr_CLI field of the Process structure will contain NULL. In that case the startup code removes the WBStartup message from the Process message port with GetMsg() before using any functions in the DOS library.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Do Not Use the Process Message Port for Anything Else|text=The message port in a Process structure is for the exclusive use of the DOS library.}}&lt;br /&gt;
&lt;br /&gt;
Standard startup code will pass the WBStartup message pointer in argv and 0 (zero) in argc if the program is started from Workbench. The startup code calls the application code that it is linked with as a function. When the application code exits back to the startup code, the startup code closes and frees all opens and allocations it made. It will then Forbid(), and ReplyMsg() the WBStartup message, notifying Workbench that the application Process may be terminated and its code unloaded from memory.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Avoid the DOS Exit() function|text=The DOS Exit() function does &#039;&#039;not&#039;&#039; return an application to the startup code that called it. If you wish to exit your application, use the exit function provided by your startup code (usually lower-case exit(), or _exit for assembler), passing it a valid DOS return code as listed in the include file &amp;amp;lt;libraries/dos.h&amp;amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
=== Shell Startup ===&lt;br /&gt;
&lt;br /&gt;
When a program is started from the Shell (or a Shell script), standard startup modules will parse the command line into an array of pointers to individual argument strings placing them in argv, and an argument count in argc.&lt;br /&gt;
&lt;br /&gt;
If a program is started from the Shell, argc will always equal at least one and the first element in argv will always be a pointer to the command name. Other command line arguments are stored in turn. For example, if the command line was:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
df0:myprogram &amp;quot;my file1&amp;quot; file2 ;this is a comment&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then argc will be 3, argv[0] will be &amp;quot;df0:myprogram&amp;quot;, argv[1] will be &amp;quot;my file1&amp;quot;, and argv[2] will be &amp;quot;file2&amp;quot;. Correct startup code will strip spaces between arguments and trailing spaces from the last argument and will also properly deal with quoted arguments with embedded spaces.&lt;br /&gt;
&lt;br /&gt;
As with Workbench, standard startup code for the Shell sets up SysBase, the pointer to the Exec master library, and opens the DOS library setting up DOSBase. C applications that are linked with standard startup code can call an Exec or AmigaDOS functions without opening the library first.&lt;br /&gt;
&lt;br /&gt;
The startup code also fills in the &#039;&#039;stdio&#039;&#039; file handles (_stdin, _stdout, etc.) for the application. Finally argv and argc, are pushed onto the stack and the application is called. When the application returns or exits back to the startup code, the startup code closes and frees all opens and allocations it has made for the application, and then returns to the system with the whatever value the program exited with.&lt;br /&gt;
&lt;br /&gt;
Link your applications only with standard, tested startup code of some type such as the module supplied with your compiler. Startup code provides your programs with correct, consistent handling of Shell command line and Workbench arguments and will perform some initializations and cleanups which would otherwise need to be handled by your own code. Very small startups can be used for programs that do not require command line arguments.&lt;br /&gt;
&lt;br /&gt;
A few words of warning for those of you who do not use standard startup code:&lt;br /&gt;
&lt;br /&gt;
* If you are started as a Workbench process, you &#039;&#039;must&#039;&#039; GetMsg() the WBStartup message before using any functions in the DOS library.&lt;br /&gt;
* You &#039;&#039;must&#039;&#039; turn off task switching (with Forbid()) before replying the WBStartup message from Workbench. This will prevent Workbench from unloading your code before you can exit properly.&lt;br /&gt;
* If you do your own command line parsing, you &#039;&#039;must&#039;&#039; provide the user with consistent and correct handling of command line arguments.&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the functions in workbench.library. See SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Workbench Library Functions&lt;br /&gt;
|-&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AddAppIcon()&lt;br /&gt;
| Add an AppIcon to Workbench&lt;br /&gt;
|-&lt;br /&gt;
| AddAppMenuItem()&lt;br /&gt;
| Add an AppMenuItem to the Workbench Tools menu&lt;br /&gt;
|-&lt;br /&gt;
| AddAppWindow()&lt;br /&gt;
| Add an AppWindow to Workbench&lt;br /&gt;
|-&lt;br /&gt;
| RemoveAppIcon()&lt;br /&gt;
| Remove an AppIcon to Workbench&lt;br /&gt;
|-&lt;br /&gt;
| RemoveAppMenuItem()&lt;br /&gt;
| Remove an AppMenuItem to the Workbench Tools menu&lt;br /&gt;
|-&lt;br /&gt;
| RemoveAppWindow()&lt;br /&gt;
| Remove an AppWindow to Workbench&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&amp;diff=12389</id>
		<title>How to open and use the exec debug interface</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=How_to_open_and_use_the_exec_debug_interface&amp;diff=12389"/>
		<updated>2024-01-29T13:20:11Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Added missing backslash&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Debug]]&lt;br /&gt;
== Author ==&lt;br /&gt;
&lt;br /&gt;
Alfkil Wennermark&amp;lt;br/&amp;gt;&lt;br /&gt;
Copyright (c) 2010 Alfkil Wennermark&amp;lt;br/&amp;gt;&lt;br /&gt;
Used by permission.&lt;br /&gt;
&lt;br /&gt;
== Tutorial ==&lt;br /&gt;
&lt;br /&gt;
Next step in my small series concerns itself with how to open and use the &amp;quot;secret&amp;quot; (but very useful) debug interface. Thanks to Steven Solie and Thomas Frieden.&lt;br /&gt;
&lt;br /&gt;
Probably the best way to trap exceptions from within your code is to use the debug interface, that is &amp;quot;hidden&amp;quot; inside exec.library. The main problem with this interface is, that it is not very well documented. My main source of documentation on this issue is the amigaos-nat.c file from Thomas Friedens [[GDB_for_Beginners|GDB]] sources. These can be found inside [https://sourceforge.net/projects/adtools/ the adtools project on sourceforge.net].&lt;br /&gt;
&lt;br /&gt;
The following code just plainly opens the interface, attaches a debug hook to itself, causes an exception and tells you what has happened. Normally you wouldn&#039;t attach the hook to your own process. Rather you would open whatever code you want to debug with fx. LoadSeg(), run it with CreateNewProc() (or some other way) and attach the debug hook to it. To keep things simple, though, this code just attaches the hook to itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* debugtrap.cExample of use of the exec debug interface&lt;br /&gt;
by Alfkil Wennermark 2010&lt;br /&gt;
&lt;br /&gt;
Thanks to Steven Solie, Thomas Frieden and others&lt;br /&gt;
&lt;br /&gt;
This code is partially copied from Thomas&#039; GDB source&lt;br /&gt;
*/&lt;br /&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;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/interrupts.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/tasks.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
struct DebugIFace *IDebug = 0;&lt;br /&gt;
&lt;br /&gt;
struct KernelDebugMessage&lt;br /&gt;
{&lt;br /&gt;
  uint32 type;&lt;br /&gt;
  union&lt;br /&gt;
  {&lt;br /&gt;
    struct ExceptionContext *context;&lt;br /&gt;
    struct Library *library;&lt;br /&gt;
  } message;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static ULONG amigaos_debug_callback(struct Hook *, struct Task *, struct KernelDebugMessage *);&lt;br /&gt;
&lt;br /&gt;
struct Hook debug_hook;&lt;br /&gt;
struct Task *amiga_task;&lt;br /&gt;
&lt;br /&gt;
BPTR exec_seglist;&lt;br /&gt;
ULONG debug_data = 1234;&lt;br /&gt;
&lt;br /&gt;
void init()&lt;br /&gt;
{&lt;br /&gt;
  IDebug = (struct DebugIFace *)IExec-&amp;gt;GetInterface((struct Library *)SysBase, &amp;quot;debug&amp;quot;, 1, 0);&lt;br /&gt;
  if (!IDebug)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Can&#039;t get DEBUG access\n&amp;quot;);&lt;br /&gt;
    exit(RETURN_FAIL);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  debug_hook.h_Entry = (ULONG (*)())amigaos_debug_callback;&lt;br /&gt;
  debug_hook.h_Data =(APTR)&amp;amp;debug_data;&lt;br /&gt;
&lt;br /&gt;
  /* NB: Ideally we would start up another task, that&lt;br /&gt;
  we want to debug, and attach ourselves to that&lt;br /&gt;
  task using the debug hook, but for simplicity&lt;br /&gt;
  we just use our own task here */&lt;br /&gt;
  amiga_task = IExec-&amp;gt;FindTask(NULL);&lt;br /&gt;
  IDebug-&amp;gt;AddDebugHook(amiga_task, &amp;amp;debug_hook);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void end()&lt;br /&gt;
{&lt;br /&gt;
  IDebug-&amp;gt;AddDebugHook(amiga_task, 0);&lt;br /&gt;
&lt;br /&gt;
  if (IDebug)IExec-&amp;gt;DropInterface((struct Interface *)IDebug);&lt;br /&gt;
  IDebug = NULL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ULONG&lt;br /&gt;
amigaos_debug_callback(struct Hook *hook, struct Task *currentTask,&lt;br /&gt;
struct KernelDebugMessage *dbgmsg)&lt;br /&gt;
{&lt;br /&gt;
  struct ExecIFace *IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)-&amp;gt;MainInterface;&lt;br /&gt;
&lt;br /&gt;
  uint32 *data = (uint32 *)hook-&amp;gt;h_Data;&lt;br /&gt;
&lt;br /&gt;
  /* these are the 4 types of debug msgs: */&lt;br /&gt;
  switch (dbgmsg-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case DBHMT_REMTASK:&lt;br /&gt;
    *data = 9;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_EXCEPTION:&lt;br /&gt;
    *data = 11;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_OPENLIB:&lt;br /&gt;
    *data = 13;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    case DBHMT_CLOSELIB:&lt;br /&gt;
    *data = 15;&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
    *data = 0;&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  /* returning 1 will suspend the task ! */&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  init();&lt;br /&gt;
&lt;br /&gt;
  /* Cause an exception on purpose: */&lt;br /&gt;
  uint32 *beef = 0;&lt;br /&gt;
  *beef = 0L;&lt;br /&gt;
&lt;br /&gt;
  printf(&amp;quot;We received a&amp;quot;);&lt;br /&gt;
  switch (debug_data)&lt;br /&gt;
  {&lt;br /&gt;
  case 9:&lt;br /&gt;
    printf(&amp;quot; REMTASK&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 11:&lt;br /&gt;
    printf(&amp;quot;n EXCEPTION&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 13:&lt;br /&gt;
    printf(&amp;quot;n OPENLIB&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
  case 15:&lt;br /&gt;
    printf(&amp;quot; CLOSELIB&amp;quot;);&lt;br /&gt;
    break;&lt;br /&gt;
&lt;br /&gt;
  default:&lt;br /&gt;
    printf(&amp;quot;n unknown&amp;quot;);&lt;br /&gt;
  break;&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot; signal!n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  end();&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>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12367</id>
		<title>Exec Signals</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12367"/>
		<updated>2023-09-13T07:15:54Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* SIGBREAKB_CTRL_E */ Typo fixes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Signals ==&lt;br /&gt;
&lt;br /&gt;
[[Exec Tasks|Tasks]] often need to coordinate with other concurrent system activities (like other [[Exec Tasks|tasks]] and [[Exec Interrupts|interrupts]]). This coordination is handled by Exec through the synchronized exchange of specific event indicators called &#039;&#039;signals&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is the primary mechanism responsible for all inter-task communication and synchronization on the Amiga. This signal mechanism operates at a low level and is designed for high performance. Signals are used extensively by the Exec message system as a way to indicate the arrival of an inter-task message. The message system is described in more detail in [[Exec Messages and Ports]].&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Not for Beginners.|text=This section concentrates on details about signals that most applications do not need to understand for general Amiga programming. For a general overview of signals, see [[Introduction to Exec]].}}&lt;br /&gt;
&lt;br /&gt;
== The Signal System ==&lt;br /&gt;
&lt;br /&gt;
The signal system is designed to support independent simultaneous events, so several signals can occur at the same time. Each task has 32 independent signals, 16 of which are pre-allocated for use by the operating system. The signals in use by a particular task are represented as bits in a 32-bit field in its Task structure (&amp;amp;lt;exec/tasks.h&amp;amp;gt;). Two other 32-bit fields in the Task structure indicate which signals the task is waiting for, and which signals have been received.&lt;br /&gt;
&lt;br /&gt;
Signals are &#039;&#039;task relative&#039;&#039;. A task can only allocate its own signals, and may only wait on its own signals. In addition, a task may assign its own significance to a particular signal. Signals are not broadcast to all tasks; they are directed only to individual tasks. A signal has meaning to the task that defined it and to those tasks that have been informed of its meaning.&lt;br /&gt;
&lt;br /&gt;
For example, signal bit 12 may indicate a timeout event to one task, but to another task it may indicate a message arrival event. You can never wait on a signal that you did not directly or indirectly allocate yourself, and any other task that wishes to signal you must use a signal that &#039;&#039;you&#039;&#039; allocated.&lt;br /&gt;
&lt;br /&gt;
=== Signal Allocation ===&lt;br /&gt;
&lt;br /&gt;
As mentioned above, a task assigns its own meaning to a particular signal. Because certain system libraries may occasionally require the use of a signal, there is a convention for signal allocation. It is unwise ever to make assumptions about which signals are actually in use.&lt;br /&gt;
&lt;br /&gt;
Before a signal can be used, it must be allocated with the AllocSignal() function. When a signal is no longer needed, it should be freed for reuse with FreeSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BYTE AllocSignal( LONG signalNum );&lt;br /&gt;
VOID FreeSignal( LONG signalNum );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AllocSignal() marks a signal as being in use and prevents the accidental use of the same signal for more than one event. You may ask for either a specific signal number, or more commonly, you would pass -1 to request the next available signal. The state of the newly allocated signal is cleared (ready for use). Generally it is best to let the system assign you the next free signal. Of the 32 available signals, the lower 16 are reserved for system use (see [[#Reserved_System_Signals|Reserved System Signals]]). This leaves the upper 16 signals free for application programs to allocate. Other subsystems that you may call depend on AllocSignal().&lt;br /&gt;
&lt;br /&gt;
The following example asks for the next free signal to be allocated for its use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if (-1 == (signal = IExec-&amp;gt;AllocSignal(-1)))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;no signal bits available\n&amp;quot;);&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;allocated signal number %ld\n&amp;quot;, signal);&lt;br /&gt;
    /* Other code could go here */&lt;br /&gt;
    IExec-&amp;gt;FreeSignal(signal)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The value returned by AllocSignal() is a signal bit number. This value cannot be used directly in calls to signal-related functions without first being converted to a mask:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 mask = 1UL &amp;lt;&amp;lt; signal;&lt;br /&gt;
 or&lt;br /&gt;
uint32 mask = (uint32)1 &amp;lt;&amp;lt; signal;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is important to realize that signal bit allocation is relevant &#039;&#039;only&#039;&#039; to the running task. You &#039;&#039;cannot&#039;&#039; allocate a signal from another task. Note that functions which create a signal MsgPort will allocate a signal from the task that calls the function. Such functions include OpenWindow() and AllocSysObject(). For this reason, only the creating task may Wait() (directly or indirectly) on the MsgPort&#039;s signal. Functions which call Wait() include DoIO(), WaitIO() and WaitPort().&lt;br /&gt;
&lt;br /&gt;
=== Waiting for a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals are most often used to wake up a task upon the occurrence of some external event. Applications call the Exec Wait() function, directly or indirectly, in order to enter a wait state until some external event triggers a signal which awakens the task.&lt;br /&gt;
&lt;br /&gt;
Though signals are usually not used to interrupt an executing task, they can be used this way. Task &#039;&#039;exceptions&#039;&#039;, described in [[Exec_Interrupts|Exec Interrupts]], allow signals to act as a task-local interrupt.&lt;br /&gt;
&lt;br /&gt;
The Wait() function specifies the set of signals that will wake up the task and then puts the task to sleep (into the waiting state).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG Wait( ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any one signal or any combination of signals from this set are sufficient to awaken the task. Wait() returns a mask indicating which signals satisfied the Wait() call. Note that when signals are used in conjunction with a message port, a set signal bit does not necessarily mean that there is a message at the message port.&lt;br /&gt;
&lt;br /&gt;
See [[Exec_Messages_and_Ports|Exec Messages and Ports]] for details about proper handling of messages.&lt;br /&gt;
&lt;br /&gt;
Because tasks (and interrupts) normally execute asynchronously, it is often possible to receive a particular signal before a task actually Wait()s for it. In such cases the Wait() will be immediately satisfied, and the task will not be put to sleep.&lt;br /&gt;
&lt;br /&gt;
The Wait() function implicitly clears those signal bits that satisfied the wait condition. This effectively resets those signals for reuse. However, keep in mind that a task might get more signals while it is still processing the previous signal. If the same signal is received multiple times and the signal bit is not cleared between them, some signals will go unnoticed.&lt;br /&gt;
&lt;br /&gt;
Be aware that using Wait() will break a Forbid() or Disable() state. Wait() cannot be used in supervisor mode or within interrupts.&lt;br /&gt;
&lt;br /&gt;
A task may Wait() for a combination of signal bits and will wake up when any of the signals occur. Wait() returns a signal mask specifying which signal or signals were received. Usually the program must check the returned mask for each signal it was waiting on and take the appropriate action for each that occurred. The order in which these bits are checked is often important.&lt;br /&gt;
&lt;br /&gt;
Here is a hypothetical example of a process that is using the console and timer devices, and is waiting for a message from either device and a possible break character issued by the user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 consoleSignal = 1L &amp;lt;&amp;lt; ConsolePort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 timerSignal   = 1L &amp;lt;&amp;lt; TimerPort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 userSignal    = SIGBREAKF_CTRL_C;    /* Defined in &amp;lt;dos/dos.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
uint32 signals = IExec-&amp;gt;Wait(consoleSignal | timerSignal | userSignal);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; consoleSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;new character\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; timeOutSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;timeout\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; userSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;User Ctrl-C Abort\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will put the task to sleep waiting for a new character, or the expiration of a time period, or a &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt; break character issued by the user. Notice that this code checks for an incoming character signal before checking for a timeout. Although a program can check for the occurrence of a particular event by checking whether its signal has occurred, this may lead to busy wait polling. Such polling is wasteful of the processor and is usually harmful to the proper function of the Amiga system. However, if a program needs to do constant processing and also check signals (a compiler for example) SetSignal(0,0) can be used to get a copy of your task&#039;s current signals.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG SetSignal( ULONG newSignals, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetSignal() can also be used to set or clear the state of the signals. Implementing this can be dangerous and should generally not be done. The following fragment illustrates a possible use of SetSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 signals = SetSignal(0,0);           /* Get current state of signals */&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; SIGBREAKF_CTRL_C)            /* Check for Ctrl-C.           */&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Break\n&amp;quot;);               /* Ctrl-C signal has been set. */&lt;br /&gt;
    IExec-&amp;gt;SetSignal(0, SIGBREAKF_CTRL_C)  /* Clear Ctrl-C signal.        */&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Looking for Break Keys ====&lt;br /&gt;
&lt;br /&gt;
One common usage of signals on the Amiga is for processing a user break. The OS reserves 16 of a tasks 32 signals for system use. Four of those 16 signals are used to tell a task about the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; break keys. An application can process these signals. Usually, only CLI-based programs receive these signals because the Amiga&#039;s console handler is about the only user input source that sets these signals when it sees the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; key presses.&lt;br /&gt;
&lt;br /&gt;
The signal masks for each of these key presses are defined in &amp;amp;lt;dos/dos.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
SIGBREAKF_CTRL_C&lt;br /&gt;
SIGBREAKF_CTRL_D&lt;br /&gt;
SIGBREAKF_CTRL_E&lt;br /&gt;
SIGBREAKF_CTRL_F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that these are &#039;&#039;bit masks&#039;&#039; and &#039;&#039;&#039;not&#039;&#039;&#039; &#039;&#039;bit numbers&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Generating a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals may be generated from both tasks and system interrupts with the Signal() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID Signal( struct Task *task, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example Signal(tc,mask) would signal the task with the specified mask signals. More than one signal can be specified in the mask. The following example code illustrates Wait() and Signal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// signals.c&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;
&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;
static CONST_STRPTR VersTag = &amp;quot;$VER: signals 53.1 (20.6.2012)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void);    /* prototype for our subtask routine */&lt;br /&gt;
&lt;br /&gt;
struct Task *maintask = NULL;&lt;br /&gt;
uint32 mainsig = 0;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  BOOL Done = FALSE;&lt;br /&gt;
  BOOL WaitingForSubtask = TRUE;&lt;br /&gt;
&lt;br /&gt;
  /* We must allocate any special signals we want to receive. */&lt;br /&gt;
  int8 mainsignum = IExec-&amp;gt;AllocSignal(-1);&lt;br /&gt;
  if (mainsignum == -1)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;No signals available\n&amp;quot;);&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    mainsig = 1U &amp;lt;&amp;lt; mainsignum;        /* subtask can access this global */&lt;br /&gt;
    maintask = IExec-&amp;gt;FindTask(NULL);  /* subtask can access this global */&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;We alloc a signal, create a task, wait for signals\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    struct Task *subtask = IDOS-&amp;gt;CreateTaskTags(&amp;quot;subtask&amp;quot;, 0, subtaskcode, 16000, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (subtask == NULL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t create subtask\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;After subtask signals, press CTRL-C or CTRL-D to exit\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      while (!Done || WaitingForSubtask)&lt;br /&gt;
      {&lt;br /&gt;
        /* Wait on the combined mask for all of the signals we are&lt;br /&gt;
         * interested in.  All processes have the CTRL_C thru CTRL_F&lt;br /&gt;
         * signals.  We&#039;re also Waiting on the mainsig we allocated&lt;br /&gt;
         * for our subtask to signal us with.  We could also Wait on&lt;br /&gt;
         * the signals of any ports/windows our main task created ... */&lt;br /&gt;
&lt;br /&gt;
        uint32 wakeupsigs = IExec-&amp;gt;Wait(mainsig | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);&lt;br /&gt;
&lt;br /&gt;
        /* Deal with all signals that woke us up - may be more than one */&lt;br /&gt;
        if (wakeupsigs &amp;amp; mainsig)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Signalled by subtask\n&amp;quot;);&lt;br /&gt;
          WaitingForSubtask = FALSE;   /* OK to kill subtask now */&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if (wakeupsigs &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-C signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(wakeupsigs &amp;amp; SIGBREAKF_CTRL_D)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-D signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    IExec-&amp;gt;FreeSignal(mainsignum);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void)&lt;br /&gt;
{&lt;br /&gt;
  IExec-&amp;gt;Signal(maintask, mainsig);&lt;br /&gt;
  IExec-&amp;gt;RemTask(0);  // Remove myself from the system. &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reserved System Signals ==&lt;br /&gt;
&lt;br /&gt;
There are 16 signal bits which are reserved for system use. Applications are never allowed to use these signal bits unless explicitly documented (e.g. SIGF_SINGLE).&lt;br /&gt;
&lt;br /&gt;
=== SIGB_ABORT ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_CHILD ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE (SIGB_BLIT) ===&lt;br /&gt;
&lt;br /&gt;
When a system function needs a Task to stop and IExec-&amp;gt;Wait() for a single signal it will use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
This signal used to be named SIGB_BLIT when it was used to wait on the classic hardware blitter.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_INTUITION ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_NET ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_DOS ===&lt;br /&gt;
&lt;br /&gt;
SIGB_DOS is currently used as the wait signal bit for the embedded message port in the process structure. This message port is initialised by the IDOS-&amp;gt;CreateNewProc() function. This message port is used by default for DosPacket transactions via IDOS-&amp;gt;DoPkt() and IDOS-&amp;gt;WaitPkt() and it is also used for sending the initial ACTION_STARTUP DosPacket for DOS handlers. This message port is also where the workbench.library sends the initial struct WBStartup message to every process it starts.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_C ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_C signal bit is used extensively as the general purpose &amp;quot;Break&amp;quot; signal. It is used by all shell handler commands&lt;br /&gt;
and many applications to invoke a normal exit of the program. It is up to all applications to follow this recommendation.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_C signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_D ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_D signal bit is used mostly by the shell handler to stop execution of a script file or non-interactive stream. It may also be used by applications.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_D signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_E ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_E signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signaling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_E signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_F ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_F signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signaling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_F signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
== Signalling with SIGB_SINGLE ==&lt;br /&gt;
&lt;br /&gt;
Many of a task&#039;s 32 signal bits are reserved for the operating system&#039;s private use, but, like any good rule, there is an exception. One of these bits, the SIGB_SINGLE bit, can be useful to some applications, if used correctly.&lt;br /&gt;
&lt;br /&gt;
Many system functions need to put their task to sleep while waiting for a single event, which requires using one of the task&#039;s signals. Rather than forcing each of these system functions to allocate a signal, then Wait(), then deallocate the signal, the operating system&lt;br /&gt;
has permanently allocated one signal, the SIGB_SINGLE, for this type of signalling. When a system function needs stop a task to Wait()&lt;br /&gt;
for a single signal, it can use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
The only purpose a program can use SIGB_SINGLE for is Wait()ing because the task cannot call any system functions while it is using SIGB_SINGLE. A program that calls system functions while using SIGB_SINGLE can cause itself and the operating system serious problems because the system functions can use SIGB_SINGLE as well. If a program calls a system function while using SIGB_SINGLE, two bad&lt;br /&gt;
things can happen:&lt;br /&gt;
&lt;br /&gt;
1) The errant task&#039;s event takes place before the system function waits on SIGB_SINGLE (or while the system function is waiting on&lt;br /&gt;
SIGB_SINGLE). In this case, the system function will think its event has taken place because its signal became set. The errant task will never find out that its event has taken place, as the system function will clear the SIGB_SINGLE bit after Wait()ing on it.&lt;br /&gt;
&lt;br /&gt;
2) The errant task&#039;s event and the system function&#039;s event take place while the system function is waiting on SIGB_SINGLE. In this case,&lt;br /&gt;
the system function will function normally, clear the SIGB_SINGLE bit, and exit. The errant task will never know that its event has&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
Before Wait()ing on SIGB_SINGLE, clear it using SetSignal():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;SetSignal(0, SIGF_SINGLE); // Note SIGF_SINGLE is the bit mask. SIGB_SINGLE is the signal bit.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This step is necessary because it is possible that the last system function that used the SIGB_SINGLE signal did not clear the SIGB_SINGLE bit.&lt;br /&gt;
&lt;br /&gt;
Also, an application should not wait on other signals while it is waiting on SIGB_SINGLE. Waiting on other signals at the same time&lt;br /&gt;
makes it possible for a program to wake up while the SIGB_SINGLE is still outstanding. If this happens, the program will still have to&lt;br /&gt;
go back to sleep, which requires calling a system function.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a simple example of using the SIGB_SINGLE signal. It starts a child process and waits for that child process to signal&lt;br /&gt;
the main process using the SIGB_SINGLE signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// This example program illustrates simple usage of the SIGB_SINGLE&lt;br /&gt;
// signal for &amp;quot;single shot&amp;quot; signalling. This signal is one of the&lt;br /&gt;
// system private signals, but applications can use it in certain&lt;br /&gt;
// cases, but only if used carefully. Specifically, applications&lt;br /&gt;
// should use it only to Wait() on, and using only that signal&lt;br /&gt;
// (applications cannot Wait() on other signals in the same&lt;br /&gt;
// Wait()). Not following these rules can cause serious system&lt;br /&gt;
// problems.&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/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dostags.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;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void);      /* prototype for our childprocess routine. */&lt;br /&gt;
&lt;br /&gt;
struct Process *mainprocess = NULL, *childprocess = NULL;&lt;br /&gt;
UBYTE childprocessname[] = &amp;quot;RKM_signal_childprocess&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
BPTR output;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (output = IDOS-&amp;gt;Open(&amp;quot;CONSOLE:&amp;quot;, MODE_OLDFILE))  /* Open the console for the  */&lt;br /&gt;
                                                        /*            child process. */&lt;br /&gt;
    {&lt;br /&gt;
        mainprocess = (struct Process *)IExec-&amp;gt;FindTask(NULL); /* childprocess can   */&lt;br /&gt;
                                                               /* access this global.*/&lt;br /&gt;
&lt;br /&gt;
        if (childprocess = IDOS-&amp;gt;CreateNewProcTags(&lt;br /&gt;
                    NP_Entry,       childprocesscode,  /* The child process  */&lt;br /&gt;
                    NP_Name,        childprocessname,&lt;br /&gt;
                    NP_Output,      output,&lt;br /&gt;
                    NP_FreeSeglist, FALSE,&lt;br /&gt;
                    NP_CloseOutput, TRUE,&lt;br /&gt;
                    NP_Child,       TRUE,&lt;br /&gt;
                    TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Created a child process and waiting on SIGB_SINGLE.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());  /* Make sure the Printf() above appears      */&lt;br /&gt;
                                           /* in the console window before the child    */&lt;br /&gt;
                                           /* process starts printing to the console. */&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;SetSignal(0, SIGF_SINGLE);  /* Use SIGF_SINGLE only after */&lt;br /&gt;
                                               /* clearing it.               */&lt;br /&gt;
&lt;br /&gt;
            /* Wake up the child. */&lt;br /&gt;
            IExec-&amp;gt;Signal((struct Task *)childprocess, SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;Wait(SIGF_SINGLE);  /* Only use SIGF_SINGLE for Wait()ing and */&lt;br /&gt;
                                       /* Wait on that signal alone!             */&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Received signal from child.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t create child process. Exiting.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t open CONSOLE:.  Exiting.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void)     /* This function is what CreateNewProcTags() */&lt;br /&gt;
{                               /* loads as the child process.  This child   */&lt;br /&gt;
                                /* signals the parent using SIGF_SINGLE.     */&lt;br /&gt;
&lt;br /&gt;
    /* Wait for a startup signal. This is to allow the parent process to&lt;br /&gt;
     * print its banner message and clear SIGF_SINGLE.&lt;br /&gt;
     */&lt;br /&gt;
    IExec-&amp;gt;Wait(SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: I&#039;m alive and starting a 5 second TimeDelay()&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    for (uint32 x = 0; x &amp;lt; 5; x++)&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;Delay(50);        /* Delay for 5 seconds, printing a */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot; .&amp;quot;);     /* dot during each second.         */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot; Finished.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: Signalling main process and exiting.  Bye.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Signal((struct Task *)mainprocess, SIGF_SINGLE);  /* Finished waiting, */&lt;br /&gt;
                                                             /* signal the main   */&lt;br /&gt;
                                                             /* process and exit  */&lt;br /&gt;
                                                             /* child process.    */&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 chart gives a brief description of the Exec functions that control task signalling. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Exec Signal Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocSignal()&lt;br /&gt;
| Allocate a signal bit.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSignal()&lt;br /&gt;
| Free a signal bit allocated with AllocSignal().&lt;br /&gt;
|-&lt;br /&gt;
| SetSignal()&lt;br /&gt;
| Query or set the state of the signals for the current task.&lt;br /&gt;
|-&lt;br /&gt;
| Signal()&lt;br /&gt;
| Signal a task by setting signal bits in its Task structure.&lt;br /&gt;
|-&lt;br /&gt;
| Wait()&lt;br /&gt;
| Wait for one or more signals from other tasks or interrupts.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12366</id>
		<title>Exec Signals</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Signals&amp;diff=12366"/>
		<updated>2023-09-13T07:15:21Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* SIGBREAKB_CTRL_F */  Typo fixes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Signals ==&lt;br /&gt;
&lt;br /&gt;
[[Exec Tasks|Tasks]] often need to coordinate with other concurrent system activities (like other [[Exec Tasks|tasks]] and [[Exec Interrupts|interrupts]]). This coordination is handled by Exec through the synchronized exchange of specific event indicators called &#039;&#039;signals&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is the primary mechanism responsible for all inter-task communication and synchronization on the Amiga. This signal mechanism operates at a low level and is designed for high performance. Signals are used extensively by the Exec message system as a way to indicate the arrival of an inter-task message. The message system is described in more detail in [[Exec Messages and Ports]].&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Not for Beginners.|text=This section concentrates on details about signals that most applications do not need to understand for general Amiga programming. For a general overview of signals, see [[Introduction to Exec]].}}&lt;br /&gt;
&lt;br /&gt;
== The Signal System ==&lt;br /&gt;
&lt;br /&gt;
The signal system is designed to support independent simultaneous events, so several signals can occur at the same time. Each task has 32 independent signals, 16 of which are pre-allocated for use by the operating system. The signals in use by a particular task are represented as bits in a 32-bit field in its Task structure (&amp;amp;lt;exec/tasks.h&amp;amp;gt;). Two other 32-bit fields in the Task structure indicate which signals the task is waiting for, and which signals have been received.&lt;br /&gt;
&lt;br /&gt;
Signals are &#039;&#039;task relative&#039;&#039;. A task can only allocate its own signals, and may only wait on its own signals. In addition, a task may assign its own significance to a particular signal. Signals are not broadcast to all tasks; they are directed only to individual tasks. A signal has meaning to the task that defined it and to those tasks that have been informed of its meaning.&lt;br /&gt;
&lt;br /&gt;
For example, signal bit 12 may indicate a timeout event to one task, but to another task it may indicate a message arrival event. You can never wait on a signal that you did not directly or indirectly allocate yourself, and any other task that wishes to signal you must use a signal that &#039;&#039;you&#039;&#039; allocated.&lt;br /&gt;
&lt;br /&gt;
=== Signal Allocation ===&lt;br /&gt;
&lt;br /&gt;
As mentioned above, a task assigns its own meaning to a particular signal. Because certain system libraries may occasionally require the use of a signal, there is a convention for signal allocation. It is unwise ever to make assumptions about which signals are actually in use.&lt;br /&gt;
&lt;br /&gt;
Before a signal can be used, it must be allocated with the AllocSignal() function. When a signal is no longer needed, it should be freed for reuse with FreeSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
BYTE AllocSignal( LONG signalNum );&lt;br /&gt;
VOID FreeSignal( LONG signalNum );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
AllocSignal() marks a signal as being in use and prevents the accidental use of the same signal for more than one event. You may ask for either a specific signal number, or more commonly, you would pass -1 to request the next available signal. The state of the newly allocated signal is cleared (ready for use). Generally it is best to let the system assign you the next free signal. Of the 32 available signals, the lower 16 are reserved for system use (see [[#Reserved_System_Signals|Reserved System Signals]]). This leaves the upper 16 signals free for application programs to allocate. Other subsystems that you may call depend on AllocSignal().&lt;br /&gt;
&lt;br /&gt;
The following example asks for the next free signal to be allocated for its use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if (-1 == (signal = IExec-&amp;gt;AllocSignal(-1)))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;no signal bits available\n&amp;quot;);&lt;br /&gt;
else&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;allocated signal number %ld\n&amp;quot;, signal);&lt;br /&gt;
    /* Other code could go here */&lt;br /&gt;
    IExec-&amp;gt;FreeSignal(signal)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The value returned by AllocSignal() is a signal bit number. This value cannot be used directly in calls to signal-related functions without first being converted to a mask:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 mask = 1UL &amp;lt;&amp;lt; signal;&lt;br /&gt;
 or&lt;br /&gt;
uint32 mask = (uint32)1 &amp;lt;&amp;lt; signal;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is important to realize that signal bit allocation is relevant &#039;&#039;only&#039;&#039; to the running task. You &#039;&#039;cannot&#039;&#039; allocate a signal from another task. Note that functions which create a signal MsgPort will allocate a signal from the task that calls the function. Such functions include OpenWindow() and AllocSysObject(). For this reason, only the creating task may Wait() (directly or indirectly) on the MsgPort&#039;s signal. Functions which call Wait() include DoIO(), WaitIO() and WaitPort().&lt;br /&gt;
&lt;br /&gt;
=== Waiting for a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals are most often used to wake up a task upon the occurrence of some external event. Applications call the Exec Wait() function, directly or indirectly, in order to enter a wait state until some external event triggers a signal which awakens the task.&lt;br /&gt;
&lt;br /&gt;
Though signals are usually not used to interrupt an executing task, they can be used this way. Task &#039;&#039;exceptions&#039;&#039;, described in [[Exec_Interrupts|Exec Interrupts]], allow signals to act as a task-local interrupt.&lt;br /&gt;
&lt;br /&gt;
The Wait() function specifies the set of signals that will wake up the task and then puts the task to sleep (into the waiting state).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG Wait( ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any one signal or any combination of signals from this set are sufficient to awaken the task. Wait() returns a mask indicating which signals satisfied the Wait() call. Note that when signals are used in conjunction with a message port, a set signal bit does not necessarily mean that there is a message at the message port.&lt;br /&gt;
&lt;br /&gt;
See [[Exec_Messages_and_Ports|Exec Messages and Ports]] for details about proper handling of messages.&lt;br /&gt;
&lt;br /&gt;
Because tasks (and interrupts) normally execute asynchronously, it is often possible to receive a particular signal before a task actually Wait()s for it. In such cases the Wait() will be immediately satisfied, and the task will not be put to sleep.&lt;br /&gt;
&lt;br /&gt;
The Wait() function implicitly clears those signal bits that satisfied the wait condition. This effectively resets those signals for reuse. However, keep in mind that a task might get more signals while it is still processing the previous signal. If the same signal is received multiple times and the signal bit is not cleared between them, some signals will go unnoticed.&lt;br /&gt;
&lt;br /&gt;
Be aware that using Wait() will break a Forbid() or Disable() state. Wait() cannot be used in supervisor mode or within interrupts.&lt;br /&gt;
&lt;br /&gt;
A task may Wait() for a combination of signal bits and will wake up when any of the signals occur. Wait() returns a signal mask specifying which signal or signals were received. Usually the program must check the returned mask for each signal it was waiting on and take the appropriate action for each that occurred. The order in which these bits are checked is often important.&lt;br /&gt;
&lt;br /&gt;
Here is a hypothetical example of a process that is using the console and timer devices, and is waiting for a message from either device and a possible break character issued by the user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 consoleSignal = 1L &amp;lt;&amp;lt; ConsolePort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 timerSignal   = 1L &amp;lt;&amp;lt; TimerPort-&amp;gt;mp_SigBit;&lt;br /&gt;
uint32 userSignal    = SIGBREAKF_CTRL_C;    /* Defined in &amp;lt;dos/dos.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
uint32 signals = IExec-&amp;gt;Wait(consoleSignal | timerSignal | userSignal);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; consoleSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;new character\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; timeOutSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;timeout\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; userSignal)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;User Ctrl-C Abort\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will put the task to sleep waiting for a new character, or the expiration of a time period, or a &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt; break character issued by the user. Notice that this code checks for an incoming character signal before checking for a timeout. Although a program can check for the occurrence of a particular event by checking whether its signal has occurred, this may lead to busy wait polling. Such polling is wasteful of the processor and is usually harmful to the proper function of the Amiga system. However, if a program needs to do constant processing and also check signals (a compiler for example) SetSignal(0,0) can be used to get a copy of your task&#039;s current signals.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ULONG SetSignal( ULONG newSignals, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SetSignal() can also be used to set or clear the state of the signals. Implementing this can be dangerous and should generally not be done. The following fragment illustrates a possible use of SetSignal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 signals = SetSignal(0,0);           /* Get current state of signals */&lt;br /&gt;
&lt;br /&gt;
if (signals &amp;amp; SIGBREAKF_CTRL_C)            /* Check for Ctrl-C.           */&lt;br /&gt;
    {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Break\n&amp;quot;);               /* Ctrl-C signal has been set. */&lt;br /&gt;
    IExec-&amp;gt;SetSignal(0, SIGBREAKF_CTRL_C)  /* Clear Ctrl-C signal.        */&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Looking for Break Keys ====&lt;br /&gt;
&lt;br /&gt;
One common usage of signals on the Amiga is for processing a user break. The OS reserves 16 of a tasks 32 signals for system use. Four of those 16 signals are used to tell a task about the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; break keys. An application can process these signals. Usually, only CLI-based programs receive these signals because the Amiga&#039;s console handler is about the only user input source that sets these signals when it sees the &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;D&amp;lt;/kbd&amp;gt;, &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;E&amp;lt;/kbd&amp;gt;, and &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;F&amp;lt;/kbd&amp;gt; key presses.&lt;br /&gt;
&lt;br /&gt;
The signal masks for each of these key presses are defined in &amp;amp;lt;dos/dos.h&amp;amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
SIGBREAKF_CTRL_C&lt;br /&gt;
SIGBREAKF_CTRL_D&lt;br /&gt;
SIGBREAKF_CTRL_E&lt;br /&gt;
SIGBREAKF_CTRL_F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that these are &#039;&#039;bit masks&#039;&#039; and &#039;&#039;&#039;not&#039;&#039;&#039; &#039;&#039;bit numbers&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Generating a Signal ===&lt;br /&gt;
&lt;br /&gt;
Signals may be generated from both tasks and system interrupts with the Signal() function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
VOID Signal( struct Task *task, ULONG signalSet );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example Signal(tc,mask) would signal the task with the specified mask signals. More than one signal can be specified in the mask. The following example code illustrates Wait() and Signal().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// signals.c&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;
&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;
static CONST_STRPTR VersTag = &amp;quot;$VER: signals 53.1 (20.6.2012)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void);    /* prototype for our subtask routine */&lt;br /&gt;
&lt;br /&gt;
struct Task *maintask = NULL;&lt;br /&gt;
uint32 mainsig = 0;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  BOOL Done = FALSE;&lt;br /&gt;
  BOOL WaitingForSubtask = TRUE;&lt;br /&gt;
&lt;br /&gt;
  /* We must allocate any special signals we want to receive. */&lt;br /&gt;
  int8 mainsignum = IExec-&amp;gt;AllocSignal(-1);&lt;br /&gt;
  if (mainsignum == -1)&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;No signals available\n&amp;quot;);&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    mainsig = 1U &amp;lt;&amp;lt; mainsignum;        /* subtask can access this global */&lt;br /&gt;
    maintask = IExec-&amp;gt;FindTask(NULL);  /* subtask can access this global */&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;We alloc a signal, create a task, wait for signals\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    struct Task *subtask = IDOS-&amp;gt;CreateTaskTags(&amp;quot;subtask&amp;quot;, 0, subtaskcode, 16000, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (subtask == NULL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t create subtask\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      IDOS-&amp;gt;Printf(&amp;quot;After subtask signals, press CTRL-C or CTRL-D to exit\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      while (!Done || WaitingForSubtask)&lt;br /&gt;
      {&lt;br /&gt;
        /* Wait on the combined mask for all of the signals we are&lt;br /&gt;
         * interested in.  All processes have the CTRL_C thru CTRL_F&lt;br /&gt;
         * signals.  We&#039;re also Waiting on the mainsig we allocated&lt;br /&gt;
         * for our subtask to signal us with.  We could also Wait on&lt;br /&gt;
         * the signals of any ports/windows our main task created ... */&lt;br /&gt;
&lt;br /&gt;
        uint32 wakeupsigs = IExec-&amp;gt;Wait(mainsig | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);&lt;br /&gt;
&lt;br /&gt;
        /* Deal with all signals that woke us up - may be more than one */&lt;br /&gt;
        if (wakeupsigs &amp;amp; mainsig)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Signalled by subtask\n&amp;quot;);&lt;br /&gt;
          WaitingForSubtask = FALSE;   /* OK to kill subtask now */&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if (wakeupsigs &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-C signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(wakeupsigs &amp;amp; SIGBREAKF_CTRL_D)&lt;br /&gt;
        {&lt;br /&gt;
          IDOS-&amp;gt;Printf(&amp;quot;Got CTRL-D signal\n&amp;quot;);&lt;br /&gt;
          Done = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    IExec-&amp;gt;FreeSignal(mainsignum);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void subtaskcode(void)&lt;br /&gt;
{&lt;br /&gt;
  IExec-&amp;gt;Signal(maintask, mainsig);&lt;br /&gt;
  IExec-&amp;gt;RemTask(0);  // Remove myself from the system. &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reserved System Signals ==&lt;br /&gt;
&lt;br /&gt;
There are 16 signal bits which are reserved for system use. Applications are never allowed to use these signal bits unless explicitly documented (e.g. SIGF_SINGLE).&lt;br /&gt;
&lt;br /&gt;
=== SIGB_ABORT ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_CHILD ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE (SIGB_BLIT) ===&lt;br /&gt;
&lt;br /&gt;
When a system function needs a Task to stop and IExec-&amp;gt;Wait() for a single signal it will use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
This signal used to be named SIGB_BLIT when it was used to wait on the classic hardware blitter.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_INTUITION ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_NET ===&lt;br /&gt;
&lt;br /&gt;
Reserved for system use.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_DOS ===&lt;br /&gt;
&lt;br /&gt;
SIGB_DOS is currently used as the wait signal bit for the embedded message port in the process structure. This message port is initialised by the IDOS-&amp;gt;CreateNewProc() function. This message port is used by default for DosPacket transactions via IDOS-&amp;gt;DoPkt() and IDOS-&amp;gt;WaitPkt() and it is also used for sending the initial ACTION_STARTUP DosPacket for DOS handlers. This message port is also where the workbench.library sends the initial struct WBStartup message to every process it starts.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_C ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_C signal bit is used extensively as the general purpose &amp;quot;Break&amp;quot; signal. It is used by all shell handler commands&lt;br /&gt;
and many applications to invoke a normal exit of the program. It is up to all applications to follow this recommendation.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_C signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_D ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_D signal bit is used mostly by the shell handler to stop execution of a script file or non-interactive stream. It may also be used by applications.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_D signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_E ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_E signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signalling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_E signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
=== SIGBREAKB_CTRL_F ===&lt;br /&gt;
&lt;br /&gt;
The SIGBREAKB_CTRL_F signal bit is not currently used by the shell handler but may be used by some other handlers, OS subsystems or multi-process applications for general undefined inter process signaling.&lt;br /&gt;
&lt;br /&gt;
Applications should never crash or generally misbehave when receiving a SIGBREAKB_CTRL_F signal bit from any source.&lt;br /&gt;
&lt;br /&gt;
== Signalling with SIGB_SINGLE ==&lt;br /&gt;
&lt;br /&gt;
Many of a task&#039;s 32 signal bits are reserved for the operating system&#039;s private use, but, like any good rule, there is an exception. One of these bits, the SIGB_SINGLE bit, can be useful to some applications, if used correctly.&lt;br /&gt;
&lt;br /&gt;
Many system functions need to put their task to sleep while waiting for a single event, which requires using one of the task&#039;s signals. Rather than forcing each of these system functions to allocate a signal, then Wait(), then deallocate the signal, the operating system&lt;br /&gt;
has permanently allocated one signal, the SIGB_SINGLE, for this type of signalling. When a system function needs stop a task to Wait()&lt;br /&gt;
for a single signal, it can use SIGB_SINGLE.&lt;br /&gt;
&lt;br /&gt;
The only purpose a program can use SIGB_SINGLE for is Wait()ing because the task cannot call any system functions while it is using SIGB_SINGLE. A program that calls system functions while using SIGB_SINGLE can cause itself and the operating system serious problems because the system functions can use SIGB_SINGLE as well. If a program calls a system function while using SIGB_SINGLE, two bad&lt;br /&gt;
things can happen:&lt;br /&gt;
&lt;br /&gt;
1) The errant task&#039;s event takes place before the system function waits on SIGB_SINGLE (or while the system function is waiting on&lt;br /&gt;
SIGB_SINGLE). In this case, the system function will think its event has taken place because its signal became set. The errant task will never find out that its event has taken place, as the system function will clear the SIGB_SINGLE bit after Wait()ing on it.&lt;br /&gt;
&lt;br /&gt;
2) The errant task&#039;s event and the system function&#039;s event take place while the system function is waiting on SIGB_SINGLE. In this case,&lt;br /&gt;
the system function will function normally, clear the SIGB_SINGLE bit, and exit. The errant task will never know that its event has&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
Before Wait()ing on SIGB_SINGLE, clear it using SetSignal():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IExec-&amp;gt;SetSignal(0, SIGF_SINGLE); // Note SIGF_SINGLE is the bit mask. SIGB_SINGLE is the signal bit.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This step is necessary because it is possible that the last system function that used the SIGB_SINGLE signal did not clear the SIGB_SINGLE bit.&lt;br /&gt;
&lt;br /&gt;
Also, an application should not wait on other signals while it is waiting on SIGB_SINGLE. Waiting on other signals at the same time&lt;br /&gt;
makes it possible for a program to wake up while the SIGB_SINGLE is still outstanding. If this happens, the program will still have to&lt;br /&gt;
go back to sleep, which requires calling a system function.&lt;br /&gt;
&lt;br /&gt;
=== SIGB_SINGLE Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a simple example of using the SIGB_SINGLE signal. It starts a child process and waits for that child process to signal&lt;br /&gt;
the main process using the SIGB_SINGLE signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// This example program illustrates simple usage of the SIGB_SINGLE&lt;br /&gt;
// signal for &amp;quot;single shot&amp;quot; signalling. This signal is one of the&lt;br /&gt;
// system private signals, but applications can use it in certain&lt;br /&gt;
// cases, but only if used carefully. Specifically, applications&lt;br /&gt;
// should use it only to Wait() on, and using only that signal&lt;br /&gt;
// (applications cannot Wait() on other signals in the same&lt;br /&gt;
// Wait()). Not following these rules can cause serious system&lt;br /&gt;
// problems.&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/dosextens.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dostags.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;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void);      /* prototype for our childprocess routine. */&lt;br /&gt;
&lt;br /&gt;
struct Process *mainprocess = NULL, *childprocess = NULL;&lt;br /&gt;
UBYTE childprocessname[] = &amp;quot;RKM_signal_childprocess&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
BPTR output;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (output = IDOS-&amp;gt;Open(&amp;quot;CONSOLE:&amp;quot;, MODE_OLDFILE))  /* Open the console for the  */&lt;br /&gt;
                                                        /*            child process. */&lt;br /&gt;
    {&lt;br /&gt;
        mainprocess = (struct Process *)IExec-&amp;gt;FindTask(NULL); /* childprocess can   */&lt;br /&gt;
                                                               /* access this global.*/&lt;br /&gt;
&lt;br /&gt;
        if (childprocess = IDOS-&amp;gt;CreateNewProcTags(&lt;br /&gt;
                    NP_Entry,       childprocesscode,  /* The child process  */&lt;br /&gt;
                    NP_Name,        childprocessname,&lt;br /&gt;
                    NP_Output,      output,&lt;br /&gt;
                    NP_FreeSeglist, FALSE,&lt;br /&gt;
                    NP_CloseOutput, TRUE,&lt;br /&gt;
                    NP_Child,       TRUE,&lt;br /&gt;
                    TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Created a child process and waiting on SIGB_SINGLE.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());  /* Make sure the Printf() above appears      */&lt;br /&gt;
                                           /* in the console window before the child    */&lt;br /&gt;
                                           /* process starts printing to the console. */&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;SetSignal(0, SIGF_SINGLE);  /* Use SIGF_SINGLE only after */&lt;br /&gt;
                                               /* clearing it.               */&lt;br /&gt;
&lt;br /&gt;
            /* Wake up the child. */&lt;br /&gt;
            IExec-&amp;gt;Signal((struct Task *)childprocess, SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;Wait(SIGF_SINGLE);  /* Only use SIGF_SINGLE for Wait()ing and */&lt;br /&gt;
                                       /* Wait on that signal alone!             */&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Received signal from child.\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t create child process. Exiting.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Main Process: Can&#039;t open CONSOLE:.  Exiting.\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int32 childprocesscode(void)     /* This function is what CreateNewProcTags() */&lt;br /&gt;
{                               /* loads as the child process.  This child   */&lt;br /&gt;
                                /* signals the parent using SIGF_SINGLE.     */&lt;br /&gt;
&lt;br /&gt;
    /* Wait for a startup signal. This is to allow the parent process to&lt;br /&gt;
     * print its banner message and clear SIGF_SINGLE.&lt;br /&gt;
     */&lt;br /&gt;
    IExec-&amp;gt;Wait(SIGBREAKF_CTRL_F);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: I&#039;m alive and starting a 5 second TimeDelay()&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    for (uint32 x = 0; x &amp;lt; 5; x++)&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;Delay(50);        /* Delay for 5 seconds, printing a */&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot; .&amp;quot;);     /* dot during each second.         */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Delay(50);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot; Finished.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Child Process: Signalling main process and exiting.  Bye.\n&amp;quot;);&lt;br /&gt;
    IDOS-&amp;gt;FFlush(IDOS-&amp;gt;Output());&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Signal((struct Task *)mainprocess, SIGF_SINGLE);  /* Finished waiting, */&lt;br /&gt;
                                                             /* signal the main   */&lt;br /&gt;
                                                             /* process and exit  */&lt;br /&gt;
                                                             /* child process.    */&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 chart gives a brief description of the Exec functions that control task signalling. See the SDK for details about each call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Exec Signal Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| AllocSignal()&lt;br /&gt;
| Allocate a signal bit.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSignal()&lt;br /&gt;
| Free a signal bit allocated with AllocSignal().&lt;br /&gt;
|-&lt;br /&gt;
| SetSignal()&lt;br /&gt;
| Query or set the state of the signals for the current task.&lt;br /&gt;
|-&lt;br /&gt;
| Signal()&lt;br /&gt;
| Signal a task by setting signal bits in its Task structure.&lt;br /&gt;
|-&lt;br /&gt;
| Wait()&lt;br /&gt;
| Wait for one or more signals from other tasks or interrupts.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Icon_Library&amp;diff=12095</id>
		<title>Icon Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Icon_Library&amp;diff=12095"/>
		<updated>2021-05-22T21:33:16Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Replaced &amp;quot;Flags&amp;quot;, &amp;quot;Activation Flags&amp;quot; and &amp;quot;Gadget Type&amp;quot; with their modern counterparts&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= The Icon Library =&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;.info&#039;&#039; file is the center of interaction between applications and Workbench. To help support the Workbench iconic interface and manage &#039;&#039;.info&#039;&#039; files, the Amiga operating system provides the icon library. The icon library allows you to create icons for data files and directories under program control and examine icons to obtain their Tool Types and other characteristics.&lt;br /&gt;
&lt;br /&gt;
= Icon Library Data Structures =&lt;br /&gt;
&lt;br /&gt;
The preceding sections discussed how icons are used to pass file name arguments to an application run from the Workbench. Workbench allows other types of arguments to be passed in the Tool Types array of an icon. To examine the Tool Types array or find other characteristics of the icon such as its type, applications need to read in the &#039;&#039;.info&#039;&#039; file for the icon.&lt;br /&gt;
&lt;br /&gt;
== The DiskObject Structure ==&lt;br /&gt;
&lt;br /&gt;
The actual data present in the &#039;&#039;.info&#039;&#039; file is organized as a DiskObject structure which is defined in the include file &amp;amp;lt;workbench/workbench.h&amp;amp;gt;. For a complete listing, see the SDK. The DiskObject structure contains the following elements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DiskObject&lt;br /&gt;
    {&lt;br /&gt;
    UWORD              do_Magic;       /* magic number at start of file */&lt;br /&gt;
    UWORD              do_Version;     /* so we can change structure    */&lt;br /&gt;
    struct Gadget      do_Gadget;      /* a copy of in core gadget      */&lt;br /&gt;
    UBYTE              do_Type;&lt;br /&gt;
    char              *do_DefaultTool;&lt;br /&gt;
    char             **do_ToolTypes;&lt;br /&gt;
    LONG               do_CurrentX;&lt;br /&gt;
    LONG               do_CurrentY;&lt;br /&gt;
    struct DrawerData *do_DrawerData;&lt;br /&gt;
    char              *do_ToolWindow;  /* only applies to tools */&lt;br /&gt;
    LONG               do_StackSize;   /* only applies to tools */&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; do_Magic&lt;br /&gt;
: A magic number that the icon library looks for to make sure that the file it is reading really contains an icon. It should be the manifest constant WB_DISKMAGIC. PutDiskObject() will put this value in the structure, and GetDiskObject will not believe that a file is really an icon unless this value is correct.&lt;br /&gt;
&lt;br /&gt;
; do_Version&lt;br /&gt;
: This provides a way to enhance the .info file in an upwardly-compatible way. It should be WB_DISKVERSION. The icon library will set this value for you and will not believe weird values.&lt;br /&gt;
&lt;br /&gt;
; do_Gadget&lt;br /&gt;
: This contains all the imagery for the icon. See the &amp;quot;Gadget Structure&amp;quot; section below for more details.&lt;br /&gt;
&lt;br /&gt;
; do_Type&lt;br /&gt;
: The type of the icon; can be set to any of the following values.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| WBDISK || The root of a disk&lt;br /&gt;
|-&lt;br /&gt;
| WBDRAWER || A directory on the disk&lt;br /&gt;
|-&lt;br /&gt;
| WBTOOL || An executable program&lt;br /&gt;
|-&lt;br /&gt;
| WBPROJECT || A data file&lt;br /&gt;
|-&lt;br /&gt;
| WBGARBAGE || The Trashcan directory&lt;br /&gt;
|-&lt;br /&gt;
| WBKICK || A Kickstart disk&lt;br /&gt;
|-&lt;br /&gt;
| WBAPPICON || Any object not directly associated with a filing system object, such as a print spooler.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
; do_DefaultTool&lt;br /&gt;
: Default tools are used for project and disk icons. For projects (data files), the default tool is the program Workbench runs when the project is activated. Any valid AmigaDOS path may be entered in this field such as &amp;quot;SYS:myprogram&amp;quot;, &amp;quot;df0:mypaint&amp;quot;, &amp;quot;myeditor&amp;quot; or &amp;quot;:work/mytool&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
: For disk icons, the default tool is the diskcopy program (&amp;quot;SYS:System/DiskCopy&amp;quot;) that will be used when &#039;&#039;this disk is the source&#039;&#039; of a copy.&lt;br /&gt;
&lt;br /&gt;
; do_ToolTypes&lt;br /&gt;
: This is an array of free-format strings. Workbench does not enforce any rules on these strings, but they are useful for passing environment information. See the section on &amp;quot;The ToolTypes Array&amp;quot; below for more information.&lt;br /&gt;
&lt;br /&gt;
; do_CurrentX, do_CurrentY&lt;br /&gt;
: Drawers have a virtual coordinate system. The user can scroll around in this system using the scroll gadgets on the window that opens when the drawer is activated. Each icon in the drawer has a position in the coordinate system. CurrentX and CurrentY contain the icon&#039;s current position in the drawer. Picking a position for a newly created icon can be tricky. NO_ICON_POSITION is a system constant for do_CurrentX and do_CurrentY that instructs Workbench to pick a reasonable place for the icon. Workbench will place the icon in an unused region of the drawer. If there is no space in the drawers window, the icon will be placed just to the right of the visible region.&lt;br /&gt;
&lt;br /&gt;
; do_DrawerData&lt;br /&gt;
: If the icon is associated with a directory (WBDISK, WBDRAWER, WBGARBAGE), it needs a DrawerData structure to go with it. This structure contains an Intuition NewWindow structure (see [[Intuition Windows]] for more information):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DrawerData&lt;br /&gt;
{&lt;br /&gt;
    struct NewWindow dd_NewWindow; /* structure to open window       */&lt;br /&gt;
    LONG             dd_CurrentX;  /* current x coordinate of origin */&lt;br /&gt;
    LONG             dd_CurrentY;  /* current y coordinate of origin */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: Workbench uses this to hold the current window position and size of the window so it will reopen in the same place.&lt;br /&gt;
&lt;br /&gt;
; do_ToolWindow&lt;br /&gt;
: This field is reserved for future use.&lt;br /&gt;
&lt;br /&gt;
; do_StackSize&lt;br /&gt;
: This is the size of the stack (in bytes) used for running the tool. If this is NULL, then Workbench will use a reasonable default stack size (currently 4K bytes).&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Stack Size is Taken from the Project Icon|text=When a tool is run &#039;&#039;via the default tool mechanism&#039;&#039; (i.e., a project was activated, not the tool itself), Workbench uses the stack size specified in the project&#039;s &#039;&#039;.info&#039;&#039; file and the tool&#039;s &#039;&#039;.info&#039;&#039; file is ignored.}}&lt;br /&gt;
&lt;br /&gt;
== The Gadget Structure ==&lt;br /&gt;
&lt;br /&gt;
To hold the icon&#039;s image, Workbench uses an Intuition Gadget structure, defined in &amp;amp;lt;intuition/intuition.h&amp;amp;gt;. Workbench restricts some of the values of the gadget. All unused fields should be set to 0 or NULL. The Intuition gadget structure members that Workbench icons use are listed below.&lt;br /&gt;
&lt;br /&gt;
; Width&lt;br /&gt;
: This is the width (in pixels) of the icon&#039;s active region. Any mouse button press within this range will be interpreted as having selected this icon.&lt;br /&gt;
&lt;br /&gt;
; Height&lt;br /&gt;
: This is the height (in pixels) of the icon&#039;s active region. Any mouse button press within this range will be interpreted as having selected this icon.&lt;br /&gt;
&lt;br /&gt;
; Flags&lt;br /&gt;
: The gadget &#039;&#039;must&#039;&#039; be of type GADGIMAGE. Three highlight modes are supported: GADGHCOMP, GADGHIMAGE, and GADGBACKFILL. GADGHCOMP complements everything within the area defined by CurrentX, CurrentY, Width, Height. GADGHIMAGE uses an alternate selection image. GADGBACKFILL is similar to GADGHCOMP, but ensures that there is no &amp;quot;ring&amp;quot; around the selected image. It does this by first complementing the image, and then flooding all color 3 pixels that are on the border of the image to color 0. All other flag bits should be 0.&lt;br /&gt;
&lt;br /&gt;
; Activation&lt;br /&gt;
: The activation should have only RELVERIFY and GADGIMMEDIATE set.&lt;br /&gt;
&lt;br /&gt;
; Type&lt;br /&gt;
: The gadget type should be BOOLGADGET.&lt;br /&gt;
&lt;br /&gt;
; GadgetRender&lt;br /&gt;
: Set this to an appropriate Image structure.&lt;br /&gt;
&lt;br /&gt;
; SelectRender&lt;br /&gt;
: Set this to an appropriate alternate Image structure if and only if the highlight mode is GADGHIMAGE.&lt;br /&gt;
&lt;br /&gt;
The Image structure is typically the same size as the gadget, except that Height is often one pixel less than the gadget height. This allows a blank line between the icon image and the icon name. The image depth &#039;&#039;must&#039;&#039; be 2; PlanePick &#039;&#039;must&#039;&#039; be 3; and PlaneOnOff should be 0. The NextImage field should be null.&lt;br /&gt;
&lt;br /&gt;
= Icon Library Functions =&lt;br /&gt;
&lt;br /&gt;
The icon library functions do all the work needed to read, write and examine an icon&#039;s &#039;&#039;.info&#039;&#039; file and corresponding DiskObject structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct DiskObject *GetDiskObject(UBYTE *name);&lt;br /&gt;
struct DiskObject *GetDiskObjectNew(UBYTE *name);&lt;br /&gt;
BOOL PutDiskObject(UBYTE *name, struct DiskObject *diskobj);&lt;br /&gt;
void FreeDiskObject(struct DiskObject *diskobj);&lt;br /&gt;
BOOL DeleteDiskObject(UBYTE *);&lt;br /&gt;
&lt;br /&gt;
UBYTE *FindToolType(UBYTE **toolTypeArray, UBYTE *typeName);&lt;br /&gt;
BOOL MatchToolValue(UBYTE *typeString, UBYTE *value);&lt;br /&gt;
&lt;br /&gt;
struct DiskObject *GetDefDiskObjectNew(LONG type);&lt;br /&gt;
BOOL PutDefDiskObject(struct DiskObject *diskobj);&lt;br /&gt;
&lt;br /&gt;
UBYTE *BumpRevision(UBYTE *newbuf, UBYTE *oldname);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The icon library routine GetDiskObject() reads an icon&#039;s &#039;&#039;.info&#039;&#039; file from disk into a DiskObject structure it creates in memory where it can be examined or altered. PutDiskObject() writes the DiskObject out to disk and FreeDiskObject() frees the memory it used. If you modify any pointers in a DiskObject acquired via GetDiskObject(), replace the old pointers before calling FreeDiskObject() so that the proper memory will be freed.&lt;br /&gt;
&lt;br /&gt;
GetDiskObjectNew() works the same as GetDiskObject() except that if no &#039;&#039;.info&#039;&#039; file is found, a default DiskObject will be created for you. DeleteDiskObject() is for removing &#039;&#039;.info&#039;&#039; files from disk, and the functions GetDefDiskObject() and PutDefDiskObject() allow the default icons to be copied or replaced with new defaults.&lt;br /&gt;
&lt;br /&gt;
Once an icon&#039;s &#039;&#039;.info&#039;&#039; file has been read into a DiskObject structure, the functions FindToolType() and MatchToolType() can be used to examine the icon&#039;s Tool Types array.&lt;br /&gt;
&lt;br /&gt;
= The Tool Types Array =&lt;br /&gt;
&lt;br /&gt;
Earlier sections discussed how Workbench passes filenames as arguments to a program that&#039;s about to run. Workbench also allows other types of arguments to be passed in the Tool Types array of an icon. The Tool Types array is found in the do_ToolTypes field of the icon&#039;s DiskObject structure.&lt;br /&gt;
&lt;br /&gt;
In brief, Tool Types is an array of pointers to strings that contain any information an application wants to store such as the program options that were in effect when the icon was created. These strings can be used to encode information which will be available to all applications that read the icon&#039;s &#039;&#039;.info&#039;&#039; file. Users can enter and change a selected icon&#039;s Tool Types by choosing Information in the Workbench Icons menu.&lt;br /&gt;
&lt;br /&gt;
Workbench does not place many restrictions on the Tool Types array, but there are a few conventions you should follow. A string may be no more than 128 bytes long. The alphabet used is 8-bit ANSI (for example, normal ASCII with foreign-language extensions). This means that users may enter Tool Type strings containing international characters. Avoid special or nonprinting characters. The case of the characters is currently significant, so the string &amp;quot;Window&amp;quot; is not equal to &amp;quot;WINDOW&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The general format for a Tool Types entry is &amp;amp;lt;&#039;&#039;name&#039;&#039;&amp;amp;gt;=&amp;amp;lt;&#039;&#039;value&#039;&#039;&amp;amp;gt;[|&amp;amp;lt;&#039;&#039;value&#039;&#039;&amp;amp;gt;], where &amp;amp;lt;&#039;&#039;name&#039;&#039;&amp;amp;gt; is the field name and &amp;amp;lt;&#039;&#039;value&#039;&#039;&amp;amp;gt; is the text to associate with that name. Multiple values for one name may be separated by a vertical bar. The values may be the type of the file, programs that can access the data, parameters to be passed to an application, etc. For example, a paint program might set:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FILETYPE = PaintProgram | ILBM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This Tool Type indicates that the file is an ILBM, perhaps with some additional chunks of data specific to PaintProgram.&lt;br /&gt;
&lt;br /&gt;
Tool Type strings have few restrictions but there are some reserved Tool Types that are parsed by Workbench itself when an application is started from an icon. The reserved Tool Types are TOOLPRI=&#039;&#039;n&#039;&#039; (sets the Exec task priority at which Workbench will start the application), STARTPRI=&#039;&#039;n&#039;&#039; (sets the starting order for icons in the Wbstartup drawer), and DONOTWAIT (tells Workbench not to wait for the return of a program started via an icon in the Wbstartup drawer). In addition to the reserved Tool Types, which applications should not use, there are standard Tool Types, which applications should use only in the standard way. For a list of standard Tool Types refer to the [[UI_Style_Guide_Workbench#Tool_Types_and_Default_Tool|Amiga User Interface Style Guide]].&lt;br /&gt;
&lt;br /&gt;
Two routines are provided to help you deal with the Tool Types array. FindToolType() returns the value of a Tool Type element. Using the above example, if you are looking for FILETYPE, the string &amp;quot;PaintProgram|ILBM&amp;quot; will be returned. MatchToolValue() returns nonzero if the specified string is in the reference value string. This routine knows how to parse vertical bars. For example, using the reference value strings of &amp;quot;PaintProgram&amp;quot; or &amp;quot;ILBM&amp;quot;, MatchToolValue() will return TRUE for &amp;quot;ILBM&amp;quot; and &amp;quot;PaintProgram&amp;quot; and FALSE for everything else.&lt;br /&gt;
&lt;br /&gt;
= Example of Reading Icons and Parsing Tool Types =&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates icon creation, icon reading and Tool Type parsing in the Workbench environment. When called from the Shell, the example creates a small data file in RAM: and creates or updates a project icon for the data file. The created project icon points to this example as its default tool. When the new project icon is double-clicked, Workbench will invoke the default tool (this example) as a Workbench process, and pass it a description of the project data file as a Workbench argument (WBArg) in the WBStartup message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** iconexample.c - Workbench icon startup, creation, and parsing example&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;workbench/workbench.h&amp;gt;&lt;br /&gt;
#include &amp;lt;workbench/startup.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/icon.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our functions */&lt;br /&gt;
void cleanexit(UBYTE *,LONG);&lt;br /&gt;
void cleanup(void);&lt;br /&gt;
void message(UBYTE *);&lt;br /&gt;
BOOL makeIcon(UBYTE *, char **, char *);&lt;br /&gt;
BOOL showToolTypes(struct WBArg *);&lt;br /&gt;
&lt;br /&gt;
UBYTE *projname     = &amp;quot;RAM:Example_Project&amp;quot;;&lt;br /&gt;
UBYTE *conwinname   = &amp;quot;CON:10/10/620/180/iconexample&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
UBYTE deftoolname[] = {&amp;quot;iconexample&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
USHORT IconImageData1[] =  {&lt;br /&gt;
/* Plane 0 */&lt;br /&gt;
    0x0000,0x0000,0x0000,0x1000,0x0000,0x0000,0x0000,0x3000,&lt;br /&gt;
    0x0FFF,0xFFFC,0x0000,0x3000,0x0800,0x0004,0x0000,0x3000,&lt;br /&gt;
    0x0800,0x07FF,0xFFC0,0x3000,0x08A8,0xA400,0x00A0,0x3000,&lt;br /&gt;
    0x0800,0x0400,0x0090,0x3000,0x08AA,0xA400,0x0088,0x3000,&lt;br /&gt;
    0x0800,0x042A,0xA0FC,0x3000,0x082A,0xA400,0x0002,0x3000,&lt;br /&gt;
    0x0800,0x0400,0x0002,0x3000,0x0800,0xA42A,0xA0A2,0x3000,&lt;br /&gt;
    0x0800,0x0400,0x0002,0x3000,0x0950,0xA42A,0x8AA2,0x3000,&lt;br /&gt;
    0x0800,0x0400,0x0002,0x3000,0x082A,0xA400,0x0002,0x3000,&lt;br /&gt;
    0x0800,0x042A,0x2AA2,0x3000,0x0FFF,0xFC00,0x0002,0x3000,&lt;br /&gt;
    0x0000,0x0400,0x0002,0x3000,0x0000,0x07FF,0xFFFE,0x3000,&lt;br /&gt;
    0x0000,0x0000,0x0000,0x3000,0x7FFF,0xFFFF,0xFFFF,0xF000,&lt;br /&gt;
/* Plane 1 */&lt;br /&gt;
    0xFFFF,0xFFFF,0xFFFF,0xE000,0xD555,0x5555,0x5555,0x4000,&lt;br /&gt;
    0xD000,0x0001,0x5555,0x4000,0xD7FF,0xFFF9,0x5555,0x4000,&lt;br /&gt;
    0xD7FF,0xF800,0x0015,0x4000,0xD757,0x5BFF,0xFF55,0x4000,&lt;br /&gt;
    0xD7FF,0xFBFF,0xFF65,0x4000,0xD755,0x5BFF,0xFF75,0x4000,&lt;br /&gt;
    0xD7FF,0xFBD5,0x5F01,0x4000,0xD7D5,0x5BFF,0xFFFD,0x4000,&lt;br /&gt;
    0xD7FF,0xFBFF,0xFFFD,0x4000,0xD7FF,0x5BD5,0x5F5D,0x4000,&lt;br /&gt;
    0xD7FF,0xFBFF,0xFFFD,0x4000,0xD6AF,0x5BD5,0x755D,0x4000,&lt;br /&gt;
    0xD7FF,0xFBFF,0xFFFD,0x4000,0xD7D5,0x5BFF,0xFFFD,0x4000,&lt;br /&gt;
    0xD7FF,0xFBD5,0xD55D,0x4000,0xD000,0x03FF,0xFFFD,0x4000,&lt;br /&gt;
    0xD555,0x53FF,0xFFFD,0x4000,0xD555,0x5000,0x0001,0x4000,&lt;br /&gt;
    0xD555,0x5555,0x5555,0x4000,0x8000,0x0000,0x0000,0x0000,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct Image iconImage1 =&lt;br /&gt;
    {&lt;br /&gt;
    0, 0,                /* Top Corner */&lt;br /&gt;
    52, 22, 2,           /* Width, Height, Depth */&lt;br /&gt;
    &amp;amp;IconImageData1[0],  /* Image Data */&lt;br /&gt;
    0x003, 0x000,        /* PlanePick,PlaneOnOff */&lt;br /&gt;
    NULL                 /* Next Image */&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
UBYTE *toolTypes[] =&lt;br /&gt;
    {&lt;br /&gt;
    &amp;quot;FILETYPE=text&amp;quot;,&lt;br /&gt;
    &amp;quot;FLAGS=BOLD|ITALICS&amp;quot;,&lt;br /&gt;
    NULL&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
struct DiskObject projIcon =&lt;br /&gt;
    {&lt;br /&gt;
    WB_DISKMAGIC,                       /* Magic Number */&lt;br /&gt;
    WB_DISKVERSION,                     /* Version */&lt;br /&gt;
        {                               /* Embedded Gadget Structure */&lt;br /&gt;
        NULL,                           /* Next Gadget Pointer */&lt;br /&gt;
        97,12,52,23,                    /* Left,Top,Width,Height */&lt;br /&gt;
	GFLG_GADGHIMAGE|GFLG_GADGHBOX,  /* Flags */&lt;br /&gt;
	GACT_IMMEDIATE|GACT_RELVERIFY,  /* Activation Flags */&lt;br /&gt;
	GTYP_BOOLGADGET,                /* Gadget Type */&lt;br /&gt;
        (APTR)&amp;amp;iconImage1,              /* Render Image */&lt;br /&gt;
        NULL,                           /* Select Image */&lt;br /&gt;
        NULL,                           /* Gadget Text */&lt;br /&gt;
        NULL,                           /* Mutual Exclude */&lt;br /&gt;
        NULL,                           /* Special Info */&lt;br /&gt;
        0,                              /* Gadget ID */&lt;br /&gt;
        NULL                            /* User Data */&lt;br /&gt;
        },&lt;br /&gt;
    WBPROJECT,                          /* Icon Type */&lt;br /&gt;
    deftoolname,                        /* Default Tool */&lt;br /&gt;
    toolTypes,                          /* Tool Type Array */&lt;br /&gt;
    NO_ICON_POSITION,                   /* Current X */&lt;br /&gt;
    NO_ICON_POSITION,                   /* Current Y */&lt;br /&gt;
    NULL,                               /* Drawer Structure */&lt;br /&gt;
    NULL,                               /* Tool Window */&lt;br /&gt;
    4000                                /* Stack Size */&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
/* Opens and allocations we must clean up */&lt;br /&gt;
struct Library *IconBase = NULL;&lt;br /&gt;
struct IconIFace *IIcon = NULL;&lt;br /&gt;
FILE *conwin = NULL;&lt;br /&gt;
LONG olddir = -1;&lt;br /&gt;
&lt;br /&gt;
BOOL FromWb;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct WBStartup *WBenchMsg;&lt;br /&gt;
    struct WBArg *wbarg;&lt;br /&gt;
    FILE  *file;&lt;br /&gt;
    LONG  wLen;&lt;br /&gt;
    SHORT i;&lt;br /&gt;
&lt;br /&gt;
    FromWb = (argc==0) ? TRUE : FALSE;&lt;br /&gt;
&lt;br /&gt;
    /* Open icon.library */&lt;br /&gt;
    IconBase = IExec-&amp;gt;OpenLibrary(&amp;quot;icon.library&amp;quot;, 50);&lt;br /&gt;
    IIcon = (struct IconIFace*)IExec-&amp;gt;GetInterface(IconBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (IIcon == NULL)&lt;br /&gt;
         cleanexit(&amp;quot;Can&#039;t open icon.library\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    /* If started from CLI, this example will create a small text&lt;br /&gt;
     * file RAM:Example_Project, and create an icon for the file&lt;br /&gt;
     * which points to this program as its default tool.&lt;br /&gt;
     */&lt;br /&gt;
    if(!FromWb)&lt;br /&gt;
        {&lt;br /&gt;
        /* Make a sample project (data) file */&lt;br /&gt;
        wLen = -1;&lt;br /&gt;
        if(file=fopen(projname,&amp;quot;w&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
            wLen = fprintf(file,&amp;quot;Have a nice day\n&amp;quot;);&lt;br /&gt;
            fclose(file);&lt;br /&gt;
            }&lt;br /&gt;
        if(wLen &amp;lt; 0) cleanexit(&amp;quot;Error writing data file\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
        /* Now save/update icon for this data file */&lt;br /&gt;
        if(makeIcon(projname, toolTypes, deftoolname))&lt;br /&gt;
             {&lt;br /&gt;
             IDOS-&amp;gt;Printf(&amp;quot;%s data file and icon saved.\n&amp;quot;,projname);&lt;br /&gt;
             IDOS-&amp;gt;Printf(&amp;quot;Use Workbench menu Icon Information to examine the icon.\n&amp;quot;);&lt;br /&gt;
             IDOS-&amp;gt;Printf(&amp;quot;Then copy this example (iconexample) to RAM:\n&amp;quot;);&lt;br /&gt;
             IDOS-&amp;gt;Printf(&amp;quot;and double-click the %s project icon\n&amp;quot;,projname);&lt;br /&gt;
             }&lt;br /&gt;
        else cleanexit(&amp;quot;Error writing icon\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    else  /* Else we are FromWb - ie. we were either&lt;br /&gt;
           * started by a tool icon, or as in this case,&lt;br /&gt;
           * by being the default tool of a project icon.&lt;br /&gt;
           */&lt;br /&gt;
        {&lt;br /&gt;
        if(!(conwin = fopen(conwinname,&amp;quot;r+&amp;quot;)))&lt;br /&gt;
             cleanexit(&amp;quot;Can&#039;t open output window\n&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
        WBenchMsg = (struct WBStartup *)argv;&lt;br /&gt;
&lt;br /&gt;
        /* Note wbarg++ at end of FOR statement steps through wbargs.&lt;br /&gt;
         * First arg is our executable (tool).  Any additional args&lt;br /&gt;
         * are projects/icons passed to us via either extend select&lt;br /&gt;
         * or default tool method.&lt;br /&gt;
         */&lt;br /&gt;
        for(i=0, wbarg=WBenchMsg-&amp;gt;sm_ArgList;&lt;br /&gt;
            i &amp;lt; WBenchMsg-&amp;gt;sm_NumArgs;&lt;br /&gt;
            i++, wbarg++)&lt;br /&gt;
            {&lt;br /&gt;
            /* if there&#039;s a directory lock for this wbarg, CD there */&lt;br /&gt;
            olddir = -1;&lt;br /&gt;
            if((wbarg-&amp;gt;wa_Lock)&amp;amp;&amp;amp;(*wbarg-&amp;gt;wa_Name))&lt;br /&gt;
                olddir = IDOS-&amp;gt;CurrentDir(wbarg-&amp;gt;wa_Lock);&lt;br /&gt;
&lt;br /&gt;
            showToolTypes(wbarg);&lt;br /&gt;
&lt;br /&gt;
            if((i&amp;gt;0)&amp;amp;&amp;amp;(*wbarg-&amp;gt;wa_Name))&lt;br /&gt;
                fprintf(conwin,&amp;quot;In Main. We could open the %s file here\n&amp;quot;,&lt;br /&gt;
                                 wbarg-&amp;gt;wa_Name);&lt;br /&gt;
            if(olddir != -1)  IDOS-&amp;gt;CurrentDir(olddir); /* CD back where we were */&lt;br /&gt;
            }&lt;br /&gt;
        IDOS-&amp;gt;Delay(500);&lt;br /&gt;
        }&lt;br /&gt;
    cleanup();&lt;br /&gt;
    return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL makeIcon(UBYTE *name, char **newtooltypes, char *newdeftool)&lt;br /&gt;
{&lt;br /&gt;
    struct DiskObject *dobj;&lt;br /&gt;
    char *olddeftool;&lt;br /&gt;
    char **oldtooltypes;&lt;br /&gt;
    BOOL success = FALSE;&lt;br /&gt;
&lt;br /&gt;
    if(dobj = IIcon-&amp;gt;GetDiskObject(name))&lt;br /&gt;
        {&lt;br /&gt;
        /* If file already has an icon, we will save off any fields we&lt;br /&gt;
         * need to update, update those fields, put the object, restore&lt;br /&gt;
         * the old field pointers and then free the object.  This will&lt;br /&gt;
         * preserve any custom imagery the user has, and the user&#039;s&lt;br /&gt;
         * current placement of the icon.  If your application does&lt;br /&gt;
         * not know where the user currently keeps your application,&lt;br /&gt;
         * you should not update his dobj-&amp;gt;do_DefaultTool.&lt;br /&gt;
         */&lt;br /&gt;
         oldtooltypes = dobj-&amp;gt;do_ToolTypes;&lt;br /&gt;
         olddeftool = dobj-&amp;gt;do_DefaultTool;&lt;br /&gt;
&lt;br /&gt;
         dobj-&amp;gt;do_ToolTypes = newtooltypes;&lt;br /&gt;
         dobj-&amp;gt;do_DefaultTool = newdeftool;&lt;br /&gt;
&lt;br /&gt;
         success = IIcon-&amp;gt;PutDiskObject(name,dobj);&lt;br /&gt;
&lt;br /&gt;
         /* we must restore the original pointers before freeing */&lt;br /&gt;
         dobj-&amp;gt;do_ToolTypes = oldtooltypes;&lt;br /&gt;
         dobj-&amp;gt;do_DefaultTool = olddeftool;&lt;br /&gt;
         IIcon-&amp;gt;FreeDiskObject(dobj);&lt;br /&gt;
         }&lt;br /&gt;
    /* Else, put our default icon */&lt;br /&gt;
    if(!success)  success = IIcon-&amp;gt;PutDiskObject(name,&amp;amp;projIcon);&lt;br /&gt;
    return(success);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL showToolTypes(struct WBArg *wbarg)&lt;br /&gt;
{&lt;br /&gt;
    struct DiskObject *dobj;&lt;br /&gt;
    char **toolarray;&lt;br /&gt;
    char *s;&lt;br /&gt;
    BOOL success = FALSE;&lt;br /&gt;
&lt;br /&gt;
    fprintf(conwin,&amp;quot;\nWBArg Lock=0x%lx, Name=%s\n&amp;quot;,&lt;br /&gt;
                           wbarg-&amp;gt;wa_Lock,wbarg-&amp;gt;wa_Name);&lt;br /&gt;
&lt;br /&gt;
    if((*wbarg-&amp;gt;wa_Name) &amp;amp;&amp;amp; (dobj = IIcon-&amp;gt;GetDiskObject(wbarg-&amp;gt;wa_Name)))&lt;br /&gt;
        {&lt;br /&gt;
        fprintf(conwin,&amp;quot;  We have read the DiskObject (icon) for this arg\n&amp;quot;);&lt;br /&gt;
        toolarray = (char **)dobj-&amp;gt;do_ToolTypes;&lt;br /&gt;
&lt;br /&gt;
        if(s=(char *)IIcon-&amp;gt;FindToolType(toolarray,&amp;quot;FILETYPE&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
            fprintf(conwin,&amp;quot;    Found tooltype FILETYPE with value %s\n&amp;quot;,s);&lt;br /&gt;
            }&lt;br /&gt;
        if(s=(char *)IIcon-&amp;gt;FindToolType(toolarray,&amp;quot;FLAGS&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
            fprintf(conwin,&amp;quot;    Found tooltype FLAGS with value %s\n&amp;quot;,s);&lt;br /&gt;
            if(IIcon-&amp;gt;MatchToolValue(s,&amp;quot;BOLD&amp;quot;))&lt;br /&gt;
                fprintf(conwin,&amp;quot;      BOLD flag requested\n&amp;quot;);&lt;br /&gt;
            if(IIcon-&amp;gt;MatchToolValue(s,&amp;quot;ITALICS&amp;quot;))&lt;br /&gt;
                fprintf(conwin,&amp;quot;      ITALICS flag requested\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        /* Free the diskobject we got */&lt;br /&gt;
        IIcon-&amp;gt;FreeDiskObject(dobj);&lt;br /&gt;
        success = TRUE;&lt;br /&gt;
        }&lt;br /&gt;
    else if(!(*wbarg-&amp;gt;wa_Name))&lt;br /&gt;
        fprintf(conwin,&amp;quot;  Must be a disk or drawer icon\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
        fprintf(conwin,&amp;quot;  Can&#039;t find any DiskObject (icon) for this WBArg\n&amp;quot;);&lt;br /&gt;
    return(success);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Workbench-started programs with no output window may want to display&lt;br /&gt;
 * messages in a different manner (requester, window title, etc)&lt;br /&gt;
 */&lt;br /&gt;
void message(UBYTE *s)&lt;br /&gt;
{&lt;br /&gt;
    if(FromWb &amp;amp;&amp;amp; conwin)  fprintf(conwin,s,strlen(s));&lt;br /&gt;
    else if (!FromWb) printf(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cleanexit(UBYTE *s, LONG n)&lt;br /&gt;
{&lt;br /&gt;
    if(*s)  message(s);&lt;br /&gt;
    cleanup();&lt;br /&gt;
    exit(n);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void cleanup()&lt;br /&gt;
{&lt;br /&gt;
   if(conwin)    fclose(conwin);&lt;br /&gt;
   IExec-&amp;gt;DropInterface((struct Interface*)IIcon);&lt;br /&gt;
   IExec-&amp;gt;CloseLibrary(IconBase);&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 functions in icon.library. See SDK for details on each function call.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Icon Library Functions&lt;br /&gt;
|-&lt;br /&gt;
! Function&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| GetDiskObject()&lt;br /&gt;
| Read the &#039;&#039;.info&#039;&#039; file of an icon into a DiskObject structure&lt;br /&gt;
|-&lt;br /&gt;
| GetDiskObjectNew()&lt;br /&gt;
| Same as GetDiskObject() but returns a default icon if none exists&lt;br /&gt;
|-&lt;br /&gt;
| PutDiskObject()&lt;br /&gt;
| Write a DiskObject structure to disk as a &#039;&#039;.info&#039;&#039; file&lt;br /&gt;
|-&lt;br /&gt;
| FreeDiskObject()&lt;br /&gt;
| Free the DiskObject structure created by GetDiskObject()&lt;br /&gt;
|-&lt;br /&gt;
| DeleteDiskObject()&lt;br /&gt;
| Deletes a given &#039;&#039;.info&#039;&#039; file from disk&lt;br /&gt;
|-&lt;br /&gt;
| FindToolType()&lt;br /&gt;
| Return the value of an entry in the icon&#039;s Tool Type array&lt;br /&gt;
|-&lt;br /&gt;
| MatchToolValue()&lt;br /&gt;
| Check a Tool Type entry against a given value&lt;br /&gt;
|-&lt;br /&gt;
| GetDefDiskObject()&lt;br /&gt;
| Read the default icon for a given icon type&lt;br /&gt;
|-&lt;br /&gt;
| PutDefDiskObject()&lt;br /&gt;
| Replace the default icon for a given icon type&lt;br /&gt;
|-&lt;br /&gt;
| AddFreeList()&lt;br /&gt;
| Add memory you have allocated to a FreeList&lt;br /&gt;
|-&lt;br /&gt;
| FreeFreeList()&lt;br /&gt;
| Free all the memory for entries in the FreeList&lt;br /&gt;
|-&lt;br /&gt;
| BumpRevision()&lt;br /&gt;
| Create a new name for a second copy of a Workbench object&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Tags&amp;diff=12084</id>
		<title>Tags</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Tags&amp;diff=12084"/>
		<updated>2021-03-11T22:25:38Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Removed an extraneous IUtility-&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Tags make it possible to add new parameters to system functions without interfering with the original parameters. They also make specifying parameter lists much clearer and easier.&lt;br /&gt;
&lt;br /&gt;
= Tag Structures =&lt;br /&gt;
&lt;br /&gt;
A tag is made up of an attribute/value pair as defined below (from &amp;amp;lt;utility/tagitem.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem&lt;br /&gt;
{&lt;br /&gt;
    uint32 ti_Tag;    /* identifies the type of this item */&lt;br /&gt;
    uint32 ti_Data;   /* type-specific data, can be a pointer */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ti_Tag field specifies an attribute to set. The possible values of ti_Tag are implementation specific. System tags are defined in the include files. The value the attribute is set to is specified in ti_Data. An example of the attribute/value pair that will specify a window&#039;s name is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ti_Tag  = WA_Title;&lt;br /&gt;
ti_Data = &amp;quot;My Window&#039;s Name&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ti_Data field often contains 32-bit data as well as pointers.&lt;br /&gt;
&lt;br /&gt;
These are brief descriptions of the utility functions you can use to manipulate and access tags. For complete descriptions, see the &amp;quot;Simple Tag Usage&amp;quot; and &amp;quot;Advanced Tag Usage&amp;quot; sections.&lt;br /&gt;
&lt;br /&gt;
= Simple Tag Usage =&lt;br /&gt;
&lt;br /&gt;
One way tags are passed to system functions is in the form of tag lists. A tag list is an array or chain of arrays of TagItem structures. Within this array, different data items are identified by the value of ti_Tag. Items specific to a subsystem (Intuition, Graphics,...) have a ti_Tag value which has the TAG_USER bit set. Global system tags have a ti_Tag value with TAG_USER bit clear. The global system tags include:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Global System Tags&lt;br /&gt;
! Tag Value&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| TAG_IGNORE || A no-op. The data item is ignored.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_MORE || The ti_Data points to another tag list, to support chaining of TagItem arrays.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_END || Terminates the TagItem array (or chain). TAG_DONE is a synonym.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_SKIP || Ignore the current tag item, and skip the next &#039;&#039;n&#039;&#039; array elements, where &#039;&#039;n&#039;&#039; is kept in ti_Data.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that user tags need only be unique within the particular context of their use. For example, the attribute tags defined for OpenWindow() have the same numeric value as some tags used by OpenScreen(), but the same numeric value has different meaning in the different contexts.&lt;br /&gt;
&lt;br /&gt;
System functions receive TagItems in several ways. One way is illustrated in the Intuition function OpenWindow(). This function supports an extented NewWindow structure called ExtNewWindow. When the NW_EXTENDED flag is set in the ExtNewWindow.Flags field, OpenWindow() assumes that the ExtNewWindow.Extension field contains a pointer to a tag list.&lt;br /&gt;
&lt;br /&gt;
Another method of passing a tag list is to directly pass a pointer to a tag list, as OpenWindowTagList() does in the following code fragment.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem tagitem[3];&lt;br /&gt;
struct Screen *screen;&lt;br /&gt;
struct Window *window;&lt;br /&gt;
&lt;br /&gt;
tagitem[0].ti_Tag  = WA_CustomScreen;&lt;br /&gt;
tagitem[0].ti_Data = screen;    /* Open on my own screen */&lt;br /&gt;
tagitem[1].ti_Tag  = WA_Title;&lt;br /&gt;
tagitem[1].ti_Data = &amp;quot;Amiga Test Window&amp;quot;;&lt;br /&gt;
tagitem[2].ti_Tag  = TAG_END;   /* Marks the end of the tag array. */&lt;br /&gt;
&lt;br /&gt;
/* Use defaults for everything else. Will open as big as the screen. */&lt;br /&gt;
/* Because all window parameters are specified using tags, we don&#039;t  */&lt;br /&gt;
/* need a NewWindow structure                                        */&lt;br /&gt;
&lt;br /&gt;
if (window = IIntuition-&amp;gt;OpenWindowTagList(NULL, tagitem))&lt;br /&gt;
{&lt;br /&gt;
    /* rest of code */&lt;br /&gt;
    IIntuition-&amp;gt;CloseWindow(window);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that window parameters need not be explicitly specified. Functions that utilize tags have reasonable defaults to fall back on in case no valid attribute/value pair was supplied for a particular parameter. This fall back capability is a useful feature. An application only has to specify the attributes that differ from the default, rather than unnecessarily listing all the possible attributes.&lt;br /&gt;
&lt;br /&gt;
Many functions also support another way to pass TagItems. Rather than passing a tag list, the function OpenWindowTags() receives the attribute/value pairs in the argument list, much like the standard C function printf() receives its arguments. Any number of attribute/value pairs can be specified. This type of argument passing is called VarArgs. The following code fragment illustrates the usage of OpenWindowTags().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Window *window;&lt;br /&gt;
&lt;br /&gt;
/* Just pass NULL to show we aren&#039;t using a NewWindow */&lt;br /&gt;
window = IIntuition-&amp;gt;OpenWindowTags( NULL,&lt;br /&gt;
                         WA_CustomScreen, screen,&lt;br /&gt;
                         WA_Title, &amp;quot;Amiga Test Window&amp;quot;,&lt;br /&gt;
                         TAG_END );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Tags are not exclusively for use with the operating system; the programmer can implement them as well. The run-time utility library contains several functions to make using tags easier.&lt;br /&gt;
&lt;br /&gt;
== Simple Tag Usage Example ==&lt;br /&gt;
&lt;br /&gt;
The following example shows simple usage of tags. It shows how to allocate a tag array and use it, it also shows how to build a tag array on the stack.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** tag1.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/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;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;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
int main (int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem *tags;&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;
    if (IIntuition != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
        /* This section allocates a tag array, fills it in with values, */&lt;br /&gt;
        /* and then uses it.                                            */&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
&lt;br /&gt;
        /* Allocate a tag array */&lt;br /&gt;
        if (tags = IUtility-&amp;gt;AllocateTagItems (7))&lt;br /&gt;
        {&lt;br /&gt;
            /* Fill in our tag array */&lt;br /&gt;
            tags[0].ti_Tag = WA_Width;&lt;br /&gt;
            tags[0].ti_Data = 320;&lt;br /&gt;
            tags[1].ti_Tag = WA_Height;&lt;br /&gt;
            tags[1].ti_Data = 50;&lt;br /&gt;
            tags[2].ti_Tag = WA_Title;&lt;br /&gt;
            tags[2].ti_Data = (uint32) &amp;quot;Amiga Tag Example 1&amp;quot;;&lt;br /&gt;
            tags[3].ti_Tag = WA_IDCMP;&lt;br /&gt;
            tags[3].ti_Data = IDCMP_CLOSEWINDOW;&lt;br /&gt;
            tags[4].ti_Tag = WA_CloseGadget;&lt;br /&gt;
            tags[4].ti_Data = TRUE;&lt;br /&gt;
            tags[5].ti_Tag = WA_DragBar;&lt;br /&gt;
            tags[5].ti_Data = TRUE;&lt;br /&gt;
            tags[6].ti_Tag = TAG_END;&lt;br /&gt;
&lt;br /&gt;
            /* Open the window, using the tag attributes as the&lt;br /&gt;
             * only description. */&lt;br /&gt;
            if (win = IIntuition-&amp;gt;OpenWindowTagList (NULL, tags))&lt;br /&gt;
            {&lt;br /&gt;
                /* Wait for an event to occur */&lt;br /&gt;
                IExec-&amp;gt;WaitPort (win-&amp;gt;UserPort);&lt;br /&gt;
&lt;br /&gt;
                /* Close the window now that we&#039;re done with it */&lt;br /&gt;
                IIntuition-&amp;gt;CloseWindow (win);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Free the tag list now that we&#039;re done with it */&lt;br /&gt;
            IUtility-&amp;gt;FreeTagItems(tags);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
        /* This section builds the tag array on the stack, and passes   */&lt;br /&gt;
        /* the array to a function.                                     */&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
&lt;br /&gt;
        /* Now use the VarArgs (or stack based) version. */&lt;br /&gt;
        if (win = IIntuition-&amp;gt;OpenWindowTags ( NULL,&lt;br /&gt;
                                   WA_Width, 320,&lt;br /&gt;
                                   WA_Height, 50,&lt;br /&gt;
                                   WA_Title, &amp;quot;Amiga Tag Example 1&amp;quot;,&lt;br /&gt;
                                   WA_IDCMP, IDCMP_CLOSEWINDOW,&lt;br /&gt;
                                   WA_CloseGadget, TRUE,&lt;br /&gt;
                                   WA_DragBar, TRUE,&lt;br /&gt;
                                   TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            /* Wait for an event to occur */&lt;br /&gt;
            IExec-&amp;gt;WaitPort (win-&amp;gt;UserPort);&lt;br /&gt;
&lt;br /&gt;
            /* Close the window now that we&#039;re done with it */&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;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Advanced Tag Usage =&lt;br /&gt;
&lt;br /&gt;
The previous section provided the background material necessary to start using tags. This section will show how to use the more advanced features of tags using functions within utility library.&lt;br /&gt;
&lt;br /&gt;
== Creating a New Tag List ==&lt;br /&gt;
&lt;br /&gt;
The AllocSysObjectTags() function with an object type of ASOT_TAGLIST can be used to create a new tag array ready for use. The tag array should be passed to FreeSysObject() when the application is done with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Indicate how many tags we need */&lt;br /&gt;
uint32 tags_needed = 10;&lt;br /&gt;
&lt;br /&gt;
/* Allocate a tag array */&lt;br /&gt;
struct TagItem *tags = IExec-&amp;gt;AllocSysObjectTags(ASOT_TAGLIST,&lt;br /&gt;
  ASOTAGS_NumEntries, tags_needed,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (tags != NULL)&lt;br /&gt;
{&lt;br /&gt;
    /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
    /* Free the array when your done with it */&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject (ASOT_TAGLIST, tags);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copying an Existing Tag List ==&lt;br /&gt;
&lt;br /&gt;
The CloneTagItems() function is used to copy an existing tag array into a new tag array.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *otags;      /* Original tag array */&lt;br /&gt;
struct TagItem *ntags;      /* New tag array */&lt;br /&gt;
&lt;br /&gt;
/* Make sure there is a TagItem array */&lt;br /&gt;
if (otags)&lt;br /&gt;
{&lt;br /&gt;
    /* Copy the original tags into a new tag array */&lt;br /&gt;
    if (ntags = IUtility-&amp;gt;CloneTagItems(otags))&lt;br /&gt;
    {&lt;br /&gt;
        /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
        /* Free the array when your done with it */&lt;br /&gt;
        IUtility-&amp;gt;FreeTagItems (ntags);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function can also be used to implement a function that will insert tag items into an array.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *otags;      /* Original tag array */&lt;br /&gt;
struct TagItem *tags;       /* New tag array */&lt;br /&gt;
&lt;br /&gt;
/* Insert a couple of tags into an existing tag array */&lt;br /&gt;
if (tags = MakeNewTagList (GA_LeftEdge, 10,&lt;br /&gt;
                           GA_TopEdge, 20,&lt;br /&gt;
                           TAG_MORE, otags))&lt;br /&gt;
{&lt;br /&gt;
    /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
    /* Free the array when your done with it */&lt;br /&gt;
    IUtility-&amp;gt;FreeTagItems (tags);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* This function will create a tag array from tag pairs placed on&lt;br /&gt;
 * the stack */&lt;br /&gt;
struct TagItem *MakeNewTagList (uint32 data,...)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem *tags = (struct TagItem *) &amp;amp;data;&lt;br /&gt;
&lt;br /&gt;
    return IUtility-&amp;gt;CloneTagItems (tags);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Filtering an Existing Tag List ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is necessary to only allow certain attributes to be visible in a tag list. In order to achieve this, the tag array would need to be filtered.&lt;br /&gt;
&lt;br /&gt;
A number of functions are provided for filtering items in a tag array. They are FilterTagChanges(), FilterTagItems() and RefreshTagItemClone().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* We want the text entry gadget to receive the following tags */&lt;br /&gt;
Tag string_attrs[] =&lt;br /&gt;
{&lt;br /&gt;
    STRINGA_MaxChars,&lt;br /&gt;
    STRINGA_Buffer,&lt;br /&gt;
    STRINGA_TextVal,&lt;br /&gt;
    TAG_END,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* These are attributes that the model understands */&lt;br /&gt;
Tag model_attrs[] =&lt;br /&gt;
{&lt;br /&gt;
    CGTA_Total,&lt;br /&gt;
    CGTA_Visible,&lt;br /&gt;
    CGTA_Top,&lt;br /&gt;
    ICA_TARGET,&lt;br /&gt;
    ICA_MAP,&lt;br /&gt;
    TAG_END,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct TagItem *otags;      /* Original tag list */&lt;br /&gt;
struct TagItem *ntags;      /* New, work, tag list */&lt;br /&gt;
&lt;br /&gt;
/* Make a copy of the original for us to work with */&lt;br /&gt;
ntags = IUtility-&amp;gt;CloneTagItems (otags);&lt;br /&gt;
&lt;br /&gt;
/* Create a tag list that only contains attributes that are&lt;br /&gt;
 * listed in the model_attrs list. */&lt;br /&gt;
if (IUtility-&amp;gt;FilterTagItems (ntags, model_attrs, TAGFILTER_AND))&lt;br /&gt;
{&lt;br /&gt;
    /* Work with filtered tag list (ntags) */&lt;br /&gt;
&lt;br /&gt;
    /* Restore the tag list */&lt;br /&gt;
    IUtility-&amp;gt;RefreshTagItemClones (ntags, otags);&lt;br /&gt;
&lt;br /&gt;
    /* Create a tag list that only contains attributes that&lt;br /&gt;
     * aren&#039;t in the model_attrs list. */&lt;br /&gt;
    if (IUtility-&amp;gt;FilterTagItems (ntags, model_attrs, TAGFILTER_NOT))&lt;br /&gt;
    {&lt;br /&gt;
        /* Work with filtered tag list (ntags) */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Restore the tag list */&lt;br /&gt;
    IUtility-&amp;gt;RefreshTagItemClones (ntags, otags);&lt;br /&gt;
&lt;br /&gt;
    /* Create a tag list that only contains attributes that&lt;br /&gt;
     * are in the string_attrs list. */&lt;br /&gt;
    if (IUtility-&amp;gt;FilterTagItems (ntags, string_attrs, TAGFILTER_AND))&lt;br /&gt;
    {&lt;br /&gt;
        /* Work with filtered tag list (ntags) */&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Free work tag list. */&lt;br /&gt;
IUtility-&amp;gt;FreeTagItems (ntags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Locating an Attribute ==&lt;br /&gt;
&lt;br /&gt;
To see if an attribute is in a tag array, the TagInArray() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* See if the listview labels attribute is located in a tag array */&lt;br /&gt;
if (IUtility-&amp;gt;TagItemArray(GTLV_Labels, tags))&lt;br /&gt;
{&lt;br /&gt;
    /* Yes, the attribute is in the list */&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    /* No, the attribute isn&#039;t in the list */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The FindTagItem() function will return a pointer to the actual tag that has the desired attribute. This allows you to manipulate the tag or to determine if the attribute exists but just has a NULL value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *tag;&lt;br /&gt;
&lt;br /&gt;
/* See if they are trying to set a sound */&lt;br /&gt;
if (tag = IUtility-&amp;gt;FindTagItem(MGA_Sound, attrs))&lt;br /&gt;
{&lt;br /&gt;
    /* Set the sound attribute to point to the specified sound data */&lt;br /&gt;
    tag-&amp;gt;ti_Data = sound;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sequential Access of Tag Lists ==&lt;br /&gt;
&lt;br /&gt;
In order to sequentially access the members of a tag array, the NextTagItem() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *tags = msg-&amp;gt;ops_AttrList;&lt;br /&gt;
struct TagItem *tstate;&lt;br /&gt;
struct TagItem *tag;&lt;br /&gt;
&lt;br /&gt;
/* Start at the beginning */&lt;br /&gt;
tstate = tags;&lt;br /&gt;
&lt;br /&gt;
/* Step through the tag list while there are still items in the&lt;br /&gt;
 * list */&lt;br /&gt;
while (tag = IUtility-&amp;gt;NextTagItem (&amp;amp;tstate))&lt;br /&gt;
{&lt;br /&gt;
    /* Cache the data for the current element */&lt;br /&gt;
    uint32 tidata = tag-&amp;gt;ti_Data;&lt;br /&gt;
&lt;br /&gt;
    /* Handle each attribute that we understand */&lt;br /&gt;
    switch (tag-&amp;gt;ti_Tag)&lt;br /&gt;
    {&lt;br /&gt;
        /* Put a case statement here for each attribute that your&lt;br /&gt;
         * function understands */&lt;br /&gt;
        case PGA_Freedom:&lt;br /&gt;
            lod-&amp;gt;lod_Flags |= tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case GTLV_Labels:&lt;br /&gt;
            lod-&amp;gt;lod_List = (struct List *) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* We don&#039;t understand this attribute */&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Random Access of Tag Lists ==&lt;br /&gt;
&lt;br /&gt;
The GetTagData() function will return the data for the specified attribute. If there isn&#039;t a tag that matches, then the default value is returned.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Get the sound data that our function will use. */&lt;br /&gt;
APTR sound = (APTR) IUtility-&amp;gt;GetTagData (MGA_Sound, (uint32) DefaultSound, attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obtaining Boolean Values ==&lt;br /&gt;
&lt;br /&gt;
Often times data is best represented as simple boolean (TRUE or FALSE) values. The PackBoolTags() function provides an easy method for converting a tag list to bit fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* These are the attributes that we understand, with the&lt;br /&gt;
 * corresponding flag value. */&lt;br /&gt;
struct TagItem activation_bools[] =&lt;br /&gt;
{&lt;br /&gt;
    /* Attribute            Flags */&lt;br /&gt;
    {GA_ENDGADGET,          ENDGADGET},&lt;br /&gt;
    {GA_IMMEDIATE,          GADGIMMEDIATE},&lt;br /&gt;
    {GA_RELVERIFY,          RELVERIFY},&lt;br /&gt;
    {GA_FOLLOWMOUSE,        FOLLOWMOUSE},&lt;br /&gt;
    {GA_RIGHTBORDER,        RIGHTBORDER},&lt;br /&gt;
    {GA_LEFTBORDER,         LEFTBORDER},&lt;br /&gt;
    {GA_TOPBORDER,          TOPBORDER},&lt;br /&gt;
    {GA_BOTTOMBORDER,       BOTTOMBORDER},&lt;br /&gt;
    {GA_TOGGLESELECT,       TOGGLESELECT},&lt;br /&gt;
&lt;br /&gt;
    /* Terminate the array */&lt;br /&gt;
    {TAG_END}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* Set the activation field, based on the attributes passed */&lt;br /&gt;
g-&amp;gt;Activation = IUtility-&amp;gt;PackBoolTags(g-&amp;gt;Activation, tags, activation_bools);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mapping Tag Attributes ==&lt;br /&gt;
&lt;br /&gt;
To translate all occurrences of an attribute to another attribute, the MapTags() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem map_list[] =&lt;br /&gt;
{&lt;br /&gt;
    /* Original     New */&lt;br /&gt;
    {MGA_LeftEdge,  GA_LeftEdge},&lt;br /&gt;
    {MGA_TopEdge,   GA_TopEdge},&lt;br /&gt;
    {MGA_Width,     GA_Width},&lt;br /&gt;
    {MGA_Height,    GA_Height},&lt;br /&gt;
&lt;br /&gt;
    /* Terminate the array */&lt;br /&gt;
    {TAG_END}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Map the tags to the new attributes, keeping all attributes that&lt;br /&gt;
 * aren&#039;t included in the mapping array */&lt;br /&gt;
IUtility-&amp;gt;MapTags(tags, map_list, MAP_KEEP_NOT_FOUND);&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 utility library functions which pertain to tags and tag lists. 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;
| AllocSysObjectTags(ASOT_TAGLIST)&lt;br /&gt;
| Allocate a TagItem array (or chain).&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_TAGLIST)&lt;br /&gt;
| Frees allocated TagItem lists.&lt;br /&gt;
|-&lt;br /&gt;
| CloneTagItems()&lt;br /&gt;
| Copies a TagItem list.&lt;br /&gt;
|-&lt;br /&gt;
| RefreshTagItemClone()&lt;br /&gt;
| Rejuvenates a clone from the original.&lt;br /&gt;
|-&lt;br /&gt;
| FindTagItem()&lt;br /&gt;
| Scans TagItem list for a tag.&lt;br /&gt;
|-&lt;br /&gt;
| GetTagData()&lt;br /&gt;
| Obtain data corresponding to tag.&lt;br /&gt;
|-&lt;br /&gt;
| NextTagItem()&lt;br /&gt;
| Iterate TagItem lists.&lt;br /&gt;
|-&lt;br /&gt;
| TagInArray()&lt;br /&gt;
| Check if a tag value appears in a Tag array.&lt;br /&gt;
|-&lt;br /&gt;
| FilterTagChanges()&lt;br /&gt;
| Eliminate TagItems which specify no change.&lt;br /&gt;
|-&lt;br /&gt;
| FilterTagItems()&lt;br /&gt;
| Remove selected items from a TagItem list.&lt;br /&gt;
|-&lt;br /&gt;
| MapTags()&lt;br /&gt;
| Convert ti_Tag values in a list via map pairing.&lt;br /&gt;
|-&lt;br /&gt;
| PackBoolTags()&lt;br /&gt;
| Builds a &amp;quot;Flag&amp;quot; word from a TagItem list.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Tags&amp;diff=12083</id>
		<title>Tags</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Tags&amp;diff=12083"/>
		<updated>2021-03-11T22:24:14Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Fixed a typo in FreeSysObject(...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Tags make it possible to add new parameters to system functions without interfering with the original parameters. They also make specifying parameter lists much clearer and easier.&lt;br /&gt;
&lt;br /&gt;
= Tag Structures =&lt;br /&gt;
&lt;br /&gt;
A tag is made up of an attribute/value pair as defined below (from &amp;amp;lt;utility/tagitem.h&amp;amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem&lt;br /&gt;
{&lt;br /&gt;
    uint32 ti_Tag;    /* identifies the type of this item */&lt;br /&gt;
    uint32 ti_Data;   /* type-specific data, can be a pointer */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ti_Tag field specifies an attribute to set. The possible values of ti_Tag are implementation specific. System tags are defined in the include files. The value the attribute is set to is specified in ti_Data. An example of the attribute/value pair that will specify a window&#039;s name is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ti_Tag  = WA_Title;&lt;br /&gt;
ti_Data = &amp;quot;My Window&#039;s Name&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The ti_Data field often contains 32-bit data as well as pointers.&lt;br /&gt;
&lt;br /&gt;
These are brief descriptions of the utility functions you can use to manipulate and access tags. For complete descriptions, see the &amp;quot;Simple Tag Usage&amp;quot; and &amp;quot;Advanced Tag Usage&amp;quot; sections.&lt;br /&gt;
&lt;br /&gt;
= Simple Tag Usage =&lt;br /&gt;
&lt;br /&gt;
One way tags are passed to system functions is in the form of tag lists. A tag list is an array or chain of arrays of TagItem structures. Within this array, different data items are identified by the value of ti_Tag. Items specific to a subsystem (Intuition, Graphics,...) have a ti_Tag value which has the TAG_USER bit set. Global system tags have a ti_Tag value with TAG_USER bit clear. The global system tags include:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Global System Tags&lt;br /&gt;
! Tag Value&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| TAG_IGNORE || A no-op. The data item is ignored.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_MORE || The ti_Data points to another tag list, to support chaining of TagItem arrays.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_END || Terminates the TagItem array (or chain). TAG_DONE is a synonym.&lt;br /&gt;
|-&lt;br /&gt;
| TAG_SKIP || Ignore the current tag item, and skip the next &#039;&#039;n&#039;&#039; array elements, where &#039;&#039;n&#039;&#039; is kept in ti_Data.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that user tags need only be unique within the particular context of their use. For example, the attribute tags defined for OpenWindow() have the same numeric value as some tags used by OpenScreen(), but the same numeric value has different meaning in the different contexts.&lt;br /&gt;
&lt;br /&gt;
System functions receive TagItems in several ways. One way is illustrated in the Intuition function OpenWindow(). This function supports an extented NewWindow structure called ExtNewWindow. When the NW_EXTENDED flag is set in the ExtNewWindow.Flags field, OpenWindow() assumes that the ExtNewWindow.Extension field contains a pointer to a tag list.&lt;br /&gt;
&lt;br /&gt;
Another method of passing a tag list is to directly pass a pointer to a tag list, as OpenWindowTagList() does in the following code fragment.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem tagitem[3];&lt;br /&gt;
struct Screen *screen;&lt;br /&gt;
struct Window *window;&lt;br /&gt;
&lt;br /&gt;
tagitem[0].ti_Tag  = WA_CustomScreen;&lt;br /&gt;
tagitem[0].ti_Data = screen;    /* Open on my own screen */&lt;br /&gt;
tagitem[1].ti_Tag  = WA_Title;&lt;br /&gt;
tagitem[1].ti_Data = &amp;quot;Amiga Test Window&amp;quot;;&lt;br /&gt;
tagitem[2].ti_Tag  = TAG_END;   /* Marks the end of the tag array. */&lt;br /&gt;
&lt;br /&gt;
/* Use defaults for everything else. Will open as big as the screen. */&lt;br /&gt;
/* Because all window parameters are specified using tags, we don&#039;t  */&lt;br /&gt;
/* need a NewWindow structure                                        */&lt;br /&gt;
&lt;br /&gt;
if (window = IIntuition-&amp;gt;OpenWindowTagList(NULL, tagitem))&lt;br /&gt;
{&lt;br /&gt;
    /* rest of code */&lt;br /&gt;
    IIntuition-&amp;gt;CloseWindow(window);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that window parameters need not be explicitly specified. Functions that utilize tags have reasonable defaults to fall back on in case no valid attribute/value pair was supplied for a particular parameter. This fall back capability is a useful feature. An application only has to specify the attributes that differ from the default, rather than unnecessarily listing all the possible attributes.&lt;br /&gt;
&lt;br /&gt;
Many functions also support another way to pass TagItems. Rather than passing a tag list, the function OpenWindowTags() receives the attribute/value pairs in the argument list, much like the standard C function printf() receives its arguments. Any number of attribute/value pairs can be specified. This type of argument passing is called VarArgs. The following code fragment illustrates the usage of OpenWindowTags().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Window *window;&lt;br /&gt;
&lt;br /&gt;
/* Just pass NULL to show we aren&#039;t using a NewWindow */&lt;br /&gt;
window = IIntuition-&amp;gt;OpenWindowTags( NULL,&lt;br /&gt;
                         WA_CustomScreen, screen,&lt;br /&gt;
                         WA_Title, &amp;quot;Amiga Test Window&amp;quot;,&lt;br /&gt;
                         TAG_END );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Tags are not exclusively for use with the operating system; the programmer can implement them as well. The run-time utility library contains several functions to make using tags easier.&lt;br /&gt;
&lt;br /&gt;
== Simple Tag Usage Example ==&lt;br /&gt;
&lt;br /&gt;
The following example shows simple usage of tags. It shows how to allocate a tag array and use it, it also shows how to build a tag array on the stack.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
** tag1.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/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utility/tagitem.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;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;
#include &amp;lt;proto/utility.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
int main (int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem *tags;&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;
    if (IIntuition != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
        /* This section allocates a tag array, fills it in with values, */&lt;br /&gt;
        /* and then uses it.                                            */&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
&lt;br /&gt;
        /* Allocate a tag array */&lt;br /&gt;
        if (tags = IUtility-&amp;gt;AllocateTagItems (7))&lt;br /&gt;
        {&lt;br /&gt;
            /* Fill in our tag array */&lt;br /&gt;
            tags[0].ti_Tag = WA_Width;&lt;br /&gt;
            tags[0].ti_Data = 320;&lt;br /&gt;
            tags[1].ti_Tag = WA_Height;&lt;br /&gt;
            tags[1].ti_Data = 50;&lt;br /&gt;
            tags[2].ti_Tag = WA_Title;&lt;br /&gt;
            tags[2].ti_Data = (uint32) &amp;quot;Amiga Tag Example 1&amp;quot;;&lt;br /&gt;
            tags[3].ti_Tag = WA_IDCMP;&lt;br /&gt;
            tags[3].ti_Data = IDCMP_CLOSEWINDOW;&lt;br /&gt;
            tags[4].ti_Tag = WA_CloseGadget;&lt;br /&gt;
            tags[4].ti_Data = TRUE;&lt;br /&gt;
            tags[5].ti_Tag = WA_DragBar;&lt;br /&gt;
            tags[5].ti_Data = TRUE;&lt;br /&gt;
            tags[6].ti_Tag = TAG_END;&lt;br /&gt;
&lt;br /&gt;
            /* Open the window, using the tag attributes as the&lt;br /&gt;
             * only description. */&lt;br /&gt;
            if (win = IIntuition-&amp;gt;OpenWindowTagList (NULL, tags))&lt;br /&gt;
            {&lt;br /&gt;
                /* Wait for an event to occur */&lt;br /&gt;
                IExec-&amp;gt;WaitPort (win-&amp;gt;UserPort);&lt;br /&gt;
&lt;br /&gt;
                /* Close the window now that we&#039;re done with it */&lt;br /&gt;
                IIntuition-&amp;gt;CloseWindow (win);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            /* Free the tag list now that we&#039;re done with it */&lt;br /&gt;
            IUtility-&amp;gt;FreeTagItems(tags);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
        /* This section builds the tag array on the stack, and passes   */&lt;br /&gt;
        /* the array to a function.                                     */&lt;br /&gt;
        /****************************************************************/&lt;br /&gt;
&lt;br /&gt;
        /* Now use the VarArgs (or stack based) version. */&lt;br /&gt;
        if (win = IIntuition-&amp;gt;OpenWindowTags ( NULL,&lt;br /&gt;
                                   WA_Width, 320,&lt;br /&gt;
                                   WA_Height, 50,&lt;br /&gt;
                                   WA_Title, &amp;quot;Amiga Tag Example 1&amp;quot;,&lt;br /&gt;
                                   WA_IDCMP, IDCMP_CLOSEWINDOW,&lt;br /&gt;
                                   WA_CloseGadget, TRUE,&lt;br /&gt;
                                   WA_DragBar, TRUE,&lt;br /&gt;
                                   TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
            /* Wait for an event to occur */&lt;br /&gt;
            IExec-&amp;gt;WaitPort (win-&amp;gt;UserPort);&lt;br /&gt;
&lt;br /&gt;
            /* Close the window now that we&#039;re done with it */&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;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Advanced Tag Usage =&lt;br /&gt;
&lt;br /&gt;
The previous section provided the background material necessary to start using tags. This section will show how to use the more advanced features of tags using functions within utility library.&lt;br /&gt;
&lt;br /&gt;
== Creating a New Tag List ==&lt;br /&gt;
&lt;br /&gt;
The AllocSysObjectTags() function with an object type of ASOT_TAGLIST can be used to create a new tag array ready for use. The tag array should be passed to FreeSysObject() when the application is done with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Indicate how many tags we need */&lt;br /&gt;
uint32 tags_needed = 10;&lt;br /&gt;
&lt;br /&gt;
/* Allocate a tag array */&lt;br /&gt;
struct TagItem *tags = IExec-&amp;gt;AllocSysObjectTags(ASOT_TAGLIST,&lt;br /&gt;
  ASOTAGS_NumEntries, tags_needed,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (tags != NULL)&lt;br /&gt;
{&lt;br /&gt;
    /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
    /* Free the array when your done with it */&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject (ASOT_TAGLIST, tags);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copying an Existing Tag List ==&lt;br /&gt;
&lt;br /&gt;
The CloneTagItems() function is used to copy an existing tag array into a new tag array.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *otags;      /* Original tag array */&lt;br /&gt;
struct TagItem *ntags;      /* New tag array */&lt;br /&gt;
&lt;br /&gt;
/* Make sure there is a TagItem array */&lt;br /&gt;
if (otags)&lt;br /&gt;
{&lt;br /&gt;
    /* Copy the original tags into a new tag array */&lt;br /&gt;
    if (ntags = IUtility-&amp;gt;CloneTagItems(otags))&lt;br /&gt;
    {&lt;br /&gt;
        /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
        /* Free the array when your done with it */&lt;br /&gt;
        IUtility-&amp;gt;FreeTagItems (ntags);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function can also be used to implement a function that will insert tag items into an array.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *otags;      /* Original tag array */&lt;br /&gt;
struct TagItem *tags;       /* New tag array */&lt;br /&gt;
&lt;br /&gt;
/* Insert a couple of tags into an existing tag array */&lt;br /&gt;
if (tags = IUtility-&amp;gt;MakeNewTagList (GA_LeftEdge, 10,&lt;br /&gt;
                                     GA_TopEdge, 20,&lt;br /&gt;
                                     TAG_MORE, otags))&lt;br /&gt;
{&lt;br /&gt;
    /* ...do something with the array... */&lt;br /&gt;
&lt;br /&gt;
    /* Free the array when your done with it */&lt;br /&gt;
    IUtility-&amp;gt;FreeTagItems (tags);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* This function will create a tag array from tag pairs placed on&lt;br /&gt;
 * the stack */&lt;br /&gt;
struct TagItem *MakeNewTagList (uint32 data,...)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem *tags = (struct TagItem *) &amp;amp;data;&lt;br /&gt;
&lt;br /&gt;
    return IUtility-&amp;gt;CloneTagItems (tags);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Filtering an Existing Tag List ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is necessary to only allow certain attributes to be visible in a tag list. In order to achieve this, the tag array would need to be filtered.&lt;br /&gt;
&lt;br /&gt;
A number of functions are provided for filtering items in a tag array. They are FilterTagChanges(), FilterTagItems() and RefreshTagItemClone().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* We want the text entry gadget to receive the following tags */&lt;br /&gt;
Tag string_attrs[] =&lt;br /&gt;
{&lt;br /&gt;
    STRINGA_MaxChars,&lt;br /&gt;
    STRINGA_Buffer,&lt;br /&gt;
    STRINGA_TextVal,&lt;br /&gt;
    TAG_END,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* These are attributes that the model understands */&lt;br /&gt;
Tag model_attrs[] =&lt;br /&gt;
{&lt;br /&gt;
    CGTA_Total,&lt;br /&gt;
    CGTA_Visible,&lt;br /&gt;
    CGTA_Top,&lt;br /&gt;
    ICA_TARGET,&lt;br /&gt;
    ICA_MAP,&lt;br /&gt;
    TAG_END,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct TagItem *otags;      /* Original tag list */&lt;br /&gt;
struct TagItem *ntags;      /* New, work, tag list */&lt;br /&gt;
&lt;br /&gt;
/* Make a copy of the original for us to work with */&lt;br /&gt;
ntags = IUtility-&amp;gt;CloneTagItems (otags);&lt;br /&gt;
&lt;br /&gt;
/* Create a tag list that only contains attributes that are&lt;br /&gt;
 * listed in the model_attrs list. */&lt;br /&gt;
if (IUtility-&amp;gt;FilterTagItems (ntags, model_attrs, TAGFILTER_AND))&lt;br /&gt;
{&lt;br /&gt;
    /* Work with filtered tag list (ntags) */&lt;br /&gt;
&lt;br /&gt;
    /* Restore the tag list */&lt;br /&gt;
    IUtility-&amp;gt;RefreshTagItemClones (ntags, otags);&lt;br /&gt;
&lt;br /&gt;
    /* Create a tag list that only contains attributes that&lt;br /&gt;
     * aren&#039;t in the model_attrs list. */&lt;br /&gt;
    if (IUtility-&amp;gt;FilterTagItems (ntags, model_attrs, TAGFILTER_NOT))&lt;br /&gt;
    {&lt;br /&gt;
        /* Work with filtered tag list (ntags) */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Restore the tag list */&lt;br /&gt;
    IUtility-&amp;gt;RefreshTagItemClones (ntags, otags);&lt;br /&gt;
&lt;br /&gt;
    /* Create a tag list that only contains attributes that&lt;br /&gt;
     * are in the string_attrs list. */&lt;br /&gt;
    if (IUtility-&amp;gt;FilterTagItems (ntags, string_attrs, TAGFILTER_AND))&lt;br /&gt;
    {&lt;br /&gt;
        /* Work with filtered tag list (ntags) */&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Free work tag list. */&lt;br /&gt;
IUtility-&amp;gt;FreeTagItems (ntags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Locating an Attribute ==&lt;br /&gt;
&lt;br /&gt;
To see if an attribute is in a tag array, the TagInArray() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* See if the listview labels attribute is located in a tag array */&lt;br /&gt;
if (IUtility-&amp;gt;TagItemArray(GTLV_Labels, tags))&lt;br /&gt;
{&lt;br /&gt;
    /* Yes, the attribute is in the list */&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    /* No, the attribute isn&#039;t in the list */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The FindTagItem() function will return a pointer to the actual tag that has the desired attribute. This allows you to manipulate the tag or to determine if the attribute exists but just has a NULL value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *tag;&lt;br /&gt;
&lt;br /&gt;
/* See if they are trying to set a sound */&lt;br /&gt;
if (tag = IUtility-&amp;gt;FindTagItem(MGA_Sound, attrs))&lt;br /&gt;
{&lt;br /&gt;
    /* Set the sound attribute to point to the specified sound data */&lt;br /&gt;
    tag-&amp;gt;ti_Data = sound;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sequential Access of Tag Lists ==&lt;br /&gt;
&lt;br /&gt;
In order to sequentially access the members of a tag array, the NextTagItem() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem *tags = msg-&amp;gt;ops_AttrList;&lt;br /&gt;
struct TagItem *tstate;&lt;br /&gt;
struct TagItem *tag;&lt;br /&gt;
&lt;br /&gt;
/* Start at the beginning */&lt;br /&gt;
tstate = tags;&lt;br /&gt;
&lt;br /&gt;
/* Step through the tag list while there are still items in the&lt;br /&gt;
 * list */&lt;br /&gt;
while (tag = IUtility-&amp;gt;NextTagItem (&amp;amp;tstate))&lt;br /&gt;
{&lt;br /&gt;
    /* Cache the data for the current element */&lt;br /&gt;
    uint32 tidata = tag-&amp;gt;ti_Data;&lt;br /&gt;
&lt;br /&gt;
    /* Handle each attribute that we understand */&lt;br /&gt;
    switch (tag-&amp;gt;ti_Tag)&lt;br /&gt;
    {&lt;br /&gt;
        /* Put a case statement here for each attribute that your&lt;br /&gt;
         * function understands */&lt;br /&gt;
        case PGA_Freedom:&lt;br /&gt;
            lod-&amp;gt;lod_Flags |= tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case GTLV_Labels:&lt;br /&gt;
            lod-&amp;gt;lod_List = (struct List *) tidata;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        /* We don&#039;t understand this attribute */&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Random Access of Tag Lists ==&lt;br /&gt;
&lt;br /&gt;
The GetTagData() function will return the data for the specified attribute. If there isn&#039;t a tag that matches, then the default value is returned.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Get the sound data that our function will use. */&lt;br /&gt;
APTR sound = (APTR) IUtility-&amp;gt;GetTagData (MGA_Sound, (uint32) DefaultSound, attrs);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obtaining Boolean Values ==&lt;br /&gt;
&lt;br /&gt;
Often times data is best represented as simple boolean (TRUE or FALSE) values. The PackBoolTags() function provides an easy method for converting a tag list to bit fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* These are the attributes that we understand, with the&lt;br /&gt;
 * corresponding flag value. */&lt;br /&gt;
struct TagItem activation_bools[] =&lt;br /&gt;
{&lt;br /&gt;
    /* Attribute            Flags */&lt;br /&gt;
    {GA_ENDGADGET,          ENDGADGET},&lt;br /&gt;
    {GA_IMMEDIATE,          GADGIMMEDIATE},&lt;br /&gt;
    {GA_RELVERIFY,          RELVERIFY},&lt;br /&gt;
    {GA_FOLLOWMOUSE,        FOLLOWMOUSE},&lt;br /&gt;
    {GA_RIGHTBORDER,        RIGHTBORDER},&lt;br /&gt;
    {GA_LEFTBORDER,         LEFTBORDER},&lt;br /&gt;
    {GA_TOPBORDER,          TOPBORDER},&lt;br /&gt;
    {GA_BOTTOMBORDER,       BOTTOMBORDER},&lt;br /&gt;
    {GA_TOGGLESELECT,       TOGGLESELECT},&lt;br /&gt;
&lt;br /&gt;
    /* Terminate the array */&lt;br /&gt;
    {TAG_END}&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* Set the activation field, based on the attributes passed */&lt;br /&gt;
g-&amp;gt;Activation = IUtility-&amp;gt;PackBoolTags(g-&amp;gt;Activation, tags, activation_bools);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mapping Tag Attributes ==&lt;br /&gt;
&lt;br /&gt;
To translate all occurrences of an attribute to another attribute, the MapTags() function is used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct TagItem map_list[] =&lt;br /&gt;
{&lt;br /&gt;
    /* Original     New */&lt;br /&gt;
    {MGA_LeftEdge,  GA_LeftEdge},&lt;br /&gt;
    {MGA_TopEdge,   GA_TopEdge},&lt;br /&gt;
    {MGA_Width,     GA_Width},&lt;br /&gt;
    {MGA_Height,    GA_Height},&lt;br /&gt;
&lt;br /&gt;
    /* Terminate the array */&lt;br /&gt;
    {TAG_END}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Map the tags to the new attributes, keeping all attributes that&lt;br /&gt;
 * aren&#039;t included in the mapping array */&lt;br /&gt;
IUtility-&amp;gt;MapTags(tags, map_list, MAP_KEEP_NOT_FOUND);&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 utility library functions which pertain to tags and tag lists. 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;
| AllocSysObjectTags(ASOT_TAGLIST)&lt;br /&gt;
| Allocate a TagItem array (or chain).&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_TAGLIST)&lt;br /&gt;
| Frees allocated TagItem lists.&lt;br /&gt;
|-&lt;br /&gt;
| CloneTagItems()&lt;br /&gt;
| Copies a TagItem list.&lt;br /&gt;
|-&lt;br /&gt;
| RefreshTagItemClone()&lt;br /&gt;
| Rejuvenates a clone from the original.&lt;br /&gt;
|-&lt;br /&gt;
| FindTagItem()&lt;br /&gt;
| Scans TagItem list for a tag.&lt;br /&gt;
|-&lt;br /&gt;
| GetTagData()&lt;br /&gt;
| Obtain data corresponding to tag.&lt;br /&gt;
|-&lt;br /&gt;
| NextTagItem()&lt;br /&gt;
| Iterate TagItem lists.&lt;br /&gt;
|-&lt;br /&gt;
| TagInArray()&lt;br /&gt;
| Check if a tag value appears in a Tag array.&lt;br /&gt;
|-&lt;br /&gt;
| FilterTagChanges()&lt;br /&gt;
| Eliminate TagItems which specify no change.&lt;br /&gt;
|-&lt;br /&gt;
| FilterTagItems()&lt;br /&gt;
| Remove selected items from a TagItem list.&lt;br /&gt;
|-&lt;br /&gt;
| MapTags()&lt;br /&gt;
| Convert ti_Tag values in a list via map pairing.&lt;br /&gt;
|-&lt;br /&gt;
| PackBoolTags()&lt;br /&gt;
| Builds a &amp;quot;Flag&amp;quot; word from a TagItem list.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=11831</id>
		<title>Exec Messages and Ports</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=11831"/>
		<updated>2021-01-21T22:36:32Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* Port2.c */ Put ASOT_Message in upper case&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Messages and Ports ==&lt;br /&gt;
&lt;br /&gt;
For inter-process communication, Exec provides a consistent, high-performance mechanism of messages and ports. This mechanism is used to pass message structures of arbitrary sizes from task to task, interrupt to task, or task to software interrupt. In addition, messages are often used to coordinate operations between cooperating tasks. This section describes many of the details of using messages and ports that the casual Amiga programmer won&#039;t need. See [[Introduction_to_Exec|Introduction to Exec]] for a general introduction to using messages and ports.&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;message&#039;&#039; data structure has two parts: system linkage and message body. The system linkage is used by Exec to attach a given message to its destination. The message body contains the actual data of interest. The message body is any arbitrary data up to 64K bytes in size. The message body data can include pointers to other data blocks of any size.&lt;br /&gt;
&lt;br /&gt;
Messages are always sent to a predetermined destination &#039;&#039;port&#039;&#039;. At a port, incoming messages are queued in a first-in-first-out (FIFO) order. There are no system restrictions on the number of ports or the number of messages that may be queued to a port (other than the amount of available system memory).&lt;br /&gt;
&lt;br /&gt;
Messages are always queued by &#039;&#039;reference&#039;&#039;, i.e., by a pointer to the message. For performance reasons message copying is not performed. In essence, a message between two tasks is a temporary license for the receiving task to use a portion of the memory space of the sending task; that portion being the message itself. This means that if task A sends a message to task B, the message is still part of the task A context. Task A, however, should not access the message until it has been &#039;&#039;replied&#039;&#039;; that is, until task B has sent the message back, using the ReplyMsg() function. This technique of message exchange imposes important restrictions on message access.&lt;br /&gt;
&lt;br /&gt;
== Message Ports ==&lt;br /&gt;
&lt;br /&gt;
Message ports are rendezvous points at which messages are collected. A port may contain any number of outstanding messages from many different originators. When a message arrives at a port, the message is appended to the end of the list of messages for that port, and a pre-specified arrival action is invoked. This action may do nothing, or it may cause a predefined task signal or software interrupt (see [[Exec_Interrupts|Exec Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
Like many Exec structures, ports may be given a symbolic name. Such names are particularly useful for tasks that must rendezvous with dynamically created ports. They are also useful for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
A message port consists of a MsgPort structure as defined in the &amp;amp;lt;exec/ports.h&amp;amp;gt; and &amp;amp;lt;exec/ports.i&amp;amp;gt; include files. The C structure for a port is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort {&lt;br /&gt;
    struct Node  mp_Node;&lt;br /&gt;
    UBYTE        mp_Flags;&lt;br /&gt;
    UBYTE        mp_SigBit;&lt;br /&gt;
    struct Task *mp_SigTask;&lt;br /&gt;
    struct List  mp_MsgList;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mp_Node&lt;br /&gt;
: is a standard Node structure. This is useful for tasks that might want to rendezvous with a particular message port by name.&lt;br /&gt;
&lt;br /&gt;
; mp_FLags&lt;br /&gt;
: are used to indicate message arrival actions. See the explanation below.&lt;br /&gt;
&lt;br /&gt;
; mp_SigBit&lt;br /&gt;
: is the signal bit &#039;&#039;number&#039;&#039; when a port is used with the task signal arrival action.&lt;br /&gt;
&lt;br /&gt;
; mp_SigTask&lt;br /&gt;
: is a pointer to the task to be signaled. If a software interrupt arrival action is specified, this is a pointer to the interrupt structure.&lt;br /&gt;
&lt;br /&gt;
; mp_MsgList&lt;br /&gt;
: is the list header for all messages queued to this port. (See [[Exec_Lists_and_Queues|Exec Lists and Queues]]).&lt;br /&gt;
&lt;br /&gt;
The mp_Flags field contains a subfield indicated by the PF_ACTION mask. This sub-field specifies the message arrival action that occurs when a port receives a new message.&lt;br /&gt;
&lt;br /&gt;
The possibilities are as follows:&lt;br /&gt;
&lt;br /&gt;
; PA_SIGNAL&lt;br /&gt;
: This flag tells Exec to signal the mp_SigTask using signal number mp_SigBit on the arrival of a new message. Every time a message is put to the port another signal will occur regardless of how many messages have been queued to the port.&lt;br /&gt;
&lt;br /&gt;
; PA_SOFTINT&lt;br /&gt;
: This flag tells Exec to Cause() a software interrupt when a message arrives at the port. In this case, the mp_SigTask field must contain a pointer to a struct Interrupt rather than a Task pointer. The software interrupt will be Caused every time a message is received.&lt;br /&gt;
&lt;br /&gt;
; PA_IGNORE&lt;br /&gt;
: This flag tells Exec to perform no operation other than queuing the message. This action is often used to stop signaling or software interrupts without disturbing the contents of the mp_SigTask field.&lt;br /&gt;
&lt;br /&gt;
It is important to realize that a port&#039;s arrival action will occur for each new message queued, and that there is not a one-to-one correspondence between messages and signals. Task signals are only single-bit flags so there is no record of how many times a particular signal occurred. There may be many messages queued and only a single task signal; sometimes however there may be a signal, but no messages. All of this has certain implications when designing code that deals with these actions. Your code should not depend on receiving a signal for every message at your port. All of this is also true for software interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message Port ===&lt;br /&gt;
&lt;br /&gt;
To create a new message port use AllocSysObject() with an object type of ASOT_PORT. If you want to make the port &#039;&#039;public&#039;&#039;, you will need to use the ASOPORT_Name tag. Don&#039;t make a port public when it is not necessary for it to be so.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, functions such as CreatePort() and CreateMsgPort() were often used. Some older applications may even have created their own static message ports by hand. Although all of the older methods still function for backwards compatibility, they should no longer be used.&lt;br /&gt;
&lt;br /&gt;
Using dynamic message ports with ASOT_PORT is the only way to ensure your applications will remain compatible and its message ports automatically freed by the system when required.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message Port ===&lt;br /&gt;
&lt;br /&gt;
Before a message port is deleted, all outstanding messages from other tasks must be returned. This is done by getting and replying to all messages at the port until message queue is empty. Of course, there is no need to reply to messages owned by the current task (the task performing the port deletion). Public ports must be removed from the system properly before deallocation. If a signal was allocated for the message port, it must also be freed. FreeSysObject() handles all of this automatically.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, message ports must be freed using the correct corresponding function such as DeletePort() or DeleteMsgPort(). The FreeSysObject() function must only be used on ports which were allocated with AllocSysObject().&lt;br /&gt;
&lt;br /&gt;
=== How to Rendezvous at a Message Port ===&lt;br /&gt;
&lt;br /&gt;
The FindPort() function provides a means of finding the address of a public port given its symbolic name. For example, FindPort(&amp;amp;quot;Griffin&amp;amp;quot;) will return either the address of the message port named &amp;quot;Griffin&amp;quot; or NULL indicating that no such public port exists. Since FindPort() does not do any arbitration over access to public ports, the usage of FindPort() must be protected with Forbid()/Permit(). Names should be unique to prevent collisions among multiple applications. It is a good idea to use your application name as a prefix for your port name. FindPort() does not arbitrate for access to the port list. The owner of a port might remove it at any time. For these reasons a Forbid()/Permit() pair is required for the use of FindPort(). The port address can no longer be regarded as being valid after Permit() unless your application knows that the port cannot go away (for example, if your application created the port).&lt;br /&gt;
&lt;br /&gt;
The following is an example of how to safely put a message to a specific port:&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/ports.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL)&lt;br /&gt;
        IExec-&amp;gt;PutMsg(port,message);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
    return(port ? TRUE : FALSE);      /* If FALSE, the port was not found */&lt;br /&gt;
&lt;br /&gt;
    /* Once we&#039;ve done a Permit(), the port might go away and leave us with&lt;br /&gt;
       an invalid port address. So we return just a BOOL to indicate whether&lt;br /&gt;
       the message has been sent or not. */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, a message contains both system header information and the actual message content. The system header is of the Message form defined in &amp;lt;exec/ports.h&amp;gt;. This structure is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Message {&lt;br /&gt;
    struct Node     mn_Node;&lt;br /&gt;
    struct MsgPort *mn_ReplyPort;&lt;br /&gt;
    UWORD           mn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mn_Node&lt;br /&gt;
: is a standard Node structure used for port linkage.&lt;br /&gt;
&lt;br /&gt;
; mn_ReplyPort&lt;br /&gt;
: is used to indicate a port to which this message will be returned when a reply is necessary.&lt;br /&gt;
&lt;br /&gt;
; mn_Length&lt;br /&gt;
: indicates the total length of the message, including the Message structure itself.&lt;br /&gt;
&lt;br /&gt;
This structure is always attached to the head of all messages. For example, if you want a message structure that contains the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; coordinates of a point on the screen, you could define it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    UWORD          xy_X;&lt;br /&gt;
    UWORD          xy_Y;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this structure, the mn_Length field should be set to sizeof(struct XYMessage).&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are allocated using AllocSysObject() with an object type of ASOT_MESSAGE. The ASOMSG_ReplyPort tag is set to the reply port if desired. Some messages may be sent in one direction in which case the ASOMSG_ReplyPort tag is not used. The size of the message is indicated with ASOMSG_Length and includes the size of the payload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
  ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
  ASOMSG_ReplyPort, replyPort,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are freed using FreeSysObject(). Programmers should be careful not to free a message which is still in use or queues in a message port.&lt;br /&gt;
&lt;br /&gt;
=== Putting a Message ===&lt;br /&gt;
&lt;br /&gt;
A message is delivered to a given destination port with the PutMsg() function. The message is queued to the port, and that port&#039;s arrival action is invoked. If the action specifies a task signal or a software interrupt, the originating task may temporarily lose the processor while the destination processes the message. If a reply to the message is required, the mn_ReplyPort field must be set up prior to the call to PutMsg().&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment for putting a message to a public port. A complete example is at the end of the article.&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/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    uint16         xy_X;&lt;br /&gt;
    uint16         xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyport, *xyreplyport;&lt;br /&gt;
    struct XYMessage *msg;&lt;br /&gt;
    BOOL   foundport;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate the message we&#039;re going to send. */&lt;br /&gt;
    struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
      ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xymsg != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* The replyport we&#039;ll use to get response */&lt;br /&gt;
        if (xyreplyport = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)) {&lt;br /&gt;
&lt;br /&gt;
            xymsg-&amp;gt;xy_Msg.mn_ReplyPort = xyreplyport;&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            /* Now try to send that message to a public port named &amp;quot;xyport&amp;quot;.&lt;br /&gt;
             * If foundport eq 0, the port isn&#039;t out there.&lt;br /&gt;
             */&lt;br /&gt;
            if (foundport = SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
&lt;br /&gt;
            . . .                /* Now let&#039;s wait till the someone responds... */&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t find &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory for xymessage\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;
=== Waiting for a Message ===&lt;br /&gt;
&lt;br /&gt;
A task may go to sleep waiting for a message to arrive at one or more ports. This technique is widely used on the Amiga as a general form of event notification. For example, it is used extensively by tasks for I/O request completion.&lt;br /&gt;
&lt;br /&gt;
The MsgPort.mp_SigTask field contains the address of the task to be signaled and mp_SigBit contains a preallocated signal number (as described in [[Exec_Tasks|Exec Tasks]]).&lt;br /&gt;
&lt;br /&gt;
You can call the WaitPort() function to wait for a message to arrive at a port. This function will return the first message (it may not be the only) queued to a port. Note that your application must still call GetMsg() to remove the message from the port. If the port is empty, your task will go to sleep waiting for the first message. If the port is not empty, your task will not go to sleep. It is possible to receive a signal for a port without a message being present yet. The code processing the messages should be able to handle this. The following code illustrates WaitPort().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport == NULL)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct XYMessage *xy_msg = IExec-&amp;gt;WaitPort(xyport);  /* go to sleep until message arrives */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A more general form of waiting for a message involves the use of the Wait() function (see [[Exec_Signals|Exec Signals]]). This function waits for task event signals directly. If the signal assigned to the message port occurs, the task will awaken. Using the Wait() function is more general because you can wait for more than one signal. By combining the signal bits from each port into one mask for the Wait() function, a loop can be set up to process all messages at all ports.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example using Wait():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage *xy_msg;&lt;br /&gt;
BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport != NULL)&lt;br /&gt;
{&lt;br /&gt;
    uint32 portsig = 1 &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;&lt;br /&gt;
    uint32 usersig = SIGBREAKF_CTRL_C;            /* User can break with CTRL-C.  */&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 signal = IExec-&amp;gt;Wait(portsig | usersig);  /* Sleep till someone signals.  */&lt;br /&gt;
&lt;br /&gt;
        if (signal &amp;amp; portsig)              /* Got a signal at the msgport. */&lt;br /&gt;
        {   .  .  .&lt;br /&gt;
        }&lt;br /&gt;
        if (signal &amp;amp; usersig)              /* Got a signal from the user.  */&lt;br /&gt;
        {&lt;br /&gt;
            ABORT = TRUE;                  /* Time to clean up.            */&lt;br /&gt;
             . . .&lt;br /&gt;
        }&lt;br /&gt;
        if (ABORT) break;&lt;br /&gt;
    }&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=WaitPort() only returns a pointer to the first message in a port. It does not actually remove the message from the port queue.|title=WaitPort() Does Not Remove A Message}}&lt;br /&gt;
&lt;br /&gt;
=== Getting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are usually removed from ports with the GetMsg() function. This function removes the next message at the head of the port queue and returns a pointer to it. If there are no messages in a port, this function returns a zero.&lt;br /&gt;
&lt;br /&gt;
The example below illustrates the use of GetMsg() to print the contents of all messages in a port:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certain messages may be more important than others. Because ports impose FIFO ordering, these important messages may get queued behind other messages regardless of their priority. If it is necessary to recognize more important messages, it is easiest to create another port for these special messages.&lt;br /&gt;
&lt;br /&gt;
=== Replying ===&lt;br /&gt;
&lt;br /&gt;
When the operations associated with receiving a new message are finished, it is usually necessary to send the message back to the originator. The receiver replies the message by returning it to the originator using the ReplyMsg() function. This is important because it notifies the originator that the message can be reused or deallocated.&lt;br /&gt;
&lt;br /&gt;
The ReplyMsg() function serves this purpose. It returns the message to the port specified in the mn_ReplyPort field of the message. If this field is zero, no reply is returned.&lt;br /&gt;
&lt;br /&gt;
The previous example can be enhanced to reply to each of its messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport)) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg(xymsg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that the reply does not occur until &#039;&#039;after&#039;&#039; the message values have been used.&lt;br /&gt;
&lt;br /&gt;
Often the operations associated with receiving a message involve returning &#039;&#039;results&#039;&#039; to the originator. Typically this is done within the message itself. The receiver places the results in fields defined (or perhaps reused) within the message body before replying the message back to the originator. Receipt of the replied message at the originator reply port indicates it is once again safe for the originator to use or change the values found within the message.&lt;br /&gt;
&lt;br /&gt;
The following are two short example tasks that communicate by sending, waiting for and replying to messages. Run these two programs together.&lt;br /&gt;
&lt;br /&gt;
==== Port1.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port1.c - port and message example, run at the same time as port2.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.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;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xym_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
      ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 portsig = 1U &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;       /* Give user a `break&#039; signal. */&lt;br /&gt;
        uint32 usersig = SIGBREAKF_CTRL_C;&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Start port2 in another shell.  CTRL-C here when done.\n&amp;quot;);&lt;br /&gt;
        do&lt;br /&gt;
        {                                                   /* port1 will wait forever and reply   */&lt;br /&gt;
            uint32 signal = IExec-&amp;gt;Wait(portsig | usersig); /* to messages, until the user breaks. */&lt;br /&gt;
            struct XYMessage *xymsg = 0;&lt;br /&gt;
&lt;br /&gt;
                                   /* Since we only have one port that might get messages we     */&lt;br /&gt;
            if (signal &amp;amp; portsig)  /* have to reply to, it is not really necessary to test for   */&lt;br /&gt;
            {                      /* the portsignal. If there is not message at the port, xymsg */&lt;br /&gt;
&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* simply will be NULL. */&lt;br /&gt;
                {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 received: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&lt;br /&gt;
                    xymsg-&amp;gt;xy_X += 50;       /* Since we have not replied yet to the owner of    */&lt;br /&gt;
                    xymsg-&amp;gt;xy_Y += 50;       /* xymsg, we can change the data contents of xymsg. */&lt;br /&gt;
&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 replying with: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            if (signal &amp;amp; usersig)  /* The user wants to abort. */&lt;br /&gt;
            {&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* Make sure port is empty. */&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                ABORT = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while (ABORT == FALSE);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
        }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Port2.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port2.c - port and message example, run at the same time as port1.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.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;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyreplyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyreplyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        struct XYMessage *reply = NULL;&lt;br /&gt;
&lt;br /&gt;
        struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
          ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
          ASOMSG_ReplyPort, xyreplyport,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (xymsg != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;   /* our special message information. */&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Sending to port1: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
            &lt;br /&gt;
                                                                   /* port2 will simply try to put  */&lt;br /&gt;
            if (SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))  /* one message to port1 wait for */&lt;br /&gt;
            {                                                      /*  the reply, and then exit     */&lt;br /&gt;
                IExec-&amp;gt;WaitPort(xyreplyport);&lt;br /&gt;
                if (reply = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyreplyport))&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Reply contains: x = %d y = %d\n&amp;quot;,   /* We don&#039;t ReplyMsg since   */&lt;br /&gt;
                            xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);                /* WE initiated the message. */&lt;br /&gt;
&lt;br /&gt;
                      /* Since we only use this private port for receiving replies, and we sent     */&lt;br /&gt;
                      /* only one and got one reply there is no need to cleanup. For a public port, */&lt;br /&gt;
                      /* or if you pass a pointer to the port to another process, it is a very good */&lt;br /&gt;
                      /* habit to always handle all messages at the port before you delete it.      */&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t find &#039;xyport&#039;; start port1 in a separate shell\n&amp;quot;);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory\n&amp;quot;);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyreplyport\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL) IExec-&amp;gt;PutMsg(port, message);&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
    return(port ? TRUE : FALSE); /* FALSE if the port was not found */&lt;br /&gt;
&lt;br /&gt;
         /* Once we&#039;ve done a Permit(), the port might go away and leave us with an invalid port    */&lt;br /&gt;
}        /* address. So we return just a BOOL to indicate whether the message has been sent or not. */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control inter-task communication with messages and ports. See the SDK for 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;
| AddPort()&lt;br /&gt;
| Add a public message port to the system list.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_PORT)&lt;br /&gt;
| Allocate and initialize a new message port.&lt;br /&gt;
|-&lt;br /&gt;
| FindPort()&lt;br /&gt;
| Find a public message port in the system list.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_PORT)&lt;br /&gt;
| Free a message port.&lt;br /&gt;
|-&lt;br /&gt;
| GetMsg()&lt;br /&gt;
| Get next message from the message port.&lt;br /&gt;
|-&lt;br /&gt;
| PutMsg()&lt;br /&gt;
| Put a message to a message port.&lt;br /&gt;
|-&lt;br /&gt;
| RemPort()&lt;br /&gt;
| Remove a message port from the system list.&lt;br /&gt;
|-&lt;br /&gt;
| ReplyMsg()&lt;br /&gt;
| Reply to a message on its reply port.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=11830</id>
		<title>Exec Messages and Ports</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=11830"/>
		<updated>2021-01-21T22:15:20Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* Port2.c */ Added missing IExec-&amp;gt; to FindPort call&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Messages and Ports ==&lt;br /&gt;
&lt;br /&gt;
For inter-process communication, Exec provides a consistent, high-performance mechanism of messages and ports. This mechanism is used to pass message structures of arbitrary sizes from task to task, interrupt to task, or task to software interrupt. In addition, messages are often used to coordinate operations between cooperating tasks. This section describes many of the details of using messages and ports that the casual Amiga programmer won&#039;t need. See [[Introduction_to_Exec|Introduction to Exec]] for a general introduction to using messages and ports.&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;message&#039;&#039; data structure has two parts: system linkage and message body. The system linkage is used by Exec to attach a given message to its destination. The message body contains the actual data of interest. The message body is any arbitrary data up to 64K bytes in size. The message body data can include pointers to other data blocks of any size.&lt;br /&gt;
&lt;br /&gt;
Messages are always sent to a predetermined destination &#039;&#039;port&#039;&#039;. At a port, incoming messages are queued in a first-in-first-out (FIFO) order. There are no system restrictions on the number of ports or the number of messages that may be queued to a port (other than the amount of available system memory).&lt;br /&gt;
&lt;br /&gt;
Messages are always queued by &#039;&#039;reference&#039;&#039;, i.e., by a pointer to the message. For performance reasons message copying is not performed. In essence, a message between two tasks is a temporary license for the receiving task to use a portion of the memory space of the sending task; that portion being the message itself. This means that if task A sends a message to task B, the message is still part of the task A context. Task A, however, should not access the message until it has been &#039;&#039;replied&#039;&#039;; that is, until task B has sent the message back, using the ReplyMsg() function. This technique of message exchange imposes important restrictions on message access.&lt;br /&gt;
&lt;br /&gt;
== Message Ports ==&lt;br /&gt;
&lt;br /&gt;
Message ports are rendezvous points at which messages are collected. A port may contain any number of outstanding messages from many different originators. When a message arrives at a port, the message is appended to the end of the list of messages for that port, and a pre-specified arrival action is invoked. This action may do nothing, or it may cause a predefined task signal or software interrupt (see [[Exec_Interrupts|Exec Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
Like many Exec structures, ports may be given a symbolic name. Such names are particularly useful for tasks that must rendezvous with dynamically created ports. They are also useful for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
A message port consists of a MsgPort structure as defined in the &amp;amp;lt;exec/ports.h&amp;amp;gt; and &amp;amp;lt;exec/ports.i&amp;amp;gt; include files. The C structure for a port is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort {&lt;br /&gt;
    struct Node  mp_Node;&lt;br /&gt;
    UBYTE        mp_Flags;&lt;br /&gt;
    UBYTE        mp_SigBit;&lt;br /&gt;
    struct Task *mp_SigTask;&lt;br /&gt;
    struct List  mp_MsgList;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mp_Node&lt;br /&gt;
: is a standard Node structure. This is useful for tasks that might want to rendezvous with a particular message port by name.&lt;br /&gt;
&lt;br /&gt;
; mp_FLags&lt;br /&gt;
: are used to indicate message arrival actions. See the explanation below.&lt;br /&gt;
&lt;br /&gt;
; mp_SigBit&lt;br /&gt;
: is the signal bit &#039;&#039;number&#039;&#039; when a port is used with the task signal arrival action.&lt;br /&gt;
&lt;br /&gt;
; mp_SigTask&lt;br /&gt;
: is a pointer to the task to be signaled. If a software interrupt arrival action is specified, this is a pointer to the interrupt structure.&lt;br /&gt;
&lt;br /&gt;
; mp_MsgList&lt;br /&gt;
: is the list header for all messages queued to this port. (See [[Exec_Lists_and_Queues|Exec Lists and Queues]]).&lt;br /&gt;
&lt;br /&gt;
The mp_Flags field contains a subfield indicated by the PF_ACTION mask. This sub-field specifies the message arrival action that occurs when a port receives a new message.&lt;br /&gt;
&lt;br /&gt;
The possibilities are as follows:&lt;br /&gt;
&lt;br /&gt;
; PA_SIGNAL&lt;br /&gt;
: This flag tells Exec to signal the mp_SigTask using signal number mp_SigBit on the arrival of a new message. Every time a message is put to the port another signal will occur regardless of how many messages have been queued to the port.&lt;br /&gt;
&lt;br /&gt;
; PA_SOFTINT&lt;br /&gt;
: This flag tells Exec to Cause() a software interrupt when a message arrives at the port. In this case, the mp_SigTask field must contain a pointer to a struct Interrupt rather than a Task pointer. The software interrupt will be Caused every time a message is received.&lt;br /&gt;
&lt;br /&gt;
; PA_IGNORE&lt;br /&gt;
: This flag tells Exec to perform no operation other than queuing the message. This action is often used to stop signaling or software interrupts without disturbing the contents of the mp_SigTask field.&lt;br /&gt;
&lt;br /&gt;
It is important to realize that a port&#039;s arrival action will occur for each new message queued, and that there is not a one-to-one correspondence between messages and signals. Task signals are only single-bit flags so there is no record of how many times a particular signal occurred. There may be many messages queued and only a single task signal; sometimes however there may be a signal, but no messages. All of this has certain implications when designing code that deals with these actions. Your code should not depend on receiving a signal for every message at your port. All of this is also true for software interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message Port ===&lt;br /&gt;
&lt;br /&gt;
To create a new message port use AllocSysObject() with an object type of ASOT_PORT. If you want to make the port &#039;&#039;public&#039;&#039;, you will need to use the ASOPORT_Name tag. Don&#039;t make a port public when it is not necessary for it to be so.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, functions such as CreatePort() and CreateMsgPort() were often used. Some older applications may even have created their own static message ports by hand. Although all of the older methods still function for backwards compatibility, they should no longer be used.&lt;br /&gt;
&lt;br /&gt;
Using dynamic message ports with ASOT_PORT is the only way to ensure your applications will remain compatible and its message ports automatically freed by the system when required.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message Port ===&lt;br /&gt;
&lt;br /&gt;
Before a message port is deleted, all outstanding messages from other tasks must be returned. This is done by getting and replying to all messages at the port until message queue is empty. Of course, there is no need to reply to messages owned by the current task (the task performing the port deletion). Public ports must be removed from the system properly before deallocation. If a signal was allocated for the message port, it must also be freed. FreeSysObject() handles all of this automatically.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, message ports must be freed using the correct corresponding function such as DeletePort() or DeleteMsgPort(). The FreeSysObject() function must only be used on ports which were allocated with AllocSysObject().&lt;br /&gt;
&lt;br /&gt;
=== How to Rendezvous at a Message Port ===&lt;br /&gt;
&lt;br /&gt;
The FindPort() function provides a means of finding the address of a public port given its symbolic name. For example, FindPort(&amp;amp;quot;Griffin&amp;amp;quot;) will return either the address of the message port named &amp;quot;Griffin&amp;quot; or NULL indicating that no such public port exists. Since FindPort() does not do any arbitration over access to public ports, the usage of FindPort() must be protected with Forbid()/Permit(). Names should be unique to prevent collisions among multiple applications. It is a good idea to use your application name as a prefix for your port name. FindPort() does not arbitrate for access to the port list. The owner of a port might remove it at any time. For these reasons a Forbid()/Permit() pair is required for the use of FindPort(). The port address can no longer be regarded as being valid after Permit() unless your application knows that the port cannot go away (for example, if your application created the port).&lt;br /&gt;
&lt;br /&gt;
The following is an example of how to safely put a message to a specific port:&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/ports.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL)&lt;br /&gt;
        IExec-&amp;gt;PutMsg(port,message);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
    return(port ? TRUE : FALSE);      /* If FALSE, the port was not found */&lt;br /&gt;
&lt;br /&gt;
    /* Once we&#039;ve done a Permit(), the port might go away and leave us with&lt;br /&gt;
       an invalid port address. So we return just a BOOL to indicate whether&lt;br /&gt;
       the message has been sent or not. */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, a message contains both system header information and the actual message content. The system header is of the Message form defined in &amp;lt;exec/ports.h&amp;gt;. This structure is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Message {&lt;br /&gt;
    struct Node     mn_Node;&lt;br /&gt;
    struct MsgPort *mn_ReplyPort;&lt;br /&gt;
    UWORD           mn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mn_Node&lt;br /&gt;
: is a standard Node structure used for port linkage.&lt;br /&gt;
&lt;br /&gt;
; mn_ReplyPort&lt;br /&gt;
: is used to indicate a port to which this message will be returned when a reply is necessary.&lt;br /&gt;
&lt;br /&gt;
; mn_Length&lt;br /&gt;
: indicates the total length of the message, including the Message structure itself.&lt;br /&gt;
&lt;br /&gt;
This structure is always attached to the head of all messages. For example, if you want a message structure that contains the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; coordinates of a point on the screen, you could define it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    UWORD          xy_X;&lt;br /&gt;
    UWORD          xy_Y;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this structure, the mn_Length field should be set to sizeof(struct XYMessage).&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are allocated using AllocSysObject() with an object type of ASOT_MESSAGE. The ASOMSG_ReplyPort tag is set to the reply port if desired. Some messages may be sent in one direction in which case the ASOMSG_ReplyPort tag is not used. The size of the message is indicated with ASOMSG_Length and includes the size of the payload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
  ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
  ASOMSG_ReplyPort, replyPort,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are freed using FreeSysObject(). Programmers should be careful not to free a message which is still in use or queues in a message port.&lt;br /&gt;
&lt;br /&gt;
=== Putting a Message ===&lt;br /&gt;
&lt;br /&gt;
A message is delivered to a given destination port with the PutMsg() function. The message is queued to the port, and that port&#039;s arrival action is invoked. If the action specifies a task signal or a software interrupt, the originating task may temporarily lose the processor while the destination processes the message. If a reply to the message is required, the mn_ReplyPort field must be set up prior to the call to PutMsg().&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment for putting a message to a public port. A complete example is at the end of the article.&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/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    uint16         xy_X;&lt;br /&gt;
    uint16         xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyport, *xyreplyport;&lt;br /&gt;
    struct XYMessage *msg;&lt;br /&gt;
    BOOL   foundport;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate the message we&#039;re going to send. */&lt;br /&gt;
    struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
      ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xymsg != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* The replyport we&#039;ll use to get response */&lt;br /&gt;
        if (xyreplyport = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)) {&lt;br /&gt;
&lt;br /&gt;
            xymsg-&amp;gt;xy_Msg.mn_ReplyPort = xyreplyport;&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            /* Now try to send that message to a public port named &amp;quot;xyport&amp;quot;.&lt;br /&gt;
             * If foundport eq 0, the port isn&#039;t out there.&lt;br /&gt;
             */&lt;br /&gt;
            if (foundport = SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
&lt;br /&gt;
            . . .                /* Now let&#039;s wait till the someone responds... */&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t find &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory for xymessage\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;
=== Waiting for a Message ===&lt;br /&gt;
&lt;br /&gt;
A task may go to sleep waiting for a message to arrive at one or more ports. This technique is widely used on the Amiga as a general form of event notification. For example, it is used extensively by tasks for I/O request completion.&lt;br /&gt;
&lt;br /&gt;
The MsgPort.mp_SigTask field contains the address of the task to be signaled and mp_SigBit contains a preallocated signal number (as described in [[Exec_Tasks|Exec Tasks]]).&lt;br /&gt;
&lt;br /&gt;
You can call the WaitPort() function to wait for a message to arrive at a port. This function will return the first message (it may not be the only) queued to a port. Note that your application must still call GetMsg() to remove the message from the port. If the port is empty, your task will go to sleep waiting for the first message. If the port is not empty, your task will not go to sleep. It is possible to receive a signal for a port without a message being present yet. The code processing the messages should be able to handle this. The following code illustrates WaitPort().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport == NULL)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct XYMessage *xy_msg = IExec-&amp;gt;WaitPort(xyport);  /* go to sleep until message arrives */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A more general form of waiting for a message involves the use of the Wait() function (see [[Exec_Signals|Exec Signals]]). This function waits for task event signals directly. If the signal assigned to the message port occurs, the task will awaken. Using the Wait() function is more general because you can wait for more than one signal. By combining the signal bits from each port into one mask for the Wait() function, a loop can be set up to process all messages at all ports.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example using Wait():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage *xy_msg;&lt;br /&gt;
BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport != NULL)&lt;br /&gt;
{&lt;br /&gt;
    uint32 portsig = 1 &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;&lt;br /&gt;
    uint32 usersig = SIGBREAKF_CTRL_C;            /* User can break with CTRL-C.  */&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 signal = IExec-&amp;gt;Wait(portsig | usersig);  /* Sleep till someone signals.  */&lt;br /&gt;
&lt;br /&gt;
        if (signal &amp;amp; portsig)              /* Got a signal at the msgport. */&lt;br /&gt;
        {   .  .  .&lt;br /&gt;
        }&lt;br /&gt;
        if (signal &amp;amp; usersig)              /* Got a signal from the user.  */&lt;br /&gt;
        {&lt;br /&gt;
            ABORT = TRUE;                  /* Time to clean up.            */&lt;br /&gt;
             . . .&lt;br /&gt;
        }&lt;br /&gt;
        if (ABORT) break;&lt;br /&gt;
    }&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=WaitPort() only returns a pointer to the first message in a port. It does not actually remove the message from the port queue.|title=WaitPort() Does Not Remove A Message}}&lt;br /&gt;
&lt;br /&gt;
=== Getting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are usually removed from ports with the GetMsg() function. This function removes the next message at the head of the port queue and returns a pointer to it. If there are no messages in a port, this function returns a zero.&lt;br /&gt;
&lt;br /&gt;
The example below illustrates the use of GetMsg() to print the contents of all messages in a port:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certain messages may be more important than others. Because ports impose FIFO ordering, these important messages may get queued behind other messages regardless of their priority. If it is necessary to recognize more important messages, it is easiest to create another port for these special messages.&lt;br /&gt;
&lt;br /&gt;
=== Replying ===&lt;br /&gt;
&lt;br /&gt;
When the operations associated with receiving a new message are finished, it is usually necessary to send the message back to the originator. The receiver replies the message by returning it to the originator using the ReplyMsg() function. This is important because it notifies the originator that the message can be reused or deallocated.&lt;br /&gt;
&lt;br /&gt;
The ReplyMsg() function serves this purpose. It returns the message to the port specified in the mn_ReplyPort field of the message. If this field is zero, no reply is returned.&lt;br /&gt;
&lt;br /&gt;
The previous example can be enhanced to reply to each of its messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport)) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg(xymsg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that the reply does not occur until &#039;&#039;after&#039;&#039; the message values have been used.&lt;br /&gt;
&lt;br /&gt;
Often the operations associated with receiving a message involve returning &#039;&#039;results&#039;&#039; to the originator. Typically this is done within the message itself. The receiver places the results in fields defined (or perhaps reused) within the message body before replying the message back to the originator. Receipt of the replied message at the originator reply port indicates it is once again safe for the originator to use or change the values found within the message.&lt;br /&gt;
&lt;br /&gt;
The following are two short example tasks that communicate by sending, waiting for and replying to messages. Run these two programs together.&lt;br /&gt;
&lt;br /&gt;
==== Port1.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port1.c - port and message example, run at the same time as port2.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.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;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xym_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
      ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 portsig = 1U &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;       /* Give user a `break&#039; signal. */&lt;br /&gt;
        uint32 usersig = SIGBREAKF_CTRL_C;&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Start port2 in another shell.  CTRL-C here when done.\n&amp;quot;);&lt;br /&gt;
        do&lt;br /&gt;
        {                                                   /* port1 will wait forever and reply   */&lt;br /&gt;
            uint32 signal = IExec-&amp;gt;Wait(portsig | usersig); /* to messages, until the user breaks. */&lt;br /&gt;
            struct XYMessage *xymsg = 0;&lt;br /&gt;
&lt;br /&gt;
                                   /* Since we only have one port that might get messages we     */&lt;br /&gt;
            if (signal &amp;amp; portsig)  /* have to reply to, it is not really necessary to test for   */&lt;br /&gt;
            {                      /* the portsignal. If there is not message at the port, xymsg */&lt;br /&gt;
&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* simply will be NULL. */&lt;br /&gt;
                {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 received: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&lt;br /&gt;
                    xymsg-&amp;gt;xy_X += 50;       /* Since we have not replied yet to the owner of    */&lt;br /&gt;
                    xymsg-&amp;gt;xy_Y += 50;       /* xymsg, we can change the data contents of xymsg. */&lt;br /&gt;
&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 replying with: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            if (signal &amp;amp; usersig)  /* The user wants to abort. */&lt;br /&gt;
            {&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* Make sure port is empty. */&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                ABORT = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while (ABORT == FALSE);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
        }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Port2.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port2.c - port and message example, run at the same time as port1.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.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;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyreplyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyreplyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        struct XYMessage *reply = NULL;&lt;br /&gt;
&lt;br /&gt;
        struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
          ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
          ASOMSG_ReplyPort, xyreplyport,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (xymsg != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;   /* our special message information. */&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Sending to port1: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
            &lt;br /&gt;
                                                                   /* port2 will simply try to put  */&lt;br /&gt;
            if (SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))  /* one message to port1 wait for */&lt;br /&gt;
            {                                                      /*  the reply, and then exit     */&lt;br /&gt;
                IExec-&amp;gt;WaitPort(xyreplyport);&lt;br /&gt;
                if (reply = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyreplyport))&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Reply contains: x = %d y = %d\n&amp;quot;,   /* We don&#039;t ReplyMsg since   */&lt;br /&gt;
                            xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);                /* WE initiated the message. */&lt;br /&gt;
&lt;br /&gt;
                      /* Since we only use this private port for receiving replies, and we sent     */&lt;br /&gt;
                      /* only one and got one reply there is no need to cleanup. For a public port, */&lt;br /&gt;
                      /* or if you pass a pointer to the port to another process, it is a very good */&lt;br /&gt;
                      /* habit to always handle all messages at the port before you delete it.      */&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t find &#039;xyport&#039;; start port1 in a separate shell\n&amp;quot;);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_Message, xymsg);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory\n&amp;quot;);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyreplyport\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL) IExec-&amp;gt;PutMsg(port, message);&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
    return(port ? TRUE : FALSE); /* FALSE if the port was not found */&lt;br /&gt;
&lt;br /&gt;
         /* Once we&#039;ve done a Permit(), the port might go away and leave us with an invalid port    */&lt;br /&gt;
}        /* address. So we return just a BOOL to indicate whether the message has been sent or not. */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control inter-task communication with messages and ports. See the SDK for 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;
| AddPort()&lt;br /&gt;
| Add a public message port to the system list.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_PORT)&lt;br /&gt;
| Allocate and initialize a new message port.&lt;br /&gt;
|-&lt;br /&gt;
| FindPort()&lt;br /&gt;
| Find a public message port in the system list.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_PORT)&lt;br /&gt;
| Free a message port.&lt;br /&gt;
|-&lt;br /&gt;
| GetMsg()&lt;br /&gt;
| Get next message from the message port.&lt;br /&gt;
|-&lt;br /&gt;
| PutMsg()&lt;br /&gt;
| Put a message to a message port.&lt;br /&gt;
|-&lt;br /&gt;
| RemPort()&lt;br /&gt;
| Remove a message port from the system list.&lt;br /&gt;
|-&lt;br /&gt;
| ReplyMsg()&lt;br /&gt;
| Reply to a message on its reply port.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Error_Messages&amp;diff=11811</id>
		<title>AmigaOS Manual: AmigaDOS Error Messages</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=AmigaOS_Manual:_AmigaDOS_Error_Messages&amp;diff=11811"/>
		<updated>2020-12-25T10:26:54Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Missing word &amp;#039;deletion&amp;#039; at line 222&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This appendix lists AmigaDOS errors with their probable causes and suggestions for recovery. These error messages are the output from the system when your program fails or if a command is not executed as the result of a user error. Error messages differ from requesters, which are messages from the system that allow you to enter specific corrections, changes, or input so that the program, script, or command can continue execution. A requester that is not satisfied produces an error.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Error !! Messages !! Probable Cause !! Recovery Suggestion&lt;br /&gt;
|-&lt;br /&gt;
| 103&lt;br /&gt;
| Not enough memory available&lt;br /&gt;
| Not enough Memory in your Amiga to execute the operation. Memory may be fragmented.&lt;br /&gt;
| Close unnecessary windows and applications and re-issue the command. Reboot if this does not work. You may need to add more RAM to your system.&lt;br /&gt;
|-&lt;br /&gt;
| 105&lt;br /&gt;
| Task Table Full&lt;br /&gt;
| The Amiga is limited to 20 CLI tasks.&lt;br /&gt;
| Try closing a few and then enter the command again.&lt;br /&gt;
|-&lt;br /&gt;
| 115&lt;br /&gt;
| Bad number&lt;br /&gt;
| The command requires a numerical argument.&lt;br /&gt;
| Use the correct command format.&lt;br /&gt;
|-&lt;br /&gt;
| 116&lt;br /&gt;
| Required argument missing&lt;br /&gt;
| The command requires an argument that you did not supply.&lt;br /&gt;
| Use the correct command format.&lt;br /&gt;
|-&lt;br /&gt;
| 117&lt;br /&gt;
| Value after keyword missing&lt;br /&gt;
| Keyword was specified with no argument.&lt;br /&gt;
| Use the correct command format.&lt;br /&gt;
|-&lt;br /&gt;
| 118&lt;br /&gt;
| Wrong number of arguments&lt;br /&gt;
| Too few or too many arguments.&lt;br /&gt;
| Use the correct command format.&lt;br /&gt;
|-&lt;br /&gt;
| 119&lt;br /&gt;
| Unmatched quotes&lt;br /&gt;
| You have an odd number of quotation marks.&lt;br /&gt;
| Place double quotation marks at the beginning and end of the path or string.&lt;br /&gt;
|-&lt;br /&gt;
| 120&lt;br /&gt;
| Argument line invalid or too long&lt;br /&gt;
| Your command line is incorrect or contains too many arguments.&lt;br /&gt;
| Use the correct command format.&lt;br /&gt;
|-&lt;br /&gt;
| 121&lt;br /&gt;
| File is not executable&lt;br /&gt;
| You misspelled the command name or the file is not a loadable (program or script) file.&lt;br /&gt;
| Retype the file name, ensuring that the file is a program file. To execute a script, the s bit must be set or the EXECUTE command must be used.&lt;br /&gt;
|-&lt;br /&gt;
| 122&lt;br /&gt;
| Invalid resident library during load&lt;br /&gt;
| The required library was found but was not the correct type. This could be caused by having an old version of the library or the file is corrupt.&lt;br /&gt;
| Try searching for a newer version on the Internet or copying the file from your Workbench disks.&lt;br /&gt;
|-&lt;br /&gt;
| 202&lt;br /&gt;
| Object is in use&lt;br /&gt;
| The specified file or directory is being edited by another application or is assigned.&lt;br /&gt;
| Stop the application using the file or directory or remove the assignment.&lt;br /&gt;
|-&lt;br /&gt;
| 203&lt;br /&gt;
| Object already exists&lt;br /&gt;
| The name specified is assigned to another file or directory.&lt;br /&gt;
| Use another name or delete the existing file or directory first.&lt;br /&gt;
|-&lt;br /&gt;
| 204&lt;br /&gt;
| Directory not found&lt;br /&gt;
| AmigaDOS cannot find the specified directory.&lt;br /&gt;
| Check the directory name and location (use DIR if necessary).&lt;br /&gt;
|-&lt;br /&gt;
| 205&lt;br /&gt;
| Object not found&lt;br /&gt;
| AmigaDOS cannot find the specified file or device.&lt;br /&gt;
| Check the file name (use DIR) or the device name (use INFO).&lt;br /&gt;
|-&lt;br /&gt;
| 206&lt;br /&gt;
| Invalid window description&lt;br /&gt;
| Occurs when specifying a window size for a Shell, ED, or ICONX window. The window may be too big or too small or you omitted an argument. Also occurs with the NEWSHELL command, if a device name is supplied that is not a window.&lt;br /&gt;
| Use the correct window specification.&lt;br /&gt;
|-&lt;br /&gt;
| 209&lt;br /&gt;
| Packet request type unknown&lt;br /&gt;
| The device handler cannot do the requested operation. For example, the console handler cannot rename things.&lt;br /&gt;
| Check the request code passed to device handlers for the appropriate request.&lt;br /&gt;
|-&lt;br /&gt;
| 210&lt;br /&gt;
| Object name invalid&lt;br /&gt;
| There is an invalid character in the file name or the file name is too long.&lt;br /&gt;
| Retype the name; do not use any invalid characters or exceed the maximum length.&lt;br /&gt;
|-&lt;br /&gt;
| 211&lt;br /&gt;
| Invalid object lock&lt;br /&gt;
| The lock code was not recognized by the AmigaDOS call.&lt;br /&gt;
| This is a programming fault.&lt;br /&gt;
|-&lt;br /&gt;
| 212&lt;br /&gt;
| Object is not of required type&lt;br /&gt;
| You may have specified a file name for an operation that requires a directory name, or vice versa.&lt;br /&gt;
| Use the correct name and command format.&lt;br /&gt;
|-&lt;br /&gt;
| 213&lt;br /&gt;
| Disk not validated&lt;br /&gt;
| If you have just inserted a disk, the disk validation process may be in progress. It is also possible that the disk is corrupt.&lt;br /&gt;
| Wait for the validation process to finish. Watch for drive light to turn off. Allow a minute for floppy disks and several minutes for hard disks. Corrupt disks cannot be validated. If corrupted, try retrieving and copying the files to another disk.&lt;br /&gt;
|-&lt;br /&gt;
| 214&lt;br /&gt;
| Disk is write-protected&lt;br /&gt;
| The plastic tab is in the write-protect position or the disk has been lokked.&lt;br /&gt;
| Remove the disk, move the tab, and reinsert the disk, use a different disk, or use LOCK OFF command.&lt;br /&gt;
|-&lt;br /&gt;
| 215&lt;br /&gt;
| Rename across devices attempted&lt;br /&gt;
| RENAME can move a file from one directory to another, but not from one volume to another.&lt;br /&gt;
| Use COPY to copy the file to the destination volume. Delete it from the source volume, if desired. Then use RENAME, if desired.&lt;br /&gt;
|-&lt;br /&gt;
| 216&lt;br /&gt;
| Directory not empty&lt;br /&gt;
| You tried to delete a directory that contains file or subdirectories.&lt;br /&gt;
| Use the ALL option of DELETE if you wish to delete the directory and its contents.&lt;br /&gt;
|-&lt;br /&gt;
| 217&lt;br /&gt;
| Too many levels&lt;br /&gt;
| Directory nesting is too deep.&lt;br /&gt;
| Reorganize directories so that there are fewer levels or change directories in stages to reach the desired level.&lt;br /&gt;
|-&lt;br /&gt;
| 218&lt;br /&gt;
| Device (or volume) is not mounted&lt;br /&gt;
| If the devices is a floppy disk, it has not been inserted in a drive. If it is another type of device, it has not been mounted, or the name is misspelled.&lt;br /&gt;
| Insert the correct floppy disk, mount the device, check the spelling of the device name, revise your MountList/mount file, or assign the device name appropriately.&lt;br /&gt;
|-&lt;br /&gt;
| 219&lt;br /&gt;
| Seek error&lt;br /&gt;
| An error occurred while processing a file.&lt;br /&gt;
| Be sure that you only SEEK within the file. You cannot SEEK outside the bounds of the file.&lt;br /&gt;
|-&lt;br /&gt;
| 220&lt;br /&gt;
| Comment is too long&lt;br /&gt;
| You filenote has exceeded the maximum number of characters (79).&lt;br /&gt;
| Use a shorter filenote.&lt;br /&gt;
|-&lt;br /&gt;
| 221&lt;br /&gt;
| Disk is full&lt;br /&gt;
| There is not enough room on the disk to perform the requested operation.&lt;br /&gt;
| Delete unnecessary files or directories or use a different disk.&lt;br /&gt;
|-&lt;br /&gt;
| 222&lt;br /&gt;
| Object is protected from deletion&lt;br /&gt;
| The d (deletable) protection bit of the file or directory is clear.&lt;br /&gt;
| If you are certain that you want to delete the file or directory, use PROTECT to set the d bit or use the FORCE option of DELETE.&lt;br /&gt;
|-&lt;br /&gt;
| 223&lt;br /&gt;
| File is write protected&lt;br /&gt;
| The w (writable) protection bit of the file is clear.&lt;br /&gt;
| If you are certain that you want to overwrite the file, use PROTECT to set the w bit.&lt;br /&gt;
|-&lt;br /&gt;
| 224&lt;br /&gt;
| File is read protected&lt;br /&gt;
| The r (readable) protection bit of the file is clear.&lt;br /&gt;
| Use PROTECT to set the r bit of the file.&lt;br /&gt;
|-&lt;br /&gt;
| 225&lt;br /&gt;
| Not a valid DOS disk&lt;br /&gt;
| The disk in the drive is not an AmigaDOS disk, it has not been formatted, or it is corrupt.&lt;br /&gt;
| Be sure you are using the correct disk. If the disk worked previously, use a disk recovery program to salvage its files. Format unformatted disks.&lt;br /&gt;
|-&lt;br /&gt;
| 226&lt;br /&gt;
| No disk in drive&lt;br /&gt;
| The disk is not inserted in the specified drive.&lt;br /&gt;
| Insert the appropriate disk in the specified drive.&lt;br /&gt;
|-&lt;br /&gt;
| 232&lt;br /&gt;
| No more entries in directory&lt;br /&gt;
| The AmigaDOS call EXNEXT has no further entries in the directory you are examining.&lt;br /&gt;
| Stop calling EXNEXT.&lt;br /&gt;
|-&lt;br /&gt;
| 233&lt;br /&gt;
| Object is soft link&lt;br /&gt;
| Attempt was made to access a soft-link for a device that does not support it.&lt;br /&gt;
| No recovery.&lt;br /&gt;
|-&lt;br /&gt;
| 235&lt;br /&gt;
| Bad load file hunk&lt;br /&gt;
| The program loaded is corrupted.&lt;br /&gt;
| Load a new or original copy of the program.&lt;br /&gt;
|-&lt;br /&gt;
| 241&lt;br /&gt;
| Record lock collision&lt;br /&gt;
| Another application is accessing the database.&lt;br /&gt;
| Try accessing the database again.&lt;br /&gt;
|-&lt;br /&gt;
| 242&lt;br /&gt;
| Record lock timeout&lt;br /&gt;
| Another application has the database entry lokked.&lt;br /&gt;
| Try again or quit the other application and retry.&lt;br /&gt;
|-&lt;br /&gt;
| 303&lt;br /&gt;
| Buffer overflow&lt;br /&gt;
| Occurs if pattern matching string is too long.&lt;br /&gt;
| Make pattern matching string shorter.&lt;br /&gt;
|-&lt;br /&gt;
| 304&lt;br /&gt;
| ***Break&lt;br /&gt;
| Occurs if program stopped via &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;Ctrl&amp;lt;/kbd&amp;gt; + &amp;lt;kbd class=&amp;quot;keyboard-key nowrap&amp;quot; style=&amp;quot;border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;&amp;quot;&amp;gt;C&amp;lt;/kbd&amp;gt;.&lt;br /&gt;
| No recovery&lt;br /&gt;
|-&lt;br /&gt;
| 305&lt;br /&gt;
| File not executable&lt;br /&gt;
| The e (executable) bit of the file is clear.&lt;br /&gt;
| Same as Error 121.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Commodities_Exchange_Library&amp;diff=11779</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=11779"/>
		<updated>2020-10-22T08:29:04Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Replaced a &amp;quot;stdio&amp;quot; printf by a DOS Printf, as stdio.h is not included in this source example&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;
                    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;
                            /* 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>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=11778</id>
		<title>RealTime Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=RealTime_Library&amp;diff=11778"/>
		<updated>2020-10-11T18:47:41Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* Conductors and Playerlnfos */&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 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>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Newlib_Library&amp;diff=11599</id>
		<title>Newlib Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Newlib_Library&amp;diff=11599"/>
		<updated>2020-07-05T13:34:41Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Small correction in the last Shared Interface Pointer sentence.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The built-in AmigaOS C library is a variant of the [http://sourceware.org/newlib/ Newlib C standard library] implementation. This section describes features of Newlib which are unique to AmigaOS.&lt;br /&gt;
&lt;br /&gt;
== Shared Interface Pointer ==&lt;br /&gt;
&lt;br /&gt;
Newlib is a rather unique in that it uses a shared interface pointer name INewlib (struct Interface* type). This is only a concern when you are not using the standard C startup code and opening newlib.library directly. One consequence of using a shared interface pointer is that you must specify the NP_Child tag when using IDOS-&amp;gt;CreateNewProc() if your child process is going to share the parent process&#039; newlib context information (e.g. stdin, stdout and stderr).&lt;br /&gt;
&lt;br /&gt;
== Startup Code ==&lt;br /&gt;
&lt;br /&gt;
The standard C startup code provides information on whether your application was launched from Workbench, Shell console or as a file system. Your program always starts using the standard &#039;&#039;&#039;argc&#039;&#039;&#039; and &#039;&#039;&#039;argv&#039;&#039;&#039; parameters but they are interpreted differently depending on how it was started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Workbench ===&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;argc&#039;&#039;&#039; argument is equal to zero. The &#039;&#039;&#039;argv&#039;&#039;&#039; argument is a pointer to a struct WBStartup.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;workbench/startup.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  struct WBStartup *wbstartup;&lt;br /&gt;
&lt;br /&gt;
  if (argc == 0)&lt;br /&gt;
  {&lt;br /&gt;
    wbstartup = (struct WBStartup*)argv;&lt;br /&gt;
    // Parse wbstartup and enter main processing loop.&lt;br /&gt;
    ...&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;
=== Shell ===&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;&#039;argc&#039;&#039;&#039; is greater than zero then it means the application was started from the Shell console and the normal C startup rules apply.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  if (argc &amp;gt; 0)&lt;br /&gt;
  {&lt;br /&gt;
    // Parse argv and enter main processing loop.&lt;br /&gt;
    ...&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 System ===&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;&#039;argc&#039;&#039;&#039; is -1 then means the application was started as a file system. This special option should only be used by file systems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dosextens.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
  int result;&lt;br /&gt;
&lt;br /&gt;
  if (argc == -1)&lt;br /&gt;
  {&lt;br /&gt;
    struct DosPacket *pkt = (struct DosPacket*)argv;&lt;br /&gt;
    struct Message *msg = pkt-&amp;gt;dp_Link;&lt;br /&gt;
 &lt;br /&gt;
    // File system initialization.&lt;br /&gt;
    BOOL initialized = file_system_init(msg);&lt;br /&gt;
&lt;br /&gt;
    if (initialized)&lt;br /&gt;
    {&lt;br /&gt;
      // Reply to the startup packet before entering main loop.&lt;br /&gt;
      pkt-&amp;gt;dp_Res1 = DOSTRUE;&lt;br /&gt;
      pkt-&amp;gt;dp_Res2 = 0;&lt;br /&gt;
      IExec-&amp;gt;PutMsg(pkt-&amp;gt;dp_Port, msg);&lt;br /&gt;
&lt;br /&gt;
      // File system main loop.&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      result = RETURN_OK;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      // File system failed to initialize so return a DOS error code.&lt;br /&gt;
      pkt-&amp;gt;dp_Res1 = DOSFALSE;&lt;br /&gt;
      pkt-&amp;gt;dp_Res2 = ERROR_NO_FREE_STORE;&lt;br /&gt;
      IExec-&amp;gt;PutMsg(pkt-&amp;gt;dp_Port, msg);&lt;br /&gt;
&lt;br /&gt;
      result = RETURN_FAIL;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standard Interfaces ===&lt;br /&gt;
&lt;br /&gt;
The following interfaces are handled by the C startup code and are guaranteed to be present when main() is called:&lt;br /&gt;
* IExec&lt;br /&gt;
* IDOS&lt;br /&gt;
* IUtility&lt;br /&gt;
&lt;br /&gt;
=== Options ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
extern const char *__stdiowin;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Define &#039;&#039;&#039;__stdiowin&#039;&#039;&#039; to control the default standard I/O window which will open automatically if your program needs it. The default is &amp;quot;CON:64/48/800/200/[CLI command name|Task name]/AUTO/CLOSE/WAIT&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
extern int __unix_path_semantics;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Define to 1 to enable POSIX style path semantics which is useful when porting applications from UNIX.&lt;br /&gt;
&lt;br /&gt;
== DOS File Handles ==&lt;br /&gt;
&lt;br /&gt;
Sometimes the underlying DOS file handle may be required in some special circumstances. The &#039;&#039;&#039;_get_osfhandle()&#039;&#039;&#039; function is provided in the &#039;&#039;&#039;fcntl.h&#039;&#039;&#039; header file to access DOS file handles.&lt;br /&gt;
&lt;br /&gt;
The following is an example program which demonstrates the feature.&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
	printf(&amp;quot;0x%08x 0x%08x 0x%08x\n&amp;quot;,&lt;br /&gt;
		(unsigned int)IDOS-&amp;gt;Input(),&lt;br /&gt;
		(unsigned int)IDOS-&amp;gt;Output(),&lt;br /&gt;
		(unsigned int)IDOS-&amp;gt;ErrorOutput());&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;0x%08x 0x%08x 0x%08x\n&amp;quot;,&lt;br /&gt;
		_get_osfhandle( fileno(stdin) ),&lt;br /&gt;
		_get_osfhandle( fileno(stdout) ),&lt;br /&gt;
		_get_osfhandle( fileno(stderr) ));&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;
Note that this feature is available in newlib.library 53.20 and higher.&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Keymap_Library&amp;diff=10859</id>
		<title>Keymap Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Keymap_Library&amp;diff=10859"/>
		<updated>2019-08-18T15:54:50Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Fixed small typos in MapRawKey.c preventing accurate value to be printed to the console&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP}}&lt;br /&gt;
== Keymap Library ==&lt;br /&gt;
&lt;br /&gt;
Amiga computers are sold internationally with a variety of local keyboards which match the standards of particular countries. All Amigas have keyboards which are physically similar, and keys which output the same low-level raw key code for any particular physical key. However, in different countries, the keycaps of the keys may contain different letters or symbols. Since the physical position of a key determines the raw key code that it generates, raw key codes are not internationally compatible. For instance, on the German keyboard, the Y and Z keys are swapped when compared to the USA keyboard. The second key on the fifth row will generate the same raw key code on all Amiga keyboards, but should be decoded as a Z on a US keyboard and as a Y on a German keyboard.&lt;br /&gt;
&lt;br /&gt;
The Amiga uses the [http://www.ecma-international.org/publications/standards/Ecma-094.htm ECMA-94 Latin 1 International 8-bit] character set, and can map raw key codes to any desired ANSI character value, string, or escape sequence. This allows national keyboards to be supported by using keymaps. A keymap is a file which describes what character or string is tied to what key code. Generally, the user&#039;s startup-sequence will set a system default keymap that is correct for the user&#039;s keyboard. The console.device translates the raw key codes into the correct characters based on the installed keymap. This includes the translation of special deadkey sequential key combinations to produce accented international characters.&lt;br /&gt;
&lt;br /&gt;
Programs which perform keyboard input using the console.device, CON:, RAW:, or Intuition VANILLAKEY, will receive custom keymaps, or may need to perform their own translation between raw key codes and ANSI characters. In this article, the term ANSI refers to the standard 8-bit character definitions which include printable ASCII characters, special characters, and escape sequences.&lt;br /&gt;
&lt;br /&gt;
Keymap.library offers some of the keymap commands of the console.device, enabling applications to inquire after the default keymap and map key codes to ANSI characters. It also provides the ability to map ANSI characters back into raw codes. Unlike the console.device however, it can not be used to select a keymap for only one application, i.e., one console window.&lt;br /&gt;
&lt;br /&gt;
As a prelude to the following material, note that the Amiga keyboard transmits raw key information to the computer in the form of a key position and a transition. Raw key positions range from hexadecimal 00 to 7F. When a key is released, its raw key position, plus hexadecimal 80, is transmitted.&lt;br /&gt;
&lt;br /&gt;
== Keymap Functions ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Keymap Library Functions&lt;br /&gt;
|-&lt;br /&gt;
|AskKeyMapDefault()&lt;br /&gt;
|Ask for a pointer to the current default keymap&lt;br /&gt;
|-&lt;br /&gt;
|MapANSI()&lt;br /&gt;
|Encode an ANSI string into key codes&lt;br /&gt;
|-&lt;br /&gt;
|MapRawKey()&lt;br /&gt;
|Decode a raw key input event to an ANSI string&lt;br /&gt;
|-&lt;br /&gt;
|SetKeyMapDefault()&lt;br /&gt;
|Set the current default keymap&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Console Device Keymap Commands&lt;br /&gt;
|-&lt;br /&gt;
|CD_ASKKEYMAP&lt;br /&gt;
|Ask for the current console&#039;s keymap&lt;br /&gt;
|-&lt;br /&gt;
|CD_SETKEYMAP&lt;br /&gt;
|Set the current console&#039;s keymap&lt;br /&gt;
|-&lt;br /&gt;
|CD_ASKDEFAULTKEYMAP*&lt;br /&gt;
|Set the current default keymap&lt;br /&gt;
|-&lt;br /&gt;
|CD_SETDEFAULTKEYMAP**&lt;br /&gt;
|Ask for a pointer to the current default keymap&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|* Obsolete - use AskKeyMapDefault()&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|** Obsolete - use SetKeyMapDefault()&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All of these commands deal with a set of pointers to key translation arrays, known as the KeyMap structure. The KeyMap structure is defined in &amp;amp;lt;devices/keymap.h&amp;amp;gt; and is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct KeyMap&lt;br /&gt;
    {&lt;br /&gt;
    UBYTE *km_LoKeyMapTypes;&lt;br /&gt;
    ULONG *km_LoKeyMap;&lt;br /&gt;
    UBYTE *km_LoCapsable;&lt;br /&gt;
    UBYTE *km_LoRepeatable;&lt;br /&gt;
    UBYTE *km_HiKeyMapTypes;&lt;br /&gt;
    ULONG *km_HiKeyMap;&lt;br /&gt;
    UBYTE *km_HiCapsable;&lt;br /&gt;
    UBYTE *km_HiRepeatable;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The KeyMap structure contains pointers to arrays which define the ANSI character or string that should be produced when a key or a combination of keys are pressed. For example, a keymap might specify that the key with raw value hex 20 should produce the ANSI character &amp;quot;a&amp;quot;, and if the Shift key is being held it should produce the character &amp;quot;A&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Asking for the Default Keymap ===&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;AskKeyMapDefault()&#039;&#039;&#039; returns a pointer to the current default keymap. To use the mapping functions in keymap.library it is normally not necessary to call this function. They accept NULL as equivalent to &#039;use default keymap&#039; and will call this function for you. You can use this pointer for example to cache the system default in order to temporarily change the keymap your applications uses, or find the keymap in the keymap.resource list of loaded keymaps.  You should never change the system wide default keymap unless the user asks you to do so; since the Amiga is a multitasking system, changing the keymap could interfere with the behaviour of other applications.&lt;br /&gt;
&lt;br /&gt;
=== Setting the Default Keymap ===&lt;br /&gt;
&lt;br /&gt;
The system default keymap can be set with the &#039;&#039;&#039;SetKeyMapDefault()&#039;&#039;&#039; function.  This function takes a pointer to a loaded keymap.&lt;br /&gt;
In general, this function should never be used by an application unless the application is a system Preferences editor, or an&lt;br /&gt;
application that takes over the system. Normal applications should instead attach a console.device unit to their own Intuition window (see the &#039;&#039;Devices&#039;&#039; volume), and use the console.device command &#039;&#039;&#039;CD_SETKEYMAP&#039;&#039;&#039; to set a keymap only for their own console.&lt;br /&gt;
&lt;br /&gt;
When making a keymap the system default, first check whether the keymap has been loaded previously by checking the keymap list of the keymap.resource.  If it has not been loaded already, it can be loaded from DEVS:Keymaps, and added to the keymap list of keymap.resource. This will ensure that other applications which may want the keymap will not have to load a second instance. Once made the default, the keymap can never be safely removed from memory, even after if it is no longer the default, since other applications may still have and use a pointer to it.&lt;br /&gt;
&lt;br /&gt;
=== Accessing the Keymap for the Current Console ===&lt;br /&gt;
&lt;br /&gt;
The function &#039;&#039;&#039;AskKeyMap()&#039;&#039;&#039; shown below does not return a pointer to a table of pointers to currently assigned key mapping. Instead, it &#039;&#039;copies&#039;&#039; the current set of pointers to a user-designated area of memory. &#039;&#039;&#039;AskKeyMap()&#039;&#039;&#039; returns a TRUE or FALSE value that says whether or not the function succeeded.&lt;br /&gt;
&lt;br /&gt;
The function &#039;&#039;&#039;SetKeyMap()&#039;&#039;&#039;, also shown below, copies the designated key map data structure to the console device. Thus this routine is complementary to &#039;&#039;&#039;AskKeymap()&#039;&#039;&#039; in that it can restore an original key mapping as well as establish a new one.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Ask/SetKeyMap()|text=These functions assume that you have already opened the &#039;&#039;console.device&#039;&#039; and that &#039;&#039;request&#039;&#039; is a valid IOStdReq structure for the newly opened console. These functions are not part of the keymap.library, nor of the console.device.  These merely demonstrate &#039;&#039;&#039;CD_ASKKEYMAP&#039;&#039;&#039; and &#039;&#039;&#039;CD_SETKEYMAP&#039;&#039;&#039; which are console.device commands.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* These functions require that you have created a port and an IO request,&lt;br /&gt;
 * and have opened the console device as shown in the &amp;quot;Console Device&amp;quot; section.&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;lt;devices/keymap.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL AskKeyMap(struct IOStdReq *request, struct KeyMap *keymap)&lt;br /&gt;
{&lt;br /&gt;
    request-&amp;gt;io_Command = CD_ASKKEYMAP;&lt;br /&gt;
    request-&amp;gt;io_Length = sizeof(struct KeyMap);&lt;br /&gt;
    request-&amp;gt;io_Data = (APTR)keymap;  /* where to put it */&lt;br /&gt;
    IExec-&amp;gt;DoIO(request);&lt;br /&gt;
    if(request-&amp;gt;io_Error) return(FALSE);&lt;br /&gt;
    else  return(TRUE); /* if no error, it worked. */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL SetKeyMap(struct IOStdReq *request, struct KeyMap *keymap)&lt;br /&gt;
{&lt;br /&gt;
    request-&amp;gt;io_Command = CD_SETKEYMAP;&lt;br /&gt;
    request-&amp;gt;io_Length = sizeof(struct KeyMap);&lt;br /&gt;
    request-&amp;gt;io_Data = (APTR)keymap;      /* where to get it */&lt;br /&gt;
    IExec-&amp;gt;DoIO(request);&lt;br /&gt;
    if(request-&amp;gt;io_Error) return(FALSE);&lt;br /&gt;
    else  return(TRUE);     /* if no error, it worked. */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mapping Key Codes to ANSI Strings ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MapRawKey()&#039;&#039;&#039; converts raw key codes in ANSI characters based on a default or supplied keymap.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
WORD MapRawKey(struct InputEvent *inputevent, STRPTR buffer, WORD bufferlength, struct Keymap *keymap);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MapRawKey()&#039;&#039;&#039; takes an IECLASS_RAWKEY inputevent, which may be chained, and converts the key codes to ANSI characters which are placed in the specified buffer. If the buffer would overflow, for example because a longer string is attached to a key, -1 will be returned. If no error occured, &#039;&#039;&#039;MapRawKey()&#039;&#039;&#039; will return the number of bytes written in the buffer. The keymap argument can be set to NULL if the default keymap is to be used for translation, or can be a pointer to a specific &#039;&#039;&#039;KeyMap&#039;&#039;&#039; structure.&lt;br /&gt;
&lt;br /&gt;
The following example shows how to implement the &#039;&#039;&#039;MapRawKey()&#039;&#039;&#039; function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * maprawkey.c - Map Intuition RAWKEY events to ANSI with MapRawKey();&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;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;devices/inputevent.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/keymap.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our function prototypes */&lt;br /&gt;
void openall(void);&lt;br /&gt;
void closeall(void);&lt;br /&gt;
void closeout(UBYTE *errstring, LONG rc);&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase = NULL;&lt;br /&gt;
struct IntuitionIFace *IIntuition = NULL;&lt;br /&gt;
&lt;br /&gt;
struct Library *KeymapBase = NULL;&lt;br /&gt;
struct KeymapIFace *IKeymap = NULL;&lt;br /&gt;
&lt;br /&gt;
struct Window *window = NULL;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    struct IntuiMessage *imsg;&lt;br /&gt;
    APTR                *eventptr;&lt;br /&gt;
    struct InputEvent   inputevent = {0};&lt;br /&gt;
    LONG                windowsignal;&lt;br /&gt;
    UBYTE               buffer[8];&lt;br /&gt;
    COUNT               i;&lt;br /&gt;
    BOOL                Done = FALSE;&lt;br /&gt;
&lt;br /&gt;
    openall();&lt;br /&gt;
&lt;br /&gt;
    window = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                            WA_Width,  500,&lt;br /&gt;
                            WA_Height, 60,&lt;br /&gt;
                            WA_Title, &amp;quot;MapRawKey - Press Keys&amp;quot;,&lt;br /&gt;
                            WA_Flags, WFLG_CLOSEGADGET | WFLG_ACTIVATE,&lt;br /&gt;
                            WA_IDCMP, IDCMP_RAWKEY | IDCMP_CLOSEWINDOW,&lt;br /&gt;
                            TAG_END);&lt;br /&gt;
    if(window == NULL)   closeout(&amp;quot;Can&#039;t open window&amp;quot;,RETURN_FAIL);&lt;br /&gt;
            &lt;br /&gt;
    windowsignal = 1L &amp;lt;&amp;lt; window-&amp;gt;UserPort-&amp;gt;mp_SigBit;&lt;br /&gt;
&lt;br /&gt;
    /* Initialize InputEvent structure (already cleared to 0) */&lt;br /&gt;
    inputevent.ie_Class = IECLASS_RAWKEY;&lt;br /&gt;
&lt;br /&gt;
    while(!Done)&lt;br /&gt;
       {&lt;br /&gt;
       IExec-&amp;gt;Wait(windowsignal);&lt;br /&gt;
&lt;br /&gt;
       while (imsg = (struct IntuiMessage *)IExec-&amp;gt;GetMsg(window-&amp;gt;UserPort))&lt;br /&gt;
           {&lt;br /&gt;
           switch(imsg-&amp;gt;Class)&lt;br /&gt;
               {&lt;br /&gt;
                case IDCMP_CLOSEWINDOW:&lt;br /&gt;
                    Done = TRUE;&lt;br /&gt;
                    break;&lt;br /&gt;
&lt;br /&gt;
                case IDCMP_RAWKEY:&lt;br /&gt;
                    inputevent.ie_Code = imsg-&amp;gt;Code;&lt;br /&gt;
                    inputevent.ie_Qualifier = imsg-&amp;gt;Qualifier;&lt;br /&gt;
&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;RAWKEY: Code=$%04lx  Qualifier=$%04lx\n&amp;quot;,&lt;br /&gt;
                             imsg-&amp;gt;Code, imsg-&amp;gt;Qualifier); &lt;br /&gt;
&lt;br /&gt;
                    /* Make sure deadkeys and qualifiers are taken&lt;br /&gt;
                     * into account.&lt;br /&gt;
                     */&lt;br /&gt;
                    eventptr = imsg-&amp;gt;IAddress;&lt;br /&gt;
                    inputevent.ie_EventAddress = *eventptr;&lt;br /&gt;
&lt;br /&gt;
                    /* Map RAWKEY to ANSI */&lt;br /&gt;
                    i = IKeymap-&amp;gt;MapRawKey(&amp;amp;inputevent, buffer, 8, NULL);&lt;br /&gt;
&lt;br /&gt;
                    if (i == -1) IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(),&amp;quot;*Overflow*&amp;quot;,10);&lt;br /&gt;
                    else if (i)&lt;br /&gt;
                        {&lt;br /&gt;
                        /* This key or key combination mapped to something */&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;MAPS TO: &amp;quot;);&lt;br /&gt;
                        IDOS-&amp;gt;Write(IDOS-&amp;gt;Output(),buffer,i);&lt;br /&gt;
                        IDOS-&amp;gt;Printf(&amp;quot;\en&amp;quot;);&lt;br /&gt;
                        }&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            IExec-&amp;gt;ReplyMsg((struct Message *)imsg);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    IIntuition-&amp;gt;CloseWindow(window);&lt;br /&gt;
    closeall();&lt;br /&gt;
    return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void openall(void)&lt;br /&gt;
{&lt;br /&gt;
    KeymapBase = IExec-&amp;gt;OpenLibrary(&amp;quot;keymap.library&amp;quot;, 50);&lt;br /&gt;
    IKeymap = (struct KeymapIFace*)IExec-&amp;gt;GetInterface(KeymapBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (IKeymap == NULL)    closeout(&amp;quot;Can&#039;t get IKeymap&amp;quot;,RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    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) closeout(&amp;quot;Can&#039;t get IIntuition&amp;quot;,RETURN_FAIL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void closeall(void)&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;DropInterface((struct Interface*)IKeymap);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(KeymapBase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void closeout(UBYTE *errstring, LONG rc)&lt;br /&gt;
{&lt;br /&gt;
    if (*errstring)  IDOS-&amp;gt;Printf(&amp;quot;%s\en&amp;quot;,errstring);&lt;br /&gt;
    closeall();&lt;br /&gt;
    exit(rc);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mapping ANSI Strings to Key Codes ===&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;MapANSI()&#039;&#039;&#039; function translates ANSI strings into raw key codes, complete with qualifiers and (double) dead keys, based on a default or supplied keymap.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
LONG MapANSI(UBYTE *string, LONG stringlength, UBYTE *buffer, LONG bufferlength, struct KeyMap *keymap);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The string argument is a pointer to an ANSI string, of length stringlength. The buffer argument is a pointer to the memory block where the translated key codes will be placed.  The length of this buffer must be indicated in WORDs since each translation will occupy one byte for the key code and one for the qualifier. Since one ANSI character can be translated to two dead keys and one key, the buffer must be at least 3 WORDs per character in the string to be translated. The keymap argument can be set to NULL if the default keymap is to be used, or can be a pointer to a &#039;&#039;&#039;KeyMap&#039;&#039;&#039; structure. Upon return, the function will indicate how many key code/qualifier combinations are placed in the buffer or a negative number in case an error occurred. If zero is returned, the character could not be translated.&lt;br /&gt;
&lt;br /&gt;
The following example shows the usage of &#039;&#039;&#039;MapANSI()&#039;&#039;&#039; and demonstrates how returned key codes can be processed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
mapansi.c - converts a string to input events using MapANSI() function.&lt;br /&gt;
&lt;br /&gt;
            This example will also take the created input events&lt;br /&gt;
            and add them to the input stream using the simple&lt;br /&gt;
            commodities.library function AddIEvents().  Alternately,&lt;br /&gt;
            you could open the input.device and use the input device&lt;br /&gt;
            command IND_WRITEEVENT to add events to the input stream.&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;exec/io.h&amp;gt;&lt;br /&gt;
#include       &amp;lt;dos/dos.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;
&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/keymap.h&amp;gt;&lt;br /&gt;
#include       &amp;lt;proto/commodities.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct Library *KeymapBase = NULL;       /* MapAnsi() function    */&lt;br /&gt;
struct KeymapIFace *IKeymap = NULL;&lt;br /&gt;
&lt;br /&gt;
struct Library *CxBase = NULL;           /* AddIEvents() function */&lt;br /&gt;
struct CommoditiesIFace *ICommodities = NULL;&lt;br /&gt;
&lt;br /&gt;
struct InputEvent       *InputEvent = NULL;       /* we&#039;ll allocate this */&lt;br /&gt;
&lt;br /&gt;
/* prototypes for our program functions */&lt;br /&gt;
&lt;br /&gt;
void openall(void);&lt;br /&gt;
void closeall(void);&lt;br /&gt;
void closeout(UBYTE *errstring, LONG rc);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    UBYTE               *string;&lt;br /&gt;
    UBYTE               *tmp1;&lt;br /&gt;
    UBYTE               *tmp2;&lt;br /&gt;
    UBYTE               iebuffer[6];            /* Space for two dead keys */&lt;br /&gt;
                                                /* + 1 key + qualifiers    */&lt;br /&gt;
    COUNT               i;&lt;br /&gt;
    LONG                rc = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    openall();&lt;br /&gt;
&lt;br /&gt;
    string = &amp;quot;;String converted to input events and sent to input device\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    InputEvent-&amp;gt;ie_Class = IECLASS_RAWKEY;&lt;br /&gt;
&lt;br /&gt;
    /* Turn each character into an InputEvent */&lt;br /&gt;
    tmp1 = string;&lt;br /&gt;
&lt;br /&gt;
    while (*tmp1)&lt;br /&gt;
    {&lt;br /&gt;
        /* Convert one character, use default key map */&lt;br /&gt;
        i = IKeymap-&amp;gt;MapANSI(tmp1, 1, iebuffer, 3, NULL);&lt;br /&gt;
&lt;br /&gt;
        /* Make sure we start without deadkeys */&lt;br /&gt;
        InputEvent-&amp;gt;ie_Prev1DownCode = InputEvent-&amp;gt;ie_Prev1DownQual = 0;&lt;br /&gt;
        InputEvent-&amp;gt;ie_Prev2DownCode = InputEvent-&amp;gt;ie_Prev2DownQual = 0;&lt;br /&gt;
&lt;br /&gt;
        tmp2 = iebuffer;&lt;br /&gt;
&lt;br /&gt;
        switch (i)&lt;br /&gt;
        {&lt;br /&gt;
          case -2:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Internal error\n&amp;quot;, NULL);&lt;br /&gt;
            rc = RETURN_FAIL;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
          case -1:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Overflow\n&amp;quot;, NULL);&lt;br /&gt;
            rc = RETURN_FAIL;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
          case 0:&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t generate code\n&amp;quot;, NULL);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
          case 3:&lt;br /&gt;
            InputEvent-&amp;gt;ie_Prev2DownCode = *tmp2++;&lt;br /&gt;
            InputEvent-&amp;gt;ie_Prev2DownQual = *tmp2++;&lt;br /&gt;
            /* FALL THROUGH */&lt;br /&gt;
&lt;br /&gt;
          case 2:&lt;br /&gt;
            InputEvent-&amp;gt;ie_Prev1DownCode = *tmp2++;&lt;br /&gt;
            InputEvent-&amp;gt;ie_Prev1DownQual = *tmp2++;&lt;br /&gt;
            /* FALL THROUGH */&lt;br /&gt;
&lt;br /&gt;
          case 1:&lt;br /&gt;
            InputEvent-&amp;gt;ie_Code = *tmp2++;&lt;br /&gt;
            InputEvent-&amp;gt;ie_Qualifier = *tmp2;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (rc == RETURN_OK)&lt;br /&gt;
        {&lt;br /&gt;
            /* Send the key down event */&lt;br /&gt;
            ICommodities-&amp;gt;AddIEvents(InputEvent);&lt;br /&gt;
&lt;br /&gt;
            /* Create a key up event */&lt;br /&gt;
            InputEvent-&amp;gt;ie_Code |= IECODE_UP_PREFIX;&lt;br /&gt;
&lt;br /&gt;
            /* Send the key up event */&lt;br /&gt;
            ICommodities-&amp;gt;AddIEvents(InputEvent);&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
        if (rc != RETURN_OK)&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        tmp1++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    closeall();&lt;br /&gt;
    return rc;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void openall(void)&lt;br /&gt;
{&lt;br /&gt;
    KeymapBase = IExec-&amp;gt;OpenLibrary(&amp;quot;keymap.library&amp;quot;, 50);&lt;br /&gt;
    IKeymap = (struct KeymapIFace*)IExec-&amp;gt;GetInterface(KeymapBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
    if (IKeymap == NULL)  closeout(&amp;quot;Can&#039;t get IKeymap&amp;quot;, RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    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)  closeout(&amp;quot;Can&#039;t get ICommodities&amp;quot;, RETURN_FAIL);&lt;br /&gt;
&lt;br /&gt;
    InputEvent = IExec-&amp;gt;AllocMem(sizeof(struct InputEvent), MEMF_CLEAR);&lt;br /&gt;
    if (InputEvent == NULL)  closeout(&amp;quot;Can&#039;t allocate input event&amp;quot;,RETURN_FAIL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void closeall()&lt;br /&gt;
{&lt;br /&gt;
    if (InputEvent)    IExec-&amp;gt;FreeMem(InputEvent, sizeof(struct InputEvent));&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;
    IExec-&amp;gt;DropInterface((struct Interface*)IKeymap);&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(KeymapBase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void closeout(UBYTE *errstring, LONG rc)&lt;br /&gt;
{&lt;br /&gt;
    if(*errstring)     IDOS-&amp;gt;Printf(&amp;quot;%s\n&amp;quot;,errstring);&lt;br /&gt;
    closeall();&lt;br /&gt;
    exit(rc);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Details of the Keymap Structure ===&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;KeyMap&#039;&#039;&#039; structure contains pointers to arrays which determine the translation from raw key codes to ANSI characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct KeyMap&lt;br /&gt;
    {&lt;br /&gt;
    UBYTE *km_LoKeyMapTypes;&lt;br /&gt;
    ULONG *km_LoKeyMap;&lt;br /&gt;
    UBYTE *km_LoCapsable;&lt;br /&gt;
    UBYTE *km_LoRepeatable;&lt;br /&gt;
    UBYTE *km_HiKeyMapTypes;&lt;br /&gt;
    ULONG *km_HiKeyMap;&lt;br /&gt;
    UBYTE *km_HiCapsable;&lt;br /&gt;
    UBYTE *km_HiRepeatable;&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LoKeyMap and HighKeyMap ====&lt;br /&gt;
&lt;br /&gt;
The low key map provides translation of the key values from hex 00-3F; the high key map provides translation of key values from hex 40-7F. Key values from hex 68-7F are not used by the existing keyboards, but this may change in the future. A raw key value (hex 00-7F) plus hex 80 is the release of that key. If you need to check for raw key releases do it like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if (keyvalue &amp;amp; 0x80)     {  /* do key up processing   */  }&lt;br /&gt;
else                     {  /* do key down processing */  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Raw output from the keyboard for the low key map does not include the space bar, Tab, Alt, Ctrl, arrow keys, and several other keys.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+High Key Map Hex Values&lt;br /&gt;
|-&lt;br /&gt;
!Key Number&lt;br /&gt;
!Keycap Legend for Function&lt;br /&gt;
|-&lt;br /&gt;
|40&lt;br /&gt;
|Space&lt;br /&gt;
|-&lt;br /&gt;
|41&lt;br /&gt;
|Backspace&lt;br /&gt;
|-&lt;br /&gt;
|42&lt;br /&gt;
|Tab&lt;br /&gt;
|-&lt;br /&gt;
|43&lt;br /&gt;
|Enter&lt;br /&gt;
|-&lt;br /&gt;
|44&lt;br /&gt;
|Return&lt;br /&gt;
|-&lt;br /&gt;
|45&lt;br /&gt;
|Escape&lt;br /&gt;
|-&lt;br /&gt;
|46&lt;br /&gt;
|Delete&lt;br /&gt;
|-&lt;br /&gt;
|4A&lt;br /&gt;
|Numeric Pad character&lt;br /&gt;
|-&lt;br /&gt;
|4C&lt;br /&gt;
|Cursor Up&lt;br /&gt;
|-&lt;br /&gt;
|4D&lt;br /&gt;
|Cursor Down&lt;br /&gt;
|-&lt;br /&gt;
|4E&lt;br /&gt;
|Cursor Right&lt;br /&gt;
|-&lt;br /&gt;
|4F&lt;br /&gt;
|Cursor Left&lt;br /&gt;
|-&lt;br /&gt;
|50-59&lt;br /&gt;
|Function keys F1-F10&lt;br /&gt;
|-&lt;br /&gt;
|5A-5E&lt;br /&gt;
|Numeric Pad characters&lt;br /&gt;
|-&lt;br /&gt;
|5F&lt;br /&gt;
|Help&lt;br /&gt;
|-&lt;br /&gt;
|60&lt;br /&gt;
|Left Shift&lt;br /&gt;
|-&lt;br /&gt;
|61&lt;br /&gt;
|Right Shift&lt;br /&gt;
|-&lt;br /&gt;
|62&lt;br /&gt;
|Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
|63&lt;br /&gt;
|Control&lt;br /&gt;
|-&lt;br /&gt;
|64&lt;br /&gt;
|Left Alt&lt;br /&gt;
|-&lt;br /&gt;
|65&lt;br /&gt;
|Right Alt&lt;br /&gt;
|-&lt;br /&gt;
|66&lt;br /&gt;
|Left Amiga&lt;br /&gt;
|-&lt;br /&gt;
|67&lt;br /&gt;
|Right Amiga&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The keymap table for the low and high keymaps consists of 4-byte entries, one per hex key code. These entries are interpreted in one of two possible ways:&lt;br /&gt;
&lt;br /&gt;
* As four separate bytes, specifying how the key is to be interpreted when pressed alone, with one qualifier, with another qualifier, or with both qualifiers (where a qualifier is one of three possible keys:  Ctrl, Alt, or Shift).&lt;br /&gt;
&lt;br /&gt;
* As a longword containing the address of a string descriptor, where a string of characters is to be output when this key is pressed. If a string is to be output, any combination of qualifiers can affect the string that may be transmitted.&lt;br /&gt;
&lt;br /&gt;
* As a longword containing the address of a dead-key descriptor, where additional data describe the character to be output when this key is pressed alone or with another dead key.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The keymap tables must be word aligned|text=The keymap tables &#039;&#039;must&#039;&#039; begin aligned on a word boundary. Each entry is four bytes long, thereby maintaining word alignment throughout the table. This is necessary because some of the entries may be longword addresses and &#039;&#039;must&#039;&#039; be aligned properly for the 68000.}}&lt;br /&gt;
&lt;br /&gt;
==== LoKeyMapTypes and HiKeyMapTypes ====&lt;br /&gt;
&lt;br /&gt;
The tables named &#039;&#039;&#039;km_LoKeyMapTypes&#039;&#039;&#039; and &#039;&#039;&#039;km_HiKeyMapTypes&#039;&#039;&#039; each contain one byte per raw key code. Each byte defines the type of entry that is found in the keymap table for that raw key code.&lt;br /&gt;
&lt;br /&gt;
Possible key types are:&lt;br /&gt;
&lt;br /&gt;
* Any of the qualifier groupings noted above&lt;br /&gt;
&lt;br /&gt;
* KCF_STRING +  any combination of KCF_SHIFT, KCF_ALT, KCF_CONTROL (or KC_NOQUAL) if the result of pressing the key is to be a stream of bytes (and key-with-one-or-more-qualifiers is to be one or more alternate streams of bytes). Any key can be made to output up to eight unique byte streams if KCF_STRING is set in its keytype. The only limitation is that the total length of all of the strings assigned to a key must be within the &amp;quot;jump range&amp;quot; of a single byte increment. See the &amp;quot;String Output Keys&amp;quot; section below for more information.&lt;br /&gt;
&lt;br /&gt;
* KCF_DEAD +  any combination of KCF_SHIFT, KCF_ALT, KCF_CONTROL (or KC_NOQUAL) if the key is a dead-class key and can thus modify or be modified by another dead-class key. See the &amp;quot;Dead-Class Keys&amp;quot; section below for more information.&lt;br /&gt;
&lt;br /&gt;
The low keytype table covers the raw key codes from hex 00-3F and contains one byte per key code. Therefore this table contains 64 (decimal) bytes. The high keytype table covers the raw key codes from hex 40-7F and contains 64 (decimal) bytes.&lt;br /&gt;
&lt;br /&gt;
==== More About Qualifiers ====&lt;br /&gt;
&lt;br /&gt;
For keys such as the Return key or Esc key, the qualifiers specified in the keytypes table (up to two) are the qualifiers used to establish the response to the key.  This is done as follows.  In the keytypes table, the values listed for the key types are those listed for the qualifiers in &amp;amp;lt;devices/keymap.h&amp;amp;gt; and &amp;amp;lt;devices/keymap&amp;amp;gt;. Specifically, these qualifier equates are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| KC_NOQUAL || 0x00&lt;br /&gt;
|-&lt;br /&gt;
| KCF_SHIFT || 0x01&lt;br /&gt;
|-&lt;br /&gt;
| KCF_ALT || 0x02&lt;br /&gt;
|-&lt;br /&gt;
| KCF_CONTROL || 0x04&lt;br /&gt;
|-&lt;br /&gt;
| KC_VANILLA || 0x07&lt;br /&gt;
|-&lt;br /&gt;
| KCF_DOWNUP || 0x08&lt;br /&gt;
|-&lt;br /&gt;
| KCF_STRING || 0x40&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
As shown above, the qualifiers for the various types of keys occupy specific bit positions in the key types control byte. As you may have noticed, there are three possible qualifiers, but only a 4-byte space in the table for each key. This does not allow space to describe what the computer should output for all possible combinations of qualifiers. A solution exists, however, for &amp;quot;vanilla&amp;quot; keys, such as the alphabetic keys. Here is how that works.&lt;br /&gt;
&lt;br /&gt;
Keys of type KC_VANILLA use the 4 bytes to represent the data output for the key alone, Shifted key, Alt&#039;ed key, and Shifted-and-Alt&#039;ed key. Then for the Ctrl-key-plus-vanilla-key, use the code for the key alone with bits 6 and 5 set to 0.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=The Vanilla Qualifier Does Not Mean Plain|text=The qualifier KC_VANILLA is equivalent to KCF_SHIFT+KCF_ALT+KCF_CONTROL.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
This table shows how to interpret the keymap for various combinations of the qualifier bits:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Keymap Qualifier Bits&lt;br /&gt;
|-&lt;br /&gt;
| If Keytype is:&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Then data at this position in the keytable is output when the key is pressed along with:&lt;br /&gt;
|-&lt;br /&gt;
| KC_NOQUAL || - || - || - || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_SHIFT || - || - || Shift || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_ALT || - || - || Alt || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_CONTROL || - || - || Ctrl || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_ALT+KCF_SHIFT || Shift+Alt || Alt || Shift || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_CONTROL+KCF_ALT || Ctrl+Alt || Ctrl || Alt || alone&lt;br /&gt;
|-&lt;br /&gt;
| KCF_CONTROL+KCF_SHIFT || Ctrl+Shift || Ctrl || Shift || alone&lt;br /&gt;
|-&lt;br /&gt;
| KC_VANILLA || Shift+Alt || Alt || Shift || alone*&lt;br /&gt;
|-&lt;br /&gt;
| colspan = &amp;quot;5&amp;quot; | *Special case--Ctrl key, when pressed with one of the alphabet keys and certain others, is to output key-alone value with the bits 6 and 5 set to zero.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== String Output Keys ====&lt;br /&gt;
&lt;br /&gt;
When a key is to output a string, the keymap table contains the address of a string descriptor in place of a 4-byte mapping of a key as shown above. Here is a partial table for a new high keymap table that contains only three entries thus far. The first two are for the space bar and the backspace key; the third is for the tab key, which is to output a string that says &amp;quot;[TAB]&amp;quot;. An alternate string, &amp;quot;[SHIFTED-TAB]&amp;quot;, is also to be output when a shifted TAB key is pressed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
newHiMapTypes:&lt;br /&gt;
    DC.B        KCF_ALT,KC_NOQUAL,      ;key 41&lt;br /&gt;
    DC.B        KCF_STRING+KCF_SHIFT,   ;key 42&lt;br /&gt;
        ...     ;(more)&lt;br /&gt;
newHiMap:&lt;br /&gt;
    DC.B        0,0,$A0,$20     ;key 40: space bar, and Alt-space bar&lt;br /&gt;
    DC.B        0,0,0,$08       ;key 41: Back Space key only&lt;br /&gt;
    DC.L        newkey42        ;key 42: new definition for string to output for Tab key&lt;br /&gt;
        ...     ;(more)&lt;br /&gt;
newkey42:&lt;br /&gt;
    DC.B        new42ue - new42us       ;length of the unshifted string&lt;br /&gt;
    DC.B        new42us - newkey42      ;number of bytes from start of&lt;br /&gt;
                                        ;string descriptor to start of this string&lt;br /&gt;
    DC.B        new42se - new42ss       ;length of the shifted string&lt;br /&gt;
    DC.B        new42ss - newkey42      ;number of bytes from start of&lt;br /&gt;
                                        ;string descriptor to start of this string&lt;br /&gt;
new42us:  DC.B        &#039;[TAB]&#039;&lt;br /&gt;
new42ue:&lt;br /&gt;
new42ss:  DC.B        &#039;[SHIFTED-TAB]&#039;&lt;br /&gt;
new42se:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new high map table points to the string descriptor at address newkey42. The new high map types table says that there is one qualifier, which means that there are two strings in the key string descriptor.&lt;br /&gt;
&lt;br /&gt;
Each string in the descriptor takes two bytes in this part of the table:  the first byte is the length of the string, and the second byte is the distance from the start of the descriptor to the start of the string. Therefore, a single string (KCF_STRING + KC_NOQUAL) takes 2 bytes of string descriptor. If there is one qualifier, 4 bytes of descriptor are used. If there are two qualifiers, 8 bytes of descriptor are used. If there are 3 qualifiers, 16 bytes of descriptor are used. All strings start immediately following the string descriptor in that they are accessed as single-byte offsets from the start of the descriptor itself. Therefore, the distance from the start of the descriptor to the last string in the set (the one that uses the entire set of specified qualifiers) must start within 255 bytes of the descriptor address.&lt;br /&gt;
&lt;br /&gt;
Because the length of the string is contained in a single byte, the length of any single string must be 255 bytes or less while also meeting the &amp;quot;reach&amp;quot; requirement. However, the console input buffer size limits the string output from any individual key to 32 bytes maximum.&lt;br /&gt;
&lt;br /&gt;
The length of a keymap containing string descriptors and strings is variable and depends on the number and size of the strings that you provide.&lt;br /&gt;
&lt;br /&gt;
==== Capsable Bit Tables ====&lt;br /&gt;
&lt;br /&gt;
The vectors &#039;&#039;&#039;km_LoCapsable&#039;&#039;&#039; and &#039;&#039;&#039;km_HiCapsable&#039;&#039;&#039; each point to an array of 8 bytes that contain more information about the keytable entries. Specifically, if the Caps Lock key has been pressed (the Caps Lock LED is on) and if there is a bit \fIon\fP in that position in the capsable map, then this key will be treated as though the Shift key is now currently pressed. For example, in the default key mapping, the alphabetic keys are &amp;quot;capsable&amp;quot; but the punctuation keys are not. This allows you to set the Caps Lock key, just as on a normal typewriter, and get all capital letters. However, unlike a normal typewriter, you need not go out of Caps Lock to correctly type the punctuation symbols or numeric keys.&lt;br /&gt;
&lt;br /&gt;
In the byte array, the bits that control this feature are numbered from the lowest bit in the byte, and from the lowest memory byte address to the highest. For example, the bit representing capsable status for the key that transmits raw code 00 is bit 0 in byte 0; for the key that transmits raw code 08 it is bit 0 in byte 1, and so on.&lt;br /&gt;
&lt;br /&gt;
There are 64 bits (8 bytes) in each of the two capsable tables.&lt;br /&gt;
&lt;br /&gt;
==== Repeatable Bit Tables ====&lt;br /&gt;
&lt;br /&gt;
The vectors &#039;&#039;&#039;km_LoRepeatable&#039;&#039;&#039; and &#039;&#039;&#039;km_HiRepeatable&#039;&#039;&#039; each point to an array of 8 bytes that contain additional information about&lt;br /&gt;
the keytable entries. A bit for each key indicates whether or not the specified key should repeat at the rate set by the Input Preferences program.&lt;br /&gt;
&lt;br /&gt;
The bit positions correspond to those specified in the capsable bit table. If there is a 1 in a specific position, the key can repeat. There are 64 bits (8 bytes) in each of the two repeatable tables.&lt;br /&gt;
&lt;br /&gt;
=== Key Map Standards ===&lt;br /&gt;
&lt;br /&gt;
Users and programs depend on certain predictable behaviors from all keyboards and keymaps. With the exception of dead-class keys (see &amp;quot;Dead-Class Keys&amp;quot; section), mapping of keys in the low key map should follow these general rules:&lt;br /&gt;
&lt;br /&gt;
* When pressed alone, keys should transmit the ASCII equivalent of the unshifted letter or lower symbol on the keycap.&lt;br /&gt;
&lt;br /&gt;
* When Shifted, keys should transmit the ASCII equivalent of the shifted letter or upper symbol printed on the keycap.&lt;br /&gt;
&lt;br /&gt;
* When Alt&#039;ed, keys should generally transmit the same character (or act as the same deadkey) as the Alt&#039;ed key in the usa1 keymap.&lt;br /&gt;
&lt;br /&gt;
* When pressed with CTRL alone, alphabetic keys should generally transmit their unshifted value but with bits 5 and 6 cleared. This allows keyboard typing of &amp;quot;control characters.&amp;quot;  For example, the C key (normally value $63) should transmit value $03 (Ctrl-C) when Ctrl and C are pressed together.&lt;br /&gt;
&lt;br /&gt;
The keys in the high key map (keys with raw key values $40 and higher) are generally non-alphanumeric keys such as those used for editing&lt;br /&gt;
(backspace, delete, cursor keys, etc.), and special Amiga keys such as the function and help keys. Keymaps should translate these keys to the same values or strings as those shown in ROM Default Key Mapping table.&lt;br /&gt;
&lt;br /&gt;
In addition to their normal unshifted and shifted values, the following translations are standard for particular qualified high keymap keys:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key&lt;br /&gt;
! Generates This Value&lt;br /&gt;
! If Used with Qualifier, Generates This Value&lt;br /&gt;
|-&lt;br /&gt;
| Space || $20 || $A0 with qualifier KCF_ALT&lt;br /&gt;
|-&lt;br /&gt;
| Return || $0D || $0A with qualifier KCF_CONTROL&lt;br /&gt;
|-&lt;br /&gt;
| Esc || $1B || $9B with qualifier KCF_ALT&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dead-Class Keys ===&lt;br /&gt;
&lt;br /&gt;
All of the national keymaps, including USA, contain &#039;&#039;dead-class&#039;&#039; keys. This term refers to keys that either modify or can themselves be modified by other dead-class keys.  There are two types of dead-class keys: dead and deadable. A dead key is one which can modify certain keys pressed immediately following. For example, on the German keyboard there is a dead key marked with the grave accent (`). The dead key produces no console output, but when followed by (for instance) the A key, the combination will produce the a-grave (à) character (National Character Code $E0). On the U.S. keyboard, Alt-G is the deadkey used to add the grave accent (`) to the next appropriate character typed.&lt;br /&gt;
&lt;br /&gt;
A deadable key is one that can be prefixed by a dead key. The A key in the previous example is a deadable key. Thus, a dead key can only affect the output of a deadable key.&lt;br /&gt;
&lt;br /&gt;
For any key that is to have a dead-class function, whether dead or deadable, the qualifier KCF_DEAD flag must be included in the entry for the key in the KeyMapTypes table. The KCF_DEAD type may also be used in conjunction with the other qualifiers. Furthermore, the key&#039;s keymap table entry must contain the longword address of the key&#039;s dead-key descriptor data area in place of the usual 4 ASCII character mapping.&lt;br /&gt;
&lt;br /&gt;
Below is an excerpt from the Amiga 1000 German key map which is referred to in the following discussion.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
KMLowMapType:&lt;br /&gt;
        DC.B    KCF_DEAD+KC_VANILLA     ; aA (Key 20)&lt;br /&gt;
                    ...                 ; (more...)&lt;br /&gt;
        DC.B    KCF_DEAD+KC_VANILLA     ; hH (Key 25)&lt;br /&gt;
                    ...                 ; (more...)&lt;br /&gt;
KMLowMap:&lt;br /&gt;
        DC.L    key20                   ; a, A, ae, AE&lt;br /&gt;
                    ...                 ; (more...)&lt;br /&gt;
        DC.L    key25                   ; h, H, dead ^&lt;br /&gt;
                    ...                 ; (more...)&lt;br /&gt;
;------ possible dead keys&lt;br /&gt;
key25:&lt;br /&gt;
        DC.B    0,&#039;h&#039;,0,&#039;H&#039;             ; h, H&lt;br /&gt;
        DC.B    DPF_DEAD,3,DPF_DEAD,3   ; dead ^, dead ^&lt;br /&gt;
        DC.B    0,$08,0,$08,0,$88,0,$88 ; control translation&lt;br /&gt;
                    ...                 ; (more...)&lt;br /&gt;
;------ deadable keys (modified by dead keys)&lt;br /&gt;
key20:                  ; a, A, ae, AE&lt;br /&gt;
        DC.B    DPF_MOD,key20u-key20    ; deadable flag, number of&lt;br /&gt;
                                        ; bytes from start of key20&lt;br /&gt;
                                        ; descriptor to start of un-&lt;br /&gt;
                                        ; shifted data&lt;br /&gt;
        DC.B    DPF_MOD,key20s-key20    ; deadable flag, number of&lt;br /&gt;
                                        ; bytes from start of key20&lt;br /&gt;
                                        ; descriptor to start of shift-&lt;br /&gt;
                                        ; ed data&lt;br /&gt;
        DC.B    0,$E6,0,$C6             ; null flags followed by rest&lt;br /&gt;
        DC.B    0,$01,0,$01,0,$81,0,$81 ; of values (ALT, CTRL...)&lt;br /&gt;
key20u:&lt;br /&gt;
        DC.B    &#039;a&#039;,$E1,$E0,$E2,$E3,$E4 ; &#039;a&#039; alone and characters to&lt;br /&gt;
                                        ; output when key alone is&lt;br /&gt;
                                        ; prefixed by a dead key&lt;br /&gt;
        DC.B    $E1,$E1,$E2,$E1,$E1,$E1 ; most recent is &#039;&lt;br /&gt;
        DC.B    $E0,$E2,$E0,$E0,$E0,$E0 ; most recent is `&lt;br /&gt;
key20s:&lt;br /&gt;
        DC.B    &#039;A&#039;,$C1,$C0,$C2,$C3,$C4 ; SHIFTed &#039;a&#039; and characters to&lt;br /&gt;
                                        ; output when SHIFTed key is&lt;br /&gt;
                                        ; prefixed by a dead key&lt;br /&gt;
        DC.B    $C1,$C1,$C2,$C1,$C1,$C1 ; most recent is &#039;&lt;br /&gt;
        DC.B    $C0,$C2,$C0,$C0,$C0,$C0 ; most recent is `&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example, key 25 (the H key) is a dead key and key 20 (the A key) is a deadable key. Both keys use the addresses of their descriptor data areas as entries in the LoKeyMap table. The LoKeyMapTypes table says that there are four qualifiers for both: the requisite KCF_DEAD, as well as KCF_SHIFT, KCF_ALT, and KCF_CONTROL. The number of qualifiers determine length and arrangement of the descriptor data areas for each key. The next table shows how to interpret the KeyMapTypes for various combinations of the qualifier bits. For each possible position a pair of bytes is needed. The first byte in each pair tells how to interpret the second byte (more about this below).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Dead Key Qualifier Bits&lt;br /&gt;
|-&lt;br /&gt;
! If type is:&lt;br /&gt;
! colspan=&amp;quot;8&amp;quot; | Then the pair of bytes in this position in the dead-class key descriptor data is output when the key is pressed along with:&lt;br /&gt;
|-&lt;br /&gt;
| NOQUAL || alone || - || - || - || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| A || alone || - || - || - || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| C || alone || C || - || - || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| S || alone || S || - || - || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| A+C || alone || A || C || A+C || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| A+S || alone || S || A || A+S || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| C+S || alone || S || C || C+S || - || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| S+A+C (VANILLA) || alone || S || A || S+A || C || C+S || C+A || C+S+A&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;9&amp;quot; | The abbreviations A, C, S stand for KCF_ALT, KCF_CONTROL, and KCF_SHIFT, respectively.  Also note that the ordering is reversed from that in the normal &#039;&#039;&#039;KeyMap&#039;&#039;&#039; table.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because keys 20 and 25 each use three qualifier bits (not including KCF_DEAD), according to the table there must be 8 pairs of data, arranged as shown. Had only KCF_ALT been set, for instance, (not including KCF_DEAD), just two pairs would have been needed.&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, the first byte of each data pair in the descriptor data area specifies how to interpret the second byte. There are three possible values: 0, DPF_DEAD and DPF_MOD. In the Amiga 1000 German keymap listed above, DPF_DEAD appears in the data for key 25, while DPF_MOD is used for key 20. It is the use of these flags that determines whether a dead-class key has dead or deadable function. A value of zero causes the unrestricted output of the following byte.&lt;br /&gt;
&lt;br /&gt;
If the flag byte is DPF_DEAD, then that particular key combination (determined by the placement of the pair of bytes in the data table) is dead and will modify the output of the next key pressed (if deadable). How it modifies is controlled by the second byte of the pair which is used as an index into part(s) of the data area for ALL the deadable (DPF_MOD set) keys.&lt;br /&gt;
&lt;br /&gt;
Before going further, an understanding of the structure of a descriptor data area wherein DPF_MOD is set for one (or more) of its members is necessary. Referring to the example, we see that DPF_MOD is set for the first and second pairs of bytes. According to its LoKeyMapTypes entry, and using table above (Dead Key Qualifier Bits) as a guide, these pairs represent the alone and SHIFTed values for the key. When DPF_MOD is set, the byte immediately following the flag must be the offset from the start of the key&#039;s descriptor data area to the start of a table of bytes describing the characters to output when this key combination is preceded by any dead keys.  This is where the index mentioned above comes in. The value of the index from a prefixing dead key is used to determine which of the bytes from the deadable keys special table to output. The byte in the index+1 position is sent out. (The very first byte is the value to output if the key was not prefixed by a dead key.) Thus, if Alt-H is pressed (dead) and then Shift-A, an &#039;a&#039; with a circumflex (^) accent will be output. This is because:&lt;br /&gt;
&lt;br /&gt;
* The byte pair for the ALT position of the &amp;quot;H&amp;quot; key (key 25) is DPF_DEAD,3 so the index is 3.&lt;br /&gt;
&lt;br /&gt;
* The byte pair for the SHIFT position of the &amp;quot;A&amp;quot; key (key 20) is DPF_MOD, key20s-key20, so we refer to the table-of-bytes at key20s.&lt;br /&gt;
&lt;br /&gt;
* The third+1 byte of the table-of-bytes is $C2, an &#039;a&#039; character.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=A Note About Table Size|text=The number of bytes in the table-of-bytes for all deadable keys must be equal to the highest index value of all dead keys plus 1.}}&lt;br /&gt;
&lt;br /&gt;
=== Double-Dead Keys ===&lt;br /&gt;
&lt;br /&gt;
Double-dead keys are an extension of the dead-class keys explained above. Unlike normal dead keys wherein one dead key of type DPF_DEAD can modify a second of type DPF_MOD, double-dead keys employ two consecutive keys of type DPF_DEAD to together modify a third of type DPF_MOD.&lt;br /&gt;
&lt;br /&gt;
For example, the key on the German keyboard labeled with single quotes (&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;) is a double-dead key. When this key is pressed alone and then pressed again shifted, there is no output. But when followed by an appropriate third key, for example the A key, the three keypresses combine to produce an &#039;a&#039; with a circumflex (^) accent (character code $E2). Thus the double-dead pair qualify the output of the A key.&lt;br /&gt;
&lt;br /&gt;
The system always keeps the last two down key codes for possible further translation. If they are both of type DPF_DEAD and the key immediately following is DPF_MOD then the two are used to form an index into the (third) key&#039;s translation table as follows:&lt;br /&gt;
&lt;br /&gt;
In addition to the index found after the DPF_DEAD qualifier in a normal dead key, a second factor is included in the high nibble of double-dead keys (it is shifted into place with DP_2DFACSHIFT). Its value equals the total number of dead key types + 1 in the keymap. This second index also serves as an identifying flag to the system that two dead keys can be significant.&lt;br /&gt;
&lt;br /&gt;
When a key of type DPF_MOD is pressed, the system checks the two key codes which preceded the current one. If they were both DPF_DEAD then the most recent of the two is checked for the double-dead index/flag. If it is found then a new index is formed by multiplying the value in lower nibble with that in the upper. Then, the lower nibble of the least recent DPF_DEAD key is added in to form the final offset.&lt;br /&gt;
&lt;br /&gt;
Finally, this last value is used as an index into the translation table of the current, DPF_MOD, key.&lt;br /&gt;
&lt;br /&gt;
The translation table of all deadable (DPF_MOD) keys has [number of dead key types + 1] * [number of double dead key types + 1] entries, arranged in [number of double dead key types + 1] rows of [number of dead key types + 1] entries. This is because as indices are assigned for dead keys in the keymap, those that are double dead keys are assigned the lower numbers.&lt;br /&gt;
&lt;br /&gt;
Following is a code fragment from the German (d) keymap source:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
key0C:&lt;br /&gt;
        DC.B    DPF_DEAD,1+(6&amp;lt;&amp;lt;DP_2DFACSHIFT)   ; dead &#039;&lt;br /&gt;
        DC.B    DPF_DEAD,2+(6&amp;lt;&amp;lt;DP_2DFACSHIFT)   ; dead `&lt;br /&gt;
        DC.B    0,&#039;=&#039;,0,&#039;+&#039;                     ; =, +&lt;br /&gt;
key20:                  ; a, A, ae, AE&lt;br /&gt;
        DC.B    DPF_MOD,key20u-key20,DPF_MOD,key20s-key20&lt;br /&gt;
        DC.B    0,$E6,0,$C6&lt;br /&gt;
        DC.B    0,$01,0,$01,0,$81,0,$81 ; control translation&lt;br /&gt;
key20u:&lt;br /&gt;
        DC.B    &#039;a&#039;,$E1,$E0,$E2,$E3,$E4&lt;br /&gt;
        DC.B    $E1,$E1,$E2,$E1,$E1,$E1 ; most recent is &#039;&lt;br /&gt;
        DC.B    $E0,$E2,$E0,$E0,$E0,$E0 ; most recent is `&lt;br /&gt;
key20s:&lt;br /&gt;
        DC.B    &#039;A&#039;,$C1,$C0,$C2,$C3,$C4&lt;br /&gt;
        DC.B    $C1,$C1,$C2,$C1,$C1,$C1 ; most recent is &#039;&lt;br /&gt;
        DC.B    $C0,$C2,$C0,$C0,$C0,$C0 ; most recent is `&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Raw key0C, the German single quotes (&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;) key, is a double dead key. Pressing this key alone, then again while the shift key is down will produce no output but will form a double-dead qualifier. The output of key20 (A), a deadable key, will consequently be modified, producing an &amp;quot;a&amp;quot; with a circumflex (^) accent. The mechanics are as follows:&lt;br /&gt;
&lt;br /&gt;
* When key0C is pressed alone the DPF_DEAD of the first byte pair in the key&#039;s table indicates that the key as dead. The second byte is then held by the system.&lt;br /&gt;
&lt;br /&gt;
* Next, when key0C is pressed again, this time with the Shift key down, the DPF_DEAD of the second byte pair (recall that the second pair is used because of the SHIFT qualifier) again indicates the key is a dead key.  The second byte of this pair is also held by the system.&lt;br /&gt;
&lt;br /&gt;
* Finally, when the A key is pressed the system recalls the latter of the two bytes it has saved. The upper nibble, $6, is multiplied by&lt;br /&gt;
the lower nibble, $2. The result, $0C, is then added to the lower nibble of the earlier of the two saved bytes, $1. This new value, $0D, is used as an index into the (unshifted) translation table of key20. The character at position $0D is character $E2, an &#039;a&#039; with a circumflex (^) accent.&lt;br /&gt;
&lt;br /&gt;
{{Note|title=Note About Double Dead Keys|text=If only one double-dead key is pressed before a deadable key then the output is the same as if the double-dead were a normal dead key. If shifted key0C is pressed on the German keyboard and then immediately followed by key20, the output produced is character $E0 &#039;à&#039;.  As before, the upper nibble is multiplied with the lower, resulting in $0C. But because there was no second dead-key, this product is used as the final index.}}&lt;br /&gt;
&lt;br /&gt;
== Keyboard Layout ==&lt;br /&gt;
&lt;br /&gt;
The keys with key codes $2B and $30 in the following keyboard diagrams are keys which are present on some national Amiga keyboards.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig34-1.png|800px|Amiga 1000 Keyboard Showing Key Codes in Hex]]&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig34-2.png|800px|Amiga 500/2000 Keyboard Showing Key Codes in Hex]]&lt;br /&gt;
&lt;br /&gt;
The default values given above correspond to the values the console device will return when these keys are pressed with the keycaps as shipped with the standard American keyboard.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ROM Default (USA0) and USA1 Console Key Mapping&lt;br /&gt;
|-&lt;br /&gt;
! Raw Key Number&lt;br /&gt;
! Keycap Legend&lt;br /&gt;
! Unshifted Default Value&lt;br /&gt;
! Shifted Default Value&lt;br /&gt;
|-&lt;br /&gt;
| 00 || `~ || ` (Accent grave) || ~ (tilde)&lt;br /&gt;
|-&lt;br /&gt;
| 01 || 1 ! || 1 || !&lt;br /&gt;
|-&lt;br /&gt;
| 02 || 2 @ || 2 || @&lt;br /&gt;
|-&lt;br /&gt;
| 03 || 3 # || 3 || #&lt;br /&gt;
|-&lt;br /&gt;
| 04 || 4 $ || 4 || $&lt;br /&gt;
|-&lt;br /&gt;
| 05 || 5 % || 5 || %&lt;br /&gt;
|-&lt;br /&gt;
| 06 || 6 ^ || 6 || ^&lt;br /&gt;
|-&lt;br /&gt;
| 07 || 7 &amp;amp; || 7 || &amp;amp;&lt;br /&gt;
|-&lt;br /&gt;
| 08 || 8 * || 8 ||&lt;br /&gt;
|-&lt;br /&gt;
| 09 || 9 ( || 9 || (&lt;br /&gt;
|-&lt;br /&gt;
| 0A || 0 ) || 0 || )&lt;br /&gt;
|-&lt;br /&gt;
| 0B || - _ || - (Hyphen) || _ (Underscore)&lt;br /&gt;
|-&lt;br /&gt;
| 0C || = + || = || +&lt;br /&gt;
|-&lt;br /&gt;
| 0D || | | || || | |&lt;br /&gt;
|-&lt;br /&gt;
| 0E || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 0F || 0 || 0 || 0 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Q || q || Q&lt;br /&gt;
|-&lt;br /&gt;
| 11 || W || w || W&lt;br /&gt;
|-&lt;br /&gt;
| 12 || E || e || E&lt;br /&gt;
|-&lt;br /&gt;
| 13 || R || r || R&lt;br /&gt;
|-&lt;br /&gt;
| 14 || T || t || T&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Y || y || Y&lt;br /&gt;
|-&lt;br /&gt;
| 16 || U || u || U&lt;br /&gt;
|-&lt;br /&gt;
| 17 || I || i || I&lt;br /&gt;
|-&lt;br /&gt;
| 18 || O || o || O&lt;br /&gt;
|-&lt;br /&gt;
| 19 || P || p || P&lt;br /&gt;
|-&lt;br /&gt;
| 1A || [ { || [ || {&lt;br /&gt;
|-&lt;br /&gt;
| 1B || ] } || ] || }&lt;br /&gt;
|-&lt;br /&gt;
| 1C || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 1D || 1 || 1 || 1 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 1E || 2 || 2 || 2 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 1F || 3 || 3 || 3 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 20 || A || a || A&lt;br /&gt;
|-&lt;br /&gt;
| 21 || S || s || S&lt;br /&gt;
|-&lt;br /&gt;
| 22 || D || d || D&lt;br /&gt;
|-&lt;br /&gt;
| 23 || F || f || F&lt;br /&gt;
|-&lt;br /&gt;
| 24 || G || g || G&lt;br /&gt;
|-&lt;br /&gt;
| 25 || H || h || H&lt;br /&gt;
|-&lt;br /&gt;
| 26 || J || j || J&lt;br /&gt;
|-&lt;br /&gt;
| 27 || K || k || K&lt;br /&gt;
|-&lt;br /&gt;
| 28 || L || l || L&lt;br /&gt;
|-&lt;br /&gt;
| 29 || ; : || ; || :&lt;br /&gt;
|-&lt;br /&gt;
| 2A || &#039; &amp;quot; || &#039; (single quote) || &amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 2B || || (not on most U.S. keyboards) ||&lt;br /&gt;
|-&lt;br /&gt;
| 2C || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 2D || 4 || 4 || 4 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 2E || 5 || 5 || 5 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 2F || 6 || 6 || 6 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 30 || || (not on most U.S. keyboards) ||&lt;br /&gt;
|-&lt;br /&gt;
| 31 || Z || z || Z&lt;br /&gt;
|-&lt;br /&gt;
| 32 || X || x || X&lt;br /&gt;
|-&lt;br /&gt;
| 33 || C || c || C&lt;br /&gt;
|-&lt;br /&gt;
| 34 || V || v || V&lt;br /&gt;
|-&lt;br /&gt;
| 35 || B || b || B&lt;br /&gt;
|-&lt;br /&gt;
| 36 || N || n || N&lt;br /&gt;
|-&lt;br /&gt;
| 37 || M || m || M&lt;br /&gt;
|-&lt;br /&gt;
| 38 || , &amp;lt; || , (comma) || &amp;lt;&lt;br /&gt;
|-&lt;br /&gt;
| 39 || . &amp;gt; || . (period) || &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 3A || / ? || / || ?&lt;br /&gt;
|-&lt;br /&gt;
| 3B || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 3C || . || . || . (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 3D || 7 || 7 || 7 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 3E || 8 || 8 || 8 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 3F || 9 || 9 || 9 (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 40 || Space bar || 0x20 || 0x20&lt;br /&gt;
|-&lt;br /&gt;
| 41 || Back Space || 0x08 || 0x08&lt;br /&gt;
|-&lt;br /&gt;
| 42 || Tab || 0x09 || 0x09&lt;br /&gt;
|-&lt;br /&gt;
| 43 || Enter || 0x0D || 0x0D&lt;br /&gt;
|-&lt;br /&gt;
| 44 || Return || 0x0D || 0x0D (Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 45 || Esc || 0x1B || 0x1B&lt;br /&gt;
|-&lt;br /&gt;
| 46 || Del || 0x7F || 0x7F&lt;br /&gt;
|-&lt;br /&gt;
| 47 || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 48 || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 49 || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 4A || - || - || - (Numeric Pad)&lt;br /&gt;
|-&lt;br /&gt;
| 4B || || (undefined) ||&lt;br /&gt;
|-&lt;br /&gt;
| 4C || Up arrow || &amp;lt;CSI&amp;gt;A || &amp;lt;CSI&amp;gt;T&lt;br /&gt;
|-&lt;br /&gt;
| 4D || Down arrow || &amp;lt;CSI&amp;gt;B || &amp;lt;CSI&amp;gt;S&lt;br /&gt;
|-&lt;br /&gt;
| 4E || Forward arrow || &amp;lt;CSI&amp;gt;C || &amp;lt;CSI&amp;gt; A (note blank space after &amp;lt;CSI&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 4F || Backward arrow || &amp;lt;CSI&amp;gt;D || &amp;lt;CSI&amp;gt; @ (note blank space after &amp;lt;CSI&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 50 || F1 || &amp;lt;CSI&amp;gt;0~ || &amp;lt;CSI&amp;gt;10~&lt;br /&gt;
|-&lt;br /&gt;
| 51 || F2 || &amp;lt;CSI&amp;gt;1~ || &amp;lt;CSI&amp;gt;11~&lt;br /&gt;
|-&lt;br /&gt;
| 52 || F3 || &amp;lt;CSI&amp;gt;2~ || &amp;lt;CSI&amp;gt;12~&lt;br /&gt;
|-&lt;br /&gt;
| 53 || F4 || &amp;lt;CSI&amp;gt;3~ || &amp;lt;CSI&amp;gt;13~&lt;br /&gt;
|-&lt;br /&gt;
| 54 || F5 || &amp;lt;CSI&amp;gt;4~ || &amp;lt;CSI&amp;gt;14~&lt;br /&gt;
|-&lt;br /&gt;
| 55 || F6 || &amp;lt;CSI&amp;gt;5~ || &amp;lt;CSI&amp;gt;15~&lt;br /&gt;
|-&lt;br /&gt;
| 56 || F7 || &amp;lt;CSI&amp;gt;6~ || &amp;lt;CSI&amp;gt;16~&lt;br /&gt;
|-&lt;br /&gt;
| 57 || F8 || &amp;lt;CSI&amp;gt;7~ || &amp;lt;CSI&amp;gt;17~&lt;br /&gt;
|-&lt;br /&gt;
| 58 || F9 || &amp;lt;CSI&amp;gt;8~ || &amp;lt;CSI&amp;gt;18~&lt;br /&gt;
|-&lt;br /&gt;
| 59 || F10 || &amp;lt;CSI&amp;gt;9~ || &amp;lt;CSI&amp;gt;19~&lt;br /&gt;
|-&lt;br /&gt;
| 5A || ( || ( || ( (usa1 Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 5B || ) || ) || ) (usa1 Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 5C || / || / || / (usa1 Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 5D || * || * || * (usa1 Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 5E || + || + || + (usa1 Numeric pad)&lt;br /&gt;
|-&lt;br /&gt;
| 5F || Help || &amp;lt;CSI&amp;gt;?~ || &amp;lt;CSI&amp;gt;?~&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Raw Key Number&lt;br /&gt;
! Function or Keycap Legend&lt;br /&gt;
|-&lt;br /&gt;
| 60 || Shift (left of space bar)&lt;br /&gt;
|-&lt;br /&gt;
| 61 || Shift (right of space bar)&lt;br /&gt;
|-&lt;br /&gt;
| 62 || Caps Lock&lt;br /&gt;
|-&lt;br /&gt;
| 63 || Ctrl&lt;br /&gt;
|-&lt;br /&gt;
| 64 || (Left) Alt&lt;br /&gt;
|-&lt;br /&gt;
| 65 || (Right) Alt&lt;br /&gt;
|-&lt;br /&gt;
| 66 || Amiga (left of space bar)&lt;br /&gt;
|-&lt;br /&gt;
| 67 || Amiga (right of space bar)&lt;br /&gt;
|-&lt;br /&gt;
| 68 || Left mouse button (not converted)&lt;br /&gt;
|-&lt;br /&gt;
| 69 || Right mouse button (not converted)&lt;br /&gt;
|-&lt;br /&gt;
| 6A || Middle mouse button (not converted)&lt;br /&gt;
|-&lt;br /&gt;
| 6B || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 6C || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 6D || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 6E || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 6F || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 70-7F || (undefined)&lt;br /&gt;
|-&lt;br /&gt;
| 80-F8 || Up transition (release or unpress key of one of the above keys) (80 for 00, F8 for 7F)&lt;br /&gt;
|-&lt;br /&gt;
| F9 || Last key code was bad (was sent in order to resynchronize)&lt;br /&gt;
|-&lt;br /&gt;
| FA || Keyboard buffer overflow&lt;br /&gt;
|-&lt;br /&gt;
| FB || (undefined, reserved for keyboard processor catastrophe)&lt;br /&gt;
|-&lt;br /&gt;
| FC || Keyboard selftest failed&lt;br /&gt;
|-&lt;br /&gt;
| FD || Power-up key stream start. Keys pressed or stuck at power-up will be sent between FD and FE.&lt;br /&gt;
|-&lt;br /&gt;
| FE || Power-up key stream end&lt;br /&gt;
|-&lt;br /&gt;
| FF || (undefined, reserved)&lt;br /&gt;
|-&lt;br /&gt;
| FF || Mouse event, movement only, no button change (not converted)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Notes about the preceding table:&lt;br /&gt;
&lt;br /&gt;
# &amp;quot;&amp;lt;CSI&amp;gt;&amp;quot; is the Control Sequence Introducer, value hex 9B.&lt;br /&gt;
# (undefined)&amp;quot; indicates that the current keyboard design should not generate this number.  If you are using &#039;&#039;&#039;SetKeyMap()&#039;&#039;&#039; to change the key map, the entries for these numbers must still be included.&lt;br /&gt;
# &amp;quot;(not converted)&amp;quot; refers to mouse button events. You must use the sequence &amp;quot;&amp;lt;CSI&amp;gt;2{&amp;quot; to inform the console driver that you wish to receive mouse events; otherwise these will not be transmitted.&lt;br /&gt;
# &amp;quot;(RESERVED)&amp;quot; indicates that these key codes have been reserved for national keyboards.  The $2B code key will be between the double-quote (&amp;quot;) and Return keys. The $30 code key will be between the Shift and Z keys.&lt;br /&gt;
&lt;br /&gt;
[[File:LibFig34-3.png|frame|center|ECMA-94 Latin 1 International 8-Bit Character Set]]&lt;br /&gt;
&lt;br /&gt;
== Raw Key Table ==&lt;br /&gt;
&lt;br /&gt;
The following table defines all of the Amiga raw key codes and what they mean.&lt;br /&gt;
&lt;br /&gt;
{{Note|All values are in hexadecimal}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Raw Key&lt;br /&gt;
! USB Page&lt;br /&gt;
! USB Code&lt;br /&gt;
! PS2 Set 1 Code&lt;br /&gt;
! PS2 Set 2 Code&lt;br /&gt;
! Name&lt;br /&gt;
! Classic Amiga Key&lt;br /&gt;
|-&lt;br /&gt;
| 0000 || 0007 || 0035 || 0029 || 000E || || &amp;lt;nowiki&amp;gt;`~&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0001 || 0007 || 001E || 0002 || 0016 || || &amp;lt;nowiki&amp;gt;1!&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0002 || 0007 || 001F || 0003 || 001E || || &amp;lt;nowiki&amp;gt;2@&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0003 || 0007 || 0020 || 0004 || 0026 || || &amp;lt;nowiki&amp;gt;3#&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0004 || 0007 || 0021 || 0005 || 0025 || || &amp;lt;nowiki&amp;gt;4$&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0005 || 0007 || 0022 || 0006 || 002E || || &amp;lt;nowiki&amp;gt;5%&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0006 || 0007 || 0023 || 0007 || 0036 || || &amp;lt;nowiki&amp;gt;6^&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0007 || 0007 || 0024 || 0008 || 003D || || &amp;lt;nowiki&amp;gt;7&amp;amp;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0008 || 0007 || 0025 || 0009 || 003E || || &amp;lt;nowiki&amp;gt;8*&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0009 || 0007 || 0026 || 000A || 0046 || || &amp;lt;nowiki&amp;gt;9(&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 000A || 0007 || 0027 || 000B || 0045 || || &amp;lt;nowiki&amp;gt;0)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 000B || 0007 || 002D || 000C || 004E || || &amp;lt;nowiki&amp;gt;-_&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 000C || 0007 || 002E || 000D || 0055 || || &amp;lt;nowiki&amp;gt;=+&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 000D || 0007 || 0031 || || || || &amp;lt;nowiki&amp;gt;\|&amp;lt;/nowiki&amp;gt; near Backspace&lt;br /&gt;
|-&lt;br /&gt;
| 000E || 0007 || 0089 || 007D || 006A || || Intl 3 Yen&lt;br /&gt;
|-&lt;br /&gt;
| 000F || 0007 || 0062 || 0052 || 0070 || || NP &amp;lt;nowiki&amp;gt;0&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0010 || 0007 || 0014 || 0010 || 0015 || || &amp;lt;nowiki&amp;gt;qQ&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0011 || 0007 || 001A || 0011 || 001D || || &amp;lt;nowiki&amp;gt;wW&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0012 || 0007 || 0008 || 0012 || 0024 || || &amp;lt;nowiki&amp;gt;eE&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0013 || 0007 || 0015 || 0013 || 002D || || &amp;lt;nowiki&amp;gt;rR&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0014 || 0007 || 0017 || 0014 || 002C || || &amp;lt;nowiki&amp;gt;tT&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0015 || 0007 || 001C || 0015 || 0035 || || &amp;lt;nowiki&amp;gt;yY&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0016 || 0007 || 0018 || 0016 || 003C || || &amp;lt;nowiki&amp;gt;uU&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0017 || 0007 || 000C || 0017 || 0043 || || &amp;lt;nowiki&amp;gt;iI&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0018 || 0007 || 0012 || 0018 || 0044 || || &amp;lt;nowiki&amp;gt;oO&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0019 || 0007 || 0013 || 0019 || 004D || || &amp;lt;nowiki&amp;gt;pP&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 001A || 0007 || 002F || 001A || 0054 || || &amp;lt;nowiki&amp;gt;[{&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 001B || 0007 || 0030 || 001B || 005B || || &amp;lt;nowiki&amp;gt;]}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 001C || || || || || || undefined&lt;br /&gt;
|-&lt;br /&gt;
| 001D || 0007 || 0059 || 004F || 0069 || || NP &amp;lt;nowiki&amp;gt;1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 001E || 0007 || 005A || 0050 || 0072 || || NP &amp;lt;nowiki&amp;gt;2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 001F || 0007 || 005B || 0051 || 007A || || NP &amp;lt;nowiki&amp;gt;3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0020 || 0007 || 0004 || 001E || 001C || || &amp;lt;nowiki&amp;gt;aA&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0021 || 0007 || 0016 || 001F || 001B || || &amp;lt;nowiki&amp;gt;sS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0022 || 0007 || 0007 || 0020 || 0023 || || &amp;lt;nowiki&amp;gt;dD&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0023 || 0007 || 0009 || 0021 || 002B || || &amp;lt;nowiki&amp;gt;fF&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0024 || 0007 || 000A || 0022 || 0034 || || &amp;lt;nowiki&amp;gt;gG&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0025 || 0007 || 000B || 0023 || 0033 || || &amp;lt;nowiki&amp;gt;hH&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0026 || 0007 || 000D || 0024 || 003B || || &amp;lt;nowiki&amp;gt;jJ&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0027 || 0007 || 000E || 0025 || 0042 || || &amp;lt;nowiki&amp;gt;kK&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0028 || 0007 || 000F || 0026 || 004B || || &amp;lt;nowiki&amp;gt;lL&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0029 || 0007 || 0033 || 0027 || 004C || || &amp;lt;nowiki&amp;gt;;:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 002A || 0007 || 0034 || 0028 || 0052 || || &amp;lt;nowiki&amp;gt;&#039;&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 002B || 0007 || 0032 || 002B || 005D || || Intl 1 near Return&lt;br /&gt;
|-&lt;br /&gt;
| 002C || || || || || || undefined&lt;br /&gt;
|-&lt;br /&gt;
| 002D || 0007 || 005C || 004B || 006B || || NP &amp;lt;nowiki&amp;gt;4&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 002E || 0007 || 005D || 004C || 0073 || || NP &amp;lt;nowiki&amp;gt;5&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 002F || 0007 || 005E || 004D || 0074 || || NP &amp;lt;nowiki&amp;gt;6&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0030 || 0007 || 0064 || 0056 || 0061 || || Intl 2 near LShift&lt;br /&gt;
|-&lt;br /&gt;
| 0031 || 0007 || 001D || 002C || 001A || || &amp;lt;nowiki&amp;gt;zZ&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0032 || 0007 || 001B || 002D || 0022 || || &amp;lt;nowiki&amp;gt;xX&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0033 || 0007 || 0006 || 002E || 0021 || || &amp;lt;nowiki&amp;gt;cC&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0034 || 0007 || 0019 || 002F || 002A || || &amp;lt;nowiki&amp;gt;vV&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0035 || 0007 || 0005 || 0030 || 0032 || || &amp;lt;nowiki&amp;gt;bB&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0036 || 0007 || 0011 || 0031 || 0031 || || &amp;lt;nowiki&amp;gt;nN&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0037 || 0007 || 0010 || 0032 || 003A || || &amp;lt;nowiki&amp;gt;mM&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0038 || 0007 || 0036 || 0033 || 0041 || || &amp;lt;nowiki&amp;gt;,&amp;lt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0039 || 0007 || 0037 || 0034 || 0049 || || &amp;lt;nowiki&amp;gt;.&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 003A || 0007 || 0038 || 0035 || 004A || || &amp;lt;nowiki&amp;gt;/?&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 003B || 0007 || 0087 || 0073 || 0051 || || &amp;lt;nowiki&amp;gt;/?&amp;lt;/nowiki&amp;gt; Brazil (near RShift) and International 1 (Ro)&lt;br /&gt;
|-&lt;br /&gt;
| 003C || 0007 || 0063 || 0053 || 0071 || || NP &amp;lt;nowiki&amp;gt;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 003D || 0007 || 005F || 0047 || 006C || || NP &amp;lt;nowiki&amp;gt;7&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 003E || 0007 || 0060 || 0048 || 0075 || || NP &amp;lt;nowiki&amp;gt;8&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 003F || 0007 || 0061 || 0049 || 007D || || NP &amp;lt;nowiki&amp;gt;9&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 0040 || 0007 || 002C || 0039 || 0029 || SPACE || Space&lt;br /&gt;
|-&lt;br /&gt;
| 0041 || 0007 || 002A || 000E || 0066 || BACKSPACE || Backspace&lt;br /&gt;
|-&lt;br /&gt;
| 0042 || 0007 || 002B || 000F || 000D || TAB || Tab&lt;br /&gt;
|-&lt;br /&gt;
| 0043 || 0007 || 0058 || 00E01C || 00E05A || ENTER || NP Enter&lt;br /&gt;
|-&lt;br /&gt;
| 0044 || 0007 || 0028 || 001C || 005A || RETURN || Return&lt;br /&gt;
|-&lt;br /&gt;
| 0045 || 0007 || 0029 || 0001 || 0076 || ESC || Escape&lt;br /&gt;
|-&lt;br /&gt;
| 0046 || 0007 || 004C || 00E053 || 00E071 || DEL || Delete&lt;br /&gt;
|-&lt;br /&gt;
| 0047 || 0007 || 0049 || 00E052 || 00E070 || INSERT || Insert&lt;br /&gt;
|-&lt;br /&gt;
| 0048 || 0007 || 004B || 00E049 || 00E07D || PAGE_UP || PageUp&lt;br /&gt;
|-&lt;br /&gt;
| 0049 || 0007 || 004E || 00E051 || 00E07A || PAGE_DOWN || PageDown&lt;br /&gt;
|-&lt;br /&gt;
| 004A || 0007 || 0056 || 004A || 007B || || NP &amp;lt;nowiki&amp;gt;-&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 004B || 0007 || 0044 || 0057 || 0078 || F11 || F11&lt;br /&gt;
|-&lt;br /&gt;
| 004C || 0007 || 0052 || 00E048 || 00E075 || CURSOR_UP || CrsrUp&lt;br /&gt;
|-&lt;br /&gt;
| 004D || 0007 || 0051 || 00E050 || 00E072 || CURSOR_DOWN || CrsrDown&lt;br /&gt;
|-&lt;br /&gt;
| 004E || 0007 || 004F || 00E04D || 00E074 || CURSOR_RIGHT || CrsrRight&lt;br /&gt;
|-&lt;br /&gt;
| 004F || 0007 || 0050 || 00E04B || 00E06B || CURSOR_LEFT || CrsrLeft&lt;br /&gt;
|-&lt;br /&gt;
| 0050 || 0007 || 003A || 003B || 0005 || F1 || F1&lt;br /&gt;
|-&lt;br /&gt;
| 0051 || 0007 || 003B || 003C || 0006 || F2 || F2&lt;br /&gt;
|-&lt;br /&gt;
| 0052 || 0007 || 003C || 003D || 0004 || F3 || F3&lt;br /&gt;
|-&lt;br /&gt;
| 0053 || 0007 || 003D || 003E || 000C || F4 || F4&lt;br /&gt;
|-&lt;br /&gt;
| 0054 || 0007 || 003E || 003F || 0003 || F5 || F5&lt;br /&gt;
|-&lt;br /&gt;
| 0055 || 0007 || 003F || 0040 || 000B || F6 || F6&lt;br /&gt;
|-&lt;br /&gt;
| 0056 || 0007 || 0040 || 0041 || 0083 || F7 || F7&lt;br /&gt;
|-&lt;br /&gt;
| 0057 || 0007 || 0041 || 0042 || 000A || F8 || F8&lt;br /&gt;
|-&lt;br /&gt;
| 0058 || 0007 || 0042 || 0043 || 0001 || F9 || F9&lt;br /&gt;
|-&lt;br /&gt;
| 0059 || 0007 || 0043 || 0044 || 0009 || F10 || F10&lt;br /&gt;
|-&lt;br /&gt;
| 005A || || || || || || NP &amp;lt;nowiki&amp;gt;(&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 005B || || || || || || NP &amp;lt;nowiki&amp;gt;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 005C || 0007 || 0054 || 00E035 || 00E04A || || NP &amp;lt;nowiki&amp;gt;/&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 005D || 0007 || 0055 || 0037 || 007C || || NP &amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 005E || 0007 || 0057 || 004E || 0079 || || NP &amp;lt;nowiki&amp;gt;+&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 005F || 0007 || 0075 || 0046 || 007E || HELP || Help (maps to ScrollLock on PS/2)&lt;br /&gt;
|-&lt;br /&gt;
| 0060 || 0007 || 00E1 || 002A || 0012 || LSHIFT || LShift&lt;br /&gt;
|-&lt;br /&gt;
| 0061 || 0007 || 00E5 || 0036 || 0059 || RSHIFT || RShift&lt;br /&gt;
|-&lt;br /&gt;
| 0062 || 0007 || 0039 || 003A || 0058 || CAPSLOCK || CapsLock&lt;br /&gt;
|-&lt;br /&gt;
| 0063 || 0007 || 00E0 || 001D || 0014 || CONTROL || LControl&lt;br /&gt;
|-&lt;br /&gt;
| 0064 || 0007 || 00E2 || 0038 || 0011 || LALT || LAlt&lt;br /&gt;
|-&lt;br /&gt;
| 0065 || 0007 || 00E6 || 00E038 || 00E011 || RALT || RAlt&lt;br /&gt;
|-&lt;br /&gt;
| 0066 || 0007 || 00E3 || 00E05B || 00E01F || LCOMMAND || LAmiga, LWin, LApple, LMeta&lt;br /&gt;
|-&lt;br /&gt;
| 0067 || 0007 || 00E7 || 00E05C || 00E027 || RCOMMAND || RAmiga, RWin, RApple, RMeta&lt;br /&gt;
|-&lt;br /&gt;
| 0068 || || || || || LBUTTON || LMouse reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0069 || || || || || RBUTTON || RMouse reserved&lt;br /&gt;
|-&lt;br /&gt;
| 006A || || || || || MBUTTON || MMouse reserved&lt;br /&gt;
|-&lt;br /&gt;
| 006B || 0007 || 0065 || 00E05D || 00E02F || MENU || Menu, Windows, Compose (mappable to RAmiga in Firmware)&lt;br /&gt;
|-&lt;br /&gt;
| 006C || 0007 || 0085 || 007E || 006D || || Brazil NP &amp;lt;nowiki&amp;gt;.&amp;lt;/nowiki&amp;gt; (named &amp;quot;Keypad &amp;lt;nowiki&amp;gt;,&amp;lt;/nowiki&amp;gt;&amp;quot; in USB specs)&lt;br /&gt;
|-&lt;br /&gt;
| 006D || 0007 || 0046 || 00E037 || 00E07C || PRINTSCREEN || PrintScreen/SysReq (mappable to Help in Firmware)&lt;br /&gt;
|-&lt;br /&gt;
| 006E || 0007 || 0048 || 00E046 || 00E07E || BREAK || Break, Ctrl-Pause on PS/2&lt;br /&gt;
|-&lt;br /&gt;
| 006F || 0007 || 0045 || 0058 || 0007 || F12 || F12&lt;br /&gt;
|-&lt;br /&gt;
| 0070 || 0007 || 004A || 00E047 || 00E06C || HOME || Home&lt;br /&gt;
|-&lt;br /&gt;
| 0071 || 0007 || 004D || 00E04F || 00E069 || END || End&lt;br /&gt;
|-&lt;br /&gt;
| 0072 || 000C || 00B7 || 00E024 || 00E03B || AUD_STOP || Stop, CD32 Blue Stop, CDTV Stop, Port 0 Blue&lt;br /&gt;
|-&lt;br /&gt;
| 0073 || 000C || 00CD || 00E022 || E034 || AUD_PLAY_PAUSE || Play/Pause, CD32 Grey Play/Pause, CDTV Play/Pause, Port 0 Play&lt;br /&gt;
|-&lt;br /&gt;
| 0074 || 000C || 00B6 || 00E010 || E015 || AUD_PREV_TRACK || Prev Track, CD32 Charcoal Reverse, CDTV &amp;lt;nowiki&amp;gt;&amp;lt;&amp;lt;&amp;lt;/nowiki&amp;gt; REW, Port 0 Reverse&lt;br /&gt;
|-&lt;br /&gt;
| 0075 || 000C || 00B5 || 00E019 || E04D || AUD_NEXT_TRACK || Next Track, CD32 Charcoal Forward, CDTV &amp;lt;nowiki&amp;gt;&amp;gt;&amp;gt;&amp;lt;/nowiki&amp;gt; FF, Port 0 Forward&lt;br /&gt;
|-&lt;br /&gt;
| 0076 || 000C || 00B9 || || || AUD_SHUFFLE || Random Play, CD32 Green Shuffle, Port 0 Green&lt;br /&gt;
|-&lt;br /&gt;
| 0077 || 000C || 00BC || || || AUD_REPEAT || Repeat, CD32 Yellow Repeat, Port 0 Yellow&lt;br /&gt;
|-&lt;br /&gt;
| 0078 || || || || || || Port 0 Red&lt;br /&gt;
|-&lt;br /&gt;
| 0079 || || || || || || Port 0 Joystick Up&lt;br /&gt;
|-&lt;br /&gt;
| 007A || || || || || || Port 0 Joystick Down&lt;br /&gt;
|-&lt;br /&gt;
| 007B || || || || || || Port 0 Joystick Right&lt;br /&gt;
|-&lt;br /&gt;
| 007C || || || || || || Port 0 Joystick Left&lt;br /&gt;
|-&lt;br /&gt;
| 007D || || || || || || undefined&lt;br /&gt;
|-&lt;br /&gt;
| 007E || || || || || || undefined&lt;br /&gt;
|-&lt;br /&gt;
| 007F || || || || || || undefined&lt;br /&gt;
|-&lt;br /&gt;
| 0080 - 00F8 || || || || || || Up transition (release or unpress key) (0080 for 0000, 00F8 for 0078)&lt;br /&gt;
|-&lt;br /&gt;
| 00F9 || || || || || || Last key code was bad (was sent in order to resynchronize)&lt;br /&gt;
|-&lt;br /&gt;
| 00FA || || || || || || Keyboard buffer overflow&lt;br /&gt;
|-&lt;br /&gt;
| 00FB || || || || || || (undefined, reserved for keyboard processor catastrophe)&lt;br /&gt;
|-&lt;br /&gt;
| 00FC || || || || || || Keyboard selftest failed&lt;br /&gt;
|-&lt;br /&gt;
| 00FD || || || || || || Power-up key stream start. Keys pressed or stuck at power-up will be sent between 00FD and 00FE.&lt;br /&gt;
|-&lt;br /&gt;
| 00FE || || || || || || Power-up key stream end&lt;br /&gt;
|-&lt;br /&gt;
| 00FF || || || || || || No key code defined&lt;br /&gt;
|-&lt;br /&gt;
| 0100 || 0007 || 0088 || 0070 || 0013 || || Intl 2 Hiragana/Katakana&lt;br /&gt;
|-&lt;br /&gt;
| 0101 || 0007 || 008A || 0079 || 0064 || || Intl 4 Henkan&lt;br /&gt;
|-&lt;br /&gt;
| 0102 || 0007 || 008B || 007B || 0067 || || Intl 5 Muhenkan&lt;br /&gt;
|-&lt;br /&gt;
| 0103 || 0007 || 0068 || 0064 || 0008 || F13 || F13&lt;br /&gt;
|-&lt;br /&gt;
| 0104 || 0007 || 0069 || 0065 || 0010 || F14 || F14&lt;br /&gt;
|-&lt;br /&gt;
| 0105 || 0007 || 006A || 0066 || 0018 || F15 || F15&lt;br /&gt;
|-&lt;br /&gt;
| 0180 - 0185 || || || || || || Up transition (release or unpress key) (0180 for 0100)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following are brief descriptions of the 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;
| AskKeyMapDefault()&lt;br /&gt;
| Ask for a pointer to the current default keymap&lt;br /&gt;
|-&lt;br /&gt;
| CloseKeyMapHandle()&lt;br /&gt;
| Close a keymap handle (V50)&lt;br /&gt;
|-&lt;br /&gt;
| MapANSI()&lt;br /&gt;
| Encode an ANSI string into key codes&lt;br /&gt;
|-&lt;br /&gt;
| MapRawKey()&lt;br /&gt;
| Decode a raw key input event to an ANSI string&lt;br /&gt;
|-&lt;br /&gt;
| ObtainKeyMapInfo()&lt;br /&gt;
| Obtain information about a raw key (V51.7)&lt;br /&gt;
|-&lt;br /&gt;
| OpenKeyMapHandle()&lt;br /&gt;
| Open a keymap handle (V50)&lt;br /&gt;
|-&lt;br /&gt;
| ReleaseKeyMapInfo()&lt;br /&gt;
| Release info obtained from a keymap handle (V50)&lt;br /&gt;
|-&lt;br /&gt;
| SetKeyMapDefault()&lt;br /&gt;
| Set the current default keymap&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Migration_Guide&amp;diff=10208</id>
		<title>Migration Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Migration_Guide&amp;diff=10208"/>
		<updated>2019-03-26T21:01:03Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* Include Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In spite of being largely compatible with the classic API, there are a few rules that a programmer should be aware of when porting programs from the 68k-based AmigaOS 3.x to the new PowerPC-based AmigaOS 4. This document tries to outline the process and also point to a few potential traps and issues.&lt;br /&gt;
&lt;br /&gt;
== Source Code Level ==&lt;br /&gt;
&lt;br /&gt;
==== Include Files ====&lt;br /&gt;
&lt;br /&gt;
Include files used to be a problematic issue under OS 3.x because there where a lot of different compilers, and most of them tended to use their own naming scheme and glue to bind to the operating system. The well-known SAS/C and StormC compiler systems used #pragma&#039;s for this purpose while gcc used inline functions or #define directives. AmigaOS 4 at the moment supports two compiler systems (GNU GCC and the vbcc package) and both use the same scheme for include files.&lt;br /&gt;
&lt;br /&gt;
To use a library under AmigaOS 4, you simply include its proto file, in addition to any other file that may be required. For example, to use Intuition Windows and Screens, you would add the following to your program:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&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;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proto file includes the required headers, which might depend on the compiler you are using. Just including the proto file will ensure maximum compatibility between different compilers.&lt;br /&gt;
&lt;br /&gt;
A few preprocessor symbols influence the way that the system bindings are included. Table 1 summarizes these symbols. They are typically defined in a Makefile, although they may as well be put into #define directives. Don&#039;t worry if you don&#039;t understand everything in that table; things will become clearer further down this document.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| __USE_INLINE__&lt;br /&gt;
| This symbol makes the protofile include an inline4 file as well. An inline4 file contains preprocessor macros for functions that resemble the classic way of calling system functions (i.e. functions as opposed to methods). Primarily intended for backward compatibility with older source code, or general compatibility.&lt;br /&gt;
|-&lt;br /&gt;
| __NOLIBBASE__&lt;br /&gt;
| Inhibits the definition of the library base associated with the proto file. Usually a proto file declares an extern symbol for the library base.&lt;br /&gt;
|-&lt;br /&gt;
| __USE_BASETYPE__&lt;br /&gt;
| If __NOLIBBASE__ is not defined, this symbol specifies whether the library base is declared with its &amp;quot;real&amp;quot; type (for example struct IntuitionBase for Intuition) or with the abstract base type for the class of resource (struct Library or struct Device). Only library implementors should use this symbol; normally, all library base structures are private and should not be examined. The default behavior is to declare all library bases as struct Library and all device bases as struct Device.&lt;br /&gt;
|-&lt;br /&gt;
| __NOGLOBALIFACE__&lt;br /&gt;
| Proto files for libraries that declare static global interfaces (like all &amp;quot;classic&amp;quot; libraries) normally also declare the interface pointer inside their proto file, unless this symbol is defined. Normally, you would only use this if you want to store the interface pointer elsewhere, for example in a library base.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Table 1: Preprocessor symbols that control the proto file&lt;br /&gt;
&lt;br /&gt;
Speaking of preprocessor symbols, the compiler defines a preprocessor symbol when targeting AmigaOS 4.x: __amigaos4__ and you can use this symbol for optional compilation of code.&lt;br /&gt;
&lt;br /&gt;
==== Library Bases and Interface Pointers ====&lt;br /&gt;
&lt;br /&gt;
Library bases should not normally be declared inside a source file. The proto file usually declares the required library base itself. If you want to do it anyway, you should define the preprocessor symbol __NOLIBBASE__ to prevent the proto file from doing it.&lt;br /&gt;
&lt;br /&gt;
Contrary to the old system, AmigaOS 4 has a slightly different way of calling library functions. The old system used to call a library function by making a relative jump into a jump table located directly in front of the library base. AmigaOS 4 keeps these jump tables for backwards compatibility only - only 68k functions are still called this way. The new OS now keeps jump tables in a separate pointer called the &amp;quot;Interface Pointer&amp;quot;, or short &amp;quot;interface&amp;quot;. Basically an interface is a structure with a bit of housekeeping information and a lot of inline function pointers. A library may export more than one interface pointer (it usually exports at least two).&lt;br /&gt;
&lt;br /&gt;
Almost all libraries export an interface by the name of &amp;quot;main&amp;quot;. The exact type of this interface depends on the library. For example, exec.library will export a main interface of type &amp;quot;struct ExecIFace *&amp;quot; (Note that it would theoretically be possible for another library to export an interface of type &amp;quot;struct ExecIFace *&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Like the library bases formerly used to call library functions, by convention a global variable is used to make the ExecIFace pointer available to all functions in your program. The name of that variable is constructed by prepending the library base name (&amp;quot;Exec&amp;quot; in this example) with a capital I. Thus, the global struct ExecIFace * would be called IExec. Note that this is simply a convention introduced by the proto files. You do not need to even use a global interface pointer, as much as you don&#039;t need to use a global library pointer (neither now in AmigaOS 4, nor in AmigaOS 3.9 or earlier).&lt;br /&gt;
&lt;br /&gt;
==== Type Consistency ====&lt;br /&gt;
&lt;br /&gt;
The PowerPC architectural manual frequently uses the word &amp;quot;word&amp;quot; to signify a 32 bit quantity. However in classic AmigaOS, the types WORD and UWORD meant a 16 bit signed and unsigned quantity. Likewise, an 8 bit quantity used to be called BYTE or UBYTE and 32 bit &amp;quot;words&amp;quot; where called LONG and ULONG. No 64 bit quantity existed. To clean up the naming of base types, these types have all been deprecated for AmigaOS 4. The new types are consistently called int or uint followed by the number of bits, for example uint32 signifies a 32 bit unsigned quantity. Likewise, the typing has been extended to include a 64 bit quantity. Table 2 lists the new types and the &amp;quot;classic&amp;quot; types they replace. Note that the old types are still available through preprocessor defines, but should not be used anymore in new code.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! New Type&lt;br /&gt;
! Old Type&lt;br /&gt;
! Significance&lt;br /&gt;
|-&lt;br /&gt;
| int8&lt;br /&gt;
| BYTE&lt;br /&gt;
| 8 bit signed&lt;br /&gt;
|-&lt;br /&gt;
| uint8&lt;br /&gt;
| UBYTE&lt;br /&gt;
| 8 bit unsigned integer&lt;br /&gt;
|-&lt;br /&gt;
| int16&lt;br /&gt;
| WORD&lt;br /&gt;
| 16 bit signed&lt;br /&gt;
|-&lt;br /&gt;
| uint16&lt;br /&gt;
| UWORD&lt;br /&gt;
| 16 bit unsigned integer&lt;br /&gt;
|-&lt;br /&gt;
| int32&lt;br /&gt;
| LONG&lt;br /&gt;
| 32 bit signed&lt;br /&gt;
|-&lt;br /&gt;
| uint32&lt;br /&gt;
| ULONG&lt;br /&gt;
| 32 bit unsigned integer&lt;br /&gt;
|-&lt;br /&gt;
| int64&lt;br /&gt;
| None existed&lt;br /&gt;
| 64 bit signed&lt;br /&gt;
|-&lt;br /&gt;
| uint64&lt;br /&gt;
| None existed&lt;br /&gt;
| 64 bit unsigned integer&lt;br /&gt;
|-&lt;br /&gt;
| float32&lt;br /&gt;
| FLOAT&lt;br /&gt;
| 32 bit single-precision floating point number&lt;br /&gt;
|-&lt;br /&gt;
| float64&lt;br /&gt;
| DOUBLE&lt;br /&gt;
| 64 bit double-precision floating point number&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Table 2: New Base Types&lt;br /&gt;
&lt;br /&gt;
== Programming Considerations ==&lt;br /&gt;
&lt;br /&gt;
==== Library Initialization using libauto ====&lt;br /&gt;
&lt;br /&gt;
System libraries may be opened automatically using the libauto link library. libauto can automatically open a number of system libraries (see [[#libauto_Libraries|libauto Libraries]] for a list of these libraries) and also set up their interface pointers. If you include the proto file in your program and link with libauto, the appropriate library will be all set up. For example, including &amp;lt;proto/intuition.h&amp;gt; will set up IntuitionBase and IIntuition automatically so that they are all set up when your program enters the main() function. The following example program opens a simple window, waits a bit then closes it again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&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/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
 struct Window *win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
  WA_Title, &amp;quot;Example Window&amp;quot;,&lt;br /&gt;
  WA_Width, 640,&lt;br /&gt;
  WA_Height, 480,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
 IDOS-&amp;gt;Delay(150);&lt;br /&gt;
&lt;br /&gt;
 IIntuition-&amp;gt;CloseWindow(win);&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;
For the time being, ignore the way that functions are written (IIntuition-&amp;gt;OpenWindowTags as opposed to a simple OpenWindowTags, we&#039;ll cover that later).&lt;br /&gt;
&lt;br /&gt;
Save this program as windowtest.c and compile it with:&lt;br /&gt;
&lt;br /&gt;
 gcc -o windowtest windowtest.c -lauto&lt;br /&gt;
&lt;br /&gt;
==== Manual Library Initialization ====&lt;br /&gt;
&lt;br /&gt;
Libraries can also be initialized by hand. As with the old system, the function to do this is exec.library&#039;s OpenLibrary function. However, as we have hinted above, the library base is not the only requirement for using a library - you need to retrieve the interface pointer (or rather, all interface pointers you need) from the library to make use of it. On the classic system, opening for example intuition.library looked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IntuitionBase = (struct IntuitionBase *)OpenLibrary(&amp;quot;intuition.library&amp;quot;, 36);&lt;br /&gt;
if (IntuitionBase == NULL) PanicExit(&amp;quot;Cannot open intuition library&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
struct Window *win = OpenWindowTags(NULL, ...);&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
CloseWindow(win);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Under AmigaOS 4, this will look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50);&lt;br /&gt;
IIntuition = (struct IntuitionIFace *)IExec-&amp;gt;GetInterface(&lt;br /&gt;
 IntuitionBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
if (IIntuition == NULL) {&lt;br /&gt;
 PanicExit(&amp;quot;Cannot obtain main interface from Intuition&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
struct Window *win = IIntuition-&amp;gt;OpenWindowTags(NULL, ...);&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;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Changed passes have been marked in bold. As you can see, there are some obvious changes:&lt;br /&gt;
&lt;br /&gt;
# All functions are prefixed by either IExec-&amp;amp;gt; or IIntuition-&amp;amp;gt;.&lt;br /&gt;
# An additional variable, IIntuition is initialized.&lt;br /&gt;
# IntuitionBase is now a struct Library and no longer needs a typecast.&lt;br /&gt;
&lt;br /&gt;
The new variable is called the &amp;quot;interface pointer&amp;quot;. Note that when you call GetInterface, you need to pass in the library base from which you want to retrieve the interface pointer, its name (&amp;quot;main&amp;quot; in this case, as in almost all classic libraries) and a version number (1 is the base version). The last parameter is a pointer to a tag list; for defined tag items please consult the autodoc.&lt;br /&gt;
&lt;br /&gt;
The interface pointer is needed to actually call the functions of a library (remember that the &amp;quot;original&amp;quot; jump table only contains 68k pointers). Thus, to call an Intuition function, you write &amp;quot;IIntution-&amp;amp;gt;OpenWindowTags&amp;quot; instead of a simple &amp;quot;OpenWindowTags&amp;quot;. Note that there are ways around this; as we said in the introduction, using the preprocessor symbol __USE_INLINE__ will automatically include a file that contains a macro definition of the following type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#define OpenWindowTags(...) \&lt;br /&gt;
 IIntuition-&amp;gt;OpenWindowTags(__VA_ARGS__)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will both ensure compatibility with old source code, as well as allowing you to maintain different versions of your software.&lt;br /&gt;
&lt;br /&gt;
==== Opening Devices ====&lt;br /&gt;
&lt;br /&gt;
Opening a device also works largely the same as under OS 3.x. If a device doesn&#039;t export its own function table, like serial device, then there is no additional step required. If, on the other hand, the device does export its own function table, you need to retrieve the interface pointer just as you would retrieve the interface pointer from a library. For example, timer device is usually opened like this (error checking removed for clarity):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort *TimerMP = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL);&lt;br /&gt;
&lt;br /&gt;
struct TimeRequest *TimerIO = IExec-&amp;gt;AllocSysObjectTags(AOST_IOREQUEST,&lt;br /&gt;
 ASOIOR_Size, sizeof(struct TimeRequest),&lt;br /&gt;
 ASOIOR_ReplyPort, TimerMP,&lt;br /&gt;
 TAG_END);&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;OpenDevice(TIMERNAME, UNIT_MICROHZ, TimerIO, 0);&lt;br /&gt;
&lt;br /&gt;
struct Library *TimerBase = (struct Library *)TimerIO-&amp;gt;tr_node.io_Device;&lt;br /&gt;
&lt;br /&gt;
struct TimerIFace *ITimer = (struct TimerIFace *)&lt;br /&gt;
 IExec-&amp;gt;GetInterface(TimerBase, &amp;quot;main&amp;quot;, 1, NULL);&lt;br /&gt;
&lt;br /&gt;
ITimer-&amp;gt;GetSysTime(&amp;amp;amp;tv);&lt;br /&gt;
&lt;br /&gt;
if (!IExec-&amp;gt;CheckIO(TimerIO))&lt;br /&gt;
 IExec-&amp;gt;AbortIO(TimerIO);&lt;br /&gt;
&lt;br /&gt;
IExec-&amp;gt;WaitIO(TimerIO);&lt;br /&gt;
IExec-&amp;gt;DropInterface((struct Interface *)ITimer);&lt;br /&gt;
IExec-&amp;gt;CloseDevice(TimerIO);&lt;br /&gt;
IExec-&amp;gt;FreeSysObject(ASOT_IOREQUEST, TimerIO);&lt;br /&gt;
IExec-&amp;gt;FreeSysObject(ASOT_PORT, TimerMP);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, except for the initialization of the interface pointer, everything is the same. The AllocSysObject functions at the top are new to Exec V50, and fulfill the same function as AllocDosObject in DOS. Consult the autodoc for more info. Also note the timer.device structures have been renamed.&lt;br /&gt;
&lt;br /&gt;
==== Calling Library Functions ====&lt;br /&gt;
&lt;br /&gt;
We&#039;ve basically covered this before already, but we&#039;ll review it here for completeness. A libraries interface pointer can be used to call the libraries functions. Thus, if you want to call an Exec function, you need to explicitly call the &amp;quot;method&amp;quot; from the interface.&lt;br /&gt;
&lt;br /&gt;
You might wonder why we chose this path instead of simply providing stub functions. Well, for one thing if you do not like this kind of calling mechanism, you can completely isolate yourself from it by using the inline4 macro files. These &amp;quot;simulate&amp;quot; the use of &amp;quot;normal&amp;quot; functions, however, they come with the same issues as the preprocessor based inlines of the classic gcc, namely that - as preprocessor macros - they aren&#039;t aware of name spaces and scopes, and are blindly replaced on a textual basis. Thus, if you have for example a C++ class list that contains a member function AddHead, the preprocessor will most likely complain that you used a macro with the wrong number of arguments.&lt;br /&gt;
&lt;br /&gt;
Likewise, it means that the name of a function needs to be unique across the whole system. The result are longer function names with redundant naming.&lt;br /&gt;
&lt;br /&gt;
Finally, the interfaces allow for other things. For example, expansion library uses interfaces in a sort of object-oriented way - every PCI device on the bus is represented by a separate interface. That means that instead of repeating the PCI bus, device and function ID&#039;s as parameters every time, you can write something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
a = MyDevice-&amp;gt;ReadConfigByte(PCI_INTERRUPT_LINE);&lt;br /&gt;
MyDevice-&amp;gt;WriteConfigByte(PCI_INTERRUPT_LINE, a &amp;amp; 0x0F);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following chapter goes a bit deeper into the matter; you may skip it if you want, since ported classic programs will seldom need to make use of interfaces other than the basic initialization and cleanup tasks.&lt;br /&gt;
&lt;br /&gt;
==== Interface Basics ====&lt;br /&gt;
&lt;br /&gt;
Interfaces have a usage count. That count is implicitly incremented when you call GetInterface and decremented again on DropInterface. You can explicitly do that using the Obtain() and Release() method that all interfaces implement. You must do this if you intend to use the interface you obtained somewhere outside the context of your task. For example, consider an image loader library that returns a picture in the form of an interface. Suppose that this interface contains a Print() method that you can use to send the image to a printer. Quite naturally, this will take its time, so the program launches a subtask that takes care of that. The subtask would look something like that (some error handling removed for clarity):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void print_task(struct PictureIFace *IPicture)&lt;br /&gt;
{&lt;br /&gt;
 IPicture-&amp;gt;Print(some_printer_description);&lt;br /&gt;
 IPicture-&amp;gt;Release();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Somewhere else...&lt;br /&gt;
struct PictureIFace *IPicture = ILoader-&amp;gt;Load(&amp;quot;DH0:Backdrop.jpg&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Pass the picture to the print spooler.&lt;br /&gt;
IPicture-&amp;gt;Obtain(); // Add a reference so that it will not vanish too soon&lt;br /&gt;
&lt;br /&gt;
// Create the spooler task. CreateTaskTags is new for V50.&lt;br /&gt;
// Note how you can now pass arguments to the created task.&lt;br /&gt;
if (IExec-&amp;gt;CreateTaskTags(&amp;quot;Printer Spooler&amp;quot;,&lt;br /&gt;
 SPOOLER_PRIORITY, print_task, SPOOLER_STACK_SIZE,&lt;br /&gt;
 AT_Param, IPicture,&lt;br /&gt;
 TAG_END) == NULL)&lt;br /&gt;
{&lt;br /&gt;
 // Task creation failed.&lt;br /&gt;
 IPicture-&amp;gt;Release(); // Release the unused reference.&lt;br /&gt;
&lt;br /&gt;
 do_some_error_stuff();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Spooler is running, we don&#039;t need our copy anymore.&lt;br /&gt;
IPicture-&amp;gt;Release();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What is happening here? The Load() method would create the IPicture interface, thus giving us a fully usable IPicture interface with one reference. Since we want to pass it to a subtask, we do add another reference to the internal counter (increasing it to two) and pass it on down to the spooler task. After that task is running, we simply mark our copy as unused by calling Release() on it (we&#039;re now back at one again).&lt;br /&gt;
&lt;br /&gt;
When the spooler is done, it will call Release() itself again, thus bringing the counter to zero. If the usage counter of an interface ever reaches zero, this indicates to the system that the interface is done with, and will trigger the interface&#039;s destruction.&lt;br /&gt;
&lt;br /&gt;
== Calling 68K Functions ==&lt;br /&gt;
&lt;br /&gt;
Some system code as well as some application code might still be 68k based. Usually an application programmer doesn&#039;t need to worry about this, though as most of the details are handled internally by the system. Typical places where PowerPC to 68k transitions take place are:&lt;br /&gt;
&lt;br /&gt;
# 68k libraries&lt;br /&gt;
# 68k hook functions&lt;br /&gt;
# 68k interrupts&lt;br /&gt;
&lt;br /&gt;
==== 68k Library Entries ====&lt;br /&gt;
&lt;br /&gt;
For this discussion we assume that we have a disk based library called &amp;quot;foo.library&amp;quot; that is written in 68k code. Previously we have said that library calls always go through an interface - in most cases called &amp;quot;main&amp;quot; - but obviously the writer of the 68k library was unaware of this mechanism.&lt;br /&gt;
&lt;br /&gt;
In order to present a unified calling mechanism to the programmer, 68k libraries are called through the same interfaces as a PowerPC library would. Since the original library doesn&#039;t provide this interface, a disk-resident stub library will do that. In our case, this library would be called &amp;quot;foo.l.main&amp;quot;. The &#039;l&#039; comes from the first letter of the resource type (&amp;quot;library&amp;quot; in this case), and the &amp;quot;main&amp;quot; is the name of the interface it provides. Thus, the &amp;quot;foo.l.main&amp;quot; stub will provide the interface &amp;quot;main&amp;quot; for &amp;quot;foo.library&amp;quot;. If you call ObtainInterface() on a 68k library, the system will automatically scan its library search path for the file &amp;quot;foo.l.main&amp;quot; and if found, will try to open it and obtain the interface.&lt;br /&gt;
&lt;br /&gt;
While this may look strange at first sight, it offers the possibility to migrate &amp;quot;foo.library&amp;quot; to PowerPC later on without the need to even recompile the application(s) using it. Furthermore, for a transition the &amp;quot;foo.l.main&amp;quot; interface may contain a few &amp;quot;real&amp;quot; PowerPC functions - normally it is composed from stub functions that call the 68k emulator.&lt;br /&gt;
&lt;br /&gt;
The downside is that (at the time of writing) the &amp;quot;foo.l.main&amp;quot; library will not be created automatically. There are tools available in the SDK to build it from an SFD file, though. This process is described in a different document.&lt;br /&gt;
&lt;br /&gt;
==== 68k Hook Functions ====&lt;br /&gt;
&lt;br /&gt;
Hook functions in general are completely transparent when it comes to PowerPC vs. 68k issues. You must not call a hook function directly, though - doing this used to be legal in the pre-4.0 era, but will result in an ISI exception for AmigaOS 4.&lt;br /&gt;
&lt;br /&gt;
Hook functions must be called through Utility&#039;s CallHookPkt function. Invoking a hook this way will make sure that the appropriate measures are met - either by invoking the emulator, or by a real jump into the code.&lt;br /&gt;
&lt;br /&gt;
==== 68k Interrupts ====&lt;br /&gt;
&lt;br /&gt;
As with hook functions, interrupts are handled completely transparently. When an interrupt is added to the system, a few checks are done and the interrupt&#039;s ln_Type field may be modified. This is done to ensure minimum latency when the interrupt occurs. The consequence is that you may not change the is_Code field of an interrupt node without removing and re-adding it to the system first.&lt;br /&gt;
&lt;br /&gt;
==== Calling The Emulator Directly ====&lt;br /&gt;
&lt;br /&gt;
In rare cases you might want to directly call the 68k emulator to execute 68k code. Exec provides the function Emulate for this purpose. Emulate can be called with an implicit register mapping and an arbitrary address. For more information on Emulate please refer to its autodoc.&lt;br /&gt;
&lt;br /&gt;
== Other Programming Considerations ==&lt;br /&gt;
&lt;br /&gt;
==== General Hook Functions ====&lt;br /&gt;
&lt;br /&gt;
Hook functions used to be called with a specific mapping of 68k registers. Quite naturally, there are no 68k registers on a PowerPC machine, so the programmer needs to be a bit careful when writing a hook function.&lt;br /&gt;
&lt;br /&gt;
CallHookPkt, or its vararg version CallHook, will always adhere to the PowerPC SysV ABI, and therefore the order of the parameters in the called function is important (it was always to be considered bad style to rearrange the parameters of a hook function, but possible through the explicit declaration of register mappings).&lt;br /&gt;
&lt;br /&gt;
A hook function should always look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
uint32 HookFunction(struct Hook *hook, APTR object, APTR message);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first argument, hook, points to the hook with which this function was invoked. The second paramter, objec, is a generic &#039;target&#039; for the oeration, and depends on the context that this hook is being called in. The final argument, message, is a pointer to a message package whose layout also depends on the hook context.&lt;br /&gt;
&lt;br /&gt;
Hook functions need to have the VARARGS68K tag. The next chapter will describe what that means.&lt;br /&gt;
&lt;br /&gt;
==== Varargs ====&lt;br /&gt;
&lt;br /&gt;
Although frequently used, the following code is highly system- and compiler-dependent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void somefunc(int messageid, ...)&lt;br /&gt;
{&lt;br /&gt;
 char *message = (char *)(&amp;amp;messageid + 1);&lt;br /&gt;
 if (messageid == some_id)&lt;br /&gt;
 {&lt;br /&gt;
 int x = *(int *)message; message += sizeof(int);&lt;br /&gt;
 int y = *(int *)message; message += sizeof(int);&lt;br /&gt;
 // ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code assumes that arguments are put on the stack in sequential order, which happens to be true by chance but cannot be counted on. On AmigaOS 4.0 onwards, the system uses the PowerPC SystemV ABI, which doesn&#039;t allow for such tricks. ANSI-C defines a specific mechanism for variable number of arguments functions, namely va_start(), va_end() and va_arg(). . Under all normal circumstances, these must be used on AmigaOS 4.&lt;br /&gt;
&lt;br /&gt;
The only legal way to express the above code is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;stdarg.h&amp;gt;&lt;br /&gt;
void somefunc(int messageid, ...)&lt;br /&gt;
{&lt;br /&gt;
 va_list ap;&lt;br /&gt;
 va_start(ap, messageid);&lt;br /&gt;
 if (messageid == some_id)&lt;br /&gt;
 {&lt;br /&gt;
 uint16 x = va_arg(ap, uint16);&lt;br /&gt;
 uint16 y = va_arg(ap, uint16);&lt;br /&gt;
 // ...&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 va_end(ap);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is one exception to this rule, though. Under certain circumstances it is necessary to be able to use these stack varargs because of compatibility. The AmigaOS 4 compilers must support a means to specify this as a special linkage parameter for a function. The SDK include file amiga_compiler.h encapsulates the details in a common macro VARARGS68K. A function declared with this tag is handled differently by the compiler, so that the calling method outlined at the beginning of this chapter works. The tag may be used both to declare a function within your program as well as declaring a function outside your program. The former is required for example for hook functions, while the latter may be required for legacy code.&lt;br /&gt;
&lt;br /&gt;
Such a function still needs to access the parameters in a special way. The first argument (messageid in this example) still is passed in a register according to the ABI specs. The following fragment shows how to handle this case.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
#include &amp;lt;stdarg.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void somefunc(int messageid, ...) VARARGS68K;&lt;br /&gt;
&lt;br /&gt;
void somefunc(int messageid, ...)&lt;br /&gt;
{&lt;br /&gt;
 va_list ap;&lt;br /&gt;
 va_startlinear(ap, messageid);&lt;br /&gt;
&lt;br /&gt;
 char *message = va_getlinearva(ap, char *);&lt;br /&gt;
&lt;br /&gt;
 if (messageid == some_id)&lt;br /&gt;
 {&lt;br /&gt;
 uint16 x = *(uint16 *)message; message += 4;&lt;br /&gt;
 uint16 y = *(uint16 *)message; message += 4;&lt;br /&gt;
 // ...&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 va_end(ap);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that parameters passed in this way are always longword-aligned. On the 68k this used to be different depending on the compiler - another reason why you should avoid this kind of calling mechanism.&lt;br /&gt;
&lt;br /&gt;
== Memory ==&lt;br /&gt;
&lt;br /&gt;
==== The MEMF_PUBLIC flag ====&lt;br /&gt;
&lt;br /&gt;
The memory subsystem of AmigaOS 4 has seen a radical overhaul. Although the API stays mostly the same, there are a few things to look out for when porting old code, as well as when writing new code.&lt;br /&gt;
&lt;br /&gt;
A general rule of thumb is: Don&#039;t use it. There is usually no reason why you should be using MEMF_PUBLIC memory. On AmigaOS 3.9 and earlier you had to allocate e.g. Messages and Message Ports in public memory; this is no longer needed under AmigaOS 4 - instead use the MEMF_SHARED flag. The MEMF_SHARED flag shares a lot of the properties that its public counterpart has, but is much more &amp;quot;friendly&amp;quot; to the system.&lt;br /&gt;
&lt;br /&gt;
For more information about MEMF_PUBLIC see [[Obsolete_Exec_Memory_Allocation|Obsolete Exec Memory Allocation]].&lt;br /&gt;
&lt;br /&gt;
==== Visible Changes in the Memory Subsystem ====&lt;br /&gt;
&lt;br /&gt;
Most of the changes done to the memory system under AmigaOS 4 is done &amp;quot;under the hood&amp;quot; and invisible to the application programmer. However, a few changes may affect the programmer, and therefore need to be taken care of.&lt;br /&gt;
&lt;br /&gt;
First of all, memory is virtualized. That means that any access to unallocated memory will invariably result in an abnormal termination of your program. Like the Enforcer in earlier versions of the system, the AmigaOS 4 memory system does not tolerate any such access. Contrary to the Enforcer, however, such illegal accesses are fatal. AmigaOS 4 software must not access illegal memory areas.&lt;br /&gt;
&lt;br /&gt;
Memory will, as a rule, go away at the very moment that it is freed. Being inside a forbid state does not allow you to access memory after freeing it.&lt;br /&gt;
&lt;br /&gt;
Also, virtualized memory means that the physical address where memory is located does not necessarily match the virtual address that you are using. While that doesn&#039;t have any impact in normal operation, it is something that device driver writers will have to take care of - there are calls in Exec that map physical to virtual and vice versa. In addition, memory that appears continuous to your application (which uses virtual addresses) need not be continuous in physical memory.&lt;br /&gt;
&lt;br /&gt;
In the past some clever programmers where scanning Exec&#039;s memory lists, in spite of a comment that clearly marked them as &amp;quot;private&amp;quot;. This will no longer be tolerated in AmigaOS. In fact, chances are that the memory lists are uninitialized, since the traditional memory allocation schemes are in the process of being phased out at the time of writing in favor of a new and much faster memory allocation.&lt;br /&gt;
&lt;br /&gt;
As we have already mentioned, there are some new memory flags for application programmers. MEMF_SHARED is one of them, the semantics of which have already been described above. Another one of these is MEMF_EXECUTABLE. Normally, executable code may not be placed into an arbitrary section of memory. If you intend to generate code dynamically, you must use MEMF_EXECUTABLE to allocate the memory. Also note that the code section of your program is write protected, so self-modifying is not possible unless you allocate a piece of MEMF_EXECUTABLE memory and generate your code there.&lt;br /&gt;
&lt;br /&gt;
Please refer to [[Exec_Memory_Allocation|Exec Memory Allocation]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Known Incompatibilities ==&lt;br /&gt;
&lt;br /&gt;
Due to the migration to PowerPC and other changes in the system itself, there are a few known incompatibilities with old software and/or source code that may cause trouble. This is true both for porting software to run on AmigaOS 4 natively as well as binary 68k legacy programs. The following list tries to outline the known issues and tries to list workarounds.&lt;br /&gt;
&lt;br /&gt;
Most of these issues are caused by inappropriate programming, for example exploiting undocumented or internal features. In general, if you strictly adhere to the programming guidelines your program should work unmodified, but some things that where tolerated under OS 3.9 and earlier will no longer work.&lt;br /&gt;
&lt;br /&gt;
* Assembler programmers should NOT simply check the zero flag if the function is documented to return 0 or non-null. For example, if you open a device it is NOT sufficient to check the zero flag to find out if the open was successful or not. This assumes that the return value is set up at the very end of the function and that no operation had any influence on it; furthermore, since the emulator &amp;quot;only&amp;quot; calls native code and passes the return value to your 68k function, the flag bits are not affected. This was never supposed to work, and will no longer work. A simple &amp;quot;cmp.l #0,d0&amp;quot; or &amp;quot;tst.l d0&amp;quot; will be sufficient.&lt;br /&gt;
&lt;br /&gt;
* Access to any unmapped/unallocated memory fill most likely result in a fatal crash. It is not OK to &amp;quot;temporarily store&amp;quot; results somewhere because your application &amp;quot;happens to know&amp;quot; that the area is free. Likewise, NULL pointer access will invariably result in a crash. The user will be able to click &amp;quot;continue&amp;quot; in the Grim Reaper window that will pop up, though, but the result will be undefined.&lt;br /&gt;
&lt;br /&gt;
* It has never been documented where and how (if at all) AllocVec stores the size of the block it has allocated. Therefore, it has always been illegal to try to access the four bytes before the actual allocation to find the size of the block. Code that will use this &amp;quot;feature&amp;quot; will no longer work, since AmigaOS 4&#039;s AllocVec does not necessarily put anything there.&lt;br /&gt;
&lt;br /&gt;
* Public data does not belong on the stack. If you want to send a message, allocate it with AllocSysObjectTags() using the ASOT_MESSAGE type. The following code is broken:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
void foo(void)&lt;br /&gt;
{&lt;br /&gt;
 struct Message msg;&lt;br /&gt;
 // ...&lt;br /&gt;
 IExec-&amp;gt;PutMsg(port, &amp;amp;msg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Locale library uses new country/language names. All language names are represented by their English name because it eliminates the need for special characters that the file system may not support or that require a special font to display.&lt;br /&gt;
&lt;br /&gt;
* Do not copy font flags. Font flags carry a specific meaning that may or may not be specific to a certain font. For example, setting the FSF_ANTIALIASED flag on a bitmap font may cause unpredictable behavior.&lt;br /&gt;
&lt;br /&gt;
* It never was, and never will be, legal to access memory after you have freed it. No matter if you are in Forbid() state or not, a program MUST NOT access memory after it called FreeMem() or any other function that frees memory. Doing so will invariably cause a crash of your program. It is not OK to assume that memory will stay available up to the next Permit(). Freeing memory will most likely trigger immediate un-mapping of associated memory pages, meaning that any further access will result in a Data Storage (DSI) exception. The same holds true for executable memory that is freed due to a FreeVec or UnLoadSeg. The next instruction to be executed will no longer be there because the memory has vanished, resulting in an ISI exception.&lt;br /&gt;
&lt;br /&gt;
== libauto Libraries ==&lt;br /&gt;
&lt;br /&gt;
The following global variables are automatically handled by the current libauto. Note that the entries in bold are automatically supplied by your C library&#039;s startup code and are always available (clib2 does not provide UtilityBase and IUtility):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Library&lt;br /&gt;
! Interface(s)&lt;br /&gt;
|-&lt;br /&gt;
| AmigaGuideBase&lt;br /&gt;
| IAmigaGuide&lt;br /&gt;
|-&lt;br /&gt;
| ApplicationBase&lt;br /&gt;
| IApplication, IPrefsObjects&lt;br /&gt;
|-&lt;br /&gt;
| ARexxBase&lt;br /&gt;
| IARexx&lt;br /&gt;
|-&lt;br /&gt;
| AslBase&lt;br /&gt;
| IAsl&lt;br /&gt;
|-&lt;br /&gt;
| BevelBase&lt;br /&gt;
| IBevel&lt;br /&gt;
|-&lt;br /&gt;
| BitMapBase&lt;br /&gt;
| IBitMap&lt;br /&gt;
|-&lt;br /&gt;
| SocketBase&lt;br /&gt;
| ISocket&lt;br /&gt;
|-&lt;br /&gt;
| ButtonBase&lt;br /&gt;
| IButton&lt;br /&gt;
|-&lt;br /&gt;
| CheckBoxBase&lt;br /&gt;
| ICheckBox&lt;br /&gt;
|-&lt;br /&gt;
| ChooserBase&lt;br /&gt;
| IChooser&lt;br /&gt;
|-&lt;br /&gt;
| ClickTabBase&lt;br /&gt;
| IClickTab&lt;br /&gt;
|-&lt;br /&gt;
| ColorWheelBase&lt;br /&gt;
| IColorWheel&lt;br /&gt;
|-&lt;br /&gt;
| CxBase&lt;br /&gt;
| ICommodities&lt;br /&gt;
|-&lt;br /&gt;
| DataTypesBase&lt;br /&gt;
| IDataTypes&lt;br /&gt;
|-&lt;br /&gt;
| DateBrowserBase&lt;br /&gt;
| IDateBrowser&lt;br /&gt;
|-&lt;br /&gt;
| DiskfontBase&lt;br /&gt;
| IDiskfont&lt;br /&gt;
|-&lt;br /&gt;
| DOSBase&lt;br /&gt;
| &#039;&#039;&#039;IDOS&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| DrawListBase&lt;br /&gt;
| IDrawList&lt;br /&gt;
|-&lt;br /&gt;
| ElfBase&lt;br /&gt;
| IElf&lt;br /&gt;
|-&lt;br /&gt;
| ExecBase&lt;br /&gt;
| &#039;&#039;&#039;IExec&#039;&#039;&#039;, IDebug&lt;br /&gt;
|-&lt;br /&gt;
| ExpansionBase&lt;br /&gt;
| IExpansion&lt;br /&gt;
|-&lt;br /&gt;
| FillerBase&lt;br /&gt;
| IFiller&lt;br /&gt;
|-&lt;br /&gt;
| FuelGaugeBase&lt;br /&gt;
| IFuelGauge&lt;br /&gt;
|-&lt;br /&gt;
| GadToolsBase&lt;br /&gt;
| IGadTools&lt;br /&gt;
|-&lt;br /&gt;
| GetColorBase&lt;br /&gt;
| IGetColor&lt;br /&gt;
|-&lt;br /&gt;
| GetFileBase&lt;br /&gt;
| IGetFile&lt;br /&gt;
|-&lt;br /&gt;
| GetFontBase&lt;br /&gt;
| IGetFont&lt;br /&gt;
|-&lt;br /&gt;
| GetScreenModeBase&lt;br /&gt;
| IGetScreenMode&lt;br /&gt;
|-&lt;br /&gt;
| GlyphBase&lt;br /&gt;
| IGlyph&lt;br /&gt;
|-&lt;br /&gt;
| GfxBase&lt;br /&gt;
| IGraphics&lt;br /&gt;
|-&lt;br /&gt;
| IconBase&lt;br /&gt;
| IIcon&lt;br /&gt;
|-&lt;br /&gt;
| IFFParseBase&lt;br /&gt;
| IIFFParse&lt;br /&gt;
|-&lt;br /&gt;
| InputBase&lt;br /&gt;
| IInput&lt;br /&gt;
|-&lt;br /&gt;
| IntegerBase&lt;br /&gt;
| IInteger&lt;br /&gt;
|-&lt;br /&gt;
| IntuitionBase&lt;br /&gt;
| IIntuition&lt;br /&gt;
|-&lt;br /&gt;
| KeymapBase&lt;br /&gt;
| IKeymap&lt;br /&gt;
|-&lt;br /&gt;
| LabelBase&lt;br /&gt;
| ILabel&lt;br /&gt;
|-&lt;br /&gt;
| LayersBase&lt;br /&gt;
| ILayers&lt;br /&gt;
|-&lt;br /&gt;
| LayoutBase&lt;br /&gt;
| ILayout&lt;br /&gt;
|-&lt;br /&gt;
| ListBrowserBase&lt;br /&gt;
| IListBrowser&lt;br /&gt;
|-&lt;br /&gt;
| LocaleBase&lt;br /&gt;
| &#039;&#039;&#039;ILocale&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| LowLevelBase&lt;br /&gt;
| ILowLevel&lt;br /&gt;
|-&lt;br /&gt;
| MiniGLBase&lt;br /&gt;
| IMiniGL&lt;br /&gt;
|-&lt;br /&gt;
| PaletteBase&lt;br /&gt;
| IPalette&lt;br /&gt;
|-&lt;br /&gt;
| PenMapBase&lt;br /&gt;
| IPenMap&lt;br /&gt;
|-&lt;br /&gt;
| P96Base&lt;br /&gt;
| IP96&lt;br /&gt;
|-&lt;br /&gt;
| PictureBase&lt;br /&gt;
| IPicture&lt;br /&gt;
|-&lt;br /&gt;
| PopupMenuBase&lt;br /&gt;
| IPopupMenu&lt;br /&gt;
|-&lt;br /&gt;
| RadioButtonBase&lt;br /&gt;
| IRadioButton&lt;br /&gt;
|-&lt;br /&gt;
| RequesterBase&lt;br /&gt;
| IRequester&lt;br /&gt;
|-&lt;br /&gt;
| RexxSysBase&lt;br /&gt;
| IRexxSys&lt;br /&gt;
|-&lt;br /&gt;
| ScreenBlanker&lt;br /&gt;
| IScreenBlanker&lt;br /&gt;
|-&lt;br /&gt;
| ScrollerBase&lt;br /&gt;
| IScroller&lt;br /&gt;
|-&lt;br /&gt;
| SketchBoardBase&lt;br /&gt;
| ISketchBoard&lt;br /&gt;
|-&lt;br /&gt;
| SliderBase&lt;br /&gt;
| ISlider&lt;br /&gt;
|-&lt;br /&gt;
| SpaceBase&lt;br /&gt;
| ISpace&lt;br /&gt;
|-&lt;br /&gt;
| SpeedBarBase&lt;br /&gt;
| ISpeedBar&lt;br /&gt;
|-&lt;br /&gt;
| StringBase&lt;br /&gt;
| IString&lt;br /&gt;
|-&lt;br /&gt;
| TextClipBase&lt;br /&gt;
| ITextClip&lt;br /&gt;
|-&lt;br /&gt;
| TextEditorBase&lt;br /&gt;
| ITextEditor&lt;br /&gt;
|-&lt;br /&gt;
| TimerBase&lt;br /&gt;
| ITimer&lt;br /&gt;
|-&lt;br /&gt;
| TimesyncBase&lt;br /&gt;
| ITimesync&lt;br /&gt;
|-&lt;br /&gt;
| TimezoneBase&lt;br /&gt;
| ITimezone&lt;br /&gt;
|-&lt;br /&gt;
| UserGroupBase&lt;br /&gt;
| IUserGroup&lt;br /&gt;
|-&lt;br /&gt;
| UtilityBase&lt;br /&gt;
| &#039;&#039;&#039;IUtility&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| VirtualBase&lt;br /&gt;
| IVirtual&lt;br /&gt;
|-&lt;br /&gt;
| WorkbenchBase&lt;br /&gt;
| IWorkbench&lt;br /&gt;
|-&lt;br /&gt;
| WindowBase&lt;br /&gt;
| IWindow&lt;br /&gt;
|-&lt;br /&gt;
| xadMasterBase&lt;br /&gt;
| IxadMaster&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=10207</id>
		<title>Exec Messages and Ports</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Exec_Messages_and_Ports&amp;diff=10207"/>
		<updated>2019-03-26T13:22:24Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* Replying */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Exec Messages and Ports ==&lt;br /&gt;
&lt;br /&gt;
For inter-process communication, Exec provides a consistent, high-performance mechanism of messages and ports. This mechanism is used to pass message structures of arbitrary sizes from task to task, interrupt to task, or task to software interrupt. In addition, messages are often used to coordinate operations between cooperating tasks. This section describes many of the details of using messages and ports that the casual Amiga programmer won&#039;t need. See [[Introduction_to_Exec|Introduction to Exec]] for a general introduction to using messages and ports.&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;message&#039;&#039; data structure has two parts: system linkage and message body. The system linkage is used by Exec to attach a given message to its destination. The message body contains the actual data of interest. The message body is any arbitrary data up to 64K bytes in size. The message body data can include pointers to other data blocks of any size.&lt;br /&gt;
&lt;br /&gt;
Messages are always sent to a predetermined destination &#039;&#039;port&#039;&#039;. At a port, incoming messages are queued in a first-in-first-out (FIFO) order. There are no system restrictions on the number of ports or the number of messages that may be queued to a port (other than the amount of available system memory).&lt;br /&gt;
&lt;br /&gt;
Messages are always queued by &#039;&#039;reference&#039;&#039;, i.e., by a pointer to the message. For performance reasons message copying is not performed. In essence, a message between two tasks is a temporary license for the receiving task to use a portion of the memory space of the sending task; that portion being the message itself. This means that if task A sends a message to task B, the message is still part of the task A context. Task A, however, should not access the message until it has been &#039;&#039;replied&#039;&#039;; that is, until task B has sent the message back, using the ReplyMsg() function. This technique of message exchange imposes important restrictions on message access.&lt;br /&gt;
&lt;br /&gt;
== Message Ports ==&lt;br /&gt;
&lt;br /&gt;
Message ports are rendezvous points at which messages are collected. A port may contain any number of outstanding messages from many different originators. When a message arrives at a port, the message is appended to the end of the list of messages for that port, and a pre-specified arrival action is invoked. This action may do nothing, or it may cause a predefined task signal or software interrupt (see [[Exec_Interrupts|Exec Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
Like many Exec structures, ports may be given a symbolic name. Such names are particularly useful for tasks that must rendezvous with dynamically created ports. They are also useful for debugging purposes.&lt;br /&gt;
&lt;br /&gt;
A message port consists of a MsgPort structure as defined in the &amp;amp;lt;exec/ports.h&amp;amp;gt; and &amp;amp;lt;exec/ports.i&amp;amp;gt; include files. The C structure for a port is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort {&lt;br /&gt;
    struct Node  mp_Node;&lt;br /&gt;
    UBYTE        mp_Flags;&lt;br /&gt;
    UBYTE        mp_SigBit;&lt;br /&gt;
    struct Task *mp_SigTask;&lt;br /&gt;
    struct List  mp_MsgList;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mp_Node&lt;br /&gt;
: is a standard Node structure. This is useful for tasks that might want to rendezvous with a particular message port by name.&lt;br /&gt;
&lt;br /&gt;
; mp_FLags&lt;br /&gt;
: are used to indicate message arrival actions. See the explanation below.&lt;br /&gt;
&lt;br /&gt;
; mp_SigBit&lt;br /&gt;
: is the signal bit &#039;&#039;number&#039;&#039; when a port is used with the task signal arrival action.&lt;br /&gt;
&lt;br /&gt;
; mp_SigTask&lt;br /&gt;
: is a pointer to the task to be signaled. If a software interrupt arrival action is specified, this is a pointer to the interrupt structure.&lt;br /&gt;
&lt;br /&gt;
; mp_MsgList&lt;br /&gt;
: is the list header for all messages queued to this port. (See [[Exec_Lists_and_Queues|Exec Lists and Queues]]).&lt;br /&gt;
&lt;br /&gt;
The mp_Flags field contains a subfield indicated by the PF_ACTION mask. This sub-field specifies the message arrival action that occurs when a port receives a new message.&lt;br /&gt;
&lt;br /&gt;
The possibilities are as follows:&lt;br /&gt;
&lt;br /&gt;
; PA_SIGNAL&lt;br /&gt;
: This flag tells Exec to signal the mp_SigTask using signal number mp_SigBit on the arrival of a new message. Every time a message is put to the port another signal will occur regardless of how many messages have been queued to the port.&lt;br /&gt;
&lt;br /&gt;
; PA_SOFTINT&lt;br /&gt;
: This flag tells Exec to Cause() a software interrupt when a message arrives at the port. In this case, the mp_SigTask field must contain a pointer to a struct Interrupt rather than a Task pointer. The software interrupt will be Caused every time a message is received.&lt;br /&gt;
&lt;br /&gt;
; PA_IGNORE&lt;br /&gt;
: This flag tells Exec to perform no operation other than queuing the message. This action is often used to stop signaling or software interrupts without disturbing the contents of the mp_SigTask field.&lt;br /&gt;
&lt;br /&gt;
It is important to realize that a port&#039;s arrival action will occur for each new message queued, and that there is not a one-to-one correspondence between messages and signals. Task signals are only single-bit flags so there is no record of how many times a particular signal occurred. There may be many messages queued and only a single task signal; sometimes however there may be a signal, but no messages. All of this has certain implications when designing code that deals with these actions. Your code should not depend on receiving a signal for every message at your port. All of this is also true for software interrupts.&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message Port ===&lt;br /&gt;
&lt;br /&gt;
To create a new message port use AllocSysObject() with an object type of ASOT_PORT. If you want to make the port &#039;&#039;public&#039;&#039;, you will need to use the ASOPORT_Name tag. Don&#039;t make a port public when it is not necessary for it to be so.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, functions such as CreatePort() and CreateMsgPort() were often used. Some older applications may even have created their own static message ports by hand. Although all of the older methods still function for backwards compatibility, they should no longer be used.&lt;br /&gt;
&lt;br /&gt;
Using dynamic message ports with ASOT_PORT is the only way to ensure your applications will remain compatible and its message ports automatically freed by the system when required.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message Port ===&lt;br /&gt;
&lt;br /&gt;
Before a message port is deleted, all outstanding messages from other tasks must be returned. This is done by getting and replying to all messages at the port until message queue is empty. Of course, there is no need to reply to messages owned by the current task (the task performing the port deletion). Public ports must be removed from the system properly before deallocation. If a signal was allocated for the message port, it must also be freed. FreeSysObject() handles all of this automatically.&lt;br /&gt;
&lt;br /&gt;
Prior to V50 of the operating system, message ports must be freed using the correct corresponding function such as DeletePort() or DeleteMsgPort(). The FreeSysObject() function must only be used on ports which were allocated with AllocSysObject().&lt;br /&gt;
&lt;br /&gt;
=== How to Rendezvous at a Message Port ===&lt;br /&gt;
&lt;br /&gt;
The FindPort() function provides a means of finding the address of a public port given its symbolic name. For example, FindPort(&amp;amp;quot;Griffin&amp;amp;quot;) will return either the address of the message port named &amp;quot;Griffin&amp;quot; or NULL indicating that no such public port exists. Since FindPort() does not do any arbitration over access to public ports, the usage of FindPort() must be protected with Forbid()/Permit(). Names should be unique to prevent collisions among multiple applications. It is a good idea to use your application name as a prefix for your port name. FindPort() does not arbitrate for access to the port list. The owner of a port might remove it at any time. For these reasons a Forbid()/Permit() pair is required for the use of FindPort(). The port address can no longer be regarded as being valid after Permit() unless your application knows that the port cannot go away (for example, if your application created the port).&lt;br /&gt;
&lt;br /&gt;
The following is an example of how to safely put a message to a specific port:&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/ports.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *port = IExec-&amp;gt;FindPort(portname);&lt;br /&gt;
    if (port != NULL)&lt;br /&gt;
        IExec-&amp;gt;PutMsg(port,message);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
&lt;br /&gt;
    return(port ? TRUE : FALSE);      /* If FALSE, the port was not found */&lt;br /&gt;
&lt;br /&gt;
    /* Once we&#039;ve done a Permit(), the port might go away and leave us with&lt;br /&gt;
       an invalid port address. So we return just a BOOL to indicate whether&lt;br /&gt;
       the message has been sent or not. */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, a message contains both system header information and the actual message content. The system header is of the Message form defined in &amp;lt;exec/ports.h&amp;gt;. This structure is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct Message {&lt;br /&gt;
    struct Node     mn_Node;&lt;br /&gt;
    struct MsgPort *mn_ReplyPort;&lt;br /&gt;
    UWORD           mn_Length;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; mn_Node&lt;br /&gt;
: is a standard Node structure used for port linkage.&lt;br /&gt;
&lt;br /&gt;
; mn_ReplyPort&lt;br /&gt;
: is used to indicate a port to which this message will be returned when a reply is necessary.&lt;br /&gt;
&lt;br /&gt;
; mn_Length&lt;br /&gt;
: indicates the total length of the message, including the Message structure itself.&lt;br /&gt;
&lt;br /&gt;
This structure is always attached to the head of all messages. For example, if you want a message structure that contains the &#039;&#039;x&#039;&#039; and &#039;&#039;y&#039;&#039; coordinates of a point on the screen, you could define it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    UWORD          xy_X;&lt;br /&gt;
    UWORD          xy_Y;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this structure, the mn_Length field should be set to sizeof(struct XYMessage).&lt;br /&gt;
&lt;br /&gt;
=== Creating a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are allocated using AllocSysObject() with an object type of ASOT_MESSAGE. The ASOMSG_ReplyPort tag is set to the reply port if desired. Some messages may be sent in one direction in which case the ASOMSG_ReplyPort tag is not used. The size of the message is indicated with ASOMSG_Length and includes the size of the payload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
  ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
  ASOMSG_ReplyPort, replyPort,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deleting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are freed using FreeSysObject(). Programmers should be careful not to free a message which is still in use or queues in a message port.&lt;br /&gt;
&lt;br /&gt;
=== Putting a Message ===&lt;br /&gt;
&lt;br /&gt;
A message is delivered to a given destination port with the PutMsg() function. The message is queued to the port, and that port&#039;s arrival action is invoked. If the action specifies a task signal or a software interrupt, the originating task may temporarily lose the processor while the destination processes the message. If a reply to the message is required, the mn_ReplyPort field must be set up prior to the call to PutMsg().&lt;br /&gt;
&lt;br /&gt;
Here is a code fragment for putting a message to a public port. A complete example is at the end of the article.&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/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    uint16         xy_X;&lt;br /&gt;
    uint16         xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyport, *xyreplyport;&lt;br /&gt;
    struct XYMessage *msg;&lt;br /&gt;
    BOOL   foundport;&lt;br /&gt;
&lt;br /&gt;
    /* Allocate the message we&#039;re going to send. */&lt;br /&gt;
    struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
      ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xymsg != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        /* The replyport we&#039;ll use to get response */&lt;br /&gt;
        if (xyreplyport = IExec-&amp;gt;AllocSysObject(ASOT_PORT, NULL)) {&lt;br /&gt;
&lt;br /&gt;
            xymsg-&amp;gt;xy_Msg.mn_ReplyPort = xyreplyport;&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            /* Now try to send that message to a public port named &amp;quot;xyport&amp;quot;.&lt;br /&gt;
             * If foundport eq 0, the port isn&#039;t out there.&lt;br /&gt;
             */&lt;br /&gt;
            if (foundport = SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
&lt;br /&gt;
            . . .                /* Now let&#039;s wait till the someone responds... */&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t find &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create message port\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_MESSAGE, xymsg);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory for xymessage\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;
=== Waiting for a Message ===&lt;br /&gt;
&lt;br /&gt;
A task may go to sleep waiting for a message to arrive at one or more ports. This technique is widely used on the Amiga as a general form of event notification. For example, it is used extensively by tasks for I/O request completion.&lt;br /&gt;
&lt;br /&gt;
The MsgPort.mp_SigTask field contains the address of the task to be signaled and mp_SigBit contains a preallocated signal number (as described in [[Exec_Tasks|Exec Tasks]]).&lt;br /&gt;
&lt;br /&gt;
You can call the WaitPort() function to wait for a message to arrive at a port. This function will return the first message (it may not be the only) queued to a port. Note that your application must still call GetMsg() to remove the message from the port. If the port is empty, your task will go to sleep waiting for the first message. If the port is not empty, your task will not go to sleep. It is possible to receive a signal for a port without a message being present yet. The code processing the messages should be able to handle this. The following code illustrates WaitPort().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport == NULL)&lt;br /&gt;
{&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
    return RETURN_FAIL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct XYMessage *xy_msg = IExec-&amp;gt;WaitPort(xyport);  /* go to sleep until message arrives */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A more general form of waiting for a message involves the use of the Wait() function (see [[Exec_Signals|Exec Signals]]). This function waits for task event signals directly. If the signal assigned to the message port occurs, the task will awaken. Using the Wait() function is more general because you can wait for more than one signal. By combining the signal bits from each port into one mask for the Wait() function, a loop can be set up to process all messages at all ports.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example using Wait():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
struct XYMessage *xy_msg;&lt;br /&gt;
BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
  ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
  TAG_END);&lt;br /&gt;
&lt;br /&gt;
if (xyport != NULL)&lt;br /&gt;
{&lt;br /&gt;
    uint32 portsig = 1 &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;&lt;br /&gt;
    uint32 usersig = SIGBREAKF_CTRL_C;            /* User can break with CTRL-C.  */&lt;br /&gt;
    for (;;)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 signal = IExec-&amp;gt;Wait(portsig | usersig);  /* Sleep till someone signals.  */&lt;br /&gt;
&lt;br /&gt;
        if (signal &amp;amp; portsig)              /* Got a signal at the msgport. */&lt;br /&gt;
        {   .  .  .&lt;br /&gt;
        }&lt;br /&gt;
        if (signal &amp;amp; usersig)              /* Got a signal from the user.  */&lt;br /&gt;
        {&lt;br /&gt;
            ABORT = TRUE;                  /* Time to clean up.            */&lt;br /&gt;
             . . .&lt;br /&gt;
        }&lt;br /&gt;
        if (ABORT) break;&lt;br /&gt;
    }&lt;br /&gt;
    IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
}&lt;br /&gt;
else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyport\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Note|text=WaitPort() only returns a pointer to the first message in a port. It does not actually remove the message from the port queue.|title=WaitPort() Does Not Remove A Message}}&lt;br /&gt;
&lt;br /&gt;
=== Getting a Message ===&lt;br /&gt;
&lt;br /&gt;
Messages are usually removed from ports with the GetMsg() function. This function removes the next message at the head of the port queue and returns a pointer to it. If there are no messages in a port, this function returns a zero.&lt;br /&gt;
&lt;br /&gt;
The example below illustrates the use of GetMsg() to print the contents of all messages in a port:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport))&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Certain messages may be more important than others. Because ports impose FIFO ordering, these important messages may get queued behind other messages regardless of their priority. If it is necessary to recognize more important messages, it is easiest to create another port for these special messages.&lt;br /&gt;
&lt;br /&gt;
=== Replying ===&lt;br /&gt;
&lt;br /&gt;
When the operations associated with receiving a new message are finished, it is usually necessary to send the message back to the originator. The receiver replies the message by returning it to the originator using the ReplyMsg() function. This is important because it notifies the originator that the message can be reused or deallocated.&lt;br /&gt;
&lt;br /&gt;
The ReplyMsg() function serves this purpose. It returns the message to the port specified in the mn_ReplyPort field of the message. If this field is zero, no reply is returned.&lt;br /&gt;
&lt;br /&gt;
The previous example can be enhanced to reply to each of its messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
while (xymsg = IExec-&amp;gt;GetMsg(xyport)) {&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;x=%ld y=%ld\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
    IExec-&amp;gt;ReplyMsg(xymsg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice that the reply does not occur until &#039;&#039;after&#039;&#039; the message values have been used.&lt;br /&gt;
&lt;br /&gt;
Often the operations associated with receiving a message involve returning &#039;&#039;results&#039;&#039; to the originator. Typically this is done within the message itself. The receiver places the results in fields defined (or perhaps reused) within the message body before replying the message back to the originator. Receipt of the replied message at the originator reply port indicates it is once again safe for the originator to use or change the values found within the message.&lt;br /&gt;
&lt;br /&gt;
The following are two short example tasks that communicate by sending, waiting for and replying to messages. Run these two programs together.&lt;br /&gt;
&lt;br /&gt;
==== Port1.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port1.c - port and message example, run at the same time as port2.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.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;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xym_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    BOOL ABORT = FALSE;&lt;br /&gt;
&lt;br /&gt;
    struct MsgPort *xyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT,&lt;br /&gt;
      ASOPORT_Name, &amp;quot;xyport&amp;quot;,&lt;br /&gt;
      TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        uint32 portsig = 1U &amp;lt;&amp;lt; xyport-&amp;gt;mp_SigBit;       /* Give user a `break&#039; signal. */&lt;br /&gt;
        uint32 usersig = SIGBREAKF_CTRL_C;&lt;br /&gt;
&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;Start port2 in another shell.  CTRL-C here when done.\n&amp;quot;);&lt;br /&gt;
        do&lt;br /&gt;
        {                                                   /* port1 will wait forever and reply   */&lt;br /&gt;
            uint32 signal = IExec-&amp;gt;Wait(portsig | usersig); /* to messages, until the user breaks. */&lt;br /&gt;
            struct XYMessage *xymsg = 0;&lt;br /&gt;
&lt;br /&gt;
                                   /* Since we only have one port that might get messages we     */&lt;br /&gt;
            if (signal &amp;amp; portsig)  /* have to reply to, it is not really necessary to test for   */&lt;br /&gt;
            {                      /* the portsignal. If there is not message at the port, xymsg */&lt;br /&gt;
&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* simply will be NULL. */&lt;br /&gt;
                {&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 received: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
&lt;br /&gt;
                    xymsg-&amp;gt;xy_X += 50;       /* Since we have not replied yet to the owner of    */&lt;br /&gt;
                    xymsg-&amp;gt;xy_Y += 50;       /* xymsg, we can change the data contents of xymsg. */&lt;br /&gt;
&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;port1 replying with: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            if (signal &amp;amp; usersig)  /* The user wants to abort. */&lt;br /&gt;
            {&lt;br /&gt;
                while(xymsg = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyport))  /* Make sure port is empty. */&lt;br /&gt;
                    IExec-&amp;gt;ReplyMsg((struct Message *)xymsg);&lt;br /&gt;
                ABORT = TRUE;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while (ABORT == FALSE);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyport);&lt;br /&gt;
        }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create &#039;xyport&#039;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Port2.c ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
// port2.c - port and message example, run at the same time as port1.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/ports.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;proto/exec.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/dos.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *, CONST_STRPTR);&lt;br /&gt;
&lt;br /&gt;
struct XYMessage {&lt;br /&gt;
    struct Message xy_Msg;&lt;br /&gt;
    int16          xy_X;&lt;br /&gt;
    int16          xy_Y;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct MsgPort *xyreplyport = IExec-&amp;gt;AllocSysObjectTags(ASOT_PORT, TAG_END);&lt;br /&gt;
&lt;br /&gt;
    if (xyreplyport != NULL)&lt;br /&gt;
    {&lt;br /&gt;
        struct XYMessage *reply = NULL;&lt;br /&gt;
&lt;br /&gt;
        struct XYMessage *xymsg = IExec-&amp;gt;AllocSysObjectTags(ASOT_MESSAGE,&lt;br /&gt;
          ASOMSG_Size, sizeof(struct XYMessage),&lt;br /&gt;
          ASOMSG_ReplyPort, xyreplyport,&lt;br /&gt;
          TAG_END);&lt;br /&gt;
&lt;br /&gt;
        if (xymsg != NULL)&lt;br /&gt;
        {&lt;br /&gt;
            xymsg-&amp;gt;xy_X = 10;   /* our special message information. */&lt;br /&gt;
            xymsg-&amp;gt;xy_Y = 20;&lt;br /&gt;
&lt;br /&gt;
            IDOS-&amp;gt;Printf(&amp;quot;Sending to port1: x = %d y = %d\n&amp;quot;, xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);&lt;br /&gt;
            &lt;br /&gt;
                                                                   /* port2 will simply try to put  */&lt;br /&gt;
            if (SafePutToPort((struct Message *)xymsg, &amp;quot;xyport&amp;quot;))  /* one message to port1 wait for */&lt;br /&gt;
            {                                                      /*  the reply, and then exit     */&lt;br /&gt;
                IExec-&amp;gt;WaitPort(xyreplyport);&lt;br /&gt;
                if (reply = (struct XYMessage *)IExec-&amp;gt;GetMsg(xyreplyport))&lt;br /&gt;
                    IDOS-&amp;gt;Printf(&amp;quot;Reply contains: x = %d y = %d\n&amp;quot;,   /* We don&#039;t ReplyMsg since   */&lt;br /&gt;
                            xymsg-&amp;gt;xy_X, xymsg-&amp;gt;xy_Y);                /* WE initiated the message. */&lt;br /&gt;
&lt;br /&gt;
                      /* Since we only use this private port for receiving replies, and we sent     */&lt;br /&gt;
                      /* only one and got one reply there is no need to cleanup. For a public port, */&lt;br /&gt;
                      /* or if you pass a pointer to the port to another process, it is a very good */&lt;br /&gt;
                      /* habit to always handle all messages at the port before you delete it.      */&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;Can&#039;t find &#039;xyport&#039;; start port1 in a separate shell\n&amp;quot;);&lt;br /&gt;
            IExec-&amp;gt;FreeSysObject(ASOT_Message, xymsg);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t get memory\n&amp;quot;);&lt;br /&gt;
        IExec-&amp;gt;FreeSysObject(ASOT_PORT, xyreplyport);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;Couldn&#039;t create xyreplyport\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
BOOL SafePutToPort(struct Message *message, CONST_STRPTR portname)&lt;br /&gt;
{&lt;br /&gt;
    IExec-&amp;gt;Forbid();&lt;br /&gt;
    struct MsgPort *port = FindPort(portname);&lt;br /&gt;
    if (port != NULL) IExec-&amp;gt;PutMsg(port, message);&lt;br /&gt;
    IExec-&amp;gt;Permit();&lt;br /&gt;
    return(port ? TRUE : FALSE); /* FALSE if the port was not found */&lt;br /&gt;
&lt;br /&gt;
         /* Once we&#039;ve done a Permit(), the port might go away and leave us with an invalid port    */&lt;br /&gt;
}        /* address. So we return just a BOOL to indicate whether the message has been sent or not. */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Function Reference ==&lt;br /&gt;
&lt;br /&gt;
The following chart gives a brief description of the Exec functions that control inter-task communication with messages and ports. See the SDK for 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;
| AddPort()&lt;br /&gt;
| Add a public message port to the system list.&lt;br /&gt;
|-&lt;br /&gt;
| AllocSysObject(ASOT_PORT)&lt;br /&gt;
| Allocate and initialize a new message port.&lt;br /&gt;
|-&lt;br /&gt;
| FindPort()&lt;br /&gt;
| Find a public message port in the system list.&lt;br /&gt;
|-&lt;br /&gt;
| FreeSysObject(ASOT_PORT)&lt;br /&gt;
| Free a message port.&lt;br /&gt;
|-&lt;br /&gt;
| GetMsg()&lt;br /&gt;
| Get next message from the message port.&lt;br /&gt;
|-&lt;br /&gt;
| PutMsg()&lt;br /&gt;
| Put a message to a message port.&lt;br /&gt;
|-&lt;br /&gt;
| RemPort()&lt;br /&gt;
| Remove a message port from the system list.&lt;br /&gt;
|-&lt;br /&gt;
| ReplyMsg()&lt;br /&gt;
| Reply to a message on its reply port.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Executing_External_Programs&amp;diff=9857</id>
		<title>Executing External Programs</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Executing_External_Programs&amp;diff=9857"/>
		<updated>2018-12-16T00:17:09Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: /* The System Function */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Executing External Programs =&lt;br /&gt;
&lt;br /&gt;
The Amiga operating system provides flexible methods of launching programs from an application. The System() function is more flexible and powerful than the deprecated Execute() function. Improvements to the con-handler, CON:, allow programs to create &amp;quot;auto con&amp;quot; windows, which are console windows that will only appear if input is requested from or output is sent to that console.&lt;br /&gt;
&lt;br /&gt;
== The System Function ==&lt;br /&gt;
&lt;br /&gt;
System() spawns a shell process to execute the command line which is passed to System() as an argument. The shell parses the command-line normally, just like command-lines typed directly into the shell&#039;s console. If it can, System() will pass the current path and directory to the programs it launches.&lt;br /&gt;
&lt;br /&gt;
The System() function can execute external commands either synchronously or asynchronously. When System() is used synchronously, control returns to the calling program after the external program has completed. In this case, System() returns the external program&#039;s return code, or a -1 if the command could not be found or run. On the other hand, when System() initiates a program asynchronously, the program is no longer a concern for the caller. The operating system will take care of the cleanup. This is extremely useful for an application that must start up multiple programs on user demand, such as a hot key commodity. By default, System() starts programs synchronously. To launch a program asynchronously, use the SYS_Asynch tag with the data field set to TRUE.&lt;br /&gt;
&lt;br /&gt;
With the System() call, it is easy to provide programs with specific input, output and error output handles. The tags SYS_Input, SYS_Output and SYS_ErrorOutput (defined in dos/dostags.h) are used to supply the input, output and error output file handles. A program can pass its own input, output and error handles to a synchronously launched program by passing the results of Input(), Output() and ErrorOutput() respectively. Note that with a synchronous System() call, the OS will not close these handles when the spawned process exits. In the case of an asynchronously launched program, the launching program normally must provide new IO handles since the system automatically closes these handles when the asynchronous process ends. Because AmigaDOS wants separate handles for input and output, System() will automatically create an output handle if it&#039;s passed a handle for SYS_Input and NULL for SYS_Output. This allows a program to open a CON: window for input and use it for both input and output, as System() will test to see if input is interactive and, if so, will attempt to open &amp;quot;*&amp;quot; for output to that console.  If the input file handle is not interactive, System() opens &amp;quot;*&amp;quot; on the current console task.&lt;br /&gt;
&lt;br /&gt;
Programs launched using System() are not restricted to the built-in, or Boot, shell. In the near future, System() will be able to take advantage of specially designed shells (the design requirements of these special shells have not yet been documented). Two tags, SYS_UserShell and SYS_CustomShell, specify which shell System() should use to execute the command-line. By using the SYS_UserShell tag with a tag value of TRUE, an application tells System() to send a command to the user&#039;s preferred shell rather that the boot shell (Note that the default user shell is the boot shell). If an application opens a shell for the user or executes the user&#039;s command-lines, it should&lt;br /&gt;
tell System() to use the user shell. If an application requires consistent shell behavior, it must not use the user shell because the user can change the user shell. Another shell tag, SYS_CustomShell, allows an application to choose other shells besides the boot and user shells. This tag&#039;s data field should contain the custom shell&#039;s name as it appears in the system resident list.&lt;br /&gt;
&lt;br /&gt;
Although it does offer many features, System() does have some limitations. First, because command paths currently only exist in the CLI structure, Workbench (non-CLI) processes have no paths. Consequently, when a Workbench process calls a program using System(), the shell has no path to search for that program (aside from the system default search path of C: and the current directory). A second limitation of the System() function involves CTRL-C/D/E/F handling. When a task opens a CON: window to provide a handle for a System()-launched command, that task is the owner of the handle.  This has two effects. A System()-launched command running in that CON:&lt;br /&gt;
window will only be able to receive CTRL-C/D/E/F signals while it is doing input or output to the window (i.e. when it has a pending read or write).  If the task that owns the handle is still around, it receives CTRL-C/D/E/F signals whenever those keys are pressed in the CON: window.&lt;br /&gt;
&lt;br /&gt;
== The Con-handler ==&lt;br /&gt;
&lt;br /&gt;
The con-handler (CON:) has many enhancements that allow programs to further customize their console windows. An application requests these new features by appending keywords to the end of the CON: specification string for Open().  These keywords may appear in any order after the title string in the CON: specification.&lt;br /&gt;
&lt;br /&gt;
These new keywords are:&lt;br /&gt;
&lt;br /&gt;
    AUTO            Don&#039;t open CON: window until/unless input or&lt;br /&gt;
                        output occurs&lt;br /&gt;
    CLOSE           Put a close gadget on the CON: window&lt;br /&gt;
    WAIT            Hold off Close until user clicks Close or types CTRL-\&lt;br /&gt;
    WINDOW  0xaddr  Use specified window (may be on a custom screen)&lt;br /&gt;
    SCREEN  name    Open on specified public screen&lt;br /&gt;
&lt;br /&gt;
The additional CON: keywords BACKDROP, NODRAG, NOBORDER, NOSIZE, SIMPLE, and SMART, allow control of other attributes of a CON: window.&lt;br /&gt;
&lt;br /&gt;
An AUTO/CLOSE/WAIT CON: window is perfect for C startup code and for asynchronous System() startup of arbitrary commands. Because of the &#039;&#039;auto con&#039;&#039; feature (AUTO), the system will never open the CON: window of a program that doesn&#039;t do any stdio (example: Calculator). If a command does stdio input or output, the window will not open until stdio occurs. If the window opens, the wait feature (WAIT) causes the window to stay open until the user types CTRL-\ (end of file) or clicks the Close gadget. The CLOSE keyword tells the con-handler to put a close gadget on the CON: window.&lt;br /&gt;
&lt;br /&gt;
Example: &#039;&#039;CON:/0/0/640/200/My Title/AUTO/CLOSE/WAIT&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The SCREEN keyword along with a public screen name allows an application to open a CON: window on that public screen. Alternately, an application can use the WINDOW keyword to attach a console to an already open Intuition window. The hex address of the Intuition window must follow the WINDOW keyword. This makes it possible for System()-launched programs to do their stdio in a window on a custom screen.&lt;br /&gt;
&lt;br /&gt;
== System() Example ==&lt;br /&gt;
&lt;br /&gt;
The example code, SystemTest.c, provides two simple subroutines for executing external commands, and demonstrates the following features:&lt;br /&gt;
&lt;br /&gt;
* Synchronous System() command execution using the calling program&#039;s Input()/Output().&lt;br /&gt;
* Synchronous System() command execution in a custom screen window.&lt;br /&gt;
* Asynchronous System() command startup with an AUTO/CLOSE/WAIT window.&lt;br /&gt;
* OpenScreenTags and OpenWindowTags for a window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* SystemTest.c &lt;br /&gt;
 *&lt;br /&gt;
 * Demonstration of System(), AUTO CON, and custom screen CON&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dos.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dos/dostags.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/displayinfo.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/utility.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/intuition.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* our function error codes */&lt;br /&gt;
#define SYSTEMFAIL      (-1L)&lt;br /&gt;
#define WINDOWFAIL      (-2L)&lt;br /&gt;
&lt;br /&gt;
/* function prototypes */&lt;br /&gt;
LONG beginCommand(UBYTE *command);&lt;br /&gt;
LONG doCommand(UBYTE *command, BPTR other);&lt;br /&gt;
VOID checkResult(UBYTE *command, LONG result);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Formatted version string for the VERSION command */&lt;br /&gt;
UBYTE *vers = &amp;quot;\0$VER: SystemTest 53.1&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
struct Library *IntuitionBase;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    extern struct Library *DOSBase;&lt;br /&gt;
    struct Screen *scr = NULL;&lt;br /&gt;
    struct Window *win = NULL;&lt;br /&gt;
    ULONG penspecterm = ~0;&lt;br /&gt;
    LONG result;&lt;br /&gt;
    BPTR file;&lt;br /&gt;
    UBYTE *command;&lt;br /&gt;
    UBYTE buf[128];&lt;br /&gt;
&lt;br /&gt;
    if(!(IntuitionBase = IExec-&amp;gt;OpenLibrary(&amp;quot;intuition.library&amp;quot;, 50)))&lt;br /&gt;
    {&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;This example requires intuition.library V50 or higher\n&amp;quot;);&lt;br /&gt;
        return RETURN_FAIL;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* SYNCHRONOUS SYSTEM() WITH OUR INPUT/OUTPUT&lt;br /&gt;
     */&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Synchronous System call &#039;dir libs:&#039;:\n&amp;quot;);&lt;br /&gt;
    command = &amp;quot;dir libs:&amp;quot;;&lt;br /&gt;
    result = doCommand(command,NULL);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Synchronous System call of nonexistant command:\n&amp;quot;);&lt;br /&gt;
    command = &amp;quot;badcommand&amp;quot;;&lt;br /&gt;
    result = doCommand(command,NULL);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Synchronous System call &#039;ask \&amp;quot;...Answer y now\&amp;quot;&#039;:\n&amp;quot;);&lt;br /&gt;
    command =&lt;br /&gt;
        &amp;quot;ask \&amp;quot;Ready for CON: on a Custom Screen? Answer y now (should return 5):\&amp;quot;&amp;quot;;&lt;br /&gt;
    result = doCommand(command,NULL);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
&lt;br /&gt;
   /* SYNCHRONOUS SYSTEM() WITH CON: IN A CUSTOM SCREEN AND WINDOW&lt;br /&gt;
     */&lt;br /&gt;
    if(scr = IIntuition-&amp;gt;OpenScreenTags(NULL,&lt;br /&gt;
                SA_Width, 640,&lt;br /&gt;
                SA_Height, 200,&lt;br /&gt;
                SA_Depth, 3,&lt;br /&gt;
                SA_DisplayID, HIRES_KEY,&lt;br /&gt;
                SA_Pens, &amp;amp;penspecterm,  /* Give us New Look */&lt;br /&gt;
                TAG_END))&lt;br /&gt;
        {&lt;br /&gt;
        if(win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                WA_CustomScreen, scr,&lt;br /&gt;
                WA_Flags, WINDOWDRAG|WINDOWCLOSE|ACTIVATE,&lt;br /&gt;
                WA_IDCMP, CLOSEWINDOW,&lt;br /&gt;
                WA_Top, 20,&lt;br /&gt;
                WA_Height, scr-&amp;gt;Height - 20,&lt;br /&gt;
                WA_Title, &amp;quot;Custom Window&amp;quot;,&lt;br /&gt;
                WA_ScreenTitle, &amp;quot;Custom Screen&amp;quot;,&lt;br /&gt;
                TAG_END))&lt;br /&gt;
            {&lt;br /&gt;
            IUtility-&amp;gt;SNPrintf(buf, sizeof(buf), &amp;quot;CON://///WINDOW0x%lx&amp;quot;, win); /* adds window pointer */&lt;br /&gt;
            if(file = IDOS-&amp;gt;Open(buf, MODE_OLDFILE))&lt;br /&gt;
                {&lt;br /&gt;
                command = &amp;quot;echo \&amp;quot;CLI commands on a custom screen!\&amp;quot;&amp;quot;;&lt;br /&gt;
                result = doCommand(command,file);&lt;br /&gt;
                command = &amp;quot;dir libs:&amp;quot;;&lt;br /&gt;
                result = doCommand(command,file);&lt;br /&gt;
                command = &amp;quot;echo \&amp;quot;( Click CLOSE gadget, or type CTRL-C )\&amp;quot;&amp;quot;;&lt;br /&gt;
                result = doCommand(command,file);&lt;br /&gt;
                Wait(1 &amp;lt;&amp;lt; win-&amp;gt;UserPort-&amp;gt;mp_SigBit | SIGBREAKF_CTRL_C);&lt;br /&gt;
                IDOS-&amp;gt;Close(file);    /* Closes the window too */&lt;br /&gt;
                }&lt;br /&gt;
            else IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
            }&lt;br /&gt;
        IIntuition-&amp;gt;CloseScreen(scr);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Synchronous System call &#039;ask \&amp;quot;...Answer y now\&amp;quot;&#039;:\n&amp;quot;);&lt;br /&gt;
    command = &amp;quot;ask \&amp;quot;Ready for Asynchronous demo? Answer y now (should return 5):\&amp;quot;&amp;quot;;&lt;br /&gt;
    result = doCommand(command,NULL);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    /* ASYNCHRONOUS SYSTEM() WITH ON-DEMAND AUTO/WAIT CON:&lt;br /&gt;
     */&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Asynchronous startup of &#039;Sys:Utilities/Clock&#039;:\n&amp;quot;);&lt;br /&gt;
    command = &amp;quot;SYS:Utilities/Clock&amp;quot;;&lt;br /&gt;
    result = beginCommand(command);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\n*** SystemTest: Asynchronous startup of &#039;avail&#039;:\n&amp;quot;);&lt;br /&gt;
    command = &amp;quot;avail&amp;quot;;&lt;br /&gt;
    result = beginCommand(command);&lt;br /&gt;
    checkResult(command,result);&lt;br /&gt;
&lt;br /&gt;
    IDOS-&amp;gt;Printf(&amp;quot;\nSystemTest exiting. Close Clock and Autocon window when you wish.\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    IExec-&amp;gt;CloseLibrary(IntuitionBase);&lt;br /&gt;
    return RETURN_OK;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Synchronous external command (wait for return)&lt;br /&gt;
 * Uses your Input/Output unless you supply other handle&lt;br /&gt;
 * Result will be return code of the command, unless the System() call&lt;br /&gt;
 * itself fails, in which case the result will be -1&lt;br /&gt;
 */&lt;br /&gt;
LONG doCommand(UBYTE *command, BPTR other)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem stags[4];&lt;br /&gt;
&lt;br /&gt;
    stags[0].ti_Tag = SYS_Input;&lt;br /&gt;
    stags[0].ti_Data = other ? other : Input();&lt;br /&gt;
    stags[1].ti_Tag = SYS_Output;&lt;br /&gt;
    stags[1].ti_Data = other ? NULL: Output();&lt;br /&gt;
    stags[3].ti_Tag = TAG_END;&lt;br /&gt;
    return IDOS-&amp;gt;System(command, stags);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Asynchronous external command started with its own autocon Input/Output&lt;br /&gt;
 * This routine shows use of the SYS_UserShell tag as well.&lt;br /&gt;
 * Result will only reflect whether System() call itself succeeded.&lt;br /&gt;
 * If System() call fails, result will be -1L&lt;br /&gt;
 * We are using -2L as result if our Open of CON: fails&lt;br /&gt;
 */&lt;br /&gt;
UBYTE *autocon=&amp;quot;CON:0/40/640/150/Auto CON Window Opens if Needed/auto/close/wait&amp;quot;;&lt;br /&gt;
LONG beginCommand(UBYTE *command)&lt;br /&gt;
{&lt;br /&gt;
    struct TagItem stags[5];&lt;br /&gt;
    BPTR file;&lt;br /&gt;
&lt;br /&gt;
    if(file = IDOS-&amp;gt;Open(autocon, MODE_OLDFILE))&lt;br /&gt;
        {&lt;br /&gt;
        stags[0].ti_Tag = SYS_Input;&lt;br /&gt;
        stags[0].ti_Data = file;&lt;br /&gt;
        stags[1].ti_Tag = SYS_Output;&lt;br /&gt;
        stags[1].ti_Data = NULL;&lt;br /&gt;
        stags[2].ti_Tag = SYS_Asynch;&lt;br /&gt;
        stags[2].ti_Data = TRUE;&lt;br /&gt;
        stags[3].ti_Tag = SYS_UserShell;&lt;br /&gt;
        stags[3].ti_Data = TRUE;&lt;br /&gt;
        stags[4].ti_Tag = TAG_END;&lt;br /&gt;
        return IDOS-&amp;gt;System(command, stags);&lt;br /&gt;
        }&lt;br /&gt;
    else return(WINDOWFAIL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Demo routine outputs result of System&lt;br /&gt;
 */&lt;br /&gt;
VOID checkResult(UBYTE *command, LONG result)&lt;br /&gt;
{&lt;br /&gt;
    if(result == SYSTEMFAIL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;*** SystemTest: could not start process for command\n&amp;quot;);&lt;br /&gt;
    else if(result == WINDOWFAIL)&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;*** SystemTest: can&#039;t open con: for command\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
        IDOS-&amp;gt;Printf(&amp;quot;*** SystemTest: command (if synchronous) returned %ld\n&amp;quot;,result);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Transparent_Windows&amp;diff=9854</id>
		<title>Programming AmigaOS 4: Transparent Windows</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Transparent_Windows&amp;diff=9854"/>
		<updated>2018-10-06T22:28:08Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Replaced german Word with english equivalent&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;
With AmigaOS 4.1 a new technology was introduced, which lets you display (partially) transparent windows thus showing the content behind the window. Additionally, with this technology you can create rounded window corners. We will take a closer look today at the programming characteristics behind these options.&lt;br /&gt;
&lt;br /&gt;
In the first block we will deal with transparent windows, while in the second block we will see how you can create transparent areas within a window or how you can set the window based on the outline of an image.&lt;br /&gt;
&lt;br /&gt;
= Compositing =&lt;br /&gt;
&lt;br /&gt;
In order to be able to use compositing you need AmigaOS in the version 4.1 (so, SysBase-&amp;gt;lib_Version &amp;gt;= 53). Moreover, your screen must have a colour depth of at least 16 Bit (so Highcolour or Truecolour). The user can also set in the GUI-Prefs whether transparency is allowed. &#039;Visual effects for layer&#039; must be activated for it. You can certainly check that in the following way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  if(( dri = IIntuition-&amp;gt;GetScreenDrawInfo(window-&amp;gt;WScreen) ))&lt;br /&gt;
  {&lt;br /&gt;
    IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the variable &amp;quot;specialfx&amp;quot; is set as TRUE, the user has activated the setting and the particular screen has at least 16 Bit colour depth.&lt;br /&gt;
&lt;br /&gt;
= Transparent windows =&lt;br /&gt;
&lt;br /&gt;
To make a window transparent, three new tags are sufficient. They must be passed as/when the window is opened to IIntuition-&amp;gt;OpenWindowTags() or rather, IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),...).&lt;br /&gt;
&lt;br /&gt;
With the tag &amp;quot;WA_Opaqueness&amp;quot; you can determine how opaque or transparent the complete window should be. The possible values margin begins with 0 for completely transparent, which means, you cannot see anything from the contents or the window itself and the underlying screen content is fully displayed. There are also value margins up to 255 for completely opaque display, which corresponds to the standard display. In that case, the complete window content is shown and what is behind is not visible. A mean value of 128 would display one half of the window content and one half of the screen behind it. However, if you want to use this setting, you must additionally set the tag &amp;quot;WA_OverrideOpaqueness&amp;quot; to TRUE. Only then will the global settings from the GUI-Prefs program be ignored and a divergent transparency will be possible for the selected window.&lt;br /&gt;
&lt;br /&gt;
With the third tag &amp;quot;WA_FadeTime&amp;quot; you can set up a delay time, which will be active when you open/close the window as well as when you change the transparent value (WA_Opaqueness). The value is a time span in microseconds (1,000,000 microseconds = 1 second). The standard is 0, which means, no delay. If you enter 1,000,000, it will take one second until the window is completely displayed when opened. Whatever, it&#039;s a nice gimmick.&lt;br /&gt;
&lt;br /&gt;
You can also change all three tags retrospectively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  Intuition-&amp;gt;SetWindowAttrs(window,WA_Opaqueness,opaqueness,TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the other hand, you can request the current tag values with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
 IIntuition-&amp;gt;GetAttr(WA_Opaqueness,winobj,&amp;amp;opaqueness)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The so far covered details are included in the sample program &amp;quot;OpaquenessExample.c&amp;quot;, where you can try them out.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * OpaquenessExample.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc OpaquenessExample.c -o OpaquenessExample -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gui.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction_macros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/label.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/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: OpaquenessExample 1.0 (06.01.2009) - (c) Jan.2009 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
  OBJ_SLIDER_1,&lt;br /&gt;
  OBJ_SLIDER_2,&lt;br /&gt;
  OBJ_MAX&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define OBJ(x) objects[x]&lt;br /&gt;
#define GAD(x) (struct Gadget *)objects[x]&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct RDArgs    *rda;&lt;br /&gt;
  struct Window    *window;&lt;br /&gt;
  struct DrawInfo  *dri;&lt;br /&gt;
  Object           *win;&lt;br /&gt;
  Object           *objects[OBJ_MAX];&lt;br /&gt;
&lt;br /&gt;
  BOOL   canfade    = FALSE;&lt;br /&gt;
  uint32 specialfx  = 0;&lt;br /&gt;
  uint32 fade_time  = 50;&lt;br /&gt;
  uint32 opaqueness = 255;&lt;br /&gt;
&lt;br /&gt;
  uint32 sigmask    = 0;&lt;br /&gt;
  uint32 result     = 0;&lt;br /&gt;
  uint16 code       = 0;&lt;br /&gt;
  BOOL   done       = FALSE;&lt;br /&gt;
&lt;br /&gt;
  enum { ARG_pubscreen, ARG_MAX };&lt;br /&gt;
  ULONG args[ARG_MAX]={ 0 };&lt;br /&gt;
&lt;br /&gt;
  /* AmigaOS 4.1 ist erforderlich */&lt;br /&gt;
  if(SysBase-&amp;gt;lib_Version &amp;gt;= 53)&lt;br /&gt;
  {&lt;br /&gt;
    /* Aufrufargumente auswerten */&lt;br /&gt;
    if((rda = IDOS-&amp;gt;ReadArgs(&amp;quot;PUBSCREEN/K&amp;quot;,(LONG*)args,NULL)))&lt;br /&gt;
    {&lt;br /&gt;
      win = WindowObject,&lt;br /&gt;
        WA_Title,               &amp;quot;Transparent window&amp;quot;,&lt;br /&gt;
        WA_DragBar,             TRUE,&lt;br /&gt;
        WA_CloseGadget,         TRUE,&lt;br /&gt;
        WA_SizeGadget,          TRUE,&lt;br /&gt;
        WA_DepthGadget,         TRUE,&lt;br /&gt;
        WA_Activate,            TRUE,&lt;br /&gt;
        WA_Width,               400,&lt;br /&gt;
        WA_OverrideOpaqueness,  TRUE,   /* Globale Einstellungen ignorieren */&lt;br /&gt;
        WA_Opaqueness,          255,    /* Volldeckend Ëffnen */&lt;br /&gt;
        WA_FadeTime,            500000, /* VerzËgerungszeit in Mikrosekunden */&lt;br /&gt;
        (args[ARG_pubscreen] ? WA_PubScreenName : TAG_IGNORE), args[ARG_pubscreen],&lt;br /&gt;
        WINDOW_Position,        WPOS_CENTERSCREEN,&lt;br /&gt;
        WINDOW_Layout,          VLayoutObject,&lt;br /&gt;
            LAYOUT_SpaceOuter,      TRUE,&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddChild,    OBJ(OBJ_SLIDER_1) = SliderObject,&lt;br /&gt;
                GA_ID,              OBJ_SLIDER_1,&lt;br /&gt;
                GA_RelVerify,       TRUE,&lt;br /&gt;
                SLIDER_Min,         10,&lt;br /&gt;
                SLIDER_Max,         100,&lt;br /&gt;
                SLIDER_Level,       50,&lt;br /&gt;
                SLIDER_KnobDelta,   10,&lt;br /&gt;
                SLIDER_Orientation, SLIDER_HORIZONTAL,&lt;br /&gt;
                SLIDER_Ticks,       11,&lt;br /&gt;
                SLIDER_ShortTicks,  TRUE,&lt;br /&gt;
                SLIDER_LevelFormat, &amp;quot;%3ld&amp;quot;,&lt;br /&gt;
                SLIDER_LevelPlace,  PLACETEXT_IN,&lt;br /&gt;
                SLIDER_LevelMaxLen, 3,&lt;br /&gt;
            End,&lt;br /&gt;
            Label(&amp;quot; Fade Time (1/100s) &amp;quot;),&lt;br /&gt;
            CHILD_WeightedHeight,   0,&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddChild,    OBJ(OBJ_SLIDER_2) = SliderObject,&lt;br /&gt;
                GA_ID,              OBJ_SLIDER_2,&lt;br /&gt;
                GA_RelVerify,       TRUE,&lt;br /&gt;
                SLIDER_Min,         20,&lt;br /&gt;
                SLIDER_Max,         255,&lt;br /&gt;
                SLIDER_Level,       255,&lt;br /&gt;
                SLIDER_KnobDelta,   10,&lt;br /&gt;
                SLIDER_Orientation, SLIDER_HORIZONTAL,&lt;br /&gt;
                SLIDER_Ticks,       21,&lt;br /&gt;
                SLIDER_ShortTicks,  TRUE,&lt;br /&gt;
                SLIDER_LevelFormat, &amp;quot;%3ld&amp;quot;,&lt;br /&gt;
                SLIDER_LevelPlace,  PLACETEXT_IN,&lt;br /&gt;
                SLIDER_LevelMaxLen, 3,&lt;br /&gt;
            TAG_END),&lt;br /&gt;
            Label(&amp;quot; Opaqueness &amp;quot;),&lt;br /&gt;
            CHILD_WeightedHeight,   0,&lt;br /&gt;
&lt;br /&gt;
        TAG_END),&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      if((window = RA_OpenWindow(win)))&lt;br /&gt;
      {&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, win, &amp;amp;sigmask);&lt;br /&gt;
&lt;br /&gt;
        /* ist Composing aktiviert und mËglich ? */&lt;br /&gt;
        if((dri = IIntuition-&amp;gt;GetScreenDrawInfo(window-&amp;gt;WScreen)))&lt;br /&gt;
        {&lt;br /&gt;
          IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
          canfade = (BOOL)specialfx;&lt;br /&gt;
&lt;br /&gt;
          if(!canfade)&lt;br /&gt;
          {&lt;br /&gt;
            /* deaktiviert: dann auch Gadgets sperren, da ohne Effekt */&lt;br /&gt;
            IIntuition-&amp;gt;SetGadgetAttrs(GAD(OBJ_SLIDER_1),window,NULL,&lt;br /&gt;
                                      	GA_Disabled,TRUE,TAG_END);&lt;br /&gt;
            IIntuition-&amp;gt;SetGadgetAttrs(GAD(OBJ_SLIDER_2),window,NULL,&lt;br /&gt;
                                      	GA_Disabled,TRUE,TAG_END);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        while(!done)&lt;br /&gt;
        {&lt;br /&gt;
          if(IExec-&amp;gt;Wait(sigmask | SIGBREAKF_CTRL_C) &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
          {&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
          while((result = RA_HandleInput(win,&amp;amp;code)))&lt;br /&gt;
          {&lt;br /&gt;
            switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
            {&lt;br /&gt;
              case WMHI_CLOSEWINDOW:&lt;br /&gt;
                   done = TRUE;&lt;br /&gt;
                   break;&lt;br /&gt;
&lt;br /&gt;
              case WMHI_GADGETUP:&lt;br /&gt;
                   switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
                   {&lt;br /&gt;
                     case OBJ_SLIDER_1:&lt;br /&gt;
                          if (canfade)&lt;br /&gt;
                          {&lt;br /&gt;
                            IIntuition-&amp;gt;GetAttrs(GAD(OBJ_SLIDER_1), SLIDER_Level, &amp;amp;fade_time, TAG_END);&lt;br /&gt;
                            IIntuition-&amp;gt;SetWindowAttrs(window, WA_FadeTime, fade_time * 10000, TAG_END);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
&lt;br /&gt;
                     case OBJ_SLIDER_2:&lt;br /&gt;
                          if (canfade)&lt;br /&gt;
                          {&lt;br /&gt;
                            IIntuition-&amp;gt;GetAttrs(GAD(OBJ_SLIDER_2), SLIDER_Level, &amp;amp;opaqueness, TAG_END);&lt;br /&gt;
                            IIntuition-&amp;gt;SetWindowAttrs(window, WA_Opaqueness, opaqueness, TAG_END);&lt;br /&gt;
&lt;br /&gt;
                            /* SetWindowAttr() kehrt sofort zurÂ¸ck, daher warten wir kurz */&lt;br /&gt;
                            /* bis das Fenster das Fading abgeschlossen hat, bevor die nâ°chste */&lt;br /&gt;
                            /* Verâ°nderung vorgenommen wird. */&lt;br /&gt;
                            IDOS-&amp;gt;Delay(fade_time/2);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                   }&lt;br /&gt;
                   break;&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      IIntuition-&amp;gt;DisposeObject(win);&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;FreeArgs(rda);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),&amp;quot;OpaquenessExample&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;program required AmigaOS 4.1\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;
[[File:AF108_opaqueness_grab.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Non-square windows =&lt;br /&gt;
&lt;br /&gt;
The so far mentioned information always relates to complete square windows. In this second block we will see how you can &#039;remove&#039; parts of a window or see through a hole in the window. These options are more complex, but AmigaOS supports programmers with its new functions. Since the Autodocs in this field are still quite incomplete we tried to compile all the important information.&lt;br /&gt;
&lt;br /&gt;
= Loading graphics =&lt;br /&gt;
&lt;br /&gt;
First of all, we need a graphic that will set the form of the window. The intuition.library supports us here with matching new functions that we haven&#039;t had a chance to present you yet. The image data is loaded in the memory with IIntuition-&amp;gt;ObtainBitMapSource().The tag BMS_DoShade is important here in order to create/provide the alpha-channel data as well. At the moment this is only possible with PNG graphics. Other graphic formats do not provide this information.&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;ObtainBitMapInstance() declares a pointer to the image management. To get the actual image data you must request the desired values with IIntuition-&amp;gt;BitMapInstanceControl(). Some important tags are here BMICTRL_GetWidth and BMICTRL_GetHeight for the size as well as BMICTRL_GetBitMap and BMICTRL_GetAlphaMap for the image data, i.e., the alpha mask.  There is IIntuition-&amp;gt;ReleaseBitMapInstance() and Iintuition-&amp;gt;ReleaseBitMapSource() for release. The functions that you will need are compiled in the following list. The image data can be drawn with the usual functions such as IGraphics-&amp;gt;BltBitMapTags().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct Screen *scr;&lt;br /&gt;
  APTR           source;&lt;br /&gt;
  APTR           instance;&lt;br /&gt;
  struct BitMap *bitmap;&lt;br /&gt;
  struct BitMap *alpha;&lt;br /&gt;
  ULONG          width;&lt;br /&gt;
  ULONG          height;&lt;br /&gt;
&lt;br /&gt;
  /* load the graphic in the memory, if necessary (intern via Datatype) */&lt;br /&gt;
  if((source = IIntuition-&amp;gt;ObtainBitMapSource((STRPTR)args[ARG_filename],BMS_DoShade,TRUE,TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    /* request a bitmap instance for the screen */&lt;br /&gt;
    if((instance = IIntuition-&amp;gt;ObtainBitMapInstance(source,scr,TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Request image information and bitmap*/&lt;br /&gt;
      IIntuition-&amp;gt;BitMapInstanceControl(instance,&lt;br /&gt;
                                        BMICTRL_GetBitMap,   &amp;amp;bitmap,&lt;br /&gt;
                                        BMICTRL_GetAlphaMap, &amp;amp;alpha,&lt;br /&gt;
                                        BMICTRL_GetWidth,    &amp;amp;width,&lt;br /&gt;
                                        BMICTRL_GetHeight,   &amp;amp;height,&lt;br /&gt;
                                        TAG_DONE );&lt;br /&gt;
&lt;br /&gt;
      if(bitmap &amp;amp;&amp;amp; alpha)&lt;br /&gt;
      {&lt;br /&gt;
        /* Image data can be processed /blitted now */&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;cannot get (alpha) bitmaps\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      /* r BitMap Instance */&lt;br /&gt;
      IIntuition-&amp;gt;ReleaseBitMapInstance(instance);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;cannot get instance of picture\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* release graphic file */&lt;br /&gt;
    IIntuition-&amp;gt;ReleaseBitMapSource(source);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;loading picture file failed\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Define window areas =&lt;br /&gt;
&lt;br /&gt;
The existing clipping system has been expanded in order to be able to set the areas in a window that display contents, that is, that let the background show through. With ILayers-&amp;gt;InstallClipRegion() you could set even earlier rectangular areas in a window, in which a blit operation can draw data. This way, you can, for example, protect separate areas of a window too, if graphic data is larger than the window. However, in order to apply the ClipRect structure to an alpha channel you must use a private function of the layers. library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ILayers-&amp;gt;AllocClipRect()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is provided exclusively for this task. The counterpart to it is ILayers-&amp;gt;FreeClipRect(), that releases again the structure. The structure is already pre-initialised, so that only a few fields must be adjusted to your needs. On the one hand, you must store the graphic size in &amp;quot;bounds&amp;quot; and enter it in &amp;quot;BitMap&amp;quot;. The required memory area for it can be created with IGraphics-&amp;gt;AllocBitMap(). Then you must also blit the alpha-channel bitmap with IGraphics-&amp;gt;BltBitMapTags(). The only thing you need to keep in mind in this whole procedure is that you automatically de-allocate the entered BitMap when you allocate the ClipRect structure! In case you do not want to do it, you must first set the field on NULL and de-allocate the BitMap with IGraphics-&amp;gt;FreeBitMap(). These are the matching code rows for this part of the workshop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct ClipRect *ac;&lt;br /&gt;
&lt;br /&gt;
  /* allocate and initialise ClipRect structure */&lt;br /&gt;
  if((ac = ILayers-&amp;gt;AllocClipRect(&amp;amp;(scr-&amp;gt;LayerInfo))))&lt;br /&gt;
  {&lt;br /&gt;
    struct TagItem bmtags[3] = { { BMATags_RGBFormat, RGBFB_ALPHA8 },&lt;br /&gt;
                                 { BMATags_Friend, (ULONG)scr-&amp;gt;RastPort.BitMap },&lt;br /&gt;
                                 { TAG_END, 0 } };&lt;br /&gt;
&lt;br /&gt;
    /* alllocate Alpha-ClipRect and request memory for Bitmap */&lt;br /&gt;
    ac-&amp;gt;bounds.MinX = 0;&lt;br /&gt;
    ac-&amp;gt;bounds.MinY = 0;&lt;br /&gt;
    ac-&amp;gt;bounds.MaxX = width-1,&lt;br /&gt;
    ac-&amp;gt;bounds.MaxY = height-1;&lt;br /&gt;
    ac-&amp;gt;Next        = NULL;&lt;br /&gt;
    if((ac-&amp;gt;BitMap  = IGraphics-&amp;gt;AllocBitMap(width,height,8,BMF_DISPLAYABLE|BMF_CLEAR|BMF_CHECKVALUE,(struct BitMap *)bmtags)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Fill Alpha-ClipRect with Alpha-Bitmap */&lt;br /&gt;
      IGraphics-&amp;gt;BltBitMapTags(BLITA_Source,         alpha,&lt;br /&gt;
                               BLITA_SrcType,        BLITT_CHUNKY,&lt;br /&gt;
                               BLITA_SrcBytesPerRow, width,&lt;br /&gt;
                               BLITA_Dest,           ac-&amp;gt;BitMap,&lt;br /&gt;
                               BLITA_Width,          width,&lt;br /&gt;
                               BLITA_Height,         height,&lt;br /&gt;
                               TAG_DONE );&lt;br /&gt;
&lt;br /&gt;
      /* ... */&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;cannot allocate alpha mask\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* ClipRect deallocate; it also deallocates ac-&amp;gt;BitMap! */&lt;br /&gt;
    ILayers-&amp;gt;FreeClipRect(&amp;amp;(scr-&amp;gt;LayerInfo),ac);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;cannot allocate cliprect\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Window show yourself =&lt;br /&gt;
&lt;br /&gt;
What is left is opening the window and blitting the graphic bitmap with IGraphics-&amp;gt;BltBitMapRastPort(). You will need for it the following code rows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct Window   *win;&lt;br /&gt;
&lt;br /&gt;
  /* Open window with the Alpha mask */&lt;br /&gt;
  if((win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                           WA_Width,       width,&lt;br /&gt;
                           WA_Height,      height,&lt;br /&gt;
                           WA_Borderless,  TRUE,&lt;br /&gt;
                           WA_AlphaClips,  ac,&lt;br /&gt;
                           ...&lt;br /&gt;
                           TAG_DONE)))&lt;br /&gt;
  {&lt;br /&gt;
    /* blit the graphic file into the window */&lt;br /&gt;
    IGraphics-&amp;gt;BltBitMapRastPort(bitmap, 0, 0, win-&amp;gt;RPort, 0, 0, width, height, MINTERM_ABC|MINTERM_ABNC);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you open the window, you must also enter the tag WA_AlphaClips. The window will be opened without a frame and without system symbols. They are not shown, but as soon as you activate the window, the frame elements are drawn with Intuition, at least as far as the alpha mask allows it. You can also target the alpha mask display. Just do not copy any image data into the Rastport after you have opened the window. The alpha mask does not only set the opaque and transparent points, but it can also be created gradually. Thus, even image parts can appear as half-transparent and let the background shine through. The &#039;partly transparent ball&#039; graphic shows the result.&lt;br /&gt;
&lt;br /&gt;
You should also keep in mind that the window may not be of the GimmeZeroZero type. In this case there are already internal clipping masks, which cannot be combined with the alpha-clipping.&lt;br /&gt;
&lt;br /&gt;
The program &amp;quot;BorderlessWindow&amp;quot; serves as a complete example for this topic showing all the mentioned functions in a practical interplay. A matching graphic is also included. Have fun experimenting with the source-code and looking for new possibilities!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * BorderlessWindow.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc BorderlessWindow.c -o BorderlessWindow -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/blitattr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/composite.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/bitmapshare.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gui.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/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: BorderlessWindow 1.1 (22.07.2013) - (c) Jan.2009 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
  int res = RETURN_FAIL;&lt;br /&gt;
&lt;br /&gt;
  struct RDArgs   *rda;&lt;br /&gt;
  struct Screen   *scr;&lt;br /&gt;
  struct Window   *win;&lt;br /&gt;
  struct ClipRect *ac;&lt;br /&gt;
  struct DrawInfo *dri;&lt;br /&gt;
  struct BitMap   *bitmap;&lt;br /&gt;
  struct BitMap   *alpha;&lt;br /&gt;
  APTR             source;&lt;br /&gt;
  APTR             instance;&lt;br /&gt;
  ULONG            width;&lt;br /&gt;
  ULONG            height;&lt;br /&gt;
  ULONG            specialfx;&lt;br /&gt;
&lt;br /&gt;
  enum { ARG_filename, ARG_pubscreen, ARG_MAX };&lt;br /&gt;
  ULONG args[ARG_MAX]={ (ULONG)&amp;quot;testpic.png&amp;quot;,0 };&lt;br /&gt;
&lt;br /&gt;
  /* AmigaOS 4.1 ist erforderlich */&lt;br /&gt;
  if(SysBase-&amp;gt;lib_Version &amp;gt;= 53)&lt;br /&gt;
  {&lt;br /&gt;
    /* Aufrufargumente auswerten */&lt;br /&gt;
    if((rda = IDOS-&amp;gt;ReadArgs(&amp;quot;FILENAME,PUBSCREEN/K&amp;quot;,(LONG*)args,NULL)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Bildschirm auf dem die Anzeige erfolgt festlegen */&lt;br /&gt;
      if((scr = IIntuition-&amp;gt;LockPubScreen((STRPTR)args[ARG_pubscreen])))&lt;br /&gt;
      {&lt;br /&gt;
        /* sicherstellen dass der Bildschirm Alpha Composing unterstÂ¸tzt */&lt;br /&gt;
        if(( dri = IIntuition-&amp;gt;GetScreenDrawInfo(scr)))&lt;br /&gt;
        {&lt;br /&gt;
          IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
          if(!specialfx) IDOS-&amp;gt;Printf(&amp;quot;WARNING: screen do not support alpha composing\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          IIntuition-&amp;gt;FreeScreenDrawInfo(scr,dri);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* die Grafik bei Bedarf in den Speicher laden (intern via Datatype) */&lt;br /&gt;
        if((source = IIntuition-&amp;gt;ObtainBitMapSource((STRPTR)args[ARG_filename],&lt;br /&gt;
                       BMS_DoShade, TRUE,&lt;br /&gt;
                       BMS_DoMask, TRUE,&lt;br /&gt;
                       TAG_END)))&lt;br /&gt;
        {&lt;br /&gt;
          /* eine Bitmap-Instance fÂ¸r den eigenen Bildschirm anfordern */&lt;br /&gt;
          if((instance = IIntuition-&amp;gt;ObtainBitMapInstance(source,scr,TAG_END)))&lt;br /&gt;
          {&lt;br /&gt;
            /* Bildinformationen und Biptmap erfragen */&lt;br /&gt;
            IIntuition-&amp;gt;BitMapInstanceControl(instance,&lt;br /&gt;
                                              BMICTRL_GetBitMap,   &amp;amp;bitmap,&lt;br /&gt;
                                              BMICTRL_GetAlphaMap, &amp;amp;alpha,&lt;br /&gt;
                                              BMICTRL_GetWidth,    &amp;amp;width,&lt;br /&gt;
                                              BMICTRL_GetHeight,   &amp;amp;height,&lt;br /&gt;
                                              TAG_END );&lt;br /&gt;
            if(bitmap &amp;amp;&amp;amp; alpha)&lt;br /&gt;
            {&lt;br /&gt;
              /* ClipRect Struktur anlegen und initialisieren */&lt;br /&gt;
              if((ac = ILayers-&amp;gt;AllocClipRect(&amp;amp;(scr-&amp;gt;LayerInfo))))&lt;br /&gt;
              {&lt;br /&gt;
                /* Alpha-ClipRect belegen und Speicher fÂ¸r Bitmap anfordern */&lt;br /&gt;
                ac-&amp;gt;bounds.MinX = 0;&lt;br /&gt;
                ac-&amp;gt;bounds.MinY = 0;&lt;br /&gt;
                ac-&amp;gt;bounds.MaxX = width-1,&lt;br /&gt;
                ac-&amp;gt;bounds.MaxY = height-1;&lt;br /&gt;
                ac-&amp;gt;Next        = NULL;&lt;br /&gt;
                if((ac-&amp;gt;BitMap  = IGraphics-&amp;gt;AllocBitMapTags(width, height, 8,&lt;br /&gt;
                                    BMATags_Displayable, TRUE,&lt;br /&gt;
                                    BMATags_Clear, TRUE,&lt;br /&gt;
                                    BMATags_PixelFormat, PIXF_ALPHA8,&lt;br /&gt;
                                    BMATags_Friend, scr-&amp;gt;RastPort.BitMap,&lt;br /&gt;
                                    TAG_END)))&lt;br /&gt;
                {&lt;br /&gt;
                  /* Alpha-ClipRect mit der Alpha-Bitmap fÂ¸llen */&lt;br /&gt;
                  IGraphics-&amp;gt;BltBitMapTags(BLITA_Source,         alpha,&lt;br /&gt;
                                           BLITA_SrcType,        BLITT_CHUNKY,&lt;br /&gt;
                                           BLITA_SrcBytesPerRow, width,&lt;br /&gt;
                                           BLITA_Dest,           ac-&amp;gt;BitMap,&lt;br /&gt;
                                           BLITA_Width,          width,&lt;br /&gt;
                                           BLITA_Height,         height,&lt;br /&gt;
                                           TAG_END );&lt;br /&gt;
&lt;br /&gt;
                  /* Rahmenloses Fenster Ëffnen */&lt;br /&gt;
                  if((win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                                           WA_Left,        1580,&lt;br /&gt;
                                           WA_Top,         120,&lt;br /&gt;
                                           WA_Width,       width,&lt;br /&gt;
                                           WA_Height,      height,&lt;br /&gt;
                                           WA_CloseGadget, FALSE,&lt;br /&gt;
                                           WA_DragBar,     FALSE,&lt;br /&gt;
                                           WA_DepthGadget, FALSE,&lt;br /&gt;
                                           WA_SizeGadget,  FALSE,&lt;br /&gt;
                                           WA_Borderless,  TRUE,&lt;br /&gt;
                                           WA_ToolBox,     TRUE,&lt;br /&gt;
                                           WA_AlphaClips,  ac,&lt;br /&gt;
                                           WA_CustomScreen,scr,&lt;br /&gt;
                                           WA_Title,       &amp;quot;Borderless window&amp;quot;,&lt;br /&gt;
                                           WA_IDCMP,       IDCMP_MOUSEBUTTONS,&lt;br /&gt;
                                           TAG_END )))&lt;br /&gt;
                  {&lt;br /&gt;
                    /* das komplette Fenster als dragable definieren */&lt;br /&gt;
                    struct Gadget draggad = { NULL, 0,0, width,height,&lt;br /&gt;
                                              GFLG_GADGHNONE, 0, GTYP_WDRAGGING,&lt;br /&gt;
                                              NULL,NULL,NULL,0,0,0,NULL };&lt;br /&gt;
                    win-&amp;gt;FirstGadget = &amp;amp;draggad;&lt;br /&gt;
&lt;br /&gt;
                    /* die Grafikdatei in das Fenster blitten */&lt;br /&gt;
                    IGraphics-&amp;gt;BltBitMapRastPort(bitmap, 0, 0, win-&amp;gt;RPort,&lt;br /&gt;
                      0, 0, width, height, MINTERM_ABC | MINTERM_ABNC);&lt;br /&gt;
&lt;br /&gt;
                    /* warten auf CTRL-C zum Programm beenden */&lt;br /&gt;
                    IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                    res = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
                    /* Fenster schlieï¬en */&lt;br /&gt;
                    IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
                  }&lt;br /&gt;
                  else IDOS-&amp;gt;Printf(&amp;quot;can not open window\n&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                else IDOS-&amp;gt;Printf(&amp;quot;can not allocate alpha mask\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                /* ClipRect freigeben; gibt auch ac-&amp;gt;BitMap frei ! */&lt;br /&gt;
                ILayers-&amp;gt;FreeClipRect(&amp;amp;(scr-&amp;gt;LayerInfo),ac);&lt;br /&gt;
              }&lt;br /&gt;
              else IDOS-&amp;gt;Printf(&amp;quot;can not allocate cliprect\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;can not get (alpha) bitmaps\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            /* BitMap Instance freigeben */&lt;br /&gt;
            IIntuition-&amp;gt;ReleaseBitMapInstance(instance);&lt;br /&gt;
          }&lt;br /&gt;
          else IDOS-&amp;gt;Printf(&amp;quot;can not get instance of picture\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          /* Grafikdatei freigeben */&lt;br /&gt;
          IIntuition-&amp;gt;ReleaseBitMapSource(source);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;loading picture file failed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Bildschirm freigeben */&lt;br /&gt;
        IIntuition-&amp;gt;UnlockPubScreen(NULL,scr);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;can not lock pubscreen\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;FreeArgs(rda);&lt;br /&gt;
    }&lt;br /&gt;
    else { IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),&amp;quot;BorderlessWindow&amp;quot;); res = RETURN_ERROR; }&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Program requires AmigaOS 4.1\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;
Grab the test graphic here: [[Media:testpic.png|testpic.png]].&lt;br /&gt;
&lt;br /&gt;
[[File:AF108_borderless_grab.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Outlook =&lt;br /&gt;
&lt;br /&gt;
You can look forward to the next issue, in which we will deal with datatypes. Do not expect any novelties regarding the program, but for the sake of completeness we should mention these options as well.&lt;br /&gt;
&lt;br /&gt;
[[File:AF108_FensterLoch_grab.png|frame|center]]&lt;br /&gt;
With a hole in a window you can create funny effects, especially if you move the window around.&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>Frédéric Khannouf</name></author>
	</entry>
	<entry>
		<id>https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Transparent_Windows&amp;diff=9853</id>
		<title>Programming AmigaOS 4: Transparent Windows</title>
		<link rel="alternate" type="text/html" href="https://wiki.amigaos.net/w/index.php?title=Programming_AmigaOS_4:_Transparent_Windows&amp;diff=9853"/>
		<updated>2018-10-04T21:30:48Z</updated>

		<summary type="html">&lt;p&gt;Frédéric Khannouf: Corrected small typo&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;
With AmigaOS 4.1 a new technology was introduced, which lets you display (partially) transparent windows thus showing the content behind the window. Additionally, with this technology you can create rounded window corners. We will take a closer look today at the programming characteristics behind these options.&lt;br /&gt;
&lt;br /&gt;
In the first block we will deal with transparent windows, while in the second block we will see how you can create transparent areas within a window or how you can set the window based on the outline of an image.&lt;br /&gt;
&lt;br /&gt;
= Compositing =&lt;br /&gt;
&lt;br /&gt;
In order to be able to use compositing you need AmigaOS in the version 4.1 (so, SysBase-&amp;gt;lib_Version &amp;gt;= 53). Moreover, your screen must have a colour depth of at least 16 Bit (so Highcolour or Truecolour). The user can also set in the GUI-Prefs whether transparency is allowed. &#039;Visual effects for layer&#039; must be activated for it. You can certainly check that in the following way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  if(( dri = IIntuition-&amp;gt;GetScreenDrawInfo(window-&amp;gt;WScreen) ))&lt;br /&gt;
  {&lt;br /&gt;
    IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the variable &amp;quot;specialfx&amp;quot; is set as TRUE, the user has activated the setting and the particular screen has at least 16 Bit colour depth.&lt;br /&gt;
&lt;br /&gt;
= Transparent windows =&lt;br /&gt;
&lt;br /&gt;
To make a window transparent, three new tags are sufficient. They must be passed as/when the window is opened to IIntuition-&amp;gt;OpenWindowTags() bzw. IIntuition-&amp;gt;NewObject(IWindow-&amp;gt;WINDOW_GetClass(),...).&lt;br /&gt;
&lt;br /&gt;
With the tag &amp;quot;WA_Opaqueness&amp;quot; you can determine how opaque or transparent the complete window should be. The possible values margin begins with 0 for completely transparent, which means, you cannot see anything from the contents or the window itself and the underlying screen content is fully displayed. There are also value margins up to 255 for completely opaque display, which corresponds to the standard display. In that case, the complete window content is shown and what is behind is not visible. A mean value of 128 would display one half of the window content and one half of the screen behind it. However, if you want to use this setting, you must additionally set the tag &amp;quot;WA_OverrideOpaqueness&amp;quot; to TRUE. Only then will the global settings from the GUI-Prefs program be ignored and a divergent transparency will be possible for the selected window.&lt;br /&gt;
&lt;br /&gt;
With the third tag &amp;quot;WA_FadeTime&amp;quot; you can set up a delay time, which will be active when you open/close the window as well as when you change the transparent value (WA_Opaqueness). The value is a time span in microseconds (1,000,000 microseconds = 1 second). The standard is 0, which means, no delay. If you enter 1,000,000, it will take one second until the window is completely displayed when opened. Whatever, it&#039;s a nice gimmick.&lt;br /&gt;
&lt;br /&gt;
You can also change all three tags retrospectively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  Intuition-&amp;gt;SetWindowAttrs(window,WA_Opaqueness,opaqueness,TAG_END);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the other hand, you can request the current tag values with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
 IIntuition-&amp;gt;GetAttr(WA_Opaqueness,winobj,&amp;amp;opaqueness)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The so far covered details are included in the sample program &amp;quot;OpaquenessExample.c&amp;quot;, where you can try them out.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * OpaquenessExample.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc OpaquenessExample.c -o OpaquenessExample -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gui.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/icclass.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libraries/gadtools.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction.h&amp;gt;&lt;br /&gt;
#include &amp;lt;reaction/reaction_macros.h&amp;gt;&lt;br /&gt;
#include &amp;lt;classes/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;gadgets/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;images/label.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/window.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/layout.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/slider.h&amp;gt;&lt;br /&gt;
#include &amp;lt;proto/label.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: OpaquenessExample 1.0 (06.01.2009) - (c) Jan.2009 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
  OBJ_SLIDER_1,&lt;br /&gt;
  OBJ_SLIDER_2,&lt;br /&gt;
  OBJ_MAX&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define OBJ(x) objects[x]&lt;br /&gt;
#define GAD(x) (struct Gadget *)objects[x]&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  struct RDArgs    *rda;&lt;br /&gt;
  struct Window    *window;&lt;br /&gt;
  struct DrawInfo  *dri;&lt;br /&gt;
  Object           *win;&lt;br /&gt;
  Object           *objects[OBJ_MAX];&lt;br /&gt;
&lt;br /&gt;
  BOOL   canfade    = FALSE;&lt;br /&gt;
  uint32 specialfx  = 0;&lt;br /&gt;
  uint32 fade_time  = 50;&lt;br /&gt;
  uint32 opaqueness = 255;&lt;br /&gt;
&lt;br /&gt;
  uint32 sigmask    = 0;&lt;br /&gt;
  uint32 result     = 0;&lt;br /&gt;
  uint16 code       = 0;&lt;br /&gt;
  BOOL   done       = FALSE;&lt;br /&gt;
&lt;br /&gt;
  enum { ARG_pubscreen, ARG_MAX };&lt;br /&gt;
  ULONG args[ARG_MAX]={ 0 };&lt;br /&gt;
&lt;br /&gt;
  /* AmigaOS 4.1 ist erforderlich */&lt;br /&gt;
  if(SysBase-&amp;gt;lib_Version &amp;gt;= 53)&lt;br /&gt;
  {&lt;br /&gt;
    /* Aufrufargumente auswerten */&lt;br /&gt;
    if((rda = IDOS-&amp;gt;ReadArgs(&amp;quot;PUBSCREEN/K&amp;quot;,(LONG*)args,NULL)))&lt;br /&gt;
    {&lt;br /&gt;
      win = WindowObject,&lt;br /&gt;
        WA_Title,               &amp;quot;Transparent window&amp;quot;,&lt;br /&gt;
        WA_DragBar,             TRUE,&lt;br /&gt;
        WA_CloseGadget,         TRUE,&lt;br /&gt;
        WA_SizeGadget,          TRUE,&lt;br /&gt;
        WA_DepthGadget,         TRUE,&lt;br /&gt;
        WA_Activate,            TRUE,&lt;br /&gt;
        WA_Width,               400,&lt;br /&gt;
        WA_OverrideOpaqueness,  TRUE,   /* Globale Einstellungen ignorieren */&lt;br /&gt;
        WA_Opaqueness,          255,    /* Volldeckend Ëffnen */&lt;br /&gt;
        WA_FadeTime,            500000, /* VerzËgerungszeit in Mikrosekunden */&lt;br /&gt;
        (args[ARG_pubscreen] ? WA_PubScreenName : TAG_IGNORE), args[ARG_pubscreen],&lt;br /&gt;
        WINDOW_Position,        WPOS_CENTERSCREEN,&lt;br /&gt;
        WINDOW_Layout,          VLayoutObject,&lt;br /&gt;
            LAYOUT_SpaceOuter,      TRUE,&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddChild,    OBJ(OBJ_SLIDER_1) = SliderObject,&lt;br /&gt;
                GA_ID,              OBJ_SLIDER_1,&lt;br /&gt;
                GA_RelVerify,       TRUE,&lt;br /&gt;
                SLIDER_Min,         10,&lt;br /&gt;
                SLIDER_Max,         100,&lt;br /&gt;
                SLIDER_Level,       50,&lt;br /&gt;
                SLIDER_KnobDelta,   10,&lt;br /&gt;
                SLIDER_Orientation, SLIDER_HORIZONTAL,&lt;br /&gt;
                SLIDER_Ticks,       11,&lt;br /&gt;
                SLIDER_ShortTicks,  TRUE,&lt;br /&gt;
                SLIDER_LevelFormat, &amp;quot;%3ld&amp;quot;,&lt;br /&gt;
                SLIDER_LevelPlace,  PLACETEXT_IN,&lt;br /&gt;
                SLIDER_LevelMaxLen, 3,&lt;br /&gt;
            End,&lt;br /&gt;
            Label(&amp;quot; Fade Time (1/100s) &amp;quot;),&lt;br /&gt;
            CHILD_WeightedHeight,   0,&lt;br /&gt;
&lt;br /&gt;
            LAYOUT_AddChild,    OBJ(OBJ_SLIDER_2) = SliderObject,&lt;br /&gt;
                GA_ID,              OBJ_SLIDER_2,&lt;br /&gt;
                GA_RelVerify,       TRUE,&lt;br /&gt;
                SLIDER_Min,         20,&lt;br /&gt;
                SLIDER_Max,         255,&lt;br /&gt;
                SLIDER_Level,       255,&lt;br /&gt;
                SLIDER_KnobDelta,   10,&lt;br /&gt;
                SLIDER_Orientation, SLIDER_HORIZONTAL,&lt;br /&gt;
                SLIDER_Ticks,       21,&lt;br /&gt;
                SLIDER_ShortTicks,  TRUE,&lt;br /&gt;
                SLIDER_LevelFormat, &amp;quot;%3ld&amp;quot;,&lt;br /&gt;
                SLIDER_LevelPlace,  PLACETEXT_IN,&lt;br /&gt;
                SLIDER_LevelMaxLen, 3,&lt;br /&gt;
            TAG_END),&lt;br /&gt;
            Label(&amp;quot; Opaqueness &amp;quot;),&lt;br /&gt;
            CHILD_WeightedHeight,   0,&lt;br /&gt;
&lt;br /&gt;
        TAG_END),&lt;br /&gt;
    TAG_END);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      if((window = RA_OpenWindow(win)))&lt;br /&gt;
      {&lt;br /&gt;
        IIntuition-&amp;gt;GetAttr(WINDOW_SigMask, win, &amp;amp;sigmask);&lt;br /&gt;
&lt;br /&gt;
        /* ist Composing aktiviert und mËglich ? */&lt;br /&gt;
        if((dri = IIntuition-&amp;gt;GetScreenDrawInfo(window-&amp;gt;WScreen)))&lt;br /&gt;
        {&lt;br /&gt;
          IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
          canfade = (BOOL)specialfx;&lt;br /&gt;
&lt;br /&gt;
          if(!canfade)&lt;br /&gt;
          {&lt;br /&gt;
            /* deaktiviert: dann auch Gadgets sperren, da ohne Effekt */&lt;br /&gt;
            IIntuition-&amp;gt;SetGadgetAttrs(GAD(OBJ_SLIDER_1),window,NULL,&lt;br /&gt;
                                      	GA_Disabled,TRUE,TAG_END);&lt;br /&gt;
            IIntuition-&amp;gt;SetGadgetAttrs(GAD(OBJ_SLIDER_2),window,NULL,&lt;br /&gt;
                                      	GA_Disabled,TRUE,TAG_END);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        while(!done)&lt;br /&gt;
        {&lt;br /&gt;
          if(IExec-&amp;gt;Wait(sigmask | SIGBREAKF_CTRL_C) &amp;amp; SIGBREAKF_CTRL_C)&lt;br /&gt;
          {&lt;br /&gt;
            done = TRUE;&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
          while((result = RA_HandleInput(win,&amp;amp;code)))&lt;br /&gt;
          {&lt;br /&gt;
            switch(result &amp;amp; WMHI_CLASSMASK)&lt;br /&gt;
            {&lt;br /&gt;
              case WMHI_CLOSEWINDOW:&lt;br /&gt;
                   done = TRUE;&lt;br /&gt;
                   break;&lt;br /&gt;
&lt;br /&gt;
              case WMHI_GADGETUP:&lt;br /&gt;
                   switch(result &amp;amp; WMHI_GADGETMASK)&lt;br /&gt;
                   {&lt;br /&gt;
                     case OBJ_SLIDER_1:&lt;br /&gt;
                          if (canfade)&lt;br /&gt;
                          {&lt;br /&gt;
                            IIntuition-&amp;gt;GetAttrs(GAD(OBJ_SLIDER_1), SLIDER_Level, &amp;amp;fade_time, TAG_END);&lt;br /&gt;
                            IIntuition-&amp;gt;SetWindowAttrs(window, WA_FadeTime, fade_time * 10000, TAG_END);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
&lt;br /&gt;
                     case OBJ_SLIDER_2:&lt;br /&gt;
                          if (canfade)&lt;br /&gt;
                          {&lt;br /&gt;
                            IIntuition-&amp;gt;GetAttrs(GAD(OBJ_SLIDER_2), SLIDER_Level, &amp;amp;opaqueness, TAG_END);&lt;br /&gt;
                            IIntuition-&amp;gt;SetWindowAttrs(window, WA_Opaqueness, opaqueness, TAG_END);&lt;br /&gt;
&lt;br /&gt;
                            /* SetWindowAttr() kehrt sofort zurÂ¸ck, daher warten wir kurz */&lt;br /&gt;
                            /* bis das Fenster das Fading abgeschlossen hat, bevor die nâ°chste */&lt;br /&gt;
                            /* Verâ°nderung vorgenommen wird. */&lt;br /&gt;
                            IDOS-&amp;gt;Delay(fade_time/2);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                   }&lt;br /&gt;
                   break;&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      IIntuition-&amp;gt;DisposeObject(win);&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;FreeArgs(rda);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),&amp;quot;OpaquenessExample&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;program required AmigaOS 4.1\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;
[[File:AF108_opaqueness_grab.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Non-square windows =&lt;br /&gt;
&lt;br /&gt;
The so far mentioned information always relates to complete square windows. In this second block we will see how you can &#039;remove&#039; parts of a window or see through a hole in the window. These options are more complex, but AmigaOS supports programmers with its new functions. Since the Autodocs in this field are still quite incomplete we tried to compile all the important information.&lt;br /&gt;
&lt;br /&gt;
= Loading graphics =&lt;br /&gt;
&lt;br /&gt;
First of all, we need a graphic that will set the form of the window. The intuition.library supports us here with matching new functions that we haven&#039;t had a chance to present you yet. The image data is loaded in the memory with IIntuition-&amp;gt;ObtainBitMapSource().The tag BMS_DoShade is important here in order to create/provide the alpha-channel data as well. At the moment this is only possible with PNG graphics. Other graphic formats do not provide this information.&lt;br /&gt;
&lt;br /&gt;
IIntuition-&amp;gt;ObtainBitMapInstance() declares a pointer to the image management. To get the actual image data you must request the desired values with IIntuition-&amp;gt;BitMapInstanceControl(). Some important tags are here BMICTRL_GetWidth and BMICTRL_GetHeight for the size as well as BMICTRL_GetBitMap and BMICTRL_GetAlphaMap for the image data, i.e., the alpha mask.  There is IIntuition-&amp;gt;ReleaseBitMapInstance() and Iintuition-&amp;gt;ReleaseBitMapSource() for release. The functions that you will need are compiled in the following list. The image data can be drawn with the usual functions such as IGraphics-&amp;gt;BltBitMapTags().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct Screen *scr;&lt;br /&gt;
  APTR           source;&lt;br /&gt;
  APTR           instance;&lt;br /&gt;
  struct BitMap *bitmap;&lt;br /&gt;
  struct BitMap *alpha;&lt;br /&gt;
  ULONG          width;&lt;br /&gt;
  ULONG          height;&lt;br /&gt;
&lt;br /&gt;
  /* load the graphic in the memory, if necessary (intern via Datatype) */&lt;br /&gt;
  if((source = IIntuition-&amp;gt;ObtainBitMapSource((STRPTR)args[ARG_filename],BMS_DoShade,TRUE,TAG_END)))&lt;br /&gt;
  {&lt;br /&gt;
    /* request a bitmap instance for the screen */&lt;br /&gt;
    if((instance = IIntuition-&amp;gt;ObtainBitMapInstance(source,scr,TAG_END)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Request image information and bitmap*/&lt;br /&gt;
      IIntuition-&amp;gt;BitMapInstanceControl(instance,&lt;br /&gt;
                                        BMICTRL_GetBitMap,   &amp;amp;bitmap,&lt;br /&gt;
                                        BMICTRL_GetAlphaMap, &amp;amp;alpha,&lt;br /&gt;
                                        BMICTRL_GetWidth,    &amp;amp;width,&lt;br /&gt;
                                        BMICTRL_GetHeight,   &amp;amp;height,&lt;br /&gt;
                                        TAG_DONE );&lt;br /&gt;
&lt;br /&gt;
      if(bitmap &amp;amp;&amp;amp; alpha)&lt;br /&gt;
      {&lt;br /&gt;
        /* Image data can be processed /blitted now */&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;cannot get (alpha) bitmaps\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      /* r BitMap Instance */&lt;br /&gt;
      IIntuition-&amp;gt;ReleaseBitMapInstance(instance);&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;cannot get instance of picture\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* release graphic file */&lt;br /&gt;
    IIntuition-&amp;gt;ReleaseBitMapSource(source);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;loading picture file failed\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Define window areas =&lt;br /&gt;
&lt;br /&gt;
The existing clipping system has been expanded in order to be able to set the areas in a window that display contents, that is, that let the background show through. With ILayers-&amp;gt;InstallClipRegion() you could set even earlier rectangular areas in a window, in which a blit operation can draw data. This way, you can, for example, protect separate areas of a window too, if graphic data is larger than the window. However, in order to apply the ClipRect structure to an alpha channel you must use a private function of the layers. library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ILayers-&amp;gt;AllocClipRect()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is provided exclusively for this task. The counterpart to it is ILayers-&amp;gt;FreeClipRect(), that releases again the structure. The structure is already pre-initialised, so that only a few fields must be adjusted to your needs. On the one hand, you must store the graphic size in &amp;quot;bounds&amp;quot; and enter it in &amp;quot;BitMap&amp;quot;. The required memory area for it can be created with IGraphics-&amp;gt;AllocBitMap(). Then you must also blit the alpha-channel bitmap with IGraphics-&amp;gt;BltBitMapTags(). The only thing you need to keep in mind in this whole procedure is that you automatically de-allocate the entered BitMap when you allocate the ClipRect structure! In case you do not want to do it, you must first set the field on NULL and de-allocate the BitMap with IGraphics-&amp;gt;FreeBitMap(). These are the matching code rows for this part of the workshop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct ClipRect *ac;&lt;br /&gt;
&lt;br /&gt;
  /* allocate and initialise ClipRect structure */&lt;br /&gt;
  if((ac = ILayers-&amp;gt;AllocClipRect(&amp;amp;(scr-&amp;gt;LayerInfo))))&lt;br /&gt;
  {&lt;br /&gt;
    struct TagItem bmtags[3] = { { BMATags_RGBFormat, RGBFB_ALPHA8 },&lt;br /&gt;
                                 { BMATags_Friend, (ULONG)scr-&amp;gt;RastPort.BitMap },&lt;br /&gt;
                                 { TAG_END, 0 } };&lt;br /&gt;
&lt;br /&gt;
    /* alllocate Alpha-ClipRect and request memory for Bitmap */&lt;br /&gt;
    ac-&amp;gt;bounds.MinX = 0;&lt;br /&gt;
    ac-&amp;gt;bounds.MinY = 0;&lt;br /&gt;
    ac-&amp;gt;bounds.MaxX = width-1,&lt;br /&gt;
    ac-&amp;gt;bounds.MaxY = height-1;&lt;br /&gt;
    ac-&amp;gt;Next        = NULL;&lt;br /&gt;
    if((ac-&amp;gt;BitMap  = IGraphics-&amp;gt;AllocBitMap(width,height,8,BMF_DISPLAYABLE|BMF_CLEAR|BMF_CHECKVALUE,(struct BitMap *)bmtags)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Fill Alpha-ClipRect with Alpha-Bitmap */&lt;br /&gt;
      IGraphics-&amp;gt;BltBitMapTags(BLITA_Source,         alpha,&lt;br /&gt;
                               BLITA_SrcType,        BLITT_CHUNKY,&lt;br /&gt;
                               BLITA_SrcBytesPerRow, width,&lt;br /&gt;
                               BLITA_Dest,           ac-&amp;gt;BitMap,&lt;br /&gt;
                               BLITA_Width,          width,&lt;br /&gt;
                               BLITA_Height,         height,&lt;br /&gt;
                               TAG_DONE );&lt;br /&gt;
&lt;br /&gt;
      /* ... */&lt;br /&gt;
    }&lt;br /&gt;
    else IDOS-&amp;gt;Printf(&amp;quot;cannot allocate alpha mask\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* ClipRect deallocate; it also deallocates ac-&amp;gt;BitMap! */&lt;br /&gt;
    ILayers-&amp;gt;FreeClipRect(&amp;amp;(scr-&amp;gt;LayerInfo),ac);&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;cannot allocate cliprect\n&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Window show yourself =&lt;br /&gt;
&lt;br /&gt;
What is left is opening the window and blitting the graphic bitmap with IGraphics-&amp;gt;BltBitMapRastPort(). You will need for it the following code rows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  struct Window   *win;&lt;br /&gt;
&lt;br /&gt;
  /* Open window with the Alpha mask */&lt;br /&gt;
  if((win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                           WA_Width,       width,&lt;br /&gt;
                           WA_Height,      height,&lt;br /&gt;
                           WA_Borderless,  TRUE,&lt;br /&gt;
                           WA_AlphaClips,  ac,&lt;br /&gt;
                           ...&lt;br /&gt;
                           TAG_DONE)))&lt;br /&gt;
  {&lt;br /&gt;
    /* blit the graphic file into the window */&lt;br /&gt;
    IGraphics-&amp;gt;BltBitMapRastPort(bitmap, 0, 0, win-&amp;gt;RPort, 0, 0, width, height, MINTERM_ABC|MINTERM_ABNC);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you open the window, you must also enter the tag WA_AlphaClips. The window will be opened without a frame and without system symbols. They are not shown, but as soon as you activate the window, the frame elements are drawn with Intuition, at least as far as the alpha mask allows it. You can also target the alpha mask display. Just do not copy any image data into the Rastport after you have opened the window. The alpha mask does not only set the opaque and transparent points, but it can also be created gradually. Thus, even image parts can appear as half-transparent and let the background shine through. The &#039;partly transparent ball&#039; graphic shows the result.&lt;br /&gt;
&lt;br /&gt;
You should also keep in mind that the window may not be of the GimmeZeroZero type. In this case there are already internal clipping masks, which cannot be combined with the alpha-clipping.&lt;br /&gt;
&lt;br /&gt;
The program &amp;quot;BorderlessWindow&amp;quot; serves as a complete example for this topic showing all the mentioned functions in a practical interplay. A matching graphic is also included. Have fun experimenting with the source-code and looking for new possibilities!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* Michael Christoph&lt;br /&gt;
 * BorderlessWindow.c&lt;br /&gt;
 *&lt;br /&gt;
 * gcc BorderlessWindow.c -o BorderlessWindow -l auto&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/******************************* INCLUDES *************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;exec/libraries.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/blitattr.h&amp;gt;&lt;br /&gt;
#include &amp;lt;graphics/composite.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/intuition.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/bitmapshare.h&amp;gt;&lt;br /&gt;
#include &amp;lt;intuition/gui.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/layers.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
static const char *version USED = &amp;quot;\0$VER: BorderlessWindow 1.1 (22.07.2013) - (c) Jan.2009 by Meicky-Soft\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/******************************************************************************/&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
  int res = RETURN_FAIL;&lt;br /&gt;
&lt;br /&gt;
  struct RDArgs   *rda;&lt;br /&gt;
  struct Screen   *scr;&lt;br /&gt;
  struct Window   *win;&lt;br /&gt;
  struct ClipRect *ac;&lt;br /&gt;
  struct DrawInfo *dri;&lt;br /&gt;
  struct BitMap   *bitmap;&lt;br /&gt;
  struct BitMap   *alpha;&lt;br /&gt;
  APTR             source;&lt;br /&gt;
  APTR             instance;&lt;br /&gt;
  ULONG            width;&lt;br /&gt;
  ULONG            height;&lt;br /&gt;
  ULONG            specialfx;&lt;br /&gt;
&lt;br /&gt;
  enum { ARG_filename, ARG_pubscreen, ARG_MAX };&lt;br /&gt;
  ULONG args[ARG_MAX]={ (ULONG)&amp;quot;testpic.png&amp;quot;,0 };&lt;br /&gt;
&lt;br /&gt;
  /* AmigaOS 4.1 ist erforderlich */&lt;br /&gt;
  if(SysBase-&amp;gt;lib_Version &amp;gt;= 53)&lt;br /&gt;
  {&lt;br /&gt;
    /* Aufrufargumente auswerten */&lt;br /&gt;
    if((rda = IDOS-&amp;gt;ReadArgs(&amp;quot;FILENAME,PUBSCREEN/K&amp;quot;,(LONG*)args,NULL)))&lt;br /&gt;
    {&lt;br /&gt;
      /* Bildschirm auf dem die Anzeige erfolgt festlegen */&lt;br /&gt;
      if((scr = IIntuition-&amp;gt;LockPubScreen((STRPTR)args[ARG_pubscreen])))&lt;br /&gt;
      {&lt;br /&gt;
        /* sicherstellen dass der Bildschirm Alpha Composing unterstÂ¸tzt */&lt;br /&gt;
        if(( dri = IIntuition-&amp;gt;GetScreenDrawInfo(scr)))&lt;br /&gt;
        {&lt;br /&gt;
          IIntuition-&amp;gt;GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&amp;amp;specialfx,TAG_END);&lt;br /&gt;
          if(!specialfx) IDOS-&amp;gt;Printf(&amp;quot;WARNING: screen do not support alpha composing\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          IIntuition-&amp;gt;FreeScreenDrawInfo(scr,dri);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* die Grafik bei Bedarf in den Speicher laden (intern via Datatype) */&lt;br /&gt;
        if((source = IIntuition-&amp;gt;ObtainBitMapSource((STRPTR)args[ARG_filename],&lt;br /&gt;
                       BMS_DoShade, TRUE,&lt;br /&gt;
                       BMS_DoMask, TRUE,&lt;br /&gt;
                       TAG_END)))&lt;br /&gt;
        {&lt;br /&gt;
          /* eine Bitmap-Instance fÂ¸r den eigenen Bildschirm anfordern */&lt;br /&gt;
          if((instance = IIntuition-&amp;gt;ObtainBitMapInstance(source,scr,TAG_END)))&lt;br /&gt;
          {&lt;br /&gt;
            /* Bildinformationen und Biptmap erfragen */&lt;br /&gt;
            IIntuition-&amp;gt;BitMapInstanceControl(instance,&lt;br /&gt;
                                              BMICTRL_GetBitMap,   &amp;amp;bitmap,&lt;br /&gt;
                                              BMICTRL_GetAlphaMap, &amp;amp;alpha,&lt;br /&gt;
                                              BMICTRL_GetWidth,    &amp;amp;width,&lt;br /&gt;
                                              BMICTRL_GetHeight,   &amp;amp;height,&lt;br /&gt;
                                              TAG_END );&lt;br /&gt;
            if(bitmap &amp;amp;&amp;amp; alpha)&lt;br /&gt;
            {&lt;br /&gt;
              /* ClipRect Struktur anlegen und initialisieren */&lt;br /&gt;
              if((ac = ILayers-&amp;gt;AllocClipRect(&amp;amp;(scr-&amp;gt;LayerInfo))))&lt;br /&gt;
              {&lt;br /&gt;
                /* Alpha-ClipRect belegen und Speicher fÂ¸r Bitmap anfordern */&lt;br /&gt;
                ac-&amp;gt;bounds.MinX = 0;&lt;br /&gt;
                ac-&amp;gt;bounds.MinY = 0;&lt;br /&gt;
                ac-&amp;gt;bounds.MaxX = width-1,&lt;br /&gt;
                ac-&amp;gt;bounds.MaxY = height-1;&lt;br /&gt;
                ac-&amp;gt;Next        = NULL;&lt;br /&gt;
                if((ac-&amp;gt;BitMap  = IGraphics-&amp;gt;AllocBitMapTags(width, height, 8,&lt;br /&gt;
                                    BMATags_Displayable, TRUE,&lt;br /&gt;
                                    BMATags_Clear, TRUE,&lt;br /&gt;
                                    BMATags_PixelFormat, PIXF_ALPHA8,&lt;br /&gt;
                                    BMATags_Friend, scr-&amp;gt;RastPort.BitMap,&lt;br /&gt;
                                    TAG_END)))&lt;br /&gt;
                {&lt;br /&gt;
                  /* Alpha-ClipRect mit der Alpha-Bitmap fÂ¸llen */&lt;br /&gt;
                  IGraphics-&amp;gt;BltBitMapTags(BLITA_Source,         alpha,&lt;br /&gt;
                                           BLITA_SrcType,        BLITT_CHUNKY,&lt;br /&gt;
                                           BLITA_SrcBytesPerRow, width,&lt;br /&gt;
                                           BLITA_Dest,           ac-&amp;gt;BitMap,&lt;br /&gt;
                                           BLITA_Width,          width,&lt;br /&gt;
                                           BLITA_Height,         height,&lt;br /&gt;
                                           TAG_END );&lt;br /&gt;
&lt;br /&gt;
                  /* Rahmenloses Fenster Ëffnen */&lt;br /&gt;
                  if((win = IIntuition-&amp;gt;OpenWindowTags(NULL,&lt;br /&gt;
                                           WA_Left,        1580,&lt;br /&gt;
                                           WA_Top,         120,&lt;br /&gt;
                                           WA_Width,       width,&lt;br /&gt;
                                           WA_Height,      height,&lt;br /&gt;
                                           WA_CloseGadget, FALSE,&lt;br /&gt;
                                           WA_DragBar,     FALSE,&lt;br /&gt;
                                           WA_DepthGadget, FALSE,&lt;br /&gt;
                                           WA_SizeGadget,  FALSE,&lt;br /&gt;
                                           WA_Borderless,  TRUE,&lt;br /&gt;
                                           WA_ToolBox,     TRUE,&lt;br /&gt;
                                           WA_AlphaClips,  ac,&lt;br /&gt;
                                           WA_CustomScreen,scr,&lt;br /&gt;
                                           WA_Title,       &amp;quot;Borderless window&amp;quot;,&lt;br /&gt;
                                           WA_IDCMP,       IDCMP_MOUSEBUTTONS,&lt;br /&gt;
                                           TAG_END )))&lt;br /&gt;
                  {&lt;br /&gt;
                    /* das komplette Fenster als dragable definieren */&lt;br /&gt;
                    struct Gadget draggad = { NULL, 0,0, width,height,&lt;br /&gt;
                                              GFLG_GADGHNONE, 0, GTYP_WDRAGGING,&lt;br /&gt;
                                              NULL,NULL,NULL,0,0,0,NULL };&lt;br /&gt;
                    win-&amp;gt;FirstGadget = &amp;amp;draggad;&lt;br /&gt;
&lt;br /&gt;
                    /* die Grafikdatei in das Fenster blitten */&lt;br /&gt;
                    IGraphics-&amp;gt;BltBitMapRastPort(bitmap, 0, 0, win-&amp;gt;RPort,&lt;br /&gt;
                      0, 0, width, height, MINTERM_ABC | MINTERM_ABNC);&lt;br /&gt;
&lt;br /&gt;
                    /* warten auf CTRL-C zum Programm beenden */&lt;br /&gt;
                    IExec-&amp;gt;Wait(SIGBREAKF_CTRL_C);&lt;br /&gt;
&lt;br /&gt;
                    res = RETURN_OK;&lt;br /&gt;
&lt;br /&gt;
                    /* Fenster schlieï¬en */&lt;br /&gt;
                    IIntuition-&amp;gt;CloseWindow(win);&lt;br /&gt;
                  }&lt;br /&gt;
                  else IDOS-&amp;gt;Printf(&amp;quot;can not open window\n&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                else IDOS-&amp;gt;Printf(&amp;quot;can not allocate alpha mask\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
                /* ClipRect freigeben; gibt auch ac-&amp;gt;BitMap frei ! */&lt;br /&gt;
                ILayers-&amp;gt;FreeClipRect(&amp;amp;(scr-&amp;gt;LayerInfo),ac);&lt;br /&gt;
              }&lt;br /&gt;
              else IDOS-&amp;gt;Printf(&amp;quot;can not allocate cliprect\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            else IDOS-&amp;gt;Printf(&amp;quot;can not get (alpha) bitmaps\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            /* BitMap Instance freigeben */&lt;br /&gt;
            IIntuition-&amp;gt;ReleaseBitMapInstance(instance);&lt;br /&gt;
          }&lt;br /&gt;
          else IDOS-&amp;gt;Printf(&amp;quot;can not get instance of picture\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
          /* Grafikdatei freigeben */&lt;br /&gt;
          IIntuition-&amp;gt;ReleaseBitMapSource(source);&lt;br /&gt;
        }&lt;br /&gt;
        else IDOS-&amp;gt;Printf(&amp;quot;loading picture file failed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        /* Bildschirm freigeben */&lt;br /&gt;
        IIntuition-&amp;gt;UnlockPubScreen(NULL,scr);&lt;br /&gt;
      }&lt;br /&gt;
      else IDOS-&amp;gt;Printf(&amp;quot;can not lock pubscreen\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      IDOS-&amp;gt;FreeArgs(rda);&lt;br /&gt;
    }&lt;br /&gt;
    else { IDOS-&amp;gt;PrintFault(IDOS-&amp;gt;IoErr(),&amp;quot;BorderlessWindow&amp;quot;); res = RETURN_ERROR; }&lt;br /&gt;
  }&lt;br /&gt;
  else IDOS-&amp;gt;Printf(&amp;quot;Program requires AmigaOS 4.1\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;
Grab the test graphic here: [[Media:testpic.png|testpic.png]].&lt;br /&gt;
&lt;br /&gt;
[[File:AF108_borderless_grab.png|frame|center]]&lt;br /&gt;
&lt;br /&gt;
= Outlook =&lt;br /&gt;
&lt;br /&gt;
You can look forward to the next issue, in which we will deal with datatypes. Do not expect any novelties regarding the program, but for the sake of completeness we should mention these options as well.&lt;br /&gt;
&lt;br /&gt;
[[File:AF108_FensterLoch_grab.png|frame|center]]&lt;br /&gt;
With a hole in a window you can create funny effects, especially if you move the window around.&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>Frédéric Khannouf</name></author>
	</entry>
</feed>