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