Article # 191, added by Geoworks, historical record
| first |
previous |
index |
next |
last |
How to write screen savers.
****************************************************************************** INTRODUCTION ****************************************************************************** This file describes the operation of the Generic Screen Saver process-library, also known as "GW After Hours". The purpose of this process-library is to make the writing of cool little screen savers as easy as possible, handling all interfacing with the Flow object, putting up a window on which to draw, and other functions common to screen savers. ****************************************************************************** WHAT A SCREEN SAVER MUST PROVIDE ****************************************************************************** A screen saver is a library whose token is SSAV. It is stored in the SYSTEM\SAVERS directory and provides 6 predefined entry points, whose order is defined by the SaverFunctions enumerated type defined in saver.def: SF_START enum SaverFunctions ; Begin saving the screen. Screen savers must draw *everything* ; they want for the initial screen at this point. If you wish to ; implement a saver that modifies the screen, your start routine ; needs do nothing extra to the screen. If not, you must blank ; the screen. SaverInitBlank() is provided to simplify this. ; ; Pass: cx = window handle on which to draw ; dx = width of the window ; si = height of the window ; di = gstate handle to use in the drawing ; bp.low = DisplayType ; Return: nothing SF_STOP enum SaverFunctions ; Stop saving the screen. When this function returns, the window ; used for the screen saver will be made unavailable. ; ; Pass: nothing ; Return: nothing SF_FETCH_UI enum SaverFunctions ; Fetch any UI gadgets for the setting of specific screen-saver ; parameters into the GenInteraction passed. ; ; Pass: nothing ; Return: ^lcx:dx = root of tree holding the UI for the library. All the ; gadgetry should be in this block. cx == 0 if no ; gadgetry installed. ; The root should be marked not usable. ; The returned block must be a duplicated block ; with 'saver' as the owner. ; ax = first entry point number used in output descriptors ; in tree. 0 if no entry points used. ; bx = last entry point number used. SF_FETCH_HELP enum SaverFunctions ; Fetch any saver specific help for the saver. ; Pass: nothing ; Return: ^lcx:dx = root of tree holding help for the saver. All the ; help should be in this block. cx == 0 if no help. ; The root should be marked not usable. ; The returned block must be a duplicated block ; with 'saver' as the owner. SF_SAVE_STATE enum SaverFunctions ; Return any extra state the library wants to save across shutdowns. ; ; Pass: cx = handle of block to which to append the state ; information ; dx = offset at which to store the information. ; Return: nothing ; SF_RESTORE_STATE enum SaverFunctions ; Restore any extra state the library saved before shutdown. ; ; Pass: cx = handle of block with extra saved state ; dx = offset from which to restore the information ; Return: nothing ; SF_SAVE_OPTIONS enum SaverFunctions ; If the specific saver has any options besides those dictated by ; its UI tree, or if the saver has chosen not to use the classes ; provided by the generic saver library, those options should be ; saved during this call. ; ; Pass: nothing ; Return: nothing These routines are not allowed to destroy any registers other than AX and BX, which are used to call them (via ProcCallLibraryRoutine). A screen saver *may not* use any gstate other than that provided to it in the SF_START call, nor may it change the default transformation or the application clipping region. This is to allow future versions of the generic library to have multiple specific savers operating at once, if so desired. A typical screen saver will wish to provide user-settable options to control its actions. This is accomplished with the SF_FETCH_UI function. The tree returned should meet the following criteria: * It must reside entirely within a single, duplicated block run by the UI thread. * It should contain a single SaverInteractionClass object that holds the ini file category in which the user-settable options are stored. * The root of the tree must be a SaverInteraction object. * Where you would normally use a GenRange object, use a OptRange object instead, setting the key under which the range's value is to be stored (and from which it is to be fetched when the option tree is brought up on screen). * Where you would normally use a GenList object, use an OptList object instead. The list must use the listMethod and listMethodIsData attributes (these are the defaults set in saver.uih), and should store a unique value in the method field of each GenListEntry object within the list. If the list is exclusive, the "method" for the current exclusive will be stored in the .ini file when the list's option value is saved and will be sought to set the current exclusive when the option is restored. If the list is non-exclusive, the "method" values for the entries should consist of bitmasks. When the option is saved, the method values for all set entries will be bitwise-or'ed to form the value stored in the .ini file. When the option is restored, each method value is and'ed with the value stored in the .ini file, and all entries for which the resulting value is non-zero will be turned on. Again you must provide the key under which this value is to be stored. * If you want to use a GenTextEdit object to hold an editable string option, use a OptTextEdit object instead. * Where you would normally send a method to your process (from an action descriptor in one of your objects), use something like the following macro: #define CALL_QIX(routine) METHOD_SAVER_CALL_SPECIFIC, "enum " #routine This will cause the entry-point number of the given routine to be placed in the optr field of the action descriptor. This optr will eventually be fixed-up by the generic library so the routine you give will be called when the action descriptor is activated, passing all the usual data that come with that action. The example is taken from the Qix screen saver, which is a very good place to look if you get confused. ****************************************************************************** WHAT YOU GET ****************************************************************************** "GW After Hours" provides a few routines, classes and methods for use by a screen saver: global SaverStartTimer:far ; ; Sets up a timer to call a routine in the specific screen-saver ; library on the screen saver's own thread. ; ; Pass: al = TIMER_EVENT_CONTINUAL or TIMER_EVENT_ONE_SHOT ; cx = count until first timeout ; di = interval, for continual timer ; dx = library entry point in specific screen-saver ; library to call when event comes in. ; Return: ax = timer ID ; bx = timer handle ; You are responsible for shutting off any timer you start, as well as dealing with receiving queued timer events after your SF_STOP entry point has been called. Qix and other savers deal with this by storing the GState handle they receive on start, zeroing it on stop, and checking for a non-zero GState handle before doing anything in their timed routine. global SaverRandom:far ; ; Return a random number in the passed range. ; ; Pass: dl = max for returned number ; Return: dx = random number between 0 and DL-1 ; This is a very simple random-number generator for use in all kinds of situations. Note that the number returned does *not* include that passed in DL. global SaverFindOptions:far ; ; Locate the root of the specific options tree. ; ; Pass: nothing ; Return: carry clear if found: ; ^lcx:dx = optr of tree root ; carry set if not found ; A specific saver can call this from SF_RESTORE_STATE to locate the options tree that it returned in its previous incarnation. This is useful if the saver needs the handle of the duplicated option block for whatever reason. global SaverSetSpecProcClass:far ; ; Set a class as an auxiliary process class. Any method not already ; fielded by the generic saver's own process class will be sent to the ; auxiliary class. The class should have SaverClass as its superclass. ; ; Pass: cx:dx = far pointer of auxiliary class. ; Return: nothing. ; This allows the specific saver to receive methods from other parts of the system without having to instantiate some object or other each time it is loaded. In fact, one can use this instead of special constants and METHOD_SAVER_CALL_SPECIFIC in one's options UI tree if one so desires. The class so passed is placed between the generic saver's own private process class and the published global SaverClass. All the SaverClass methods that the generic saver fields are actually bound at the SaverClass level, not the private saver class's level, allowing the specific saver to receive these methods before the generic saver does. This can be useful if one wishes to call SaverSetWinType just before the window is opened, for example.