XControl - Extensible Control Panel
for ST/MEGA/STe/TT Computers
SPECIFICATIONS SUBJECT TO CHANGE WITHOUT NOTICE
XControl Version: 1.0
This document is Copyright (c) 1990, 1991 Atari Corporation
OVERVIEW
This document describes a new Control Panel ( XControl) for ST/MEGA/STe/TT
computers, which features loadable Control Panel extensions (CPXs) that
performs various sytem 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 the 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, it looks for a a file called
CONTROL.INF in the root of the boot device. CONTROL.INF contains the
default settings for XControl.
One of the settings is the CPX DIRECTORY PATH. This is the directory
where XControl looks for CPXs. If XControl finds the folder, it reads the
header of each .CPX file that it finds there. The header's id number and
version number are compared to those already loaded. The end result is
that only the latest version of a CPX will be retained.
If the header indicates that a .CPX needs to be run at boot time, XControl
loads the CPX and calls its initialization hook. If the CPX header also
indicates that the user prefers it to be memory resident, XControl keeps
the CPX in memory.
After checking CPXs and initializing those that need it, XControl adds an
additional set of CPX slots. The number of slots available is based upon
either the default minimum number of slots set by the user, or 1-1/2 times
the number of CPXs loaded, whichever is greater. Additional CPXs may be
loaded later during a RELOAD command. All CPXs are initialized as if they
were new, except that existing 'resident' CPXs are retained. New CPXs
marked 'resident' are treated as non-resident CPXS after a reload. During
a reload, a new CPX will only be loaded if there is a vacant slot available.
After adding additional vacant slots, XControl waits like any other desk
accessory for an AC_OPEN message.
When selected from the Desk menu, XControl opens a window and displays a
menu of active CPXs. When a CPX is chosen from the menu, XControl
attempts to invoke it via the XControl <-> CPX software interface described
below. Resident CPXs are invoked immediately; non-resident CPXs are loaded
from the CPX storage directory. If the CPX is not found by name, XControl
looks at all other CPXs in that directory, comparing id numbers and version
numbers. If an exact match is found, that CPX is invoked instead.
Otherwise, a file-not-found alert is displayed.
When invoked, the CPX assumes 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 of the 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 simply handle
dialog-style interfaces within the XControl window.
It's expected that most CPXs will use this extended form_do() software
interface so that the user can move or close the XControl window at will.
Each CPX should 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
non-resident CPX, it is unloaded, and the memory that it took up is
recovered by the system.
CONTROL PANEL EXTENSIONS
The concept of what constitutes 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
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 that 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.
This in turn will jmp to the cpx_init() function. This function returns a
pointer to a structure containing information about the CPX, including
pointers to routines which XControl uses to invoke CPX functions.
"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 also checks the 'Set-Only' flag in the header to determine if the
CPX is Set-only. XControl will only execute Set-only CPXs at boot time and
at reloads. They will not appear on the XControl main menu, and thus will
never again be called after the cpx_init() call.
XControl uses an event_multi() for its user interaction. When a CPX is
chosen by the user, XControl loads the CPX into memory and calls cpx_init()
again, this time, with the 'booting' parameter set to FALSE. XControl then
invokes the cpx_call() routine to begin CPX interaction. The cpx_call()
routine should first initialize the CPX interface. It may then handle the
user interface via the extended form_do call and return FALSE to exit the
CPX ( See Form CPXs ), or to return TRUE and allow XControl to manage the
user interace by dispatching evnt_multi() events through the CPX event
handling routines. ( See Event CPXs ).
FORM CPXs versus EVENT CPXs
FORM CPXs are those which use Xform_do() to handle user interaction with a
standard AES form. XControl handles window movements and redraws. To the
CPX, it looks just like the old familiar form_do() with a few extensions:
- Keys other than those which work in editable text fields
can be handled by the CPX.
- Special redraws may be done by the CPX.
- If the user closes the XCONTROL window or quits the
parent application, the CPX is informed so that it may
clean up. AC_CLOSE should be treated as "Cancel",
and WM_CLOSE as "OK".
To give you an idea of the flexibility FORM CPXs may have, all of the
CPX's released with XControl 1.0 are FORM CPXs.
EVENT CPXs are those which use XControl to dispatch AES events directly,
for maximum flexibility. These CPXs give XControl a list of event handlers
in the CPXINFO struct returned by cpx_init(). When an Event CPX is called,
it tells XControl which events it cares about via the set_event_mask()
function, then it returns to XControl and waits for its event handlers to
be called. Event CPXs exit by setting a flag passed to the event handlers.
Because of the flexibility offered by Xform_do(), and the more complex
nature of the event handlers, it's generally much easier to write a Form
CPX than an Event one. The main reason you might want an Event CPX is to
handle timer events which are not supported by XForm_do().
XCONTROL ROUTINES
At boot-time or invocation time, XControl jsr's to the text segment of the
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 {
short handle; From XControl's Graf_Handle() Call.
See the Notes on workstations below.
short booting; Non-zero if this cpx_init() call is
part of XControl's initialization.
short reserved;
short SkipRsh Fix; The cpx must call the resource fixup routine
only once. Non-zero means skip the fixup.
void *reserve1;
void *reserve2;
void (*rsh_fix)( int num_objs, int num_frstr, int num_frimg,
int num_tree, OBJECT *rs_object,
TEDINFO *rs_tedinfo, BYTE *rs_strings[],
ICONBLK *rs_iconblk, BITBLK *rs_bitblk,
long *rs_frstr, long *rs_frimg, long *rs_trindex,
struct foobar *rs_imdope );
void (*rsh_obfix)( OBJECT *tree, int curob );
short (*Popup)( char *items[], int num_items, int default_item,
int font_size, GRECT *button, GRECT *world );
void (*Sl_size)( OBJECT *tree, int base, int slider, int num_items,
int visible, int direction, int min_size );
void ( *Sl_x) ( OBJECT *tree, int base, int slider, int value,
int num_min, int num_max, void (*foo)() );
void (*Sl_y)( OBJECT *tree, int base, int slider, int value,
int num_min, int num_max, void (*foo)() );
void (*Sl_arrow)( OBJECT *tree, int base, int slider, int obj,
int inc, int min, int max, int *numvar,
int direction, void (*foo)() );
void (*Sl_dragx)( OBJECT *tree, int base, int slider,
int min, int max, int *numvar, void (*foo)() );
void (*Sl_dragy)( OBJECT *tree, int base, int slider,
int min, int max, int *numvar, void (*foo)() );
WORD (*Xform_do)( Object *tree, WORD start_field, WORD puntmsg[] );
GRECT *(*GetFirstRect)( GRECT *prect );
GRECT *(*GetNextRext)( void );
void (*Set_Evnt_Mask)( int mask, MOBLK *m1, MOBLK *m2, long time );
BOOLEAN (*XGen_Alert)( int id );
BOOLEAN (*CPX_Save)( void *ptr, long num );
void *(*Get_Buffer)( void );
int (*getcookie)( long cookie, long *p_value );
int Country_Code; Contains the Country Code that the Control Panel
was compiled for. For a list of the Current
Country Codes, please see the Rainbow TOS
Release Notes - BIOS/XBIOS Supplemental
Documentation, page 63.
void MFsave( BOOLEAN saveit, MFORM *mf );
} XCPB;
RESOURCE MANAGEMENT:
Resource Object Tree Fixup Function:
Rsh_fix() fixes up the CPX object tree based upon 8x16 pixel characters.
This ensures that the CPX will be the same size in all resolutions.
The CPX object tree should be a 'Panel', not a 'Dialog'. It should be
created in ST HIGH resolution. In comparison, the 'rsrc_load()' function
would fixup the tree based upon the current character width and height for
that resolution. This is why panels can appear stretched or scrunched in
different resolutions when using 'rsrc_load()'.
The CPX should only call rsh_fix() when the XControl Parameter Block
'SkipRshFix' flag is ZERO. The reason for this is because a resource
should only be converted to pixels ONCE.
void (*rsh_fix)( int num_objs, int num_frstr, int num_frimg,
int num_tree, OBJECT *rs_object,
TEDINFO *rs_tedinfo, BYTE *rs_strings[],
ICONBLK *rs_iconblk, BITBLK *rs_bitblk,
long *rs_frstr, long *rs_frimg, long *rs_trindex,
struct foobar *rs_imdope );
IN: All of the input variables can be found in the CPX RSH file.
OUT: None
Resource Object Fixup Function:
Call this function ONLY when you want to convert a specific object to pixel
format AND when the object is still CHARACTER based. The only reason you
would need to call this function would be if you were doing your own
resource fixup for a resource that was not created by the Atari RCS.
void (*rsh_obfix)( OBJECT *tree, int curob );
IN: OBJECT *tree The object tree of the CPX
int curob The resource object to convert
OUT: None
POP UP MANAGEMENT:
Call this function to have the CPX display a popup box:
short (*Popup)( char *items[], int num_items, int default_item,
int font_size, GRECT *button, GRECT *world );
IN: char *items[]; Pointer to an array of strings.
int num_items; Number of items ( 1 based )
int default_item; The default item ( zero based )
int font_size; 8x16 ( Large ) or 8x8 ( small ) Font
GRECT *button; GRECT of button pressed to invoke popup.
GRECT *world; GRECT of your tree.
OUT: short; Returns the item selected (zero based ) or -1
The string array passed to the popup routine must be properly padded by
the calling cpx. There must be at least 2 spaces in front of each string,
and each string must be padded with spaces up to the length of the largest
string plus 1.
The number of items listed versus the number of strings available is not
checked. If they do not correspond, errors may occur. In addition, if
there are more than 5 items, only 3 will be displayed at any one time.
The first position will display an up arrow, and the 5th position will
display a down arrow. Scrolling through the popup will display the remaining
menu items, with a check mark indicating the default item.
Sometimes a default item is not necessary. Setting the default_item = -1
will prevent a check mark from appearing.
There are 2 font sizes available for AES objects the large and the small
font. Currently, the large font is always used and the height is assumed
to be 16 pixels.
The GRECT of the button that activated the popup menu is required so that
the menu is at least as wide as the button.
The GRECT of the world is required so that if the popup menu exceeds the
right edge, it pops left instead. If the popup menu exceeds the bottom,
it pops upwards. In most cases, the "world" is the dimensions of your
CPX's main form .
While the popup menu is displayed no other action other than popup menu
manipulation is allowed.
SLIDER MANIPULATION:
Slider Size Adjustment:
This function is used to adjust the slider size within its base, so that
the size of the slider represents the amount of data displayed, relative
to the total amount of data. In certain cases, it is best that the slider
not be sized. An example of this is when the slider also contains a text
string. It is possible, that if sized, the slider can no longer display
the text string properly by either being too small or too large.
void (*Sl_size)( OBJECT *tree, int base, int slider, int num_items,
int visible, int direction, int min_size );
IN: OBJECT *tree; The object tree: ( OBJECT *)rs_trindex[ TREENAME].
int base; The base is the object of the sliders limits.
int slider; The object that moves within the limits defined by base.
The slider must be the child of the base.
int num_items; The number of items (range)
int visible; The number of items visible
int direction; Horizontal or Vertical
int min_size: The minimum pixel size of the slider
OUT: none
Slider X/Y Functions:
Sl_x() and Sl_y() are used to update the position of the slider within its base.
void (*Sl_x)( OBJECT *tree, int base, int slider, int value,
int num_min, int num_max, void (*foo)() );
void (*Sl_y)( OBJECT *tree, int base, int slider, int value,
int num_min, int num_max, void (*foo)() );
IN: OBJECT *tree; ( OBJECT *)rs_trindex[ TREENAME ];
int base; Base of the slider ( slider limits )
int slider; The object that will be moved around.
This must be the child of the base.
int value; The NEW value related to the slider range.
( Range: 0 - 1000 )
int num_min; The minimum number value can equal to.
int num_man; The maximum number value can equal to.
(*foo)(); Pointer to a CPX defined function to update
its items. Set to NULLFUNC if there
is no routine.
OUT: none
Slider Arrow Functions:
Call this when the user selects the arrows of a slider. Direction is
either Horizontal or Vertical. Note that this is an ACTIVE slider where
objects are updated immediately, unlike the AES graf_slidebox where
objects are updated only after the user lets go of button one.
void (*Sl_arrow)( OBJECT *tree, int base, int slider, int obj,
int inc, int min, int max, int *numvar,
int direction, void (*foo)() );
IN: OBJECT *tree: The resource tree
int base: The base of the slider ( slider limits )
int slider: The object that can be moved around
int obj: The arrow button object clicked on
int inc: The increment amount ( +/- # )
int min: The minimum value possible
int max: The maximum value possible
int *numvar: The current value
int direction: Horizontal or Vertical
void (*foo)(): Pointer to a CPX defined function to
update its items. Set to NULLFUNC if
there is no routine.
OUT: none
Slider Paging Functions:
Paging is implemented by calling sl_arrow() with an increment/decrement
value representing a "page" worth of data. Paging is done when the user
clicks on the base. To implement paging the CPX can do this:
MRETS mk;
int inc, ox, oy;
Graf_mkstate( &mk );
objc_offset( tree, slider, &ox, &oy );
inc = ( ( mk.y < oy ) ? ( -1 ) : ( 1 ) );
sl_arrow( fill in variables here );
This example is for vertical sliders and the increments were set to +/- 1.
Paging usually increments or decrements by the visible amount. To do
horizontal pages, use the 'ox' and 'mk.x' variables instead and don't
forget to set the horizontal or vertical flag as necessary in sl_arrow().
Slider Drag Functions:
Called when the user 'drags' the slider around. Again, this is an ACTIVE
slider and will call Sl_x() or Sl_y() appropriately.
void (*Sl_dragx)( OBJECT *tree, int base, int slider, int min,
int max, int *numvar, void (*foo)() );
void (*Sl_dragy)( OBJECT *tree, int base, int slider, int min,
int max, int *numvar, void (*foo)() );
IN: OBJECT *tree: The resource tree
int base: The base of the slider ( slider limits )
int slider: The object that can be moved around
int min: The minimum value possible
int max: The maximum value possible
int *numvar: The current value
void (*foo)(): Pointer to a CPX defined function to
update its items. Set to NULLFUNC if
there is no routine.
OUT: none
User Supplied Slider Update Function:
The User Supplied CPX function may be required so that the CPX can perform
operations specific to the active slider. In most cases, this will simply
be updating the text string and then performing a redraw. In a more
complicated setting, this can be anything from changing colors to
performing a blit. If no function is required, pass NULLFUNC instead.
The value that you can use to update the text string is contained in the
variable that you passed by reference into the calling slider function.
Whenever you call sl_arrow(), or sl_draw(), XControl updates that variable
just before calling sl_x() and sl_y(). These in turn will call your foo()
function.
The prototype for foo() is: void (*foo)( void );
XFORM_DO FUNCTION:
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 object tree should fit within the standard
control panel window ( 256x176 pixels work area ). This restriction may
be lifted in a future version. The name of the routine is Xform_do and it
functions like the built-in AES form_do routine with a few exceptions.
One additional parameter is used, and a return value of -1 has a special
meaning.
Timer Events are not supported under XForm_do(). If timer events are
necessary, the CPX should be designed as an Event CPX.
If the CPX is looking for double clicks, the return value should be
checked for -1 BEFORE checking for a double click.
WORD (*Xform_do)( Object *tree, WORD start_field, WORD puntmsg[] );
IN: OBJECT *tree; Same as form_do;
WORD start_field; Same as form_do;
WORD puntmsg[]; Defined as WORD msg[8];
OUT: Same as form_do(): Returns the object number with the high
bit set if a touch-exit was double
clicked on.
-However-
if return is -1, this means that the CPX should look at the
puntmsg[] array and treat it like the message array from an
event multi. The three messages to look for are:
WM_REDRAW: Sometimes the CPX needs to redraw items that
are not part of the tree. This is the time
to do so. XControl makes available
GetFirstRect() and GetNextRect() so that the
CPX can get the rectangle list and redraw
accordingly.
AC_CLOSE:
WM_CLOSE: When these messages are received, the CPX
should immediately FREE any memory that
it malloc'ed and return to XCONTROL by
exiting cpx_call(). Do NOT leave any
memory allocated, else fragmentation will
occur. We strongly recommend that CPXs
Do Not Malloc any memory.
CT_KEY: A key was pressed. puntmsg[3] contains the
keycode of the key pressed as returned from
an 'evnt_keybd()'. Note that we return
non-printable keys only, such as F1-F10,
Help and Undo. However, the 'Arrow' keys
are not supported, because they are
handled by Xform_do() for editable text fields.
Note: CT_KEY == 53.
IMPORTANT: Always treat AC_CLOSE() as "Cancel" and
treat WM_CLOSE() as an "OK".
GET FIRST/NEXT RECTANGLE LIST FUNCTIONS:
When redrawing the CPX due to a WM_REDRAW message, the CPX should use
these routines to go down the rectangle list. Since the Xform_do() routine
will handle resource object redraws, the CPX must handle non_resource
objects.
GRECT *(*GetFirstRect)( GRECT *prect );
GRECT *(*GetNextRext)( void );
IN: GRECT *prect: The GRECT of the dirtied area.
OUT: The intersecting GRECT that you should redraw or
NULL if there are no more rectangles.
SET EVENT MASK
( Use only with Event CPXs )
Used to set XControl's Evnt_multi() function. Messages will be dispatched
to the CPX thru procedure variables passed in.
void (*Set_Evnt_Mask)( int mask, MOBLK *m1, MOBLK *m2, long time );
IN: int mask: Events to receive ( ie: MU_MESAG | MU_KEYBD )
MOBLK *m1: Mouse rect and direction number one.
MOBLK *m2: Mouse rect and direction number two.
long time: Time to wait for a timer event ( 1000 = 1 sec )
Note that you must set the mask with MU_TIMER.
OUT: none
MOBLK is defined as: typedef struct {
int m_out; Direction for evnt_multi() to look for.
int m_x; The x,y,w,h of the bounding rectangle.
int m_y;
int m_w;
int m_h;
} MOBLK;
XCONTROL ALERT BOX:
Use this function to display an XControl Alert Box. The dialog box will
be centered within the work area of the XControl window. The Alerts
available are:
SAVE_DEFAULTS 0 Save Defaults?
MEM_ERR 1 Memory Allocation Error
FILE_ERR 2 File I/O Error
FILE_NOT_FOUND 3 File Not Found Error
BOOLEAN (*XGen_Alert)( int id );
IN: int id: The alert id number
OUT: BOOLEAN: TRUE - OK
FALSE - Cancel
Alerts with only one button always return TRUE.
CPX SAVE DEFAULTS:
XControl allows a CPX to write configuration data directly into its file.
XControl will write the number of bytes specified from *ptr to the data
segment of the CPX. If the CPX isn't found by name, XControl will search
the CPX directory for another file with the same id number and version
number. If found, that CPX will become the active cpx. If still not
found, a file not found alert will be generated. The standard GEMDOS
error will also occur if the disk is write-protected. The start of the
DATA segment begins at the variable SAVE_VARS which is declared in the
CPXSTART.S file. The CPX designer must allocate the appropriate amount of
DATA segment storage by editing CPXSTART.S.
During boot_time initialization, the CPX should read the defaults from the
data segment and act accordingly.
Lastly, the CPX should treat a "SAVE" action as an 'OK', but do not exit the CPX.
BOOLEAN (*CPX_Save)( void *ptr, long num );
IN: void *ptr Pointer to the data that needs to be stored.
long num Number of bytes to write to data segment of CPX.
OUT: BOOLEAN: TRUE - OK
FALSE - Error occurred
XCONTROL GET_BUFFER FUNCTION:
This call returns a pointer to the 64 byte buffer in each header which can
be used by the CPX. The buffer should be used by CPXs that rely upon
write-only registers. For example, the baud rate and flow control data
cannot be read from the Rsconf() call. ( In TOS 1.4 and greater, the baud
rate CAN be inquired. ) Since a CPX cannot be guaranteed to be in memory,
a non-volatile storage location must be set aside to accomplish this. The
CPX can set the register, store the value in the buffer and when cpx_init()
is called again, the CPX can restore the data into its internal variables.
void *(*Get_Buffer)( void );
IN: none
OUT: (void *) Returns a pointer to the CPX.
The CPX should cast the pointer
it's required format.
CPX GET COOKIE FUNCTION:
Use this routine to look for a cookie. Please see the Cookie Jar
specifications for more details. The parameters are exactly the same.
int (*get_cookie)( long cookie, long *p_value );
IN: long cookie: Cookie that we are looking for.
long *p_value: Value of cookie goes here if the cookie is valid.
OUT: Zero if the cookie is not found
Non-Zero if the 'cookie' is found and places its
value in the longword pointed to by p_value.
If p_value is NULL, it doesn't put the value
anywhere, but still returns the error code.
A cookie can be a convenient marker for a TSR to indicate where a CPX can
find the configuration data used by the TSR. That's one of the reasons
the cookie jar exists! Use it!
CPX SAVE/RESTORE MOUSE FORM
Use this routine to save/restore a mouse image to/from an MFORM structure.
This is useful when one needs to use a FLAT_HAND for example, and then
must restore the mouse to its original shape. This is required so that a
CPX doesn't wipe out a custom mouse form being used by an application when
the CPX is invoked.
void MFsave( BOOLEAN saveit, MFORM *mf );
IN: BOOLEAN saveit MFSAVE - Save Mouse Form
MFRESTORE - Restore Mouse Form
MFORM *mf Mouse Form to store image in
OUT: none
CPX INFORMATION ROUTINES
INITIALIZATION:
This routine is called at boot time and also whenever the CPX is executed
and should be used by the CPX to initialize global variables, etc..
XControl passses on the stack a pointer to the XControl Parameter Block,
which was defined earlier. Cpx_init() should return a POINTER to the
following structure, or NULL if it is a "set_only" CPX:
CPXINFO *cpx_init( XCPB *xcpb );
typedef struct {
BOOLEAN (*cpx_call)( GRECT *work );
void (*cpx_draw)( GRECT *clip );
void (*cpx_wmove)( GRECT *work );
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 );
BOOLEAN (*cpx_hook)( int event, int *msg, MRETS *mrets,
int *key, int *nclicks);
void (*cpx_close)( BOOLEAN flag );
}CPXINFO;
Most of these calls are not used when the CPX is an Xform_do type. Those
routines not used should be set to NULL in cpx_init();
INVOCATION:
Called when a CPX is invoked AFTER the cpx_init() call has been completed.
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 xform_do() routine to handle its user interface.
BOOLEAN (*cpx_call)( GRECT *work );
IN: GRECT *work: GRECT of XControl work window.
OUT: FALSE Return FALSE if the CPX is done.
TRUE Return TRUE to tell XControl to continue to
dispatch events via the XControl CPXINFO
routines.
EVENT HANDLING FUNCTIONS:
While an Event CPX is active, these are called in response to the
appropriate events. The events returned by XControl are defined by
the Set_Evnt_Mask() call in the XCPB. 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 TRUE( 1 )
to return control to XCONTROL and its main menu, or leave that word
alone to continue with CPX interaction. The *event variable is the event
mask and should be ignored otherwise.
Message events are handled by XControl, unless intercepted by cpx_evhook()
as described below.
WINDOW MANAGEMENT:
CPXINFO Redraw Event:
Called when a CPX is active and the XControl window needs to be redrawn.
This call is required by a CPX that uses XControl to dispatch events
( an Event CPX ). The CPX should pass the dirty area to GetFirstRect()
and GetNextRect() in order to redraw using the rectangle list.
void (*cpx_draw)( GRECT *clip );
IN: GRECT *clip: GRECT of the dirtied area.
OUT: none
CPXINFO Window Move Event:
Called when the user moves the XControl window, so that the CPX may fix
up its object tree as necessary. GRECT contains the work window's new
coordinates. This call is required by a CPX that uses XControl to
dispatch events ( ie: an Event CPX ).
void (*cpx_wmove)( GRECT *work );
IN: GRECT *work: GRECT of the new window coordinates
OUT: none
TIMER EVENTS:
Called when a timer event occurs. This call is required by a CPX that
uses XControl to dispatch events( ie: an Event CPX ). The '*event'
variable is used to tell XControl that this event has terminated the CPX.
Set to '1' to terminate the CPX, else IGNORE it. Note that timer events
for Form CPXs are not supported.
void (*cpx_timer)( int *event );
IN: int *event: Set to '1' if this event terminates the CPX.
else ignore this variable.
OUT: none
KEYBOARD EVENTS:
Called when a keyboard event occurs. This call is required by a CPX that
uses XControl to dispatch events. The '*event' variable should be set to
'1' if this event has terminated the CPX, otherwise, ignore it.
void (*cpx_key)( int kstate, int key, int *event );
IN: int kstate: The state of the Control, Alt and Shift Keys.
int key: The high byte contains the scan code of the key
pressed, and the low byte contains the ASCII code,
if any.
int *event: Set to '1' if this event terminates the CPX.
Ignore this variable otherwise.
OUT: none
MOUSE BUTTON EVENTS:
Called when a mouse button event occurs. This call is required by a CPX
that uses XControl to dispatch events. Set the '*event' variable to '1'
if this event terminates the CPX, otherwise, ignore it.
void (*cpx_button)( MRETS *mrets, int nclicks, int *event );
IN: MRETS *mrets: The mouse parameters returned by the event.
int nclicks: The number of button clicks for this event
int *event: Set to '1' if this event terminates the CPX.
Otherwise, ignore it.
OUT: none
MRETS is defined as:
typedef struct {
WORD x;
WORD y;
WORD buttons;
WORD kstate;
}MRETS;
MOUSE RECTANGLE EVENTS:
Called when a mouse event occurs. This call is required by a CPX that
uses XControl to dispatch events ( ie: an Event CPX ). Set the '*event'
variable to '1' if this event terminates the CPX, otherwise ignore it.
void (*cpx_m1)( MRETS *mrets, int *event );
void (*cpx_m2)( MRETS *mrets, int *event );
IN: MRETS *mrets: Mouse parameters returned by this event.
int *event: Set to '1' if this event terminates the CPX.
Otherwise, ignore it.
OUT: none
MRETS is defined as:
typedef struct {
WORD x;
WORD y;
WORD buttons;
WORD kstate;
}MRETS;
CPX EVENT PREEMPTION HOOK:
Cpx_hook() is called immediately after evnt_multi returns BEFORE the event
is handled by XControl. This routine should not normally be required by a
CPX, but is included for flexibility.
BOOLEAN (*cpx_hook)( int event, int *msg, MRETS *mrets,
int *key, int *nclicks );
IN: int event: The event mask.
int *msg: The AES event message buffer.
MRETS *mrets: mouse parameters for this event.
int *key: Key returned.
int *nclicks: Number of button clicks for this event.
OUT: TRUE Return ( non-zero ) to override default event
handling.
FALSE Return ( zero ) to continue with event handling.
CPX TERMINATION FUNCTION:
This routine is called whenever an AC_CLOSE or WM_CLOSE message is
generated. The CPX should immediately free up any allocated memory and
return to XControl. This routine is required for all CPXs that use
XControl to generate events. Failure to free allocated memory will result
in a fragmented system. Note that this is for an Event CPX only and is
not necessary for Form CPXs. IMPORTANT: Always treat AC_CLOSE messages
as 'Cancel' and WM_CLOSE messages as 'OK'. In addition, CPXs should not
malloc memory if at all possible.
void (*cpx_close)( BOOLEAN flag );
IN: TRUE - WM_CLOSE Message
FALSE - AC_CLOSE Message
OUT: none
CPX FILE FORMAT:
A CPX file header looks like: ( 512 bytes - 0x200 hex )
typedef struct _cpxhead {
unsigned short magic; Magic Number == 100
struct {
unsigned reserved :13; Reserved for Expansion
unsigned resident :1; RAM Resident Flag
unsigned bootinit :1; Boot Initialization Flag
unsigned setonly :1; Set Only CPX Flag
} flags;
long cpx_id; CPX ID value
unsigned short cpx_version; CPX Version number
char i_text[14]; Icon Text
unsigned short sm_icon[48]; Icon bitmap - 32x24 pixels
unsigned short i_color; Icon Color
char title_txt[18]; Title for CPX entry.
Note: Only use 16 Characters!
The remaining 2 positions are Nulls.
unsigned short t_color; Tedinfo field for color
char buffer[64]; Buffer for RAM storage
char reserved[306]; Reserved for Expansion
} CPXHEAD;
The first file in the link must be CPXSTART.S which jmp's to cpx_init().
In addition, it also contains the default variable storage in the DATA segment.
The user will be able to set the Resident Flag, Title Text, Title Color,
Icon Text and Icon Color with a CPX.
The rest of the CPX file has the same format as a GEMDOS executable file.
PREFIX.PRG should be used to design 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 resource for the CPX must be built into the file and should be
fixed up in place using the rsh_fix() facility of XControl.
FILE: Header 512 bytes
GEMDOS Header 28 bytes
Text Segment ...
Data Segment ...
TERMINOLOGY:
*.CPX: A standard CPX file with header ready for use.
*.CP: A standard CPX file without a header.
*_R.CPX: A resident CPX
*_S.CPX: A Set-only CPX
SLOT: A slot is where a CPX is stored in memory. There are
both active and non-active slots. The number of slots
available is decided at boot-time. XControl will
create the minimum number of slots specified by the
DEFAULT or 1-1/2 times the number of active slots,
whichever is greater. This is the ONLY time slots
are allocated.
*.HDR: A CPX header created by PREFIX.PRG
Event CPX A CPX that handles the event messages explicitly.
Form CPX A CPX that uses XForm_do to handle event messages.
*.CPZ An Inactive CPX
DO's:
1) DO remember to deallocate memory whenever appropriate.
2) DO use the XControl functions whenever possible.
That's why we put them there.
3) DO take the time to design an appealing user interface.
4) DO use graphics whenever possible instead of menu commands.
5) DO have OK and Cancel buttons available for each CPX.
Please note that it is 'Cancel' and NOT 'CANCEL'.
6) DO SHADOW Popup boxes. We want the user to know that
'shadowed' boxes are Popup boxes.
7) Treat AC_CLOSE as 'Cancel'
8) Treat WM_CLOSE as 'OK'
9) Treat a "SAVE" action as an 'OK' by updating the 'Cancel' variables.
DON'T's:
1) DON'T have the CPX object tree exceed the
work area ( 256x176 pixels).
2) DON'T have CPXs stealing interrupt vectors.
3) DON'T mix XForm_do() calls with Call-CPX type functions.
4) DON'T forget to deallocate memory whenever appropriate.
Not doing so will fragment the system memory.
5) DON'T use existing ID#s for existing CPXs that were
not written by you.
6) DON'T forget to close a file if your CPX opens one.
7) DON'T forget to open and close a VDI workstation when
needed. DO NOT open a VDI workstation and leave it open.
WORKSTATION NOTES and BRIEFS
When a CPX wishes to perform VDI functions, the CPX must open the
workstation, perform its duties and then close the workstation immediately.
The CPX must not leave any workstations open when it returns to accept more
events. The handle passed to the CPX is the Physical Handle of the Control
Panel returned by a graf_handle() call. The proper procedure of opening a
workstation is:
work_in[0] = Getrez()+2;
for( i = 1; i < 10; work_in[i++] = 1 );
work_in[10] = 2;
vhandle = xcpb->handle;
v_opnvwk( work_in, &vhandle, work_out );
MEMORY ALLOCATION NOTES and BRIEFS
CPXs should not allocate any memory unnecessarily. If the CPX must
allocate memory, the CPX should perform its operation and deallocate
the memory immediately. An example of this is when calculating the
amount of free memory. The reason CPXs should not allocate memory is
because the allocated memory may be invalidated at any time by the OS.
This can occur during a resolution change or when a process exits and
returns to the desktop. On the TT, ALL memory is freed up during a
resolution change, so memory fragmentation isn't a problem there.