Instructions for writing External Screen Blanker Modules Screen Blanker modules are the 32-Bit counterpart of Deskpic modules. What do you need to write an external Screen Blanker module? Well, a pretty good working knowledge of Presentation Manager wouldn't hurt. These instructions will attempt to tell you how to write a module, but they won't tell you how to program in PM. To help, an example extension is provided and is a good place to start. When I write a new extension, I usually use 'BOUNCE' as a framework and go from there. An external Screen Blanker module is a Dynamic Link Library that contains three functions and has the file extension of '.BBS' instead of '.DLL'. The three functions must be exported with the following ordinals: pchStatus @1 dpBlanker @2 vdBlankerThread @3 The three functions are specified by the following function prototypes: PCHAR pchStatus(PBLANKERBLOCK, PBOOL); MRESULT EXPENTRY dpBlanker(HWND, ULONG, MPARAM, MPARAM); VOID vdBlankerThread(VOID); And the structure BLANKERBLOCK is defined as follows: typedef struct { HAB habBlanker; HWND hwndScreen; RECTL rclScreen; BOOL fClose; HMODULE hmodBlanker; } BLANKERBLOCK; typedef BLANKERBLOCK *PBLANKERBLOCK; The BLANKERBLOCK structure is how Screen Blanker provides the module with information about its environment. The fields have the following meaning: 'habBlanker' is the handle of the Screen Blanker program's Anchor Block. This may be needed by some OS/2 functions. 'hwndScreen' is the handle of the screen saver window. This will be needed by 'vdBlankerThread' to get a Presentation Space to draw into. 'rclScreen' is the coordinates of the screen saver window. 'fClose' is a flag that is used by 'vdBlankerThread' to determine when to stop. 'hModBlanker' is the module handle of the Screen Blanker module. This will be needed to access any local resources. The values of 'habBlanker' and 'hModBlanker' are always valid, but the values of 'hwndScreen', 'rclScreen', and 'fClose' are only valid during the execution of 'vdBlankerThread'. pchStatus This function is called by Screen Blanker to get the name of the screen saver. Screen Blanker passes a pointer to a BLANKERBLOCK structure, and a pointer to a BOOL. The function should save the pointer to the BLANKERBLOCK structure in global memory so that it can be accessed by the other two functions. The function should also indicate whether the screen saver is enabled by setting the BOOL to TRUE if enabled, and FALSE if disabled. When finished, the function should return a pointer to a zero terminated string which is the name of the Screen Blanker module, or NULL if the screen saver has no name. Note that while Screen Blanker will call this function before calling the other two, it may call this function multiple times. dpBlanker This is a dialog procedure for the dialog that Screen Blanker will invoke when the user chooses this screen saver module from the list of External modules. The dialog should allow the user to modify any parameters that the screen saver may have. The dialog template should be part of the resources of the screen saver module and should have a dialog template ID of 42. If 'pchStatus' returns NULL instead of a screen saver name, the dialog will never be invoked. vdBlankerThread This procedure will be started as a separate thread when Screen Blanker invokes the screen saver. The procedure should clear the screen and then display whatever graphics the screen saver employs until 'fClose' in the BLANKERBLOCK structure becomes non-zero. When 'fClose' becomes non-zero, the procedure should release any resources it might have, enter a critical section using 'DosEnterCritSec', set 'fClose' to FALSE, and exit (in that order). It is important that the procedure respond quickly to 'fClose' becoming TRUE so that users will not be delayed by the screen saver. The procedure should also get its own Anchor Block, since threads shouldn't make calls to the PM API without one. Note that stack space is limited, so any sizeable memory needed should be allocated at run time. That completes the explanation. Where the instructions are a little sketchy, use the example source code to see at least one way of doing it. Please avoid statically linking the C library where possible to reduce the size of the .BSS file. Whenever possible use the following function replacements (like in BOUNCE.C): /* minirtl.c mini run time library for use with ScreenSaver modules with Borland C++ */ #define INCL_DOS #define INCL_WIN #include #include #include #include #include /* this part of the file was taken from emx 0.8g -- Copyright (c) 1990-1993 by Eberhard Mattes */ void *memcpy (void *s1, const void *s2, size_t n) { size_t i; for (i = 0; i < n; ++i) ((char *)s1)[i] = ((char *)s2)[i]; return (s1); } void *memset (void *s, int c, size_t n) { size_t i; for (i = 0; i < n; ++i) ((char *)s)[i] = (char)c; return (s); } char *strcat (char *string1, const char *string2) { char *dst; dst = string1; while (*dst != 0) ++dst; while ((*dst = *string2) != 0) ++dst, ++string2; return (string1); } char *strcpy (char *string1, const char *string2) { char *dst; dst = string1; while ((*dst = *string2) != 0) ++dst, ++string2; return (string1); } size_t strlen (const char *string) { size_t i; i = 0; while (string[i] != 0) ++i; return (i); } static unsigned int __rand = 1; int rand() { __rand = __rand * 69069 + 5; return ((__rand >> 16) & 0x7fff); } void srand(unsigned int seed) { __rand = seed; } /* the remainder of the file is (C) 1993-94 Siegfried Hanisch */ // if 1MB is not enough, increase this value // the memory is not commited, so it does not use physical memory // until it is accessed #define MEMPOOLSIZE (1024L*1024L) static void *mempool = NULL; void *calloc(size_t elements, size_t size) { return malloc(elements*size); } void *malloc(size_t size) { APIRET rc; size_t *mem; rc = DosSubAllocMem(mempool, (PVOID *)&mem, size+sizeof(size_t)); if(rc){ WinAlarm(HWND_DESKTOP, WA_ERROR); return NULL; } mem[0] = size; return (void *)(&mem[1]); } void free(void *mem) { APIRET rc; size_t *m; m = (size_t *)mem; rc = DosSubFreeMem(mempool, &m[-1], m[-1]+sizeof(size_t)); if(rc){ WinAlarm(HWND_DESKTOP, WA_ERROR); } } void *realloc(void *mem, size_t size) { void *new_mem; size_t *m; size_t old_size; m = (size_t *)mem; old_size = m[-1]; new_mem = malloc(size); memcpy(new_mem, mem, old_size); memset(((char *)new_mem)+old_size, 0 , size-old_size); free(mem); return new_mem; } char *strdup(const char *string) { return strcpy(malloc(strlen(string)+1), string); } BOOL alloc_heap() { APIRET rc; rc = DosAllocMem((PVOID *)&mempool, MEMPOOLSIZE, PAG_READ|PAG_WRITE); if(rc){ WinAlarm(HWND_DESKTOP, WA_ERROR); mempool = NULL; return FALSE; } rc = DosSubSetMem(mempool, DOSSUB_INIT|DOSSUB_SPARSE_OBJ, MEMPOOLSIZE); if(rc){ DosFreeMem(mempool); WinAlarm(HWND_DESKTOP, WA_ERROR); return FALSE; } return TRUE; } void free_heap() { APIRET rc; rc = DosSubUnsetMem(mempool); if(rc){ WinAlarm(HWND_DESKTOP, WA_ERROR); } rc = DosFreeMem(mempool); if(rc){ WinAlarm(HWND_DESKTOP, WA_ERROR); } } #if defined(__BORLANDC__) int _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg) { TID tid; if(DosCreateThread(&tid, (PFNTHREAD)__start, (ULONG)__arg, 2L, __stksize) != 0) return -1; return tid; } void _endthread(void) { DosExit(EXIT_THREAD, 0); } #elif defined(__IBMC__) int _Optlink beginthread( void(* _Optlink __thread)(void *), void *stack, unsigned stack_size, void *arg_list); { TID tid; if(DosCreateThread(&tid, (PFNTHREAD)__thread, (ULONG)arg_list, 2L, stack_size) != 0) return -1; return tid; } void _endthread() { DosExit(EXIT_THREAD, 0); } #elif defined(__EMX__) int _beginthread(void (*start)(void *arg), void *stack, unsigned stack_size, void *arg_list) { TID tid; if(DosCreateThread(&tid, (PFNTHREAD)start, (ULONG)arg_list, 2L, stack_size) != 0) return -1; return tid; } void _endthread() { DosExit(EXIT_THREAD, 0); } #endif