home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk11 / petzold / chap13 / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-20  |  12.5 KB  |  365 lines

  1. /*--------------------------------------
  2.    LIFE.C -- John Conway's Game of Life
  3.   --------------------------------------*/
  4.  
  5. #define INCL_WIN
  6. #define INCL_GPI
  7. #include <os2.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include "life.h"
  11.  
  12. #define ID_TIMER    1
  13.  
  14. MRESULT EXPENTRY ClientWndProc (HWND, USHORT, MPARAM, MPARAM) ;
  15.  
  16. CHAR szClientClass [] = "Life" ;
  17. HAB  hab ;
  18.  
  19. int main (void)
  20.      {
  21.      static ULONG flFrameFlags = FCF_TITLEBAR      | FCF_SYSMENU  |
  22.                                  FCF_SIZEBORDER    | FCF_MINMAX   |
  23.                                  FCF_SHELLPOSITION | FCF_TASKLIST |
  24.                                  FCF_MENU          | FCF_ICON ;
  25.      HMQ          hmq ;
  26.      HWND         hwndFrame, hwndClient ;
  27.      QMSG         qmsg ;
  28.  
  29.      hab = WinInitialize (0) ;
  30.      hmq = WinCreateMsgQueue (hab, 0) ;
  31.  
  32.      WinRegisterClass (hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0) ;
  33.  
  34.      hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE,
  35.                                      &flFrameFlags, szClientClass, NULL,
  36.                                      0L, NULL, ID_RESOURCE, &hwndClient) ;
  37.  
  38.      while (WinGetMsg (hab, &qmsg, NULL, 0, 0))
  39.           WinDispatchMsg (hab, &qmsg) ;
  40.  
  41.      WinDestroyWindow (hwndFrame) ;
  42.      WinDestroyMsgQueue (hmq) ;
  43.      WinTerminate (hab) ;
  44.      return 0 ;
  45.      }
  46.  
  47. VOID EnableMenuItem (HWND hwndMenu, SHORT idMenuItem, BOOL fEnable)
  48.      {
  49.      WinSendMsg (hwndMenu, MM_SETITEMATTR, 
  50.                  MPFROM2SHORT (idMenuItem, TRUE),
  51.                  MPFROM2SHORT (MIA_DISABLED, fEnable ? 0 : MIA_DISABLED)) ;
  52.      }
  53.  
  54. VOID ErrorMsg (HWND hwnd, CHAR *szMessage)
  55.      {
  56.      WinMessageBox (HWND_DESKTOP, hwnd, szMessage, szClientClass, 0,
  57.                     MB_OK | MB_ICONEXCLAMATION) ;
  58.      }
  59.  
  60. VOID DrawCell (HPS hps, SHORT x, SHORT y, SHORT cxCell, SHORT cyCell,
  61.                BYTE bCell)
  62.      {
  63.      RECTL rcl ;
  64.  
  65.      rcl.xLeft   = x * cxCell ;
  66.      rcl.yBottom = y * cyCell ;
  67.      rcl.xRight  = rcl.xLeft   + cxCell - 1 ;
  68.      rcl.yTop    = rcl.yBottom + cyCell - 1 ;
  69.  
  70.      WinFillRect (hps, &rcl, bCell & 1 ? CLR_NEUTRAL : CLR_BACKGROUND) ;
  71.      }
  72.  
  73. VOID DoGeneration (HPS hps, PBYTE pbGrid, SHORT xNumCells, SHORT yNumCells,
  74.                    SHORT cxCell, SHORT cyCell)
  75.      {
  76.      SHORT x, y, sSum ;
  77.  
  78.      for (y = 0 ; y < yNumCells - 1 ; y++)
  79.           for (x = 0 ; x < xNumCells ; x++)
  80.                {
  81.                if (x == 0 || x == xNumCells - 1 || y == 0)
  82.                     *pbGrid |= *pbGrid << 4 ;
  83.                else
  84.                     {
  85.                     sSum = (*(pbGrid             - 1) +    // Left
  86.                             *(pbGrid - xNumCells - 1) +    // Lower Left
  87.                             *(pbGrid - xNumCells    ) +    // Lower
  88.                             *(pbGrid - xNumCells + 1))     // Lower Right
  89.                                              >> 4 ;
  90.  
  91.                     sSum += *(pbGrid             + 1) +    // Right
  92.                             *(pbGrid + xNumCells + 1) +    // Upper Right
  93.                             *(pbGrid + xNumCells    ) +    // Upper
  94.                             *(pbGrid + xNumCells - 1) ;    // Upper Left
  95.  
  96.                     sSum = (sSum | *pbGrid) & 0x0F ;
  97.  
  98.                     *pbGrid <<= 4 ;
  99.  
  100.                     if (sSum == 3)
  101.                          *pbGrid |= 1 ;
  102.  
  103.                     if ((*pbGrid & 1) != *pbGrid >> 4)
  104.                          DrawCell (hps, x, y, cxCell, cyCell, *pbGrid) ;
  105.                     }
  106.                pbGrid++ ;
  107.                }
  108.      }
  109.  
  110. VOID DisplayGenerationNum (HPS hps, SHORT xGen, SHORT yGen, LONG lGeneration)
  111.      {
  112.      static CHAR szBuffer [24] = "Generation " ;
  113.      POINTL      ptl ;
  114.  
  115.      ptl.x = xGen ;
  116.      ptl.y = yGen ;
  117.  
  118.      ltoa (lGeneration, szBuffer + 11, 10) ;
  119.  
  120.      GpiSavePS (hps) ;
  121.  
  122.      GpiSetBackMix (hps, BM_OVERPAINT) ;
  123.      GpiCharStringAt (hps, &ptl, (LONG) strlen (szBuffer), szBuffer) ;
  124.  
  125.      GpiRestorePS (hps, -1L) ;
  126.      }
  127.  
  128. MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  129.      {
  130.      static BOOL  fTimerGoing ;
  131.      static HWND  hwndMenu ;
  132.      static LONG  lGeneration ;
  133.      static SEL   selGrid ;
  134.      static SHORT cxChar, cyChar, cyDesc, cxClient, cyClient, xGenNum, yGenNum,
  135.                   cxCell, cyCell, xNumCells, yNumCells, sCellScale = 1 ;
  136.      FONTMETRICS  fm ;
  137.      HPS          hps ;
  138.      PBYTE        pbGrid ;
  139.      POINTL       ptl ;
  140.      SHORT        x, y ;
  141.  
  142.      switch (msg)
  143.           {
  144.           case WM_CREATE:
  145.                hps = WinGetPS (hwnd) ;
  146.                GpiQueryFontMetrics (hps, (LONG) sizeof fm, &fm) ;
  147.                cxChar = (SHORT) fm.lAveCharWidth ;
  148.                cyChar = (SHORT) fm.lMaxBaselineExt ;
  149.                cyDesc = (SHORT) fm.lMaxDescender ;
  150.                WinReleasePS (hps) ;
  151.  
  152.                hwndMenu = WinWindowFromID (
  153.                                WinQueryWindow (hwnd, QW_PARENT, FALSE),
  154.                                FID_MENU) ;
  155.                return 0 ;
  156.  
  157.           case WM_SIZE:
  158.                if (selGrid)
  159.                     {
  160.                     DosFreeSeg (selGrid) ;
  161.                     selGrid = 0 ;
  162.                     }
  163.  
  164.                if (fTimerGoing)
  165.                     {
  166.                     WinStopTimer (hab, hwnd, ID_TIMER) ;
  167.                     fTimerGoing = FALSE ;
  168.                     }
  169.  
  170.                cxClient = SHORT1FROMMP (mp2) ;
  171.                cyClient = SHORT2FROMMP (mp2) ;
  172.  
  173.                xGenNum = cxChar ;
  174.                yGenNum = cyClient - cyChar + cyDesc ;
  175.  
  176.                cxCell = cxChar * 2 / sCellScale ;
  177.                cyCell = cyChar / sCellScale ;
  178.  
  179.                xNumCells = cxClient / cxCell ;
  180.                yNumCells = (cyClient - cyChar) / cyCell ;
  181.  
  182.                if (xNumCells <= 0 || yNumCells <= 0)
  183.                     {
  184.                     ErrorMsg (hwnd, "Not enough room for even one cell.") ;
  185.                     }
  186.  
  187.                else if ((LONG) xNumCells * yNumCells > 65536L)
  188.                     {
  189.                     ErrorMsg (hwnd, "More than 64K cells not supported.") ;
  190.                     }
  191.  
  192.                else if (DosAllocSeg (xNumCells * yNumCells, &selGrid, 0))
  193.                     {
  194.                     ErrorMsg (hwnd, "Not enough memory for this many cells.") ;
  195.                     selGrid = 0 ;
  196.                     }
  197.  
  198.                else
  199.                     {
  200.                     pbGrid = MAKEP (selGrid, 0) ;
  201.  
  202.                     for (y = 0 ; y < yNumCells ; y++)
  203.                          for (x = 0 ; x < xNumCells ; x++)
  204.                               *pbGrid++ = 0 ;
  205.                     }
  206.  
  207.                EnableMenuItem (hwndMenu, IDM_SIZE,  TRUE) ;
  208.                EnableMenuItem (hwndMenu, IDM_START, selGrid != 0) ;
  209.                EnableMenuItem (hwndMenu, IDM_STOP,  FALSE) ;
  210.                EnableMenuItem (hwndMenu, IDM_STEP,  selGrid != 0) ;
  211.                EnableMenuItem (hwndMenu, IDM_CLEAR, selGrid != 0) ;
  212.  
  213.                lGeneration = 0 ;
  214.                return 0 ;
  215.  
  216.           case WM_BUTTON1DOWN:
  217.                x = MOUSEMSG(&msg)->x / cxCell ;
  218.                y = MOUSEMSG(&msg)->y / cyCell ;
  219.  
  220.                if (selGrid && !fTimerGoing && x < xNumCells && y < yNumCells)
  221.                     {
  222.                     pbGrid = MAKEP (selGrid, 0) ;
  223.  
  224.                     hps = WinGetPS (hwnd) ;
  225.  
  226.                     DrawCell (hps, x, y, cxCell, cyCell,
  227.                               *(pbGrid + y * xNumCells + x) ^= 1) ;
  228.  
  229.                     WinReleasePS (hps) ;
  230.                     }
  231.                else
  232.                     WinAlarm (HWND_DESKTOP, WA_WARNING) ;
  233.                break ;
  234.  
  235.           case WM_COMMAND:
  236.                switch (COMMANDMSG(&msg)->cmd)
  237.                     {
  238.                     case IDM_LARGE:
  239.                     case IDM_SMALL:
  240.                     case IDM_TINY:
  241.                          WinSendMsg (hwndMenu, MM_SETITEMATTR,
  242.                                      MPFROM2SHORT (sCellScale, TRUE),
  243.                                      MPFROM2SHORT (MIA_CHECKED, 0)) ;
  244.  
  245.                          sCellScale = COMMANDMSG(&msg)->cmd ;
  246.  
  247.                          WinSendMsg (hwndMenu, MM_SETITEMATTR,
  248.                                      MPFROM2SHORT (sCellScale, TRUE),
  249.                                      MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED)) ;
  250.  
  251.                          WinSendMsg (hwnd, WM_SIZE, NULL,
  252.                                      MPFROM2SHORT (cxClient, cyClient)) ;
  253.  
  254.                          WinInvalidateRect (hwnd, NULL, FALSE) ;
  255.                          return 0 ;
  256.  
  257.                     case IDM_START:
  258.                          if (!WinStartTimer (hab, hwnd, ID_TIMER, 1))
  259.                               {
  260.                               ErrorMsg (hwnd, "Too many clocks or timers.") ;
  261.                               }
  262.                          else
  263.                               {
  264.                               fTimerGoing = TRUE ;
  265.  
  266.                               EnableMenuItem (hwndMenu, IDM_SIZE,  FALSE) ;
  267.                               EnableMenuItem (hwndMenu, IDM_START, FALSE) ;
  268.                               EnableMenuItem (hwndMenu, IDM_STOP,  TRUE) ;
  269.                               EnableMenuItem (hwndMenu, IDM_STEP,  FALSE) ;
  270.                               EnableMenuItem (hwndMenu, IDM_CLEAR, FALSE) ;
  271.                               }
  272.                          return 0 ;
  273.  
  274.                     case IDM_STOP:
  275.                          WinStopTimer (hab, hwnd, ID_TIMER) ;
  276.                          fTimerGoing = FALSE ;
  277.  
  278.                          EnableMenuItem (hwndMenu, IDM_SIZE,  TRUE) ;
  279.                          EnableMenuItem (hwndMenu, IDM_START, TRUE) ;
  280.                          EnableMenuItem (hwndMenu, IDM_STOP,  FALSE) ;
  281.                          EnableMenuItem (hwndMenu, IDM_STEP,  TRUE) ;
  282.                          EnableMenuItem (hwndMenu, IDM_CLEAR, TRUE) ;
  283.                          return 0 ;
  284.  
  285.                     case IDM_STEP:
  286.                          WinSendMsg (hwnd, WM_TIMER, NULL, NULL) ;
  287.                          return 0 ;
  288.  
  289.                     case IDM_CLEAR:
  290.                          lGeneration = 0L ;
  291.  
  292.                          pbGrid = MAKEP (selGrid, 0) ;
  293.  
  294.                          for (y = 0 ; y < yNumCells ; y++)
  295.                               for (x = 0 ; x < xNumCells ; x++)
  296.                                    *pbGrid++ = 0 ;
  297.  
  298.                          WinInvalidateRect (hwnd, NULL, FALSE) ;
  299.                          return 0 ;
  300.                     }
  301.                break ;
  302.  
  303.           case WM_TIMER:
  304.                hps = WinGetPS (hwnd) ;
  305.  
  306.                DisplayGenerationNum (hps, xGenNum, yGenNum, ++lGeneration) ;
  307.  
  308.                pbGrid = MAKEP (selGrid, 0) ;
  309.  
  310.                DoGeneration (hps, pbGrid, xNumCells, yNumCells, cxCell, cyCell);
  311.  
  312.                WinReleasePS (hps) ;
  313.                return 0 ;
  314.  
  315.           case WM_PAINT:
  316.                hps = WinBeginPaint (hwnd, NULL, NULL) ;
  317.                GpiErase (hps) ;
  318.  
  319.                if (selGrid)
  320.                     {
  321.                     for (x = 1 ; x <= xNumCells ; x++)
  322.                          {
  323.                          ptl.x = cxCell * x - 1 ;
  324.                          ptl.y = 0 ;
  325.                          GpiMove (hps, &ptl) ;
  326.  
  327.                          ptl.y = cyCell * yNumCells - 1 ;
  328.                          GpiLine (hps, &ptl) ;
  329.                          }
  330.  
  331.                     for (y = 1 ; y <= yNumCells ; y++)
  332.                          {
  333.                          ptl.x = 0 ;
  334.                          ptl.y = cyCell * y - 1 ;
  335.                          GpiMove (hps, &ptl) ;
  336.  
  337.                          ptl.x = cxCell * xNumCells - 1 ;
  338.                          GpiLine (hps, &ptl) ;
  339.                          }
  340.  
  341.                     pbGrid = MAKEP (selGrid, 0) ;
  342.  
  343.                     for (y = 0 ; y < yNumCells ; y++)
  344.                          for (x = 0 ; x < xNumCells ; x++)
  345.                               if (*pbGrid++)
  346.                                    DrawCell (hps, x, y, cxCell, cyCell,
  347.                                              *(pbGrid - 1)) ;
  348.  
  349.                     DisplayGenerationNum (hps, xGenNum, yGenNum, lGeneration) ;
  350.                     }
  351.                WinEndPaint (hps) ;
  352.                return 0 ;
  353.  
  354.           case WM_DESTROY:
  355.                if (fTimerGoing)
  356.                     WinStopTimer (hab, hwnd, ID_TIMER) ;
  357.  
  358.                if (selGrid)
  359.                     DosFreeSeg (selGrid) ;
  360.  
  361.                return 0 ;
  362.           }
  363.      return WinDefWindowProc (hwnd, msg, mp1, mp2) ;
  364.      }
  365.