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.