XControl - Extensible Control Panel for TT/MEGA/ST Computers
PRELIMINARY SPECIFICATION - SUBJECT TO CHANGE WITHOUT NOTICE
This document is Copyright (C) 1990, Atari Corporation

890814  kbad    First Draft
890816  kbad    rev 1 - xform_do() spec, set-only CPXs
890817  kbad    rev 2 - CPX file format
891220  kbad    major rewrite, incorporating the latest CPX concepts
900109  kbad    Corrected XControl window size,
                changes to XCPB and CPXINFO structs, added cpx_wmove()

OVERVIEW

This document describes a new Control Panel (XControl) for TT/MEGA/ST
computers, which features loadable Control Panel extensions (CPXs)
that perform various system configuration functions.  XControl, with
its loadable modules, gives the Control Panel the advantages of any
software with modular design: ease of maintenance and expandability. 
Improvements to any part of XControl can be distributed individually,
by distributing CPX updates, without the need for updating all parts of
XControl.  This scheme is more flexible for users, since XControl
will only load the CPXs which a user needs.  Software vendors can create
and distribute their own CPXs to extend the functionality of XControl
beyond what Atari provides, or to provide graphical front ends for their
TSR utilities.

HOW XCONTROL WORKS

XControl is a combination of a master desk accessory, which loads the
various CPXs and manages user selection of CPXs, and the extensions
themselves, which perform the various system configuration tasks.

When XControl is loaded, at boot time or on a resolution change, it
looks for a folder called XCONTROL in the root of the boot device.  If
it finds the folder, it will read the header of each .CPX file it finds
there.  If the header indicates that a .CPX needs to be run at boot
time, XControl will load the CPX and call its initialization hook. If
the CPX header also indicates that the user prefers it to be memory
resident, XControl will keep the CPX in memory.  After checking CPXs
and initializing those that need it, XControl waits like any other desk
accessory for an AC_OPEN message.

When selected from the Desk menu, XControl checks the XCONTROL folder
again, to see if any CPXs have been added or removed since it was last
invoked.  New CPXs will be initialized as required.  XControl then
displays menu of loaded extensions.  When a CPX is chosen from the
main XControl menu, that CPX is loaded from the XCONTROL directory,
and invoked via the XControl <-> CPX software interface described below.

Basically, the CPX gets control of the work area of the XControl
window, and can present its own interface there.  XControl dispatches
user events through the CPX, but handles most window related events
itself.  XControl also provides a number of utility routines to CPXs,
including an extended form_do call which CPXs can use to handle simple
dialog-style interfaces within the XControl window.

It's expected that most CPXs will use this simple extended form_do()
software interface so that the user can move or close the XControl
window at will.  Each CPX will provide at least an OK and Cancel button
so that the user can return to the XControl master from the CPX.  Each
CPX must also be able to respond to an Abort signal from XControl, so
that the user can dismiss XControl with the close box, and so that
XControl can clean up if it is active while the main application is
terminated.  When the user exits a CPX back to XControl, the CPX is
unloaded, and the memory it took up is recovered.

CONTROL PANEL EXTENSIONS

The concept of what constitues a CPX is very important to the implementation
of the extensible Control Panel.  A CPX is effectively a subroutine
call.  It is neither an application nor a desk accessory, but only a
means for setting system parameters.  Examples of CPX functions include:

 - Color selection
 - Keyboard/mouse configuration (repeat rate, audible keyclick, etc.)
 - RS232 port configuration
 - Printer configuration
 - Printer driver configuraton
 - Setting screen saver defaults

Note the key word "configuration" in most of the above functions.  A
printer driver does not belong in a CPX, but the ability to configure a
TSR printer driver would be a good thing to have in a CPX.  The key
concept to keep in mind here is that of the "Control Panel" - the one
place where a user goes to toggle switches, press buttons, or whatever,
to "control" the functions of the computer.  Obviously, it is silly to
have a CPX which controls the operation of a desk accessory.  Instead,
CPXs should primarily be used as graphical front ends for TSR
utilities.

XCONTROL <-> CPX SOFTWARE INTERFACE

When XControl first starts up, it loads the headers of all the CPXs it
can find.  At boot time, it initializes each CPX which has the
bootinit flag set in its header by jsr'ing to the start of the CPX's
text segment.  The cpx_init() function described below must be located
at the start of the text segment.  This function returns a pointer to a
structure containing information about the CPX, including pointers to
the calls it will use for further XControl interaction.

"Set-only" CPXs may be implemented, they should set whatever is needed
during the cpx_init() call and return NULL.  If a CPX is set for
bootinit, XControl will also check the setonly flag in the header to
determine if the CPX is Set-only.  XControl will only execute Set-only
CPXs at boot time. They will not appear on the XControl main menu, and
thus will never again be called after the cpx_init() call.

[Does this make sense?  The idea is that Set-only's don't get run]
[every time you bring up XControl.  So, you can't execute a Set-only]
[just by copying it into your XCONTROL folder.. you must reboot]

XControl uses an evnt_multi() for its user interaction.  When a CPX is
chosen by the user, XControl loads the CPX into memory, and calls
cpx_init() with the `booting' paramter set to FALSE.  XControl then
invokes the cpx_call() routine to begin CPX interaction.  This routine
should first initialize the CPX interface. It may then handle the user
interface via the extended form_do call (described below) and return
FALSE, or return TRUE to allow XControl to manage the user interface by
dispatching evnt_multi() events through the CPX event handling
routines.

XCONTROL ROUTINES

XControl makes a custom form handler available to CPXs, so that they
may use the standard AES forms interface, in a window, without worrying
about handling window messages.  The name of the routine is xform_do,
and it functions exactly like the built-in AES form_do routine:

    int     xform_do( OBJECT tree[], int startob );

Parameters and returns are as documented for the AES form_do call. The
object tree should fit within the standard control panel window
(256x160 pixels work area).  This restriction may be lifted in a future
version. A pointer to the xform_do routine is passed to a CPX when it
is invoked from the XControl manager with the cpx_call() routine
described below.  More flexible user interfaces may be implemented by
having XControl dispatch events from its evnt_multi() through a CPX
when it is activated, but usually the xform_do() call will suffice.

XControl also allows a CPX to save configuration information into
its file.  Normally, a CPX will have its default configuration
located somewhere in its data segment.  The cpx_save() call allows
a CPX to write configuration data directly into its file, regardless
of the name the user may have given it:

    void        cpx_save( void *buf, long len, long file_offset );

XControl will handle disk errors and retries.

Other XControl routines will be avaliable to CPXs as well, including
resource fixup, AES utility routines, and string handling functions.
[details later...]

CPX ROUTINES

Initialization

    CPXINFO *cpx_init( XCPB *xcpb );

This routine is called as soon as a CPX is loaded by XControl, and
should be used by the CPX to initialize global variables, etc. This
call must be the first routine in the text segment of a CPX.  XControl
passes on the stack a pointer to an XControl Parameter Block, with
information of interest to the CPX.  The XCPB struct looks like:

    typedef struct {
    unsigned    cpx_version;    /* hi byte major, low byte minor # */
    short       vdi_handle;     /* from XControl's v_opnvwk() */
    short       booting;        /* nonzero if this cpx_init() call is
                                 * part of XControl's initialization
                                 * (i.e. bootup or res change)
                                 */
    short       reserved;
    /* utility routines... */
    short       (*xform_do)( OBJECT tree[], short startob );
    void        (*rsh_fix)( void );
    void        (*cpx_save)( void *buf, long len, long file_offset );
    short       (*sl_x)( short base, short value, short min, short max );
    short       (*sl_arrow)( short base, short obj, short inc,
                             short min, short max, short *numvar );
    void        (*sl_drag)( short base, short min, short max, short *numvar );
    short       (*mn_popup)( /* interface TBD */ );
    /* etc... TBD */
    } XCPB;

cpx_init() returns a pointer to the following structure, or NULL
if it is a "set-only" CPX:

    typedef struct {
    char        cpx_title[];    /* Window title of CPX */
    unsigned    cpx_evmask;     /* Wait mask for CPX evnt_multi */
    /* Required for all CPXs: */
    BOOLEAN     (*cpx_call)( GRECT *work );
    /* Optional interface routines, described below: */
    void        (*cpx_draw)( GRECT *clip );
    void        (*cpx_wmove)( GRECT *work );
    BOOLEAN     (*cpx_evhook)( int event, int *msg, MRETS *mrets,
                                int *key, int *nclicks );
    void        (*cpx_timer)( int *event );
    void        (*cpx_key)( int kstate, int key, int *event );
    void        (*cpx_button)( MRETS *mrets, int nclicks, int *event );
    void        (*cpx_m1)( MRETS *mrets, int *event );
    void        (*cpx_m2)( MRETS *mrets, int *event );
    } CPXINFO;

Unused optional routines in the CPXINFO structure should be set to NULL.

Invocation

    BOOLEAN cpx_call( GRECT *work );

Called when a CPX is first activated.  The function is passed a
rectangle describing the current work area of the XControl window. 
This allows a CPX to set up for user interaction, and optionally call
the custom form_do routine to handle its user interface.  cpx_call()
should return FALSE if it is done (if it has used xform_do for its user
interface), or TRUE to continue (if it wants XControl to continue
dispatching events via the routines described below).

Window management

    void    cpx_draw( GRECT *clip );

Called when a CPX is active and the XControl window needs to be redrawn.
This routine is required for all CPXs which use XControl to dispatch
events.

    void    cpx_wmove( GRECT *work );

Called when the user moves the XControl window, so that the CPX may
fix up its object tree as necessary.

Event Preemption Hook

    BOOLEAN cpx_evhook( int event, int *msg, MRETS *mrets,
                        int *key, int *nclicks );
Hook called immediately after evnt_multi returns, BEFORE the event is
handled by XControl. This routine should return TRUE (nonzero) to
override default event handling, or FALSE (zero) to continue with event
handling.  This routine should not normally be required by a CPX, but is
included for flexibility.

Event Handling Functions

While a CPX is active, these are called in response to the appropriate
events.  The contents of the `cpx_evmask' word in the CPXINFO structure
determines what events will be returned by evnt_multi().  The event mask
may be changed at any time while a CPX is active, and the new mask will
be used for the next evnt_multi.  Note that the routines are listed in
the same order they will be called for multiple event returns from
evnt_multi().  These routines should set the word pointed to by `event'
to FALSE (zero) to return control to XControl and its main menu, or
leave that word alone to continue with CPX interaction.

Event       Function
----------  ---------------------------------------------------------
MU_TIMER    void cpx_timer( int *event );
MU_KEYBD    void cpx_key( int kstate, int key, int *event );
MU_BUTTON   void cpx_button( MRETS *mrets, int nclicks, int *event );
MU_M1       void cpx_m1( MRETS *mrets, int *event );
MU_M2       void cpx_m2( MRETS *mrets, int *event );

Message events are handled by XControl, unless intercepted by
cpx_evhook() as described above.

CPX FILE FORMAT

A CPX file header looks like:

    typedef struct {
        unsigned        magic;          /* magic number 0x????  */
        struct {
            unsigned reserved : 30;
            unsigned resident : 1;
            unsigned bootinit : 1;
        }               flags;
        long            cpx_id;         /* unique cpx identifier */
        unsigned        cpx_version;    /* version of this cpx */
        char            i_text[14];     /* 12 characters icon text */
        unsigned        sm_icon[8*4];
        unsigned        lg_icon[16*4];
        char            reserved[300];
    } CPXHEAD; /* sizeof(CPXHEAD) = 512 bytes */

The user can set the `resident' flag and the icon text via XControl.

The rest of the CPX file has the same format as a GEMDOS executable
file.  A program will be provided to set up and prepend the header to
the CPX executable.  The executable part does not need to be completely
relocatable, as XControl will perform whatever relocation is necessary
when it loads the CPX.  The first routine in the text segment of the
object file must be the cpx_init() routine described above.  The
resource for the CPX file must be built into the file, and should be
fixed up in place using the rsh_fix() facility of XControl.

