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.