Metropoli BBS
VIEWER: bounce.c MODE: TEXT (ASCII)
/***************************************************************************\
* BOUNCE.C - An example Desktop Screen Saver extension by John Ridges
*            Converted to a 32-Bit Screen Blanker module by Peter Wansch
\***************************************************************************/

#define MAXBITMAPS 25   /* How many happy faces can be on the screen */

#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS
#define INCL_GPIBITMAPS
#define INCL_WINBUTTONS
#define INCL_WINDIALOGS
#define INCL_WINENTRYFIELDS
#define INCL_WININPUT
#define INCL_WINMESSAGEMGR
#define INCL_WINTIMER
#define INCL_WINWINDOWMGR
#define INCL_WINSHELLDATA

#include <os2.h>

typedef struct {
  HAB habBlanker;
  HWND hwndScreen;
  RECTL rclScreen;
  BOOL fClose;
  HMODULE hmodBlanker;
} BLANKERBLOCK;

typedef BLANKERBLOCK *PBLANKERBLOCK;

int rand();
void srand(unsigned int seed);
PCHAR pchStatus(PBLANKERBLOCK, PBOOL);
MRESULT EXPENTRY dpBlanker(HWND, ULONG, MPARAM, MPARAM);
VOID vdBlankerThread(VOID);

static PBLANKERBLOCK pBlankerBlock;

/*
   This is the record that describes the position and velocity of
   a happy face
*/

typedef struct {
  FIXED xvelocity,yvelocity;
  FIXED xposition,yposition;
} BITMAPREC;

/*
  This is the record that contains the options of the screen saver.
  These options are kept in the OS2.INI profile file.
*/

typedef struct {
  BOOL enabled;   /* The screen saver enabled status */
  int numbitmaps;   /* The number of happy faces on the screen */
} PROFILEREC;

static char name[] = "Bounce";  /* The name of this screen saver */
static BOOL gotprofile = FALSE;  /* Indicates that we've read the profile */
static PROFILEREC profile = {TRUE, 4};   /* Default values */

static unsigned int __rand = 1;
int rand()
{
  __rand = __rand * 69069 + 5;
  return ((__rand >> 16) & 0x7fff);
}
void srand(unsigned int seed)
{
  __rand = seed;
}

PCHAR pchStatus(PBLANKERBLOCK initptr, PBOOL enabledptr)
{
  ULONG i;

  /* Save the SAVERBLOCK address locally */
  pBlankerBlock = initptr;

  /* Read the profile (but only once!) */
  if (!gotprofile) {
    i = sizeof(PROFILEREC);
    PrfQueryProfileData(HINI_PROFILE,"Blanker",name,&profile,&i);
    gotprofile = TRUE;
  }
  /* Return the enabled status */
  *enabledptr = profile.enabled;

  /* Return the screen saver name */
  return name;
}

MRESULT EXPENTRY dpBlanker(HWND hwnd, ULONG message, MPARAM mp1, MPARAM mp2)
{
  SHORT i;

  switch(message) {
  case WM_INITDLG:
    /* Check the enabled button if enabled */
    if (profile.enabled)
      WinSendDlgItemMsg(hwnd,3,BM_SETCHECK,MPFROMSHORT(1),0);

    /* Set the Quantity field */
    WinSetDlgItemShort(hwnd,4,profile.numbitmaps,TRUE);

    /* Bring up the dialog */
    WinShowWindow(hwnd,TRUE);
    return FALSE;

  case WM_COMMAND:
    /* If OK is pushed */
    if (SHORT1FROMMP(mp1) == 1) {
      /* Get the value of the Quantity field */
      WinQueryDlgItemShort(hwnd,4,&i,TRUE);

      /* Check to see if the Quantity is in bounds */
      if (i < 1 || i > MAXBITMAPS) {
        /* Bring up an error message box */
        WinMessageBox(HWND_DESKTOP,hwnd,"The number of happy faces must "
          "be between 1 and 25",NULL,0,MB_OK|MB_ICONHAND);

        /* Hilight the Quantity field */
        WinSendDlgItemMsg(hwnd,4,EM_SETSEL,MPFROM2SHORT(0,
          WinQueryDlgItemTextLength(hwnd,4)),0);

        /* Give the Quantity field the focus */
        WinSetFocus(HWND_DESKTOP,WinWindowFromID(hwnd,4));

        /* Don't exit the dialog */
        return FALSE;
      }
      /* Save the number of happy faces */
      profile.numbitmaps = i;

      /* Get the enabled status */
      profile.enabled =
        SHORT1FROMMR(WinSendDlgItemMsg(hwnd,3,BM_QUERYCHECK,0,0));

      /* Write the profile data */
      PrfWriteProfileData(HINI_PROFILE,"Blanker",name,&profile,
        sizeof(PROFILEREC));
    }
  }
  return WinDefDlgProc(hwnd,message,mp1,mp2);
}

VOID vdBlankerThread(VOID)
{
  HAB hab;
  HPS hps;
  int i;
  FIXED tempx,tempy;
  ULONG sqrt;
  POINTL aptl[4];
  BITMAPREC *bitmaps;
  HBITMAP hbmp;

  /* Get an HAB for this thread (since we make PM calls) */
  hab = WinInitialize(0);

  /* Get an HPS of the screen */
  hps = WinGetPS(pBlankerBlock->hwndScreen);

  /* Paint the screen black */
  WinFillRect(hps,&pBlankerBlock->rclScreen,CLR_BLACK);

  /* Get memory for the array of BITMAPRECs */
        bitmaps = NULL;
        DosAllocMem((PPVOID)&bitmaps, sizeof(BITMAPREC)*profile.numbitmaps, PAG_READ | PAG_WRITE | PAG_COMMIT);

  /* Flag all the happy faces as 'position unknown' */
  for (i = 0; i < profile.numbitmaps; i++) 
          bitmaps[i].xposition = -1L;

  /* Get the handle to the happy face and its dimensions */
  hbmp = GpiLoadBitmap(hps,pBlankerBlock->hmodBlanker,1,0L,0L);
  aptl[2].x = aptl[2].y = 0;
  aptl[3].x = aptl[3].y = 34;

  /* Randomize RAND using the time */
  srand((unsigned int)WinGetCurrentTime(hab));

  /* Loop until Screen Blanker tells us to stop */
  while (!pBlankerBlock->fClose)

    /* Process for each happy face */
    for (i = 0; i < profile.numbitmaps; i++) {

    /* If the happy face's position is unknown, initialize it */
    if (bitmaps[i].xposition < 0) {

      /* Pick a random x velocity between -1 and 1 (fixed) */
      tempx = (long)rand()<<1;
      bitmaps[i].xvelocity = rand()&1 ? tempx : -tempx;

      /*
        Make the total velocity 1 by computing:
        yvelocity = sqrt(1 - xvelocity * xvelocity)
      */
      tempy = MAKEFIXED(0,65535)-((ULONG)tempx*(ULONG)tempx>>16);

      /* Cheesy sqrt routine to avoid linking in floating point */
      sqrt = 0;
      tempx = 1L<<15;
      do {
        sqrt ^= tempx;
        if (sqrt*sqrt>>16 > (ULONG)tempy) sqrt ^= tempx;
        tempx >>= 1;
      } while (tempx);

      /* Randomly set y velocity sign */
      bitmaps[i].yvelocity = rand()&1 ? sqrt : -sqrt;

      /* Randomly choose the x and y position of the happy face */
      tempx = MAKEFIXED(pBlankerBlock->rclScreen.xLeft,0)+(rand()*
        (pBlankerBlock->rclScreen.xRight-pBlankerBlock->rclScreen.xLeft-32)<<1);
      tempy = MAKEFIXED(pBlankerBlock->rclScreen.yBottom,0)+(rand()*
        (pBlankerBlock->rclScreen.yTop-pBlankerBlock->rclScreen.yBottom-32)<<1);
    }
    else {
      /* Find the new position of the happy face */
      tempx = bitmaps[i].xposition+bitmaps[i].xvelocity;
      tempy = bitmaps[i].yposition+bitmaps[i].yvelocity;

      /* See if the happy face has hit an edge of the screen */
      if (FIXEDINT(tempx) < (int)pBlankerBlock->rclScreen.xLeft) {

        /* Bounced off the left edge */
        tempx = (pBlankerBlock->rclScreen.xLeft<<17)-tempx;
        bitmaps[i].xvelocity = -bitmaps[i].xvelocity;
      }
      else if (FIXEDINT(tempx) >= (int)pBlankerBlock->rclScreen.xRight-32) {

        /* Bounced off the right edge */
        tempx = (pBlankerBlock->rclScreen.xRight-32<<17)-tempx;
        bitmaps[i].xvelocity = -bitmaps[i].xvelocity;
      }
      if (FIXEDINT(tempy) < (int)pBlankerBlock->rclScreen.yBottom) {

        /* Bounced off the bottom edge */
        tempy = (pBlankerBlock->rclScreen.yBottom<<17)-tempy;
        bitmaps[i].yvelocity = -bitmaps[i].yvelocity;
      }
      else if (FIXEDINT(tempy) >= (int)pBlankerBlock->rclScreen.yTop-32) {

        /* Bounced off the top edge */
        tempy = (pBlankerBlock->rclScreen.yTop-32<<17)-tempy;
        bitmaps[i].yvelocity = -bitmaps[i].yvelocity;
      }
    }
    /* Draw the happy face in the new position */
    aptl[0].x = FIXEDINT(tempx)-1;
    aptl[0].y = FIXEDINT(tempy)-1;
    aptl[1].x = FIXEDINT(tempx)+32;
    aptl[1].y = FIXEDINT(tempy)+32;
    GpiWCBitBlt(hps,hbmp,4L,aptl,ROP_SRCCOPY,BBO_IGNORE);

    /* Save the position of the happy face */
    bitmaps[i].xposition = tempx;
    bitmaps[i].yposition = tempy;
  }
  /* Release the happy face bitmap */
  GpiDeleteBitmap(hbmp);

  /* Release the memory for the array of BITMAPRECs */
  DosFreeMem(bitmaps);

  /* Release the HPS of the screen */
  WinReleasePS(hps);

  /* Get rid of the HAB */
  WinTerminate(hab);

  /* Make sure the stack doesn't vanish before we're completely gone */
  DosEnterCritSec();

  /* Tell Screen Blanker that we're gone */
 
  pBlankerBlock->fClose = FALSE;
}

[ RETURN TO DIRECTORY ]