A Guide to writing a Windows Screensaver in C

Note: This guide is really meant for programmers, however, if you wish to try your hand at making a screensaver without any programming experience, this could get you started on the right track. I MAKE NO GUARANTEES!
-EvilPuffBall

Stuff you'll probably need if you don't have it already

(Compilers and referrences)

     Ye wish to write screensavers, yesss? Well, you'll need a compiler of some sort. I, myself, utilize MinGW. (Minimalist GNU for Windows) In fact, I will be focusing primarily on it throughout the course of this tutorial. However, if you already have a compiler of some sort, I shall assume you have enough experience with it to be able to carry over some of the concepts (if not the code) for use in your own programming set-up.
     As I've mentioned earlier. You'll need a compiler of some sort to make screensavers by hand. MinGW is what I happen to base the compiler-dependant stuff off of. If you are allergic to the CLI (Command-Line Interface) I'd suggest you grab an IDE (Integrated Development Environment) to help you along by giving you a nice GUI to work in. Something else you'll probably want is referrence material.
     As I've stated, you'll probably want some referrence material. If you don't already have it, some documentation on the Win32 API (set of functions, in this case dealing with basic Windows stuff) can be found at http://www.mingw.org/docs.shtml#win32api. Now that we've got that out of the way, we can continue on into slightly more important stuff, ne?

Basic functions that comprise a screensaver

     A Windows screensaver is a program made up of a specific set of functions.
Here's a sort of template for making a screensaver in MinGW. It can be ported to other compilers by changing the way the functions are defined and/or using a compiler specific screensaver header, and some other stuff.
#include <windows.h>             /*Proudly to be compiled with MingW!*/
#include <scrnsave.h>
#define TIMER 101

int randomx(int max, int seeder); /*Crappy RNG written by me*/

HINSTANCE hInstance;

LONG WINAPI ScreenSaverProc(HWND hWnd, UINT message, 
                               WPARAM wParam, LPARAM lParam)
{
  static HDC hDC;
  static int Width, Height;
  static RECT rect;
  PAINTSTRUCT ps;

  switch ( message ) {

  case WM_CREATE: 
    // get window dimensions
    GetClientRect( hWnd, &rect );
    Width = rect.right;		
    Height = rect.bottom;

    SetTimer( hWnd, TIMER, 200 /*Delay in milliseconds*/, NULL );
    hInstance = hMainInstance;
/*Initialization stuff goes here*/




    return 0;
  case WM_DESTROY:
    KillTimer( hWnd, TIMER );
 
    /* Clean up after yourself... */
    return 0;
  case WM_TIMER:
/*Evertime the timer ticks, the stuff in here happens, you can add more timers and have*/
/*pretty complex timing thingys with certain things happening when a particular timer ticks*/
/*However, I have no intention of going into that now.*/



    InvalidateRect(hWnd, NULL, FALSE);
    /*Currently, the only thing it'll do on a timer message is tell*/
    /*the Window that it needs to repaint the Window.*/ 
    return 0;				
  case WM_PAINT:
    hDC = BeginPaint(hWnd, &ps);
/*PAINT THE WINDOW TIME!  Here's were you get to paint the Window of the ScreenSaver*/



    EndPaint(hWnd, &ps);
    return 0;
  }
     
  /*Any other messages?...It's the screensaver-library's problem...*/

  return DefScreenSaverProc(hWnd, message, wParam, lParam );
}

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, 
                           WPARAM wParam, LPARAM lParam)
{
/*After you add a configuration dialog, you can fill in all the stuff that goes here,*/
/*it's just a dialog proc really*/

    return FALSE;
}

BOOL WINAPI RegisterDialogClasses(HANDLE dlg)
{

return TRUE;
}


BOOL VerifyPassword(HWND hwnd) /*Thanks to B. G. Edmond for writing these to password functions.*/
{   
  OSVERSIONINFO osv;
  osv.dwOSVersionInfoSize=sizeof(osv);
  GetVersionEx(&osv);
  if (osv.dwPlatformId==VER_PLATFORM_WIN32_NT)
  {return TRUE;}
  
  HINSTANCE hpwdcpl=LoadLibrary("PASSWORD.CPL");
  if (hpwdcpl==NULL) {return TRUE;}
  typedef BOOL (WINAPI *VERIFYSCREENSAVEPWD)(HWND hwnd);
  VERIFYSCREENSAVEPWD VerifyScreenSavePwd;
  VerifyScreenSavePwd=(VERIFYSCREENSAVEPWD)GetProcAddress(hpwdcpl,"VerifyScreenSavePwd");
  if (VerifyScreenSavePwd==NULL) {FreeLibrary(hpwdcpl);return TRUE;}
  BOOL bres=VerifyScreenSavePwd(hwnd); FreeLibrary(hpwdcpl);
  return bres;
}

void ChangePassword(HWND hwnd)
{ // This only ever gets called under '95, when started with the /a option.
     HINSTANCE hmpr=LoadLibrary("MPR.DLL");
     if (hmpr==NULL) {return;}
     typedef VOID (WINAPI *PWDCHANGEPASSWORD) (LPCSTR lpcRegkeyname,HWND hwnd,UINT uiReserved1,UINT uiReserved2);
     PWDCHANGEPASSWORD PwdChangePassword=(PWDCHANGEPASSWORD)GetProcAddress(hmpr,"PwdChangePasswordA");
     if (PwdChangePassword==NULL) {FreeLibrary(hmpr); return;}
     PwdChangePassword("SCRSAVE",hwnd,0,0); FreeLibrary(hmpr);
}



int randomx(int max, int seeder)           /*A random number generator...very basic*/
{
   SYSTEMTIME gen;
   static seed = 1;
   float temp;
   int number;

   if(max == -1)
   {seed = seeder; return 0;}

   GetLocalTime(&gen);
   /*
   temp = (gen.wMilliseconds * 1.34765350008) + (gen.wSecond * 1.00000865) + (seed * 0.985) + (seed * 2.341188106 * max);
   temp = temp * 2;
      -Most Random so far*/

   temp = (gen.wMilliseconds * 1.34765350008) + (gen.wSecond * 1.00000865) + (seed * 0.985) + (seed * 2.341188106 * max * (seed*0.15));
   temp = temp * 2.1;

   if(max == 0)
   {return (int)temp;}

   seed++;
   if(seed > 1900)
   {seed = 1;}
   number = (long)temp % max;
   return number;
}

Adding resources to your Screensaver

     Resources are such things as bitmaps, icons, cursors, and dialog boxes. When it comes to Windows resources, every compiler handles them differently. In MinGW, you have to make a resource script and then compile it into a resource file you can link into your program. Here's an example of such a script:
#include 
#include 
#define COL 7
#define photo 80
#define flash 81
#define SEMIMASK 9
#define SuperBlack 556
#define BG1 900
#define BG2 901
#define BG3 902
#define BG4 903
#define BG5 904
#define BG6 905
#define BG7 906
#define BG8 907
#define MUSAK 1000

COL BITMAP "sprites.bmp"
photo BITMAP "photoguy.bmp"
BG1 BITMAP "1.bmp"
BG2 BITMAP "2.bmp"
BG3 BITMAP "3.bmp"
BG4 BITMAP "4.bmp"
BG5 BITMAP "5.bmp"
BG6 BITMAP "6.bmp"
BG7 BITMAP "7.bmp"
BG8 BITMAP "8.bmp"
flash BITMAP "Flash256.bmp"
SEMIMASK BITMAP "Semimask.bmp"
SuperBlack BITMAP "black.bmp"
MUSAK WAVE "battle01.it"
ID_APP ICON "a.ico"

STRINGTABLE
BEGIN
 IDS_DESCRIPTION "EarthBound ScreenSaver"
 idsScreenSaver "EarthBound Screensaver"
 idsNoHelpMemory "This Screensaver needs at least 7.2MB of free physical memory"
END

DLG_SCRNSAVECONFIGURE DIALOG 6, 15, 205, 75 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "EBScreenSaver" FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "Save", IDOK, 16, 35, 50, 14
    PUSHBUTTON      "Close", IDCANCEL, 80, 35, 50, 14
    PUSHBUTTON      "A", 900, 191, 61, 14, 14
    AUTOCHECKBOX    "Sprites", 501, 9, 6, 34, 10
    AUTOCHECKBOX    "FullScreen Backgrounds", 502, 54, 19, 93, 10
    AUTOCHECKBOX    "Cropping (Effects FullScreen Graphics)", 504, 54, 6, 138, 10
    LTEXT           "EvilPuffBall", 78, 144, 38, 40, 10
    AUTOCHECKBOX    "Music", 503, 9, 19, 34, 10
    EDITTEXT         600, 8, 49, 55, 15, ES_NUMBER | ES_AUTOHSCROLL
    LTEXT           "Delay (lower=Screensaver faster, higher=slower)", 79, 68, 55, 100,20
    AUTOCHECKBOX    "PhotoGuy", 505, 8, 64, 55, 10
    AUTOCHECKBOX    "Low-Op Mode", 506, 136, 47, 60, 10
    AUTOCHECKBOX    "No Backgrounds", 507, 136,28,66,10
END
The last item in the script describes the configuration dialog.

With MinGW, to compile the script into a .cof file, you normally use this CLI command:
windres -i [insert .rc file here] -o [insert output .cof file name here] -O coff -F pe-i386

     I remember with the Borland IDE, you got a resource editor to help you with resources. I'm guessing that most IDE's come with some sort of help for resources and dialog-box editors.
     Of course, one does not need to add in the bitmaps she/he wishes to use like this. The windows API also allows you to load file resources. (like bitmaps, and cursors)

Compiling your Screensaver

     This is, I assume, to be the easiest part of making a screensaver, but I am probably wrong in most cases. To compile a screensaver you've written, simply go to compile as you normally would, but in this case, link in the screensaver library included with your compiler/the Windows API and the target's extension should be: ".scr".

In the case of MinGW, one way a person could compile their screensaver is this way: (the way in which I compile mine):

windres -i scrn.rc -o scrn.cof -O coff -F pe-i386
gcc -c scrn.c
gcc scrn.o scrn.cof -l scrnsave -l scrnsavw -l gdi32 -l winmm -l zlib -lmsimg32 -ldumbd -o a.scr -mwindows

It could probably also be done as:
windres -i scrn.rc -o scrn.cof -O coff -F pe-i386
gcc scrn.c -l scrnsave -l scrnsavw -l gdi32 -l winmm -l zlib -lmsimg32 -ldumbd -o screensaver.scr -mwindows

I tend to like the Command-line, eh?

Related Articles

Including other ways to make screensavers

Hosted by www.Geocities.ws

1