/**************************************************************************** * * FILE: LIFE.C * * DESCRIPTION: Windows 3.0 Version of the classic simulation of "LIFE". * This program incorporates a minor twist in the basic * algorithm by utilizing color to represent various * life "phases". The final phase of any life cycle is * always death! * * This software is hereby placed in the public domain. * Use and abuse it at your own risk! * * * AUTHOR: Tom Wheeler * 31294 Morlock * Livonia, Mich. 48152 * [72037,1742] * ***************************************************************************** * * DATE VER DESCRIPTION * ---------- --- --------------------------------------------------- * 05/20/91 1.0 Initial Development 1.0 * 05/22/91 1.1 Added Color Support * ****************************************************************************/ #include #include #include #include #include #include "life.h" #define CLASSNAME "LifeClass" #define APPNAME "Life" #define DEAD 0 /* state: cell is dead */ #define ALIVE 0x1 /* state: cell is alive */ #define CHILD 0x2 /* state: cell is a child */ #define ADOLESCENT 0x4 /* state: cell is an adolescent */ #define ADULT 0x8 /* state: cell is an adult */ #define MIDAGED 0x10 /* state: cell is middle aged */ #define RETIRED 0x20 /* state: cell is retirement age */ #define SENIOR 0x40 /* state: cell is a senior citizen */ #define METHUSULA 0x80 /* state: cell is to be terminated */ #define AGECYCLE 10 /* # of cycles till next age */ #define MAX_XCELL 100 /* max number of horizontal cells */ #define MAX_YCELL 100 /* max number of vertical cells */ #define DEFAULT_WIDTH 15 /* default number of cells to display */ #define DEFAULT_HEIGHT 15 /* default number of cells to display */ #define CYCLE_TIMER 1 /* Macro to get a random integer within a specified range */ #define getrandom( min, max ) ((rand() % (int)(((max)+1) - (min))) + (min)) typedef unsigned char UCHAR; /* MATRIX defines the cell matrix used to represent the grid of entities. * Life states are represented as the low byte of the matrix entry, the high * byte is used to is used to keep a count representing the age of the * individual entities. */ typedef int MATRIX[MAX_XCELL+2][MAX_YCELL+2]; typedef struct _Life { int CellX; /* individual cell width */ int CellY; /* individual cell height */ int TopCellX; /* horiz. id of first cell displayed */ int TopCellY; /* vert. id of frist cell displayed */ int CellsPerPageX; /* num of visible X cells on screen */ int CellsPerPageY; /* num of visible Y cells on screen */ BOOL Grid; /* grid on/off state */ BOOL Run; /* run state of matrix */ MATRIX Matrix; /* state matrix */ }LIFE; typedef LIFE *PLIFE; MATRIX Scratch; /* temporary cell matrix */ HWND hInst = NULL; /* instance variable */ HMENU hMenuFrame = NULL; /* instance menu handle */ HWND hWndFrame = NULL; /* instance window handle */ HANDLE hAccelFrame = NULL; /* instance keyboard accelerator */ HANDLE hMatrix = NULL; /* handle to cell matrix structure */ HPEN hBluePen = NULL; /* Blue drawing pen */ HPEN hGreenPen = NULL; /* Green drawing pen */ HPEN hCyanPen = NULL; /* Cyan drawing pen */ HPEN hRedPen = NULL; /* Red drawing pen */ HPEN hMagentaPen = NULL; /* Magenta drawing pen */ HPEN hBrownPen = NULL; /* Brown drawing pen */ HPEN hGrayPen = NULL; /* Gray drawing pen */ HPEN hLightRedPen = NULL; /* Light Red drawing pen */ HPEN hWhitePen = NULL; /* White drawing pen */ HBRUSH hBlueBrush = NULL; /* Solid Blue brush */ HBRUSH hGreenBrush = NULL; /* Solid Green brush */ HBRUSH hCyanBrush = NULL; /* Solid Cyan brush */ HBRUSH hRedBrush = NULL; /* Solid Red brush */ HBRUSH hMagentaBrush = NULL; /* Solid Magenta brush */ HBRUSH hBrownBrush = NULL; /* Solid Brown brush */ HBRUSH hGrayBrush = NULL; /* Solid Gray brush */ HBRUSH hLightRedBrush = NULL; /* Solid Light Red brush */ HBRUSH hWhiteBrush = NULL; /* Solid White brush */ PLIFE pLife = NULL; /* pointer to cell life structure */ int iSmall; /* size in pixels of a small cell */ int iNorm; /* size in pixels of a normal cell */ int iLarge; /* size in pixels of a large cell */ int iCycleTime = 250; /* default timer (milliseconds) */ WORD wCycleTimer = 0; /* timer used for life cycling */ /**************************************************************************** * void DrawGridLines(HDC hDC,RECT *rect) * * Description: DrawsGridLines on the Life Display Window * * Input: hDC - Device Context to draw on * rect - pointer to client rectangle * * Output: N/A ****************************************************************************/ void DrawGridLines(HDC hDC,RECT *rect) { int iX,iY; SelectObject(hDC,hGrayPen); for(iX = pLife->CellX; iX < rect->right; iX += pLife->CellX) { MoveTo(hDC,iX,0); LineTo(hDC,iX,rect->bottom); } for(iY = pLife->CellY; iY < rect->bottom; iY += pLife->CellY) { MoveTo(hDC,0,iY); LineTo(hDC,rect->right,iY); } } /**************************************************************************** * void DrawCell(HDC hMouseMoveDC,int iXPos,int iYPos,UCHAR ucState) * * Description: Draws the cell at the indicated grid position in the color * appropriate to its current state. * * Input: hDC - Device Context to draw on * iXPos * iYPos - Screen Matrix position to draw at * ucState - State to draw * * Output: N/A ****************************************************************************/ void DrawCell(HDC hDC,int iXPos,int iYPos,UCHAR ucState) { int iX,iY; HPEN hPen; HBRUSH hBrush; iX = iXPos * pLife->CellX; iY = iYPos * pLife->CellY; if(ucState & ALIVE) { if(ucState & SENIOR) { hPen = hBluePen; hBrush = hBlueBrush; } else if(ucState & RETIRED) { hPen = hBrownPen; hBrush = hBrownBrush; } else if(ucState & MIDAGED) { hPen = hMagentaPen; hBrush = hMagentaBrush; } else if(ucState & ADULT) { hPen = hRedPen; hBrush = hRedBrush; } else if(ucState & ADOLESCENT) { hPen = hCyanPen; hBrush = hCyanBrush; } else if(ucState & CHILD) { hPen = hGreenPen; hBrush = hGreenBrush; } } else { hPen = hWhitePen; hBrush = hWhiteBrush; } SelectObject(hDC,hPen); SelectObject(hDC,hBrush); Rectangle(hDC,iX+2,iY+2,iX+pLife->CellX-1,iY+pLife->CellY-1); } /**************************************************************************** * void DrawMatrix(HDC hDC) * * Description: Draws The Matrix (only draws the cells that will be visible * in the display window) * * Input: hDC - Device Context to draw on * * Output: N/A ****************************************************************************/ void DrawMatrix(HDC hDC) { int iX,iY,iXPos,iYPos,iCellTemp; for(iXPos = 0,iX = pLife->TopCellX; iX < pLife->CellsPerPageX + pLife->TopCellX; iX++,iXPos++) { for(iYPos = 0,iY = pLife->TopCellY; iY < pLife->CellsPerPageY + pLife->TopCellY; iY++,iYPos++) { iCellTemp = pLife->Matrix[iX][iY] & 0xff; if(iCellTemp & ALIVE) DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp); } } } /**************************************************************************** * void CycleMatrix(HDC hDC) * * Description: Runs the Life Algorithm on the Cell Matrix (only updates * the cells that would be visible, the matrix is forced to * wrap at the limits of the screen matrix). * * Input: hDC - Device Context to draw on * * Output: N/A ****************************************************************************/ void CycleMatrix(HDC hDC) { register int iX,iY; int iXPos,iYPos,iNeighbor,iCellTemp; UCHAR ucState; BOOL bNoneAlive = TRUE; for(iX = pLife->TopCellX,iXPos = 0; iX < (pLife->CellsPerPageX + pLife->TopCellX); iX++,iXPos++) { for(iY = pLife->TopCellY,iYPos = 0; iY < (pLife->CellsPerPageY + pLife->TopCellY); iY++,iYPos++) { iNeighbor = 0; if(pLife->Matrix[iX-1][iY] & ALIVE) iNeighbor++; if(pLife->Matrix[iX+1][iY] & ALIVE) iNeighbor++; if(pLife->Matrix[iX][iY-1] & ALIVE) iNeighbor++; if(pLife->Matrix[iX][iY+1] & ALIVE) iNeighbor++; if(pLife->Matrix[iX-1][iY-1] & ALIVE) iNeighbor++; if(pLife->Matrix[iX+1][iY+1] & ALIVE) iNeighbor++; if(pLife->Matrix[iX-1][iY+1] & ALIVE) iNeighbor++; if(pLife->Matrix[iX+1][iY-1] & ALIVE) iNeighbor++; ucState = (UCHAR)pLife->Matrix[iX][iY]; if(ucState == DEAD) { /* birth of a cell if 3 neighbors present */ if(iNeighbor == 3) { Scratch[iX][iY] = ALIVE | CHILD; DrawCell(hDC,iXPos,iYPos,ALIVE | CHILD); } else { Scratch[iX][iY] = pLife->Matrix[iX][iY]; } } else { bNoneAlive = FALSE; if((iNeighbor < 2) || (iNeighbor > 3)) { /* kill it off */ Scratch[iX][iY] = DEAD; DrawCell(hDC,iXPos,iYPos,DEAD); } else { /* Keep cell and increment cycle count. If cycle count limit * is reached, advance cell to next life cycle */ Scratch[iX][iY] = pLife->Matrix[iX][iY] + 0x100; if(!((Scratch[iX][iY] & 0xff00) % (AGECYCLE << 8))) { iCellTemp = ((Scratch[iX][iY] & 0xff) << 1) | ALIVE; if(iCellTemp <= METHUSULA) Scratch[iX][iY] = (Scratch[iX][iY] & 0xff00) + iCellTemp; else { /* if it has aged past SENIOR, kill it off */ Scratch[iX][iY] = iCellTemp = DEAD; } DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp); } } } } } /* copy all changes to permanent cell matrix */ memmove(&pLife->Matrix,&Scratch,sizeof(MATRIX)); /* randomize the matrix if no alive cells are displayed */ if(bNoneAlive) { for(iX = pLife->TopCellX; iX < (pLife->CellsPerPageX + pLife->TopCellX); iX++) { for(iY = pLife->TopCellY; iY < (pLife->CellsPerPageY + pLife->TopCellY); iY++) { if(getrandom(0,1)) { pLife->Matrix[iX][iY] = ALIVE | CHILD; } } } } } /**************************************************************************** * void UpdateScrollBarRange(HWND hWnd) * * Description: Updates the Scroll Bar Ranges * * Input: hWnd - Window Handle owning the scroll bars * * Output: N/A ****************************************************************************/ void UpdateScrollBarRange(HWND hWnd) { RECT rect; GetClientRect(hWnd,&rect); pLife->CellsPerPageX = (rect.right / pLife->CellX) + 1; pLife->CellsPerPageY = (rect.bottom / pLife->CellY) + 1; pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL - pLife->CellsPerPageX)); pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL - pLife->CellsPerPageY)); SetScrollRange(hWnd,SB_HORZ,1,MAX_XCELL - pLife->CellsPerPageX,TRUE); SetScrollRange(hWnd,SB_VERT,1,MAX_YCELL - pLife->CellsPerPageY,TRUE); } /**************************************************************************** * BOOL InitLife(void) * * Description: Life Initialization * * Input: N/A * * Output: Fails if no memory available ****************************************************************************/ BOOL InitLife(void) { int iWidth,iHeight,iX,iY; /* allocate local storage to hold the Life Cell Matrix */ if((hMatrix = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(LIFE))) == NULL) return FALSE; pLife = (PLIFE)LocalLock(hMatrix); /* lock down the memory permanently */ /* set the default Life cell values */ iX = GetSystemMetrics(SM_CXSCREEN); iY = GetSystemMetrics(SM_CYSCREEN); iNorm = iY / 25; iSmall = iNorm / 2; iLarge = iNorm * 2; pLife->CellX = iNorm; pLife->CellY = iNorm; pLife->Grid = TRUE; pLife->Run = FALSE; pLife->TopCellX = pLife->TopCellY = 1; CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED); /* initialize all cells to DEAD */ for(iWidth = 0; iWidth < MAX_XCELL; iWidth++) for(iHeight = 0; iHeight < MAX_YCELL; iHeight++) pLife->Matrix[iWidth][iHeight] = DEAD; iWidth = (pLife->CellX * DEFAULT_WIDTH) + (GetSystemMetrics(SM_CXFRAME) * 2) + GetSystemMetrics(SM_CXHTHUMB); iHeight = (pLife->CellY * DEFAULT_HEIGHT) + (GetSystemMetrics(SM_CYFRAME) * 2) + GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYVTHUMB) + GetSystemMetrics(SM_CYCAPTION); SetWindowPos(hWndFrame,NULL,(iX - iWidth) / 2,(iY - iHeight) / 2, iWidth,iHeight,SWP_NOZORDER); } /**************************************************************************** * CenterDialog(HWND hDlg) * * Description: Function to Center a Dialog Box on the Screen * * Input: hDlg - Handle to Dialog Box to be centered * * Output: N/A *****************************************************************************/ void CenterDialog(HWND hDlg) { RECT rect; int iX,iY,iNewX,iNewY; GetWindowRect(hDlg,(RECT far *)&rect); iX = GetSystemMetrics(SM_CXSCREEN); iY = GetSystemMetrics(SM_CYSCREEN); iNewX = (iX/2) - ((rect.right-rect.left)/2); iNewY = (iY/2) - ((rect.bottom-rect.top)/2); SetWindowPos(hDlg,NULL,max(iNewX,0),max(iNewY,0),rect.right-rect.left, rect.bottom-rect.top,SWP_NOZORDER); } /**************************************************************************** * * BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam, * LONG lParam) * * DESCRIPTION: Life Cycle Time Edit Dialog Box Proc * * INPUT: hDlg - Handle to Dialog Box * iMessage - Dialog Box message * wParam * lParam - Standard Windows message parameters * Called externally from WINDOWS - Exported Function * * OUTPUT: N/A * ****************************************************************************/ BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam, LONG lParam) { BOOL bTransFlag; WORD wVal; char szTime[10]; switch (iMessage) { case WM_COMMAND: switch(wParam) { case IDOK: wVal = GetDlgItemInt(hDlg,TIMER_EDIT,(BOOL FAR *)&bTransFlag, FALSE); if((!bTransFlag) || (wVal < 100) || (wVal > 999)) { MessageBeep(0); sprintf(szTime,"%d",iCycleTime); SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime); } else { iCycleTime = wVal; EndDialog(hDlg,TRUE); } break; case IDCANCEL: EndDialog(hDlg,FALSE); break; } break; case WM_INITDIALOG: sprintf(szTime,"%d",iCycleTime); SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime); CenterDialog(hDlg); return(TRUE); default: return(FALSE); } return(FALSE); } /**************************************************************************** * AboutDlgProc(hDlg,iMessage,wParam,lParam) * * Description: About Dialog Box Procedure * * Input: hDlg - Handle to Dialog Box * uMessage - Dialog Box message * wParam * lParam - Standard Windows message parameters * Called externally from WINDOWS * * Output: N/A *****************************************************************************/ BOOL FAR PASCAL AboutDlgProc(HWND hDlg,unsigned iMessage, WORD wParam,LONG lParam) { BOOL Ret; char szTimeStamp[128]; Ret = FALSE; switch (iMessage) { case WM_INITDIALOG: sprintf(szTimeStamp,"Built: %s %s",__DATE__,__TIME__); SetDlgItemText(hDlg,ABOUT_TIMESTAMP,szTimeStamp); CenterDialog(hDlg); Ret = TRUE; break; case WM_COMMAND: EndDialog(hDlg, TRUE); Ret = TRUE; break; } return Ret; } /**************************************************************************** * WndProc(hWnd,iMessage,wParam,lParam) * * Description: Main Window Proc * * Input: hWnd - Handle to window * iMessage - Window message number * wParam, * lParam - Standard Windows message parameters * Called externally from WINDOWS * * Output: Calls DefWindowProc() while active,returns 0L ((LONG) FALSE) * when terminating. ****************************************************************************/ long FAR PASCAL WndProc(HWND hWnd,unsigned iMessage,WORD wParam,LONG lParam) { FARPROC lpProc; long Ret; PAINTSTRUCT ps; static RECT rect; HDC hDC; WORD wXMouse,wYMouse; UCHAR ucState; static int iXDown,iYDown; static BOOL bMouseDown = FALSE,bIconic = FALSE; static HDC hMouseMoveDC = NULL; int iXCell,iYCell; Ret = (long)FALSE; switch(iMessage) { case WM_COMMAND: switch (wParam) { case MENU_RUN: if(pLife->Run) { /* stop the Cycle Timer */ if(wCycleTimer) { KillTimer(hWnd,CYCLE_TIMER); wCycleTimer = 0; } ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN, "St&art\t^R"); EnableMenuItem(hMenuFrame,MENU_STEP,MF_ENABLED); pLife->Run = FALSE; } else { /*start the cycle timer */ if((wCycleTimer = SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) { MessageBox(hWnd,"No More System Timers!", NULL,MB_OK | MB_ICONEXCLAMATION); break; } ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN, "St&op\t^R"); pLife->Run = TRUE; EnableMenuItem(hMenuFrame,MENU_STEP,MF_GRAYED); } break; case MENU_STEP: hDC = GetDC(hWnd); CycleMatrix(hDC); ReleaseDC(hWnd,hDC); InvalidateRect(hWnd,NULL,FALSE); break; case MENU_CLEAR: /* if running, stop before clearing */ if(pLife->Run) SendMessage(hWnd,WM_COMMAND,MENU_RUN,0L); for(iXCell = 0; iXCell < MAX_XCELL; iXCell++) { for(iYCell = 0; iYCell < MAX_YCELL; iYCell++) { pLife->Matrix[iXCell][iYCell] = DEAD; Scratch[iXCell][iYCell] = DEAD; } } InvalidateRect(hWnd,NULL,TRUE); break; case MENU_GRID: if(pLife->Grid) { ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID, "&Gridlines On\t^G"); pLife->Grid = FALSE; } else { ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID, "&Gridlines Off\t^G"); pLife->Grid = TRUE; } InvalidateRect(hWnd,NULL,TRUE); break; case MENU_TIMER: lpProc = MakeProcInstance((FARPROC) TimerEditDlgProc,hInst); DialogBox(hInst,"TIMERDLGBOX",hWnd,lpProc); FreeProcInstance(lpProc); /* if already running, update the timer */ if(wCycleTimer) { KillTimer(hWnd,CYCLE_TIMER); if((wCycleTimer = SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) { MessageBox(hWnd,"No More System Timers!", NULL,MB_OK | MB_ICONEXCLAMATION); break; } } break; case MENU_SMALL: CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_CHECKED); CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED); pLife->CellX = iSmall; pLife->CellY = iSmall; UpdateScrollBarRange(hWnd); InvalidateRect(hWnd,NULL,TRUE); break; case MENU_NORM: CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED); CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED); pLife->CellX = iNorm; pLife->CellY = iNorm; UpdateScrollBarRange(hWnd); InvalidateRect(hWnd,NULL,TRUE); break; case MENU_LARGE: CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_CHECKED); pLife->CellX = iLarge; pLife->CellY = iLarge; UpdateScrollBarRange(hWnd); InvalidateRect(hWnd,NULL,TRUE); break; case MENU_ABOUT: lpProc = MakeProcInstance((FARPROC) AboutDlgProc,hInst); DialogBox(hInst,"ABOUTBOX",hWnd,lpProc); FreeProcInstance(lpProc); break; } break; case WM_TIMER: hDC = GetDC(hWnd); CycleMatrix(hDC); ReleaseDC(hWnd,hDC); if(!bIconic) InvalidateRect(hWnd,NULL,FALSE); break; case WM_LBUTTONDOWN: iXDown = -1; iYDown = -1; bMouseDown = TRUE; hMouseMoveDC = GetDC(hWnd); GetClientRect(hWnd,&rect); SelectObject(hMouseMoveDC,GetStockObject(WHITE_PEN)); break; case WM_LBUTTONUP: /* force draw at current position */ SendMessage(hWnd,WM_MOUSEMOVE,wParam,lParam); if(bMouseDown) { bMouseDown = FALSE; SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN)); ReleaseDC(hWnd,hMouseMoveDC); } break; case WM_MOUSEMOVE: if(bMouseDown) { wXMouse = LOWORD(lParam); wYMouse = HIWORD(lParam); /* translate mouse position into cell coordinates */ iXCell = (int)wXMouse / pLife->CellX; iYCell = (int)wYMouse / pLife->CellY; if((iXCell != iXDown) || (iYCell != iYDown)) { iXDown = iXCell; iYDown = iYCell; ucState = (UCHAR)pLife->Matrix[iXCell+pLife->TopCellX] [iYCell+pLife->TopCellY]; ucState = (UCHAR)((ucState & ALIVE) ? DEAD : ALIVE | CHILD); pLife->Matrix[iXCell+pLife->TopCellX] [iYCell+pLife->TopCellY] = ucState; DrawCell(hMouseMoveDC,iXCell,iYCell,ucState); } } break; case WM_NCMOUSEMOVE: /* look to see if the mouse is outside the client area, if so and * the mouse is being captured then release it */ if(bMouseDown) { bMouseDown = FALSE; SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN)); ReleaseDC(hWnd,hMouseMoveDC); } break; case WM_VSCROLL: switch(wParam) { case SB_LINEUP: pLife->TopCellY -= 1; break; case SB_PAGEUP: pLife->TopCellY -= pLife->CellsPerPageY; break; case SB_LINEDOWN: pLife->TopCellY += 1; break; case SB_PAGEDOWN: pLife->TopCellY += pLife->CellsPerPageY; break; case SB_THUMBPOSITION: pLife->TopCellY = LOWORD(lParam); break; default: break; } pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL - pLife->CellsPerPageY)); if(pLife->TopCellY != GetScrollPos(hWnd,SB_VERT)) { iXDown = -1; iYDown = -1; SetScrollPos(hWnd,SB_VERT,pLife->TopCellY,TRUE); InvalidateRect(hWnd,NULL,TRUE); } break; case WM_HSCROLL: switch(wParam) { case SB_LINEUP: pLife->TopCellX -= 1; break; case SB_PAGEUP: pLife->TopCellX -= pLife->CellsPerPageX; break; case SB_LINEDOWN: pLife->TopCellX += 1; break; case SB_PAGEDOWN: pLife->TopCellX += pLife->CellsPerPageX; break; case SB_THUMBPOSITION: pLife->TopCellX = LOWORD(lParam); break; default: break; } pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL - pLife->CellsPerPageX)); if(pLife->TopCellX != GetScrollPos(hWnd,SB_HORZ)) { iXDown = -1; iYDown = -1; SetScrollPos(hWnd,SB_HORZ,pLife->TopCellX,TRUE); InvalidateRect(hWnd,NULL,TRUE); } break; case WM_KEYDOWN: switch(wParam) { case VK_PRIOR: SendMessage(hWnd,WM_VSCROLL,SB_PAGEUP,0L); break; case VK_NEXT: SendMessage(hWnd,WM_VSCROLL,SB_PAGEDOWN,0L); break; case VK_UP: SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0L); break; case VK_DOWN: SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0L); break; case VK_LEFT: SendMessage(hWnd,WM_HSCROLL,SB_PAGEUP,0L); break; case VK_RIGHT: SendMessage(hWnd,WM_HSCROLL,SB_PAGEDOWN,0L); break; } break; case WM_SIZE: UpdateScrollBarRange(hWnd); break; case WM_PAINT: hDC = BeginPaint(hWnd,&ps); if(pLife->Grid) DrawGridLines(hDC,&ps.rcPaint); DrawMatrix(hDC); EndPaint(hWnd,&ps); break; case WM_SYSCOMMAND: switch(wParam) { case SC_MINIMIZE: bIconic = TRUE; break; case SC_MAXIMIZE: case SC_RESTORE: bIconic = FALSE; break; } return DefWindowProc(hWnd, iMessage, wParam, lParam); break; case WM_CREATE: hBluePen = CreatePen(PS_SOLID,1,RGB(0,0,128)); hGreenPen = CreatePen(PS_SOLID,1,RGB(0,128,0)); hCyanPen = CreatePen(PS_SOLID,1,RGB(0,128,128)); hRedPen = CreatePen(PS_SOLID,1,RGB(128,0,0)); hMagentaPen = CreatePen(PS_SOLID,1,RGB(128,0,128)); hBrownPen = CreatePen(PS_SOLID,1,RGB(128,128,0)); hGrayPen = CreatePen(PS_SOLID,1,RGB(192,192,192)); hLightRedPen = CreatePen(PS_SOLID,1,RGB(255,0,0)); hWhitePen = CreatePen(PS_SOLID,1,RGB(255,255,255)); hBlueBrush = CreateSolidBrush(RGB(0,0,128)); hGreenBrush = CreateSolidBrush(RGB(0,128,0)); hCyanBrush = CreateSolidBrush(RGB(0,128,128)); hRedBrush = CreateSolidBrush(RGB(128,0,0)); hMagentaBrush = CreateSolidBrush(RGB(128,0,128)); hBrownBrush = CreateSolidBrush(RGB(128,128,0)); hGrayBrush = CreateSolidBrush(RGB(192,192,192)); hLightRedBrush = CreateSolidBrush(RGB(255,0,0)); hWhiteBrush = CreateSolidBrush(RGB(255,255,255)); srand((unsigned)time(NULL)); break; case WM_CLOSE: if(wCycleTimer) { KillTimer(hWnd,CYCLE_TIMER); wCycleTimer = 0; } if(hMatrix != NULL) { LocalUnlock(hMatrix); LocalFree(hMatrix); } DeleteObject(hBluePen); DeleteObject(hGreenPen); DeleteObject(hCyanPen); DeleteObject(hRedPen); DeleteObject(hMagentaPen); DeleteObject(hBrownPen); DeleteObject(hGrayPen); DeleteObject(hWhitePen); DeleteObject(hBlueBrush); DeleteObject(hGreenBrush); DeleteObject(hCyanBrush); DeleteObject(hRedBrush); DeleteObject(hMagentaBrush); DeleteObject(hBrownBrush); DeleteObject(hGrayBrush); DeleteObject(hWhiteBrush); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, iMessage, wParam, lParam); } return 0L; } /**************************************************************************** * int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance, * LPSTR lpszCmdLine,int nCmdShow) * * Description: Life Main Window Function * * Input: hInstance - Window instance handle * hPrevInstance - Previous Instance Handle * lpszCmdLine - Window Activation Parameters (none req.) * nCmdShow - Show Window or Icon command * Called externally from WINDOWS * * Output: Handles Windows message loop,terminates to Windows *****************************************************************************/ int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpszCmdLine, int nCmdShow) { HWND hWnd; MSG msg; WNDCLASS wc; hInst = hInstance; if(!hPrevInstance) { wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance,"LIFEICON"); wc.hCursor = LoadCursor(hInstance,"HAND"); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MainMenu"; wc.lpszClassName = CLASSNAME; if(!RegisterClass(&wc)) { MessageBox(NULL, "Initialization Error!", APPNAME, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL); return TRUE; } } hWnd = CreateWindow(CLASSNAME, /* Window class name */ APPNAME, /* window caption */ WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, /* window style */ CW_USEDEFAULT, /* initial x (horiz) position */ CW_USEDEFAULT, /* initial y (vert) position */ CW_USEDEFAULT, /* initial x size */ CW_USEDEFAULT, /* initial y size */ NULL, /* parent window handle */ NULL, /* window menu handle */ hInstance, /* program instance handle */ NULL); /* create parameters */ /* initialize variables */ hWndFrame = hWnd; /* global parent window */ hMenuFrame = GetMenu(hWnd); /* global menu handle */ hAccelFrame = LoadAccelerators(hInstance,"LifeKeys"); /* key accelerator */ /* initialize the Life cell structure */ if(!InitLife()) return TRUE; ShowWindow(hWnd,nCmdShow); /* shows the window */ /* message loop */ while(GetMessage(&msg,NULL,0,0)) { if(!TranslateAccelerator(hWnd,hAccelFrame,&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }