@DATABASE AmigaMail
@NODE MAIN "XI-1: Introduction to Commodities Exchange"
@TOC "Table_of_Contents/XI"
by John Orr


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 "stream".  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.

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.
"Hot key" programs, shell pop-up programs, and screen blankers all
commonly use custom input handlers to monitor u ser input before it
gets to the Intuition input handler.

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 res ult 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.  The custom
input handler needs to take its next ev olutionary step.

Commodities Exchange is that step.  It provides a simple, standardized
way to program and control custom input handlers.  It is divided into
three parts: an Exec library, a controller program, and a scanned
library.

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 (Commoditie s Exchange Message)
corresponding to the input event, and diverts the CxMessage through
the network of Commodities Exchange input handlers.  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's input stream as input events.

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 CxMess ages, 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.

The controller program is called Commodities 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 commodit y 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.

The third component of Commodities Exchange is its scanned library
functions.  These functions are part of the amiga.lib scanned library.
They do a lot of the work involved with parsing command lines and tool
types.

Commodities Exchange is ideal for programs like hot keys/pop ups,
screen blankers, and mouse blankers that need to monitor all user
input.  Commodities Exchange should not be used as an alternate
method of receiving user input for an application.  Other a
pplications 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.

@{" CxObjects " link XI-1-1}              @{" Filter CxObjects " link XI-1-5}       @{" Uniqueness " link XI-1-9}
@{" Installing a Broker " link XI-1-2}    @{" Senders CxObjects " link XI-1-6}      @{" DeleteCxObjAll() " link XI-1-10}
@{" CxMessages " link XI-1-3}             @{" Translate CxObjects " link XI-1-7}    @{" Other CxObjects " link XI-1-11}
@{" Tool Types " link XI-1-4}             @{" CxObject Errors " link XI-1-8}        @{" IX " link XI-1-12}

@ENDNODE

@NODE XI-1-1 "CxObjects"
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 "arrives" 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.

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 d
efault, all CxObjects except the type called brokers are created in
the active state.

  @{" Figure 1: The Commodities Network " link "XI-1/cx.ilbm/MAIN"}

@ENDNODE

@NODE XI-1-2 "Installing a Broker"
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 a
broker.  The only thing a broker CxObject does is div ert 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's personal list.

Program listing 1, @{"Broker.c" link XI-1/Broker.c/MAIN}, (located at the end of this article) 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 p rocess
commands from the controller program.

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.

    CxObj *CxBroker(struct NewBroker *nb, long *error);

CxBroker()'s first argument is a pointer to a NewBroker structure:

    struct NewBroker {
       BYTE     nb_Version;  /* There's an implicit pad byte after */
       BYTE     *nb_Name;    /*     this BYTE */
       BYTE     *nb_Title;
       BYTE     *nb_Descr;
       SHORT    nb_Unique;
       SHORT    nb_Flags;
       BYTE     nb_Pri; /* There's an implicit pad byte after this BYTE */
       struct   MsgPort   *nb_Port;
       WORD     nb_ReservedChannel;
    };

Commodities Exchange gets all the information it needs about the
broker from this structure.  NewBroker's nb_Version field contains
the version number of the NewBroker structure.  This should be set to
NB_VERSION which is defined in <libraries/commodities .h>.  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.

The nb_Pri field contains the broker's priority.  Commodities Exchange
inserts the broker into the master list based on this number.  Higher
priority brokers get CxMessages before lower priority brokers.

CxBroker()'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
codes from <libraries/commodities.h>:

    CBERR_OK        0        /* No error                         */
    CBERR_SYSERR    1        /* System error , no memory, etc    */
    CBERR_DUP       2        /* uniqueness violation             */
    CBERR_VERSION   3        /* didn't understand nb_VERSION     */

@ENDNODE

@NODE XI-1-3 "CxMessages"
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's
nb_Port field points to this message port.  A c ommodity can tell the
difference between the two types of CxMessages by calling the
CxMsgType() function.

    ULONG CxMsgType(CxMsg *cxm);
    UBYTE *CxMsgData(CxMsg *cxm);
    LONG  CxMsgID(CxMsg *cxm);

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 is an InputEvent structure.  By using the CxMsgData()
function, a commodity can obtain a pointer to the correspond ing
InputEvent of a CXM_IEVENT.  Commodities Exchange does not give an ID
to the CXM_IEVENT CxMessages it introduces to the Commodities network,
but certain CxObjects can assign an ID to a CXM_IEVENT CxMessage.

CXM_COMMAND CxMessages generally do not use the data pointer.
Instead, they use an ID.  They use this ID to specify the command
passed to a commodity.  CxMsgID() extracts the ID from a CxMessage.

    oldactivationvalue = LONG ActivateCxObj(CxObj *co,
                           long newactivationvalue);

After successfully completing the initial set up and activating the
broker using ActivateCxObj(), a commodity can begin its input
processing loop.  The example @{"Broker.c" link XI-1/Broker.c/MAIN} 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.

Notice that @{"Broker.c" link XI-1/Broker.c/MAIN} uses Ctrl-C as a break key.  This is a change
from 1990 Atlanta DevCon Notes on Commodities Exchange which said to
use Ctrl-E.  The break key for any commodity should be Ctrl-C.

The commands that a commodity can receive from the controller program
(as defined in <libraries/commodities.h>) are:

    CXCMD_DISABLE     /* please disable yourself       */
    CXCMD_ENABLE      /* please enable yourself        */
    CXCMD_KILL        /* go away for good              */
    CXCMD_APPEAR      /* open your window, if you can  */
    CXCMD_DISAPPEAR   /* hide your window              */

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 "Hi de".  These gadgets
are ghosted in @{"Broker.c" link XI-1/Broker.c/MAIN} because it has no window (It doesn'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 gadge ts.  When CxBroker() sets up a broker,
it looks at the NewBroker.nb_Flags field to see if the COF_SHOW_HIDE
bit (from <libraries/commodities.h>) is set.  If it is, the "Show"
and "Hide" gadgets for this broker will be selectable. Otherwise they
are g hosted and disabled.

Shutting down a commodity is easy.  After replying to all CxMessages
waiting at the broker's message port, a commodity can delete its
CxObjects.  DeleteCxObj() removes a single CxObject from the
Commodities network.

    void DeleteCxObj(CxObj *co);

@ENDNODE

@NODE XI-1-4 "Tool Types"
A goal of Commodities Exchange is to improve user control over input
handlers.  One way in which it accomplishes this is through the use of
standard ToolTypes.  The user will expect commodities to recognize the
set of standard tool types:

    CX_PRIORITY
    CX_POPUP
    CX_POPKEY

CX_PRIORITY lets the user set the priority of a commodity.  The string
"CX_PRIORITY=" 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.

CX_POPUP and CX_POPKEY are only relevant to commodities with a window.
The string "CX_POPUP=" should be followed by a "yes" or "no", telling
the commodity if it should or shouldn't show its window when it is
first launched.  CX_POPKEY is followed by a st ring describing the key
to use as a hot key for "popping up" the commodity's window.  The
description string for CX_POPKEY describes an input event.  The
specific format of the string is discussed later in this article.

Commodities Exchange's support library functions simplify parsing
arguments from either the Workbench or a CLI.  A Workbench launched
commodity gets its arguments directly from the tool types in the
commodity's icon.  CLI launched commodities get their ar guments from
the command line, but these arguments look exactly like the tool types
from the commodity's icon.  For example, the following command line
sets the priority of a commodity called HotKey to 5:

    HotKey "CX_PRIORITY=5"

Commodities Exchange has several support library functions used to
parse arguments:

    tooltypearray = char **ArgArrayInit(int argc, char **argv);
    void ArgArrayDone(void);
    tooltypevalue = char *ArgString(char **tooltypearray,
                                char *tooltype, char *defaultvalue);
    tooltypevalue = LONG *ArgInt(char **tooltypearray,
                                char *tooltype, LONG defaultvalue);

ArgArrayInit() initializes a tool type array of strings which it
creates from the startup arguments, argc and argv.  It doesn't matter
if these startup arguments come from the Workbench or from a CLI,
ArgArrayInit() can extract arguments from either source.  Because
ArgArrayInit() uses some icon.library functions, a commodity is
responsible for opening that library before using the function.

ArgArrayInit() also uses some resources that must be returned to the
system when the commodity is done.  ArgArrayDone() performs this clean
up.  Like ArgArrayInit(), ArgArrayDone() uses icon.library, so the
library has to remain open until ArgArrayDone() is finished.

The support library has two functions that use the tool type array
set up by ArgArrayInit(), ArgString() and ArgInt().  ArgString()
scans the tool type array for a specific tool type.  If successful,
it returns a pointer to the value associated with that tool type.  If
it doesn't find the tool type, it returns the default value passed to
it. ArgInt() is similar to ArgString().  It also scans the
ArgArrayInit()'s tool type array, but it returns a LONG rather than a
string pointer.  ArgInt() extracts the i nteger value associated with
a tool type, or, if that tool type is not present, it returns the
default value.

Of course, these tool type parsing functions are not restricted to the
standard Commodities Exchange tool types.  A commodity that requires
any arguments should use these functions along with custom tool types
to obtain these values.  Because the Commodit ies Exchange standard
arguments are processed as tool types, the user will expect to enter
other arguments as tool types.

@ENDNODE

@NODE XI-1-5 "Filter CxObjects"
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.

    CxObj *CxFilter(char *descriptionstring);

The C macro CxFilter() (defined in <libraries/commodities.h>) 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 a description
string (CX_POPKEY uses the same description string format):

    [class]  ( [-] (qual | syn) )*  [ [-] upstroke]  [highmap |ANSICode]
             the * means zero or more occurances of ( [-] ( qual | syn ) )

where:

class can be any one of the strings in the table below.  Each string
corresponds to a class of input event (shown below).  The classes are
defined in <devices/inputevent.h>.  Commodities Exchange will assume
the class is rawkey if the class is not explici tly stated.

    "rawkey"        IECLASS_RAWKEY
    "rawmouse"      IECLASS_RAWMOUSE
    "event"         IECLASS_EVENT
    "pointerpos"    IECLASS_POINTERPOS
    "timer"         IECLASS_TIMER
    "newprefs"      IECLASS_NEWPREFS
    "diskremoved"   IECLASS_DISKREMOVED
    "diskinserted"  IECLASS_DISKINSERTED


qual is one of the strings in the table below.  Each string
corresponds to an input event qualifier (followed by its ID from
<devices/inputevent.h>).  A dash preceding the qualifier string tells
the filter object not to care if that qualifier is present i n the
input event.  Notice that there can be more than one qual (or none at
all) in the input description string.

    "lshift"            IEQUALIFIER_LSHIFT
    "rshift"            IEQUALIFIER_RSHIFT
    "capslock"          IEQUALIFIER_CAPSLOCK
    "control"           IEQUALIFIER_CONTROL
    "lalt"              IEQUALIFIER_LALT
    "ralt"              IEQUALIFIER_RALT
    "lcommand"          IEQUALIFIER_LCOMMAND
    "rcommand"          IEQUALIFIER_RCOMMAND
    "numericpad"        IEQUALIFIER_NUMERICPAD
    "repeat"            IEQUALIFIER_REPEAT
    "midbutton"         IEQUALIFIER_MIDBUTTON
    "rbutton"           IEQUALIFIER_RBUTTON
    "leftbutton"        IEQUALIFIER_LEFTBUTTON
    "relativemouse"     IEQUALIFIER_RELATIVEMOUSE


syn is one of the strings in the table below.  These strings act as
synonyms for groups of qualifiers.  Each string below is followed by
its identifier from <libraries/commodities.h>.  A dash preceding the
synonym string tells the filter object not to car e if that qualifier
is present in the input event.  Notice that there can be more than one
syn (or none at all) in the input description string.

    "shift"     IXSYM_SHIFT    /* look for either shift key */
    "caps"      IXSYM_CAPS     /* look for either shift key or capslock */
    "alt"       IXSYM_ALT      /* look for either alt key */


upstroke is the literal string "upstroke".  If this string is absent,
the filter considers only downstrokes.  If is present alone, the
filter considers only upstrokes.  If preceded by a dash, the filter
considers both upstrokes and downstrokes.


highmap is one of the following strings:

    "space", "backspace", "tab", "enter", "return", "esc", "del", "up",
    "down", "right", "left", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
    "f8", "f9", "f10", "help".


ANSICode is a single character (for example `a`) that Commodities
Exchange looks up in the system default keymap.


The following are some example description strings:

    "rawkey lshift alt f2"    Function key f2 with the left shift and
                                either alt key pressed.
    "-shift -alt -control a"  The key that produces an 'a' with or
                                without any shift, alt, or control keys
                                pressed.
    "rawmouse rbutton"        A mouse move with the right mouse button
                                 down.
    "timer"                   A timer event.

As of commodities.library version 37.3, ParseIX(), the function used
to parse input description strings, does not parse certain classes
correctly.  Only the rawkey, diskremoved, and newprefs classes are
properly parsed.  For the moment, any commodity that needs to look
for the other classes must directly use Commodities Exchange's
internal format for input event descriptions, input expressions or
IX, discussed later in this article.

A filter has to be inserted into the Commodities network before it can
process any CxMessages.  AttachCxObj() adds a CxObject to the personal
list of another commodity.  The @{"HotKey.c" link XI-1/HotKey.c/MAIN} example uses this function to
attach its filter to a broker.

    void AttachCxObj(CxObj *HeadObj, CxObj *co);

This function uses the Exec function AddTail() to add the CxObject to
the end of HeadObj's personal list.  The position of a CxObject list
determines which CxObject gets CxMessages first.  Other functions
exist to add CxObjects at different points in the list (see the
commodities.doc Autodoc for details).

@ENDNODE

@NODE XI-1-6 "Senders CxObjects"
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's personal list.  The filter
does this without letting the commodity know what happened.  The
sender CxObject is used to notify a commodity that it received a
CxMessage.  CxSender() is a macro that creates a sender CxObject.

    senderCxObj = CxObj *CxSender(struct MsgPort *senderport, LONG cxmID);

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 commod ity's broker and
sender(s) to share an Exec message port.  The @{"HotKey.c" link XI-1/HotKey.c/MAIN} 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.

A sender does several things when it receives a CxMessage.  First, it
duplicates the CxMessage's corresponding input event and creates a new
CxMessage.  Then, it points the new CxMessage's data field to the copy
of the input event and sets the new CxMessa ge's ID to the ID passed
to CxSender().  Finally, it sends the new CxMessage to the port passed
to CxSender(), asynchronously.

Because HotKey uses only one message port between its broker and
sender object, it has to extract the CxMessage'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's ID to the sender'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.

Although HotKey doesn'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 CxMessa ge reply.  Note
that it does not make any sense to modify the input event copy.

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's (or some other filtering
CxObject's) personal list. A commodity should not attach a CxObject
to a sender as a sender ignores any CxObjects in its personal list.

@ENDNODE

@NODE XI-1-7 "Translate CxObjects"
Normally, after a commodity processes a hot key input event, it needs
to eliminate the input event.  Other commodities may need to replace
an input event with a different one.  The translate CxObject can be
used for these purposes.

    translateCxObj = CxObj *CxTranslate(struct InputEvent *newinputevent);

The macro CxTranslate() creates a new translate CxObject.
CxTranslate()'s only argument is a pointer to a chain of one or more
InputEvent structures.  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 CxTranlate() (newinputevent from the function prototype above), in
place of the deleted input event.  The trans lator introduces the new
input event after the Commodities Exchange input handler in the
input.device's input stream.  Any CxObjects that follow the translator
in the Commodities network will see neither the deleted CxMessage nor
the new input event.

A translator is normally attached to some kind of filtering CxObject.
If it wasn'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 does n't serve any purpose to
add any to it.

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.

@ENDNODE

@NODE XI-1-8 "CxObject Errors"
A Commodities Exchange function that acts on a CxObject records errors
in the CxObject's accumulated error field.  The function CxObjError()
returns a CxObject's error field.

    co_errorfield = LONG CxObjError( CxObj *co );

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.

    COERR_ISNULL             CxObjError() was passed a NULL.
    COERR_NULLATTACH         Someone tried to attach a NULL CxObj to this
    CxObj.  COERR_BADFILTER  This CxObj is a filter and currently has an
                               invalid filter description.
    COERR_BADTYPE            Someone tried to perform a type specific
                               function on the wrong kind of CxObject
                               (for example calling SetFilter() on a
                               sender CxObject).

The remaining bits are reserved by Commodore for future use.  HotKey
checks the error field of its filter CxObject to make sure the filter
is valid.  HotKey does not need to check the other objects with
CxObjError() because it already makes sure that thes e other objects
are not NULL, which is the only other kind of error the other objects
can cause in this situation.

@ENDNODE

@NODE XI-1-9 "Uniqueness"
When a commodity opens its broker, it can ask Commodities Exchange not
to launch another broker with the same name (nb_Name).  This
uniqueness feature's purpose 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.

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():

    NBU_UNIQUE      bit 0
    NBU_NOTIFY      bit 1

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 CXCM D_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 th e two from coexisting.  For this
reason, a commodity should not use a name that is likely to be in use
by other commodities (like "filter" or "hotkey").

When HotKey gets a CXCMD_UNIQUE CxMessage, it shuts itself down.
HotKey and all the windowless commodities that come with the 2.0
Workbench shut themselves down when they get a CXCMD_UNIQUE CxMessage.
Because the user will expect all windowless commodit ies to work this
way, all windowless commodities should follow this standard.

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 "Show" gadget in the
controller program's window.  A windowed commodity should mimic
conventions set by existing windowed system commodities, and move its
window to the front of the display.

@ENDNODE

@NODE XI-1-10 "DeleteCxObjAll()"
Cleaning up after a commodity requires deleting its CxObjects.  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.

    void DeleteCxObjAll( CxObj *delete_co );

HotKey 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.

@ENDNODE

@NODE XI-1-11 "Other CxObjects"
There are three remaining CxObjects:

    signalCxObj = CxObj  *CxSignal(struct Task *,LONG signal);
    debugCxObj  = CxObj  *CxDebug(LONG ID);
    customCxObj = CxObj  *CxCustom(LONG *customfunction(), LONG  cxmID);

When a signal CxObject receives a CxMessage, it signals a task.  When
a debug CxObject receives a CxMessage, it sends debugging information
to the serial port using kprintf().  Note that the debug CxObj did not
work before V37.

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.  The custom CxObject is probably the most dangerous of the
CxObjects to use.  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 regist ers upon entry.
Any function passed to CxCustom() should be very quick and very
simple, with a minimum of stack usage.

Commodities Exchange calls a custom CxObject's function as follows:

    void customfunction(CxMsg *cxm, CxObj *customcxobj);

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 cu stom function,
Commodities Exchange sets the CxMessage's ID to the ID passed to
CxCustom().

@ENDNODE

@NODE XI-1-12 "IX"
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
<libraries/commodities.h>:

    #define IX_VERSION   2

    struct InputXpression {
       UBYTE   ix_Version;     /* must be set to IX_VERSION  */
       UBYTE   ix_Class;       /* class must match exactly   */
       UWORD   ix_Code;
       UWORD   ix_CodeMask;    /* normally used for UPCODE   */
       UWORD   ix_Qualifier;
       UWORD   ix_QualMask;
       UWORD   ix_QualSame;    /* synonyms in qualifier      */
       };
    typedef struct InputXpression IX;

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 (as
defined in <devices/inputevent.h>) of the particular class of i nput
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 an d down presses of a particular key.

IX'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_CodeMask indicate whic h bits are
relevant when comparing ix_Qualifier to ie_Qualifier.  The ix_QualSame
field tells Commodities Exchange that certain qualifiers are
equivalent.  The bits of this field can be set to any of the following
values:

    #define IXSYM_SHIFT  1  /* left- and right- shift are equivalent    */
    #define IXSYM_CAPS   2  /* either shift or caps lock are equivalent */
    #define IXSYM_ALT    4  /* left- and right- alt are equivalent      */

For example, the input description string

    "rawkey -caps -lalt -relativemouse -upstroke ralt tab"

matches a tab upstroke or downstroke with the right alt key pressed
whether or not the left alt, either shift, or the capslock keys are
down.  The following IX structure corresponds to that input
description string:


IX ix = {
    IX_VERSION,             /* The version */
    IECLASS_RAWKEY,         /* We're looking for a RAWKEY event */
    0x42,                   /* The key the usa0 keymap maps to a tab */
    0x00FF & (~IECODE_UP_PREFIX), /* We want up and down key presses */
    IEQUALIFIER_RALT,             /* The right alt key must be down */
    0xFFFF & ~(IEQUALIFIER_LALT | IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT |
                IEQUALIFIER_CAPSLOCK | IEQUALIFIER_RELATIVEMOUSE),
                /* don't care about left alt, shift, capslock,
                   or relativemouse qualifiers */
    IXSYM_CAPS  /* The shift keys and the capslock
                   key qualifers are all equivalent */
};


The CxFilter() macro only accepts a description string to describe an
input event.  A commodity can change this filter, however.

    void SetFilter( CxObj *filter, char *descrstring );
    void SetFilterIX( CxObj *filter, IX *ix );

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's error code with CxObjError() to make sure the
change worked.

    errorcode = LONG ParseIX( char *descrstring, IX *ix );

The function ParseIX() parses an input description string and
translates it into an IX input expression.  Commodities Exchange uses
ParseIX() to convert the description string in CxFilter() to an IX
input expression.  As was mentioned previously, as of co
mmodities.library version 37.3, ParseIX() does not work with certain
kinds of input strings.

This article is by no means an exhaustive description of Commodities
Exchange.  For more information, see the Commodities Exchange Autodocs
(commodities.doc for commodities.library and cx.doc for the macros and
linker library functions) and the sample commodities from the 1990
Atlanta DevCon disks.

@ENDNODE
