HomeXCONTROLXCONTROLProgramming guidelines

9.1 About XCONTROL

XControl is a modular (extendable) control field desk accessory that was first shipped by Atari with the TT030 and Mega-STE series of computers.

The individual modules are files with the extension '.CPX' (Control Panel eXtension), and XControl itself can be taken as the control program for these modules.

Important: XControl should be used only as a tool for configuration dialogs, and not misused for other purposes.

The communication between XControl and its modules takes place via two structures that are denoted XCPB and CPXINFO. The former makes some flags as well as a whole string of help functions available.

At system start-up XControl loads all available CPX headers, using up to 512 bytes of system memory per header; provided the corresponding flag is set in the header, each of the CPX files is called once for initialization. One can specify for each individual module whether it is to be loaded as resident or not (this can be changed also with the bundled configuration CPX). In addition it is possible to write CPX modules that set only certain values; these so-called 'set only' modules are called only during booting or renewed loading of the CPX modules with XControl, and simply return a NULL-pointer during the initialization.

As soon as a user selects a CPX module, XControl loads it into memory and calls the function cpx_init. Subsequently cpx_call is called, for which essentially the module itself now takes control.

One can differentiate between a Form CPX and an Event CPX. The former are relatively simple to program, but offer only limited flexibility. The latter are more flexible, since they evaluate the AES events directly. All the CPX modules supplied with XControl 1.0 are Form CPX files, from which one can deduce that Form CPXs suffice in most cases.

The following terminology applies for the filenames of CPX modules:

Suffix Meaning
*.CP Standard CPX without header
*.CPX Standard CPX, ready for use
*.CPZ Inactive CPX (deactivated by XControl)
*.HDR Header for the CPX file
*_R.CPX Resident CPX file
*_S.CPX Set-only CPX file

The format of a CPX file is very similar to that of a normal program. It consists of a 512-byte header and the remaining file content that is almost a normal GEMDOS program file, including a standard 28-byte GEMDOS header.

typedef struct
{
    uint16_t magic;                 /* Magic constant = 100   */

    struct
    {
        unsigned reserved : 13;  /* Reserved                */
        unsigned resident :  1;  /* RAM-resident if set     */
        unsigned bootinit :  1;  /* Boot initialize if set  */
        unsigned setonly  :  1;  /* Set-only CPX if set     */

    } flags;

    int32_t  cpx_id;                /* Unique CPX ID                   */
    uint16_t cpx_version;           /* CPX version number              */
    int8_t   i_text[14];            /* Icon text                       */
    uint16_t sm_icon[48];           /* Icon bitmap (32*24 pixel)       */
    uint16_t i_color;               /* Icon colour                     */
    int8_t   title_text[18];        /* Name of the CPX (16 chars. max) */
    uint16_t t_color;               /* Text colour                     */
    int8_t   buffer[64];            /* Non-volatile buffer             */
    int8_t   reserved[306];         /* Reserved                        */

} CPXHEAD;

Some notes about the header:

While programming a CPX module one should note some subtleties: As such a module has no non-volatile memory available (apart from 64 bytes), nothing is permitted that reserves fixed memory in any way. In particular, all variable contents are lost when leaving the CPX as a rule!

Hence one should:

For the programming of a CPX module one can fall back on the functions from the following categories:

Note: There are several alternatives to XCONTROL, which is not being developed by Atari any more. One that is particularly recommended is COPS (COntrol Panel Server, which not only permits simultaneous opening of any mumber of CPX modules, but also allows control fields with a larger working area than XCONTROL.

Other alternatives are ZCONTROL and a supplementary function of the Freedom2 file-selector.

9.1.1 CPX programming guidelines

When programming a CPX module one should keep to the following rules if possible:

9.1.2 CPX functions

cpx_button Mouse button event
cpx_call Activation routine
cpx_close Close event
cpx_draw Redraw event
cpx_hook Preemption hook
cpx_init Initialization
cpx_m1 Mouse rectangle event
cpx_m2 Mouse rectangle event
cpx_key Keyboard event
cpx_timer Timer event
cpx_wmove Window movement

9.1.2.1 cpx_button

Name: »cpx_button« - Mouse button event.
Declaration VOID cdecl (*cpx_button) (MRETS *mrets, int16_t nclicks, int16_t *event);
Description The call cpx_button is made when a mouse button event occurs. The following apply:
Parameter Meaning
mrets Mouse parameter for event
nclicks Number of mouse clicks
event Is to be set to the value 1 if the CPX is to be quit
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_key   cpx_timer

9.1.2.2 cpx_call

Name: »cpx_call« - Calling of the CPX module.
Declaration int16_t cdecl (*cpx_call) ( GRECT *work );

int16_t cdecl (*cpx_call) ( GRECT *work, DIALOG *dialog );
Description The call cpx_call is made when the user has selected the corresponding module. The following apply:
Parameter Meaning
work Rectangle with the coordinates of the XControl window
dialog Pointer to a window dialog


Note: The second calling procedure is only available under COPS. The parameter dialog in this case contains a pointer to the window dialog structure. The dialog is opened by COPS after the cpx_init with the aid of wdlg_create and wdlg_open. The parameter work and the object tree lie outside the visible screen area up to the first call of Xform_do or up to the return from the cpx_call function.
Return value: The function returns one of the following values:

0 = End of processing
<> 0 = CPX should continue to be processed
Group: CPX functions
See also: XCONTROL

9.1.2.3 cpx_close

Name: »cpx_close« - Event for closing the CPX window.
Declaration VOID cdecl (*cpx_close) (int16_t flag);
Description The call cpx_close serves for closing the CPX module. The following apply:
Parameter Meaning
flag Reason for closing:
0 = AC_CLOSE message
<> 0 = WM_CLOSE message


Note: The function is called for every AC_CLOSE or WM_CLOSE message. The CPX should then immediately release all memory reserved for it. The function must be implemented for every Event CPX. AC_CLOSE is to be evaluated as 'Cancel', WM_CLOSE as 'OK'.
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_init   XCONTROL

9.1.2.4 cpx_draw

Name: »cpx_draw« - Event for redrawing the CPX window.
Declaration VOID cdecl (*cpx_draw) (GRECT *clip);
Description The call cpx_draw serves for redrawing parts of the CPX window. The following apply:
Parameter Meaning
clip Region to be redrawn, which is also needed as a passing parameter for GetFirstRect


Note: The required rectangle list must be obtained with GetFirstRect and GetNextRect.
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_wmove   GetFirstRect   GetNextRect

9.1.2.5 cpx_hook

Name: »cpx_hook« - Preemption hook.
Definiton int16_t cdecl (*cpx_hook) (int16_t event, int16_t *msg, MRETS *mrets, int16_t *key, int16_t *nclicks);
Description The call cpx_hook is made immediately after evnt_multi, so before XControl processes the event. The following apply:
Parameter Meaning
event Occurring events
msg Event buffer
mrets Mouse parameters
key Keypress
nclicks Number of mouse clicks
Return value: The function returns one of the following values:

0 = Continue processing events
<> 0 = Break off processing events
Group: CPX functions
See also: cpx_button   cpx_draw   cpx_key   cpx_m1   cpx_m2   cpx_timer   cpx_wmove

9.1.2.6 cpx_init

Name: »cpx_init« - Initialization of the CPX.
Declaration CPXINFO * cdecl cpx_init (XCPB *xcpb);

CPXINFO* cdecl cpx_init (XCPB *xcpb, int32_t magic, int32_t version );
Description The call cpx_init serves for initializing the CPX. The following applies:

Parameter Meaning
xcpb Pointer to the XCPB structure of XControl


Note: The function must be placed at the start of the TEXT segment of the CPX file, and is called during XControl initialization as well as on activating the CPX.

With the aid of the second calling procedure one can ascertain from the parameters magic and version whether the CPX is running under XCONTROL or COPS. The following routine may be used:
int16_t is_COPS ( int32_t magic, int32_t version )
{
   if ((magic == 'COPS') && (version >= 0x10000L))
      return (TRUE);      /* COPS */
   else return (FALSE);   /* XCONTROL */
}


If COPS is recognized, the CPX can draw an object tree with a size up to 512*384 pixels and pass it to the function Xform_do.
Return value: The function returns one of the following values:

NULL : 'Set-only' CPX
Else : Pointer to the CPXINFO structure of the CPX
Group: CPX functions
See also: cpx_close   XCONTROL

9.1.2.7 cpx_m1

Name: »cpx_m1« - Event 1 for a mouse rectangle.
Declaration VOID cdecl (*cpx_m1) (MRETS *mrets, int16_t *event);
Description The call cpx_m1 is made when the mouse pointer enters or leaves a certain area. The following apply:
Parameter Meaning
mrets Mouse parameters
event Set to the value 1 when the CPX is to be exited
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_m2   XCONTROL

9.1.2.8 cpx_m2

Name: »cpx_m2« - Event 2 for a mouse rectangle.
Declaration VOID cdecl (*cpx_m2) (MRETS *mrets, int16_t *event);
Description The call cpx_m2 is made when the mouse pointer enters or leaves a certain area. The following apply:
Parameter Meaning
mrets Mouse parameters
event Set to the value 1 when the CPX is to be exited
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_m1   XCONTROL

9.1.2.9 cpx_key

Name: »cpx_key« - Event for a key-press.
Declaration VOID cdecl (*cpx_key) (int16_t kstate, int16_t key, int16_t *event);
Description The call cpx_key is made when a keyboard vvent has occurred. The following apply:
Parameter Meaning
kstate Status of the 'special' keys ([Alternate], [Control], [Shift], etc.)
key Triggering key:
High byte : Scancode of the key
Low byte : ASCII-code of the key
event Set to the value 1 when the CPX is to be exited
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_button   cpx_timer

9.1.2.10 cpx_timer

Name: »cpx_timer« - Timer event.
Declaration VOID cdecl (*cpx_timer) (int16_t *event);
Description The call cpx_timer is made when a timer event has occurred. The following applies:
Parameter Meaning
event Set to the value 1 when the CPX is to be exited


Note: Timer events are not supported by Form CPXs.
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_button   cpx_key   XCONTROL

9.1.2.11 cpx_wmove

Name: »cpx_wmove« - Movement of the XControl window.
Declaration VOID cdecl (*cpx_wmove) (GRECT *work);
Description The call cpx_wmove is made when the user moves the XControl window. The following applies:

Parameter Meaning
work New coordinates of the XControl window
Return value: The function does not return a result.
Group: CPX functions
See also: cpx_draw   XCONTROL

9.1.3 XCONTROL functions

CPX_Save Saves defaults
Get_Buffer Gets buffer
getcookie Gets cookie variables
GetFirstRect Gets first rectangle from list
GetNextRect Gets next rectangle from list
MFsave Saves/restores mouse form
Popup Popup menu management
rsh_fix Converts object tree
rsh_obfix Converts object from character to pixel display
Set_Evnt_Mask Sets event mask
Sl_arrow Slider arrow handling
Sl_dragx Slider drag movement (horizontal)
Sl_dragy Slider drag nmovement (vertical)
Sl_size Sets slider size
Sl_x Positions a slider (horizontal)
Sl_y Positions a slider (vertical)
Xform_do Form management
XGen_Alert Displays an alert box.

9.1.3.1 CPX_Save

Name: »CPX_Save« - Save defaults.
Declaration int16_t cdecl (*CPX_Save) (VOID *ptr, int32_t num);
Description The call CPX_Save permits saving the default settings of a CPX. The following apply:

Parameter Meaning
ptr Address of the data to be saved
num Number of bytes to be saved


Note: XControl saves the settings in the DATA segment of the CPX. Thus developers must themselves ensure that there is sufficient free space in the DATA segment. This is done with the data field 'SAVE_VARS' in CPXSTART.S.
Return value: The function returns one of the following values:

0 : An error has arisen
<> 0 : No eror has arisen
Group: XCONTROL functions
See also: XCONTROL

9.1.3.2 Get_Buffer

Name: »Get_Buffer« - Get buffer.
Declaration VOID cdecl (*Get_Buffer) (VOID);
Description The call Get_Buffer obtains the address of a 64-byte sized resident memory block.

Note: In this memory the CPX can save the contents of write-only registers if TOS offers no functions for interrogation (example: window colours). One has to emphasise again that any other memory allocated to a CPX is volatile!
Return value: The function returns a pointer to the memory block address.
Group: XCONTROL functions
See also: XCONTROL

9.1.3.3 getcookie

Name: »getcookie« - Get cookie variables.
Declaration int16_t cdecl (*getcookie) (int32_t cookie, int32_t *p_value);
Description The call getcookie searches for a cookie, and obtains its value. The following apply:
Parameter Meaning
cookie Cookie variable
p_value Address of a variable that is to receive the value, or NULL if the value is of no interest
Return value: The function returns one of the following values:

0 : Cookie not found
<>0 : Cookie found
Group: XCONTROL functions
See also: Cookie jar

9.1.3.4 GetFirstRect

Name: »GetFirstRect« - Get first rectangle from list.
Declaration GRECT * cdecl (*GetFirstRect) (GRECT *prect);
Description The call GetFirstRect obtains the first rectangle from the rectangle list. The following applies:

Parameter Meaning
prect Area to be updated


Note: The function is required for the redrawing of window portions after a WM_REDRAW message; however, the object tree is managed by XControl itself.
Return value: The function returns one of the following values:

NULL : No further window portions present
Else : Window portion to be restored
Group: XCONTROL functions
See also: GetNextRect   XCONTROL

9.1.3.5 GetNextRect

Name: »GetNextRect« - Get next rectangle from list.
Declaration GRECT * cdecl (*GetNextRect) (VOID);
Description The call GetNextRect obtains the next rectangle from the rectangle list.

Note: The function is required for the redrawing of window portions after a WM_REDRAW message; however, the object tree is managed by XControl itself.
Return value: The function returns one of the following values:

NULL : No further window portions present
Else : Window portion to be restored
Group: XCONTROL functions
See also: GetFirstRect   XCONTROL

9.1.3.6 MFsave

Name: »MFsave« - Save/restore mouse form.
Declaration VOID cdecl (*MFsave) (int16_t saveit, MFORM *mf);
Description The call MFsave saves or restores the shape of the mouse pointer. The following apply:
Parameter Meaning
savit
0 = Restore mouse-shape
1 = Save mouse-shape
mf Memory block for saving the mouse-shape
Return value: The function does not return a result.
Group: XCONTROL functions
See also: graf_mouse   XCONTROL

9.1.3.7 Popup

Name: »Popup« - Management of a popup menu.
Declaration int16_t cdecl (*Popup) (int8_t *items[], int16_t num_items, int16_t default_item, int16_t font_size, GRECT *button, GRECT *world);
Description The call Popup takers on the complete management of a popup menu. The following apply:
Parameter Meaning
items Array with character strings for the individual entries; each entry must have the same length, as well as at least two spaces at the start and at least one space at the end
num_items Number of entries
default_item Default entry (count starts at 0), or the value -1
font_size Font size: 8*16 or 8*8 font; for the parameters one should use the same values as in the TEDINFO structure (according to Atari, only the large system font is used at present)
button Rectangle of the button to which the popup belongs
world Rectangle of the background object tree (as a rule the object tree of the CPX)


Note: With too many entries (five onwards) the popup is scrolled automatically; the processing blocks all other actions.
Return value: The function returns the selected entry of the popup, or the value -1 if no element of the popup had been selected.
Group: XCONTROL functions
See also: Xform_do   XCONTROL

9.1.3.8 rsh_fix

Name: »rsh_fix« - Object tree conversion.
Declaration VOID cdecl (*rsh_fix) (int16_t num_objs, int16_t num_frstr, int16_t num_frimg, int16_t num_tree, OBJECT *rs_object, TEDINFO *rs_tedinfo, int8_t *rs_string[], ICONBLK *rs_iconblk, BITBLK *rs_bitblk, int32_t *rs_frstr, int32_t *rs_frimg, int32_t *rs_trindex, struct foobar *rs_imdope);
Description The call rsh_fix converts an object tree on the basis of 8*16 pixel-sized characters. The following apply:

Parameter Meaning
num_objs Total number of objects
num_frstr Total number of strings
num_frimg Total number of images
num_tree Total number of object trees
rs_object  
rs_tedinfo  
rs_string  
rs_iconblk  
rs_bitblk  
rs_frstr  
rs_frimg  
tr_trindex  
rs_imdope  


Note: With this the CPX has the same pixel size in all resolutions. When working with a RCS one should therefore also choose a graphics mode with 8*16 pixel-sized characters.

The coordinate conversion may only take place once, of course - hence XControl makes the flag 'SkipRshFix' available in the XCPB structure.
Return value: The function does not return a result.
Group: XCONTROL functions
See also: rsh_obfix   XCONTROL

9.1.3.9 rsh_obfix

Name: »rsh_obfix« - Object conversion: Character to pixel display.
Declaration VOID cdecl (*rsh_obfix) (OBJECT *tree, int16_t curob);
Description The call rsh_obfix converts the size and position of a specified object from a character-based display to a pixel-based display. The following apply:

Parameter Meaning
tree Address of the object tree
curob Object to be converted
Return value: The function does not return a result.
Group: XCONTROL functions
See also: rsh_fix   rsrc_obfix   XCONTROL

9.1.3.10 Set_Evnt_Mask

Name: »Set_Evnt_Mask« - Set an event mask.
Declaration VOID cdecl (*Set_Evnt_Mask) (int16_t mask, MOBLK *m1, MOBLK *m2, int32_t time);
Description The call Set_Evnt_Mask determines which events the CPX should react to. The following apply:

Parameter Meaning
mask Permitted events (as for evnt_multi)
m1 Mouse rectangle and -direction
m2 Mouse rectangle and -direction
time Time in milliseconds for timer event
Return value: The function does not return a result.
Group: XCONTROL functions
See also: XCONTROL

9.1.3.11 Sl_arrow

Name: »Sl_arrow« - Slider arrow handling.
Declaration VOID cdecl (*Sl_arrow) (OBJECT *tree, int16_t base, int16_t slider, int16_t obj, int16_t inc, int16_t min, int16_t max, int16_t *value, int16_t direction, VOID (*foo) (VOID));
Description The call Sl_arrow is to be made as soon as an arrow of a slider has been clicked on. The following apply:
Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of base object)
obj Arrow that was clicked on
inc Number of units that are to be added or subtracted
min Minimum value that can be accepted
max Maximum value that can be accepted
value Address for current value
direction Direction:
0 = Vertical
1 = Horizontal
foo Address of a function (as for Sl_x/Sl_y)
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_dragx   Sl_dragy   XCONTROL

9.1.3.12 Sl_dragx

Name: »Sl_dragx« - Slider drag movement (horizontal).
Declaration VOID cdecl (*Sl_dragx) (OBJECT *tree, int16_t base, int16_t slider, int16_t min, int16_t max, int16_t *value, VOID (*foo) (VOID));
Description The call Sl_dragx manages the movement of the horizontal slider. The following apply:

Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of the base object)
min Minimum value that can be accepted
max Maximum value that can be accepted
value Address for current value
foo Address of a function, as for Sl_x
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_dragy   XCONTROL

9.1.3.13 Sl_dragy

Name: »Sl_dragy« - Slider drag movement (vertical).
Declaration VOID cdecl (*Sl_dragy) (OBJECT *tree, int16_t base, int16_t slider, int16_t min, int16_t max, int16_t *value, VOID (*foo) (VOID));
Description The call Sl_dragy manages the movement of the vertical slider. The following apply:

Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of the base object)
min Minimum value that can be accepted
max Maximum value that can be accepted
value Address for current value
foo Address of a function, as for Sl_y
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_dragx   XCONTROL

9.1.3.14 Sl_size

Name: »Sl_size« - Sets slider size.
Declaration VOID cdecl (*Sl_size) (OBJECT *tree, int16_t base, int16_t slider, int16_t num_items, int16_t visible, int16_t direction, int16_t min_size);
Description The call Sl_size sets the size of the slider. The following apply:
Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of the base object)
num_items Number of elements present
visible Number of elements visible
direction Direction:
0 = Vertical
1 = Horizontal
min_size Minimum size of slider, in pixels


Note: The function is required to obtain the relationship of the amount of data displayed to the total that is present.
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_dragx   Sl_dragy   Sl_x   Sl_y   XCONTROL

9.1.3.15 Sl_x

Name: »Sl_x« - Positions a slider (horizontal).
Declaration VOID cdecl (*Sl_x) (OBJECT *tree, int16_t base, int16_t slider, int16_t value, int16_t min, int16_t max, VOID (*foo) (VOID));
Description The call Sl_x positions the slider within a base object in the horizontal direction. The following apply:
Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of the base object)
value New value that the slider should represent
min Minimum that value may accept
max Maximum that value may accept
foo Address of a function (or NULL), which is called simultaneously with slider repositioning; this permits slider movements to be used also to alter displayed values
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_y   XCONTROL

9.1.3.16 Sl_y

Name: »Sl_y« - Positions a slider (vertical).
Declaration VOID cdecl (*Sl_y) (OBJECT *tree, int16_t base, int16_t slider, int16_t value, int16_t min, int16_t max, VOID (*foo) (VOID));
Description The call Sl_y positions the slider within a base object in the vertical direction. The following apply:
Parameter Meaning
tree Address of the object tree
base Base object (index of slider 'track')
slider Slider (child of the base object)
value New value that the slider should represent
min Minimum that value may accept
max Maximum that value may accept
foo Address of a function (or NULL), which is called simultaneously with slider repositioning; this permits slider movements to be used also to alter displayed values
Return value: The function does not return a result.
Group: XCONTROL functions
See also: Sl_x   XCONTROL

9.1.3.17 Xform_do

Name: »Xform_do« - Manage a form.
Declaration int16_t cdecl (*Xform_do) (OBJECT *tree, int16_t startob, int16_t *puntmsg);
Description The call Xform_do takes on the management of a form, as well as (to a limited extent) the handling of AES messages. The following apply:

Parameter Meaning
tree Address of the object tree
startob Start object
puntmsg Message buffer


Note: Under COPS the CPX can draw an object tree up to 512*384 pixels in size and pass it to this function.
Return value: The function returns one of the following values:
-1: puntmsg contains a message to be evaluated:
WM_REDRAW: The CPX must itself redraw objects that do not belong to the object tree. The rectangle list can be obtained with the functions GetFirstRect and GetNextRect.
AC_CLOSE: and
WM_CLOSE: The CPX was terminated; reserved memory is to be freed immediately. AC_CLOSE is to be evaluated as 'Cancel', WM_CLOSE as 'OK'.
CT_KEY: Special message that permits evaluation of key-presses, provided these can have no repercussions on EDIT fields.

puntmsg[3] High byte : Scancode of the pressed key
puntmsg[3] Low byte : ASCII-code of the pressed key
Else: Index of object clicked on (a double-click is flagged in the upper bit).
Group: XCONTROL functions
See also: form_do   form_xdo   Rectangle-list of a window

9.1.3.18 XGen_Alert

Name: »XGen_Alert« - Display an alert box.
Declaration int16_t cdecl (*XGen_Alert) (int16_t id);
Description The call XGen_Alert enables the display of a simple alert box. The following apply:

Parameter Meaning
id Type of message:
   0 = 'Save default settings?'
   1 = 'Memory allocation error!'
   2 = 'File read/write error'
   3 = 'File not found'


Note: Further alert boxes must be defined by the user. However, form_alert is not suitable for this, as it centres the alert box with reference to the full screen area and not with reference to the XControl window.
Return value: The function returns one of the following values:
   0 : 'Cancel' was clicked on
<> 0 : 'OK' was clicked on (if the alert box has only one button, then it is the 'OK' button)
Group: XCONTROL functions
See also: form_alert   XCONTROL

HomeXCONTROLXCONTROLProgramming guidelines