/* Program: LIFE.CPP Author : Kim Moser Date : August 11th, 1992 System : IBM PC / Borland C++ 3.0 / Windows 3.1 Descrip: John Conway's game of LIFE for Windows */ #include #include #include #include // exit() #include #include "life.h" //======================================================================== #ifdef __cplusplus inline int max(int a, int b); inline int max(int a, int b) { return (a > b ? a : b); } inline int min(int a, int b); inline int min(int a, int b) { return (a < b ? a : b); } #endif static ofstream kim("life.out"); static int PtInOrOnRect(const RECT r, const POINT& p); static int PtInOrOnRect(const RECT r, const POINT& p) { return (p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom); } //======================================================================== BOOL InitWin(HANDLE hInstance); long FAR PASCAL LifeWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam); //------------------------------------------------------------------------ static int IS_GROWING = 0; static void growing(int g); static void growing(int g) { IS_GROWING = g; } static int is_growing(void); static int is_growing(void) { return (IS_GROWING); } //------------------------------------------------------------------------ #if 0 unsigned char Cell::masks[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, }; #endif class Field { private: // TYPES: typedef unsigned char Cell; // CONSTANTS: const int rows, cols; // VARIABLES: int current_generation; // Which generation is current [0..num_generations-1] Cell* cells; // Pointer to array of [width+2 * height+2 * num_generations] cells int num_generations; int top_cell, left_cell; // Index of top and left cells being displayed // FUNCTIONS: inline Cell& cell(int x, int y, int generation=0) const { return (cells[(generation*(rows+2)*(cols+2)) + ((y+1)*(cols+2))+(x+1)]); } void clear(void) { if (cells != NULL) { memset(cells, 0, num_generations*(rows+2)*(cols+2)); } } // Do NOT call these constructors! Field(Field& f) : rows(0), cols(0), current_generation(0) { } Field(void) : rows(0), cols(0), current_generation(0) { } public: // FUNCTIONS: // Constructors: Field(int width, int height, int generations = 2) : cols(width), rows(height), current_generation(0), top_cell(0), left_cell(0) { num_generations = generations; if (!(cells = new Cell[generations * (width+2) * (height+2)])) { cells = NULL; MessageBox(NULL, (LPSTR) "Out of memory allocating cells!", "Life", MB_ICONEXCLAMATION | MB_TASKMODAL); exit(-1); } else { clear(); } } inline int GetTopCell(void) const { return (top_cell); } inline int GetLeftCell(void) const { return (left_cell); } inline void SetTopCell(int i) { top_cell = i; } inline void SetLeftCell(int i) { left_cell = i; } inline int Cols(void) const { return (cols); } inline int Rows(void) const { return (rows); } inline int generation(void) const { return (current_generation); } int cell_in_range(int x, int y) { return ((x >= 0) && (x < Cols()) && (y >= 0) && (y < Rows())); } inline void setcellstate(register int x, register int y, register int generation) { cell(x, y, generation) = 1; } inline void setcellstate(register int x, register int y) { setcellstate(x, y, current_generation); } // ex: byte |= 00100000 //state[generation / 8] |= masks[generation % 8]; inline void clearcellstate(register int x, register int y, register int generation) { cell(x, y, generation) = 0; } inline void clearcellstate(register int x, register int y) { clearcellstate(x, y, current_generation); } // ex: byte &= 11011111 //state[generation / 8] &= ((char)~0) - masks[generation % 8]; inline int getcellstate(register int x, register int y, register int generation) const { return (cell(x, y, generation)); } inline int getcellstate(int x, register int y) const { return (getcellstate(x, y, current_generation)); } //return (state[generation / 8] & (((char)~0) - masks[generation % 8])); int nextgeneration(void) const { return (current_generation < num_generations-1 ? current_generation+1 : 0); } int previousgeneration(void) const { return (current_generation ? current_generation-1 : num_generations-1); } void grow(void) { int next_generation = nextgeneration(); // Process all cells in this field: for (int y=0; y < rows; y++) { for (int x=0; x < cols; x++) { int live_neighbors = 0; // How many live neighbors this cell has int new_state; // For this cell // Examine all this cell's neighbors: if (getcellstate(x-1, y-1)) live_neighbors++; if (getcellstate(x, y-1)) live_neighbors++; if (getcellstate(x+1, y-1)) live_neighbors++; if (getcellstate(x-1, y)) live_neighbors++; if (getcellstate(x+1, y)) live_neighbors++; if (getcellstate(x-1, y+1)) live_neighbors++; if (getcellstate(x, y+1)) live_neighbors++; if (getcellstate(x+1, y+1)) live_neighbors++; if (getcellstate(x, y)) { // This cell is alive: /* Alive with 2 or 3 neighbors survives: */ new_state = (live_neighbors == 2) || (live_neighbors == 3); } else { // This cell is dead: /* Dead with exactly 3 neighbors gets born: */ new_state = (live_neighbors == 3); } if (new_state) { setcellstate(x, y, next_generation); } else { clearcellstate(x, y, next_generation); } } } // The next generation is now the current one: current_generation = next_generation; } int IsLastColShowing(HWND hWnd, int cell_width, int cell_height, int border_width) { RECT range_rect; range_rect = calculate_visible_range(hWnd, cell_width, cell_height, border_width); return (range_rect.right == Cols()-1); } int IsLastRowShowing(HWND hWnd, int cell_width, int cell_height, int border_width) { RECT range_rect; range_rect = calculate_visible_range(hWnd, cell_width, cell_height, border_width); return (range_rect.bottom == Rows()-1); } // Given a window handle, determine largest size cells can be, // still fitting as many as possible on screen: void calculate_cell_size(HWND hWnd, int& cell_width, int& cell_height, int border_width) const { RECT clientrect; const MIN_CELL_WIDTH = 4, MIN_CELL_HEIGHT = 4; GetClientRect(hWnd, (LPRECT) &clientrect); cell_width = ((clientrect.right+1) / cols) - border_width; cell_height = ((clientrect.bottom+1) / rows) - border_width; // Rectangles must be at least 3 pixels wide and high: cell_width = max(cell_width, MIN_CELL_WIDTH); cell_height = max(cell_height, MIN_CELL_HEIGHT); } // Given a window handle, cell width, and cell height, // calculate range of cells which can be displayed // (not counting rows or columns whose cells would be only partially shown): RECT& calculate_visible_range(HWND hWnd, int cell_width, int cell_height, int border_width) const { static RECT r; RECT clientrect; GetClientRect(hWnd, (LPRECT) &clientrect); r.left = left_cell; r.top = top_cell; r.right = r.left + ((clientrect.right+1) / (cell_width+border_width)) - 1; r.bottom = r.top + ((clientrect.bottom+1) / (cell_height+border_width)) - 1; r.right = min(r.right, (cols-1)); r.bottom = min(r.bottom, (rows-1)); return (r); } int FitsHorizontallyInWindow(HWND hWnd, int cell_width, int cell_height, int border_width) { static RECT r; RECT clientrect; GetClientRect(hWnd, (LPRECT) &clientrect); return ((clientrect.right+1) / (cell_width+border_width) >= cols); } int FitsVerticallyInWindow(HWND hWnd, int cell_width, int cell_height, int border_width) { static RECT r; RECT clientrect; GetClientRect(hWnd, (LPRECT) &clientrect); return ((clientrect.bottom+1) / (cell_height+border_width) >= rows); } void scroll_horizontally(int direction) { int new_left = left_cell + direction; if (new_left >= 0 && new_left < cols) left_cell = new_left; } void scroll_vertically(int direction) { int new_top = top_cell + direction; if (new_top >= 0 && new_top < rows) top_cell = new_top; } void draw_cell_range(HWND hWnd, const RECT& range_rect, int cell_width, int cell_height, int border_width) { PAINTSTRUCT ps; HDC hDC; static LOGBRUSH live_brush_log = { BS_SOLID, 0x000000FF, 0 // Solid red brush }; static HANDLE live_brush = CreateBrushIndirect((LPLOGBRUSH) &live_brush_log); static HANDLE live_pen = GetStockObject(BLACK_PEN); static HANDLE dead_brush = GetStockObject(LTGRAY_BRUSH); static HANDLE dead_pen = CreatePen(PS_SOLID, 1, (DWORD) 0x00FF0000L); // Blue HANDLE last_brush_set; HANDLE desired_brush, desired_pen; hDC = BeginPaint(hWnd, &ps); //hDC = ps.hdc; SelectObject(hDC, last_brush_set = dead_brush); SelectObject(hDC, dead_pen); int cell_width_plus_border_width = cell_width + border_width; int cell_height_plus_border_width = cell_height + border_width; for (int y=range_rect.top; y <= range_rect.bottom; y++) { for (int x=range_rect.left; x <= range_rect.right; x++) { if (getcellstate(x,y)) { desired_brush = live_brush; desired_pen = live_pen; } else { desired_brush = dead_brush; desired_pen = dead_pen; } if (desired_brush != last_brush_set) { SelectObject(hDC, last_brush_set = desired_brush); SelectObject(hDC, desired_pen); } Rectangle(hDC, (x-GetLeftCell()) * cell_width_plus_border_width, (y-GetTopCell()) * cell_height_plus_border_width, (x-GetLeftCell()) * cell_width_plus_border_width + cell_width, (y-GetTopCell()) * cell_height_plus_border_width + cell_height ); } } SelectObject(hDC, GetStockObject(WHITE_PEN)); SelectObject(hDC, GetStockObject(WHITE_BRUSH)); RECT clientrect; GetClientRect(hWnd, (LPRECT) &clientrect); // Wipe currently unused rectangle on right: if (range_rect.right == cols-1) { kim << "erasing right rect " << ((range_rect.right-GetLeftCell()+1) * cell_width_plus_border_width) << "," << 0 << ", " << clientrect.right << "," << clientrect.bottom << "\n"; Rectangle(hDC, (range_rect.right-GetLeftCell()+1) * cell_width_plus_border_width, 0, clientrect.right, clientrect.bottom ); } // Wipe currently unused rectangle on bottom: if (range_rect.bottom == rows-1) { kim << "erasing bottom rect " << 0 << "," << ((range_rect.bottom-GetTopCell()+1) * cell_height_plus_border_width) << ", " << clientrect.right << "," << clientrect.bottom << "\n\n"; Rectangle(hDC, 0, (range_rect.bottom-GetTopCell()+1) * cell_height_plus_border_width, clientrect.right, clientrect.bottom ); } EndPaint(hWnd, &ps); } // Destructors: ~Field(void) { //ofstream kim("kim.out", ios::app); //kim << "~Field() called.\n"; if (cells != NULL) { delete[] cells; cells = NULL; } } friend ostream& operator <<(ostream& os, const Field& f) { os << "Current generation: " << f.generation() << '\n'; for (int y=0; y < f.rows; y++) { for (int x=0; x < f.cols; x++) { if (f.getcellstate(x, y, f.generation())) { os << '*'; } else { os << '.'; } } os << '\n'; } return (os); } }; //======================================================================== static void fieldinit(Field& f); static void fieldinit(Field& f) { for (int x=5; x < 15; x++) { f.setcellstate(x, 7); } for (int i=0; i < 1; i++) { f.grow(); } } static int GLOBAL_QUIT = 0; //------------------------------------------------------------------------ #ifdef __BORLANDC__ // Suppress "Parameter 'lpszCmdLine' is never used" warning #pragma argsused #endif static Field f(50, 50, 5); int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int cmdShow) { MSG msg; // Message (duh) HWND hWnd; // Window's handle fieldinit(f); if (!hPrevInstance) { // Init first instance if (!InitWin(hInstance)) return FALSE; } hWnd = CreateWindow("Life", // Win class name "Life", // Win caption WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, // Upper-left x pos 0, // Upper-left y pos CW_USEDEFAULT, // Initial x-size 0, // Initial y-size NULL, // No parent window LoadMenu(hInstance, "LifeMainMenu"), // Window menu hInstance, // Application instance NULL); // No creation params ShowWindow(hWnd, cmdShow); // Make win visible UpdateWindow(hWnd); // Update on screen //InvalidateRect(hWnd, (LPRECT) NULL, (BOOL) 0); while (1) { if (PeekMessage((LPMSG) &msg, hWnd, 0, 0, PM_NOREMOVE)) { // Msg(s) avail if (GetMessage(&msg, NULL, 0, 0)) { // Process msgs if ((hWnd == NULL) || (!IsDialogMessage(hWnd, &msg))) { TranslateMessage(&msg); DispatchMessage(&msg); } } else { break; } } else { // No msg(s) avail if (GLOBAL_QUIT) { break; } else if (is_growing()) { f.grow(); InvalidateRect(hWnd, (LPRECT) NULL, (BOOL) 0); } } } return (int) msg.wParam; } BOOL InitWin(HANDLE hInstance) { WNDCLASS wcLifeClass; wcLifeClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Mouse crsr wcLifeClass.hIcon = LoadIcon(hInstance, (LPSTR) "LifeIcon"); wcLifeClass.lpszMenuName = NULL; // No menu wcLifeClass.lpszClassName = "Life"; // Win class wcLifeClass.hbrBackground = GetStockObject(WHITE_BRUSH); // White bgrnd wcLifeClass.hInstance = hInstance; wcLifeClass.style = CS_VREDRAW | CS_HREDRAW | // Horiz & vert redraw of client area CS_DBLCLKS; wcLifeClass.lpfnWndProc = LifeWndProc; // Window function wcLifeClass.cbClsExtra = 0; // No extra bytes wcLifeClass.cbWndExtra = 0; // No extra bytes if (!RegisterClass(&wcLifeClass)) return FALSE; return (TRUE); } #ifdef __BORLANDC__ // Suppress "Parameter 'lParam' is never used" warning #pragma argsused #endif BOOL FAR PASCAL AboutProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam); BOOL FAR PASCAL AboutProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam) { switch (message) { case WM_COMMAND: switch (wParam) { case IDOK: EndDialog(hDlg, 1); return (TRUE); default: return (FALSE); } default: return (FALSE); } } #pragma argsused BOOL FAR PASCAL ConfigProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam); BOOL FAR PASCAL ConfigProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam) { BOOL r; switch (message) { case WM_COMMAND: switch (wParam) { case IDOK: case IDCANCEL: EndDialog(hDlg, 1); r = TRUE; goto EXIT; default: r = FALSE; goto EXIT; } default: r = FALSE; goto EXIT; } EXIT: return (r); } static ostream& operator<<(ostream& os, const RECT& r); static ostream& operator<<(ostream& os, const RECT& r) { os << "left=" << r.left << ", top=" << r.top << ", right=" << r.right << ", bottom=" << r.bottom << "\n"; return (os); } static RECT& area2cellrange(Field& f, const RECT& area, int cell_width, int cell_height, int border_width); static RECT& area2cellrange(Field& f, const RECT& area, int cell_width, int cell_height, int border_width) { static RECT cell_range; cell_range.left = f.GetLeftCell() + (area.left / (cell_width+border_width)); cell_range.top = f.GetTopCell() + (area.top / (cell_height+border_width)); cell_range.right = 1 + cell_range.left + ((area.right - area.left + 1) / (cell_width+border_width)); cell_range.bottom = 1 + cell_range.top + ((area.bottom - area.top + 1) / (cell_height+border_width)); cell_range.right = min(cell_range.right, (f.Cols()-1)); cell_range.bottom = min(cell_range.bottom, (f.Rows()-1)); return (cell_range); } static RECT& cellrange2area(Field& f, const RECT& cell_range, int cell_width, int cell_height, int border_width); static RECT& cellrange2area(Field& f, const RECT& cell_range, int cell_width, int cell_height, int border_width) { static RECT area; area.left = (cell_range.left - f.GetLeftCell()) * (cell_width+border_width); area.top = (cell_range.top - f.GetTopCell()) * (cell_height+border_width); area.right = area.left + ((cell_range.right - cell_range.left + 1) * (cell_width+border_width)); area.bottom = area.top + ((cell_range.bottom - cell_range.top + 1) * (cell_height+border_width)); return (area); } static void changecellat(Field& f, HWND hWnd, int bSet, const POINT& p, int cell_width, int cell_height, int border_width); static void changecellat(Field& f, HWND hWnd, int bSet, const POINT& p, int cell_width, int cell_height, int border_width) { RECT cell_range, area, visible; POINT cell_coord; area.left = area.right = p.x; area.top = area.bottom = p.y; // Determine what cell to change, and change it: cell_range = area2cellrange(f, area, cell_width, cell_height, border_width); // Determine visible range: visible = f.calculate_visible_range(hWnd, cell_width, cell_height, border_width); // Add another row and column (if it's within range), to allow // for clicking on cells that are not completely shown: if (visible.right < f.Cols()-1) visible.right++; if (visible.bottom < f.Rows()-1) visible.bottom++; cell_coord.x = cell_range.left; cell_coord.y = cell_range.top; if (PtInOrOnRect(visible, cell_coord)) { if (bSet) { f.setcellstate(cell_range.left, cell_range.top); } else { f.clearcellstate(cell_range.left, cell_range.top); } // Determine what screen area to invalidate, and invalidate it: area = cellrange2area(f, cell_range, cell_width, cell_height, border_width); InvalidateRect(hWnd, (LPRECT) &area, (BOOL) FALSE); SendMessage(hWnd, WM_PAINT, 0, 0); } } static void RecalcScrollBars(HWND hWnd, const Field& f, int cell_width, int cell_height, int border_width); static void RecalcScrollBars(HWND hWnd, const Field& f, int cell_width, int cell_height, int border_width) { RECT r = f.calculate_visible_range(hWnd, cell_width, cell_height, border_width); int hrange_max = r.left + (f.Cols() - (r.right + 1)), vrange_max = r.top + (f.Rows() - (r.bottom + 1)); //ofstream kim("life.out", ios::app); //kim << "r.bottom = " << r.bottom << " vrange_max = " << vrange_max << "\n"; SetScrollRange(hWnd, SB_HORZ, 0, hrange_max, 0); SetScrollRange(hWnd, SB_VERT, 0, vrange_max, 0); SetScrollPos(hWnd, SB_HORZ, f.GetLeftCell(), 1); SetScrollPos(hWnd, SB_VERT, f.GetTopCell(), 1); } long FAR PASCAL LifeWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam) { static HANDLE hInst; // For use by MakeProcInstance() static int cell_width, cell_height, border_width=1; switch (message) { case WM_CREATE: f.calculate_cell_size(hWnd, cell_width, cell_height, border_width); // Get instance handle of module which owns window, // so MakeProcInstance() has access to it when // we want to display the "About..." dialog box: hInst = GetWindowWord(hWnd, GWW_HINSTANCE); RecalcScrollBars(hWnd, f, cell_width, cell_height, border_width); //InvalidateRect(hWnd, (LPRECT) NULL, (BOOL) 0); break; case WM_PAINT: { RECT area, range; // Get screen area to be updated: if (GetUpdateRect(hWnd, (LPRECT) &area, FALSE)) { // Convert screen coords (range) to cell coords (range): range = area2cellrange(f, area, cell_width, cell_height, border_width); // Draw cell range: f.draw_cell_range(hWnd, range, cell_width, cell_height, border_width); } } break; case WM_DESTROY: GLOBAL_QUIT = 1; PostQuitMessage(0); break; case WM_MOUSEMOVE: { POINT p = MAKEPOINT(lParam); if (wParam == MK_LBUTTON) { changecellat(f, hWnd, 1, p, cell_width, cell_height, border_width); } else if (wParam == MK_RBUTTON) { changecellat(f, hWnd, 0, p, cell_width, cell_height, border_width); } } break; case WM_LBUTTONDOWN: { POINT p = MAKEPOINT(lParam); changecellat(f, hWnd, 1, p, cell_width, cell_height, border_width); } break; case WM_RBUTTONDOWN: { POINT p = MAKEPOINT(lParam); changecellat(f, hWnd, 0, p, cell_width, cell_height, border_width); } break; case WM_HSCROLL: { int tmp, oldpos, left=-1, pos=-1; switch (wParam) { case SB_THUMBPOSITION: tmp = LOWORD(lParam); oldpos = GetScrollPos(hWnd, SB_HORZ); if (tmp > oldpos) { // Scrolled right if (!f.IsLastColShowing(hWnd, cell_width, cell_height, border_width)) { pos = tmp; left = f.GetLeftCell() + (tmp - oldpos); } } else { // Scrolled left (or remained same) pos = tmp; left = f.GetLeftCell() - (oldpos - tmp); } break; case SB_LINEUP: // Left 1 col tmp = f.GetLeftCell(); if (tmp > 0) { pos = tmp - 1; left = tmp - 1; } break; case SB_LINEDOWN: // Right 1 col if (!f.IsLastColShowing(hWnd, cell_width, cell_height, border_width)) { pos = f.GetLeftCell() + 1; left = f.GetLeftCell() + 1; } break; default: break; } if (pos != -1) { f.SetLeftCell(left); SetScrollPos(hWnd, SB_HORZ, pos, 0); // Entire field might have scrolled into view, // so recalc scroll bars: RecalcScrollBars(hWnd, f, cell_width, cell_height, border_width); InvalidateRect(hWnd, (LPRECT) NULL, FALSE); } } break; case WM_VSCROLL: { int tmp, oldpos, top=-1, pos=-1; switch (wParam) { case SB_THUMBPOSITION: tmp = LOWORD(lParam); oldpos = GetScrollPos(hWnd, SB_VERT); if (tmp > oldpos) { // Scrolled down if (!f.IsLastRowShowing(hWnd, cell_width, cell_height, border_width)) { pos = tmp; top = f.GetTopCell() + (tmp - oldpos); } } else { // Scrolled up (or remained same) pos = tmp; top = f.GetTopCell() - (oldpos - tmp); } break; case SB_LINEUP: // Up 1 row tmp = f.GetTopCell(); if (tmp > 0) { pos = tmp - 1; top = tmp - 1; } break; case SB_LINEDOWN: // Right 1 col if (!f.IsLastRowShowing(hWnd, cell_width, cell_height, border_width)) { pos = f.GetTopCell() + 1; top = f.GetTopCell() + 1; } break; default: break; } if (pos != -1) { f.SetTopCell(top); SetScrollPos(hWnd, SB_VERT, pos, 0); // Entire field might have scrolled into view, // so recalc scroll bars: RecalcScrollBars(hWnd, f, cell_width, cell_height, border_width); InvalidateRect(hWnd, (LPRECT) NULL, FALSE); } } break; case WM_COMMAND: switch (wParam) { case IDM_EXIT: PostMessage(hWnd, WM_DESTROY, 0, 0); // Assume success break; case IDM_GROW: ModifyMenu(GetMenu(hWnd), IDM_GROW, MF_BYCOMMAND | MF_STRING, IDM_GROW, (LPSTR) (is_growing() ? "&Grow" : "&Stop")); DrawMenuBar(hWnd); growing(!is_growing()); break; case IDM_ABOUT: FARPROC lpprocAbout; lpprocAbout = MakeProcInstance((FARPROC) AboutProc, hInst); if (DialogBox(hInst, "AboutLife", hWnd, lpprocAbout)) { InvalidateRect(hWnd, NULL, FALSE); } FreeProcInstance(lpprocAbout); #if 0 /* Test removing the window's title bar: */ { DWORD ulStyle = GetWindowLong( hWnd, GWL_STYLE ); ulStyle &= ~( WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU ); SetWindowLong( hWnd, GWL_STYLE, ulStyle ); SendMessage(hWnd, WM_PAINT, 0, 0); } #endif break; case IDM_CONFIG: FARPROC lpprocConfig; lpprocConfig = MakeProcInstance((FARPROC) ConfigProc, hInst); if (DialogBox(hInst, "ConfigLife", hWnd, lpprocConfig)) { InvalidateRect(hWnd, NULL, FALSE); } FreeProcInstance(lpprocConfig); break; default: break; } break; case WM_SIZE: f.calculate_cell_size(hWnd, cell_width, cell_height, border_width); RecalcScrollBars(hWnd, f, cell_width, cell_height, border_width); // Fall through, so WM_SIZE message gets sent and acted on: default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0L); }