-//\r
-// matrix.c\r
-//\r
-// Matrix-window implementation\r
-//\r
-#include <windows.h>\r
-#include <windowsx.h>\r
-#include <tchar.h>\r
-#include "globals.h"\r
-#include "message.h"\r
-#include "matrix.h"\r
-#include "resource.h"\r
-\r
-void DoMatrixMessage(HDC hdc, MATRIX *matrix);\r
-\r
-// pseudo-random number generator, based on 16bit CRC algorithm\r
-static WORD _crc_reg = 0;\r
-int crc_rand()\r
-{\r
- const WORD mask = 0xb400;\r
-\r
- if(_crc_reg & 1)\r
- _crc_reg = (_crc_reg >> 1) ^ mask;\r
- else\r
- _crc_reg = (_crc_reg >> 1);\r
-\r
- return _crc_reg;\r
-}\r
-\r
-int GlyphIntensity(GLYPH glyph)\r
-{\r
- return (int)((glyph & 0x7f00) >> 8);\r
-}\r
-\r
-GLYPH DarkenGlyph(GLYPH glyph)\r
-{\r
- int intensity = GlyphIntensity(glyph);\r
- \r
- if(intensity > 0)\r
- return GLYPH_REDRAW | ((intensity - 1) << 8) | (glyph & 0x00FF);\r
- else\r
- return glyph;\r
-}\r
-\r
-GLYPH RandomGlyph(int intensity)\r
-{\r
- return GLYPH_REDRAW | (intensity << 8) | (crc_rand() % NUM_GLYPHS);\r
-}\r
-\r
-void RedrawBlip(GLYPH *glypharr, int blippos)\r
-{\r
- glypharr[blippos+0] |= GLYPH_REDRAW;\r
- glypharr[blippos+1] |= GLYPH_REDRAW;\r
- glypharr[blippos+8] |= GLYPH_REDRAW;\r
- glypharr[blippos+9] |= GLYPH_REDRAW;\r
-}\r
-\r
-void ScrollMatrixColumn(MATRIX_COLUMN *col)\r
-{\r
- int y;\r
- GLYPH lastglyph = 0;\r
- GLYPH thisglyph;\r
-\r
- // wait until we are allowed to scroll\r
- if(col->started == FALSE)\r
- {\r
- if(--col->countdown <= 0)\r
- col->started = TRUE;\r
-\r
- return;\r
- }\r
-\r
- // "seed" the glyph-run\r
- lastglyph = col->state ? (GLYPH)0 : (GLYPH)(MAX_INTENSITY << 8);\r
-\r
- //\r
- // loop over the entire length of the column, looking for changes\r
- // in intensity/darkness. This change signifies the start/end\r
- // of a run of glyphs.\r
- //\r
- for(y = 0; y < col->length; y++)\r
- {\r
- thisglyph = col->glyph[y];\r
-\r
- // bottom-most part of "run". Insert a new character (glyph)\r
- // at the end to lengthen the run down the screen..gives the\r
- // impression that the run is "falling" down the screen\r
- if(GlyphIntensity(thisglyph) < GlyphIntensity(lastglyph) && \r
- GlyphIntensity(thisglyph) == 0)\r
- {\r
- col->glyph[y] = RandomGlyph(MAX_INTENSITY - 1);\r
- y++;\r
- }\r
- // top-most part of "run". Delete a character off the top by\r
- // darkening the glyph until it eventually disappears (turns black). \r
- // this gives the effect that the run as dropped downwards\r
- else if(GlyphIntensity(thisglyph) > GlyphIntensity(lastglyph))\r
- {\r
- col->glyph[y] = DarkenGlyph(thisglyph);\r
- \r
- // if we've just darkened the last bit, skip on so\r
- // the whole run doesn't go dark\r
- if(GlyphIntensity(thisglyph) == MAX_INTENSITY - 1)\r
- y++;\r
- }\r
-\r
- lastglyph = col->glyph[y];\r
- }\r
-\r
- // change state from blanks <-> runs when the current run as expired\r
- if(--col->runlen <= 0)\r
- {\r
- if(col->state ^= 1) \r
- col->runlen = crc_rand() % (3 * DENSITY/2) + DENSITY_MIN;\r
- else\r
- col->runlen = crc_rand() % (DENSITY_MAX+1-DENSITY) + (DENSITY_MIN*2);\r
- }\r
-\r
- //\r
- // make a "blip" run down this column at double-speed\r
- //\r
-\r
- // mark current blip as redraw so it gets "erased"\r
- if(col->blippos >= 0 && col->blippos < col->length)\r
- RedrawBlip(col->glyph, col->blippos);\r
-\r
- // advance down screen at double-speed\r
- col->blippos += 2;\r
- \r
- // if the blip gets to the end of a run, start it again (for a random\r
- // length so that the blips never get synched together)\r
- if(col->blippos >= col->bliplen)\r
- {\r
- col->bliplen = col->length + crc_rand() % 50;\r
- col->blippos = 0;\r
- }\r
-\r
- // now redraw blip at new position\r
- if(col->blippos >= 0 && col->blippos < col->length)\r
- RedrawBlip(col->glyph, col->blippos);\r
-\r
-}\r
-\r
-//\r
-// randomly change a small collection glyphs in a column\r
-//\r
-void RandomMatrixColumn(MATRIX_COLUMN *col)\r
-{\r
- int i, y;\r
-\r
- for(i = 1, y = 0; i < 16; i++)\r
- {\r
- // find a run\r
- while(GlyphIntensity(col->glyph[y]) < MAX_INTENSITY-1 && y < col->length) \r
- y++;\r
-\r
- if(y >= col->length)\r
- break;\r
-\r
- col->glyph[y] = (col->glyph[y] & 0xff00) | (crc_rand() % NUM_GLYPHS);\r
- col->glyph[y] |= GLYPH_REDRAW;\r
-\r
- y += crc_rand() % 10;\r
- }\r
-}\r
-\r
-void DrawGlyph(MATRIX *matrix, HDC hdc, int xpos, int ypos, GLYPH glyph)\r
-{\r
- int intensity = GlyphIntensity(glyph);\r
- int glyphidx = glyph & 0xff;\r
-\r
- BitBlt(hdc, xpos, ypos, GLYPH_WIDTH, GLYPH_HEIGHT, matrix->hdcBitmap,\r
- glyphidx * GLYPH_WIDTH, intensity * GLYPH_HEIGHT, SRCCOPY);\r
-}\r
-\r
-void RedrawMatrixColumn(MATRIX_COLUMN *col, MATRIX *matrix, HDC hdc, int xpos)\r
-{\r
- int y;\r
-\r
- // loop down the length of the column redrawing only what needs doing\r
- for(y = 0; y < col->length; y++)\r
- {\r
- GLYPH glyph = col->glyph[y];\r
-\r
- // does this glyph (character) need to be redrawn?\r
- if(glyph & GLYPH_REDRAW)\r
- {\r
- if((y == col->blippos+0 || y == col->blippos+1 ||\r
- y == col->blippos+8 || y == col->blippos+9) && \r
- GlyphIntensity(glyph) >= MAX_INTENSITY-1)\r
- glyph |= MAX_INTENSITY << 8;\r
-\r
- DrawGlyph(matrix, hdc, xpos, y * GLYPH_HEIGHT, glyph);\r
- \r
- // clear redraw state\r
- col->glyph[y] &= ~GLYPH_REDRAW;\r
- }\r
- }\r
-}\r
-\r
-void DecodeMatrix(HWND hwnd, MATRIX *matrix)\r
-{\r
- int x;\r
- HDC hdc = GetDC(hwnd);\r
-\r
- for(x = 0; x < matrix->numcols; x++)\r
- {\r
- RandomMatrixColumn(&matrix->column[x]); \r
- ScrollMatrixColumn(&matrix->column[x]);\r
- RedrawMatrixColumn(&matrix->column[x], matrix, hdc, x * GLYPH_WIDTH);\r
- }\r
-\r
- if(matrix->message)\r
- DoMatrixMessage(hdc, matrix);\r
-\r
- ReleaseDC(hwnd, hdc);\r
-}\r
-\r
-//\r
-// Allocate matrix structures\r
-//\r
-MATRIX *CreateMatrix(HWND hwnd, int width, int height)\r
-{\r
- MATRIX *matrix;\r
- HDC hdc;\r
- int x, y;\r
-\r
- int rows = height / GLYPH_HEIGHT + 1;\r
- int cols = width / GLYPH_WIDTH + 1;\r
-\r
- // allocate matrix!\r
- if((matrix = malloc(sizeof(MATRIX) + sizeof(MATRIX_COLUMN) * cols)) == 0)\r
- return 0;\r
-\r
- matrix->numcols = cols;\r
- matrix->numrows = rows;\r
- matrix->width = width;\r
- matrix->height = height;\r
-\r
- for(x = 0; x < cols; x++)\r
- {\r
- matrix->column[x].length = rows;\r
- matrix->column[x].started = FALSE;\r
- matrix->column[x].countdown = crc_rand() % 100;\r
- matrix->column[x].state = crc_rand() % 2;\r
- matrix->column[x].runlen = crc_rand() % 20 + 3;\r
-\r
- matrix->column[x].glyph = malloc(sizeof(GLYPH) * (rows+16));\r
-\r
- for(y = 0; y < rows; y++)\r
- matrix->column[x].glyph[y] = 0;//;\r
- }\r
- \r
- // Load bitmap!!\r
- hdc = GetDC(NULL);\r
- matrix->hbmBitmap = LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BITMAP1));\r
- matrix->hdcBitmap = CreateCompatibleDC(hdc);\r
- SelectObject(matrix->hdcBitmap, matrix->hbmBitmap);\r
- ReleaseDC(NULL, hdc);\r
-\r
- // Create a message for this window...only if we are\r
- // screen-saving (not if in preview mode)\r
- if(GetParent(hwnd) == 0)\r
- matrix->message = InitMatrixMessage(hwnd, matrix->numcols, matrix->numrows);\r
- else\r
- matrix->message = 0;\r
-\r
- return matrix;\r
-}\r
-\r
-//\r
-// Free up matrix structures\r
-//\r
-void DestroyMatrix(MATRIX *matrix)\r
-{\r
- int x;\r
-\r
- // free the matrix columns\r
- for(x = 0; x < matrix->numcols; x++)\r
- free(matrix->column[x].glyph);\r
-\r
- DeleteDC(matrix->hdcBitmap);\r
- DeleteObject(matrix->hbmBitmap);\r
-\r
- // now delete the matrix!\r
- free(matrix);\r
-}\r
-\r
-MATRIX *GetMatrix(HWND hwnd)\r
-{\r
- return (MATRIX *)GetWindowLong(hwnd, 0);\r
-}\r
-\r
-void SetMatrix(HWND hwnd, MATRIX *matrix)\r
-{\r
- SetWindowLong(hwnd, 0, (LONG)matrix);\r
-}\r
-\r
-//\r
-// Window procedure for one matrix (1 per screen)\r
-//\r
-LRESULT WINAPI MatrixWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
-{\r
- static POINT ptLast;\r
- static POINT ptCursor;\r
- static BOOL fFirstTime = TRUE;\r
- \r
- MATRIX *matrix = GetMatrix(hwnd);\r
-\r
- switch(msg)\r
- {\r
- // window creation\r
- case WM_NCCREATE:\r
-\r
- // create the matrix based on how big this window is\r
- matrix = CreateMatrix(hwnd, ((CREATESTRUCT *)lParam)->cx, ((CREATESTRUCT *)lParam)->cy);\r
-\r
- // failed to allocate? stop window creation!\r
- if(matrix == 0)\r
- return FALSE;\r
-\r
- SetMatrix(hwnd, matrix);\r
-\r
- // start off an animation timer\r
- SetTimer(hwnd, 0xdeadbeef, ((SPEED_MAX - g_nMatrixSpeed) + SPEED_MIN) * 10, 0);\r
-\r
- return TRUE;\r
-\r
- // window being destroyed, cleanup\r
- case WM_NCDESTROY:\r
- DestroyMatrix(matrix);\r
- PostQuitMessage(0);\r
- return 0;\r
-\r
- // animation timer has gone off, redraw the matrix!\r
- case WM_TIMER:\r
- DecodeMatrix(hwnd, matrix);\r
- return 0;\r
-\r
- // break out of screen-saver if any keyboard activity\r
- case WM_KEYDOWN:\r
- case WM_SYSKEYDOWN:\r
- PostMessage(hwnd, WM_CLOSE, 0, 0);\r
- return 0;\r
-\r
- // break out of screen-saver if any mouse activity\r
- case WM_LBUTTONDOWN:\r
- case WM_LBUTTONUP:\r
- case WM_RBUTTONDOWN:\r
- case WM_RBUTTONUP:\r
- case WM_MBUTTONDOWN:\r
- case WM_MBUTTONUP:\r
- case WM_MOUSEMOVE:\r
- \r
- // If we've got a parent then we must be a preview\r
- if(GetParent(hwnd) != 0)\r
- return 0;\r
-\r
- if(fFirstTime)\r
- {\r
- GetCursorPos(&ptLast);\r
- fFirstTime = FALSE;\r
- }\r
-\r
- GetCursorPos(&ptCursor);\r
- \r
- // if the mouse has moved more than 3 pixels then exit\r
- if(abs(ptCursor.x - ptLast.x) >= 3 || abs(ptCursor.y - ptLast.y) >= 3)\r
- PostMessage(hwnd, WM_CLOSE, 0, 0);\r
-\r
- ptLast = ptCursor;\r
- \r
- return 0;\r
-\r
- // someone wants to close us...see if it's ok\r
- case WM_CLOSE:\r
-\r
- if(VerifyPassword(hwnd))\r
- {\r
- KillTimer(hwnd, 0xdeadbeef);\r
- DestroyWindow(hwnd);\r
- }\r
-\r
- return 0;\r
- }\r
-\r
- return DefWindowProc(hwnd, msg, wParam, lParam);\r
-}\r
-\r
-HWND CreateScreenSaveWnd(HWND hwndParent, RECT *rect)\r
-{\r
- DWORD dwStyle = hwndParent ? WS_CHILD : WS_POPUP;\r
-\r
-#ifdef _DEBUG\r
- DWORD dwStyleEx = 0;\r
-#else\r
- DWORD dwStyleEx = WS_EX_TOPMOST;\r
-#endif\r
-\r
- if(hwndParent)\r
- GetClientRect(hwndParent, rect);\r
-\r
- return CreateWindowEx( dwStyleEx, \r
- APPNAME, \r
- 0, \r
- WS_VISIBLE | dwStyle, \r
- rect->left, \r
- rect->top,\r
- rect->right - rect->left,\r
- rect->bottom - rect->top,\r
- hwndParent, \r
- 0,\r
- GetModuleHandle(0),\r
- 0\r
- );\r
-}\r
-\r
-//\r
-// Initialize class for matrix window\r
-//\r
-void InitScreenSaveClass(BOOL fPreview)\r
-{\r
- WNDCLASSEX wcx;\r
-\r
- wcx.cbSize = sizeof(WNDCLASSEX);\r
- wcx.style = 0;\r
- wcx.lpfnWndProc = MatrixWndProc;\r
- wcx.cbClsExtra = 0;\r
- wcx.cbWndExtra = sizeof(MATRIX *);\r
- wcx.hInstance = GetModuleHandle(0);\r
- wcx.hIcon = 0;\r
- wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);\r
- wcx.lpszMenuName = 0;\r
- wcx.lpszClassName = APPNAME;\r
- wcx.hIconSm = 0; \r
-\r
- if(fPreview)\r
- wcx.hCursor = LoadCursor(0, IDC_ARROW);\r
- else\r
- wcx.hCursor = LoadCursor(wcx.hInstance, MAKEINTRESOURCE(IDC_BLANKCURSOR));\r
-\r
- // initialize the crc register used for "random" number generation\r
- _crc_reg = (WORD)GetTickCount();\r
-\r
- RegisterClassEx(&wcx);\r
-}\r
+//
+// matrix.c
+//
+// Matrix-window implementation
+//
+#include <windows.h>
+#include <windowsx.h>
+#include <tchar.h>
+#include "globals.h"
+#include "message.h"
+#include "matrix.h"
+#include "resource.h"
+
+void DoMatrixMessage(HDC hdc, MATRIX *matrix);
+
+// pseudo-random number generator, based on 16bit CRC algorithm
+static WORD _crc_reg = 0;
+int crc_rand()
+{
+ const WORD mask = 0xb400;
+
+ if(_crc_reg & 1)
+ _crc_reg = (_crc_reg >> 1) ^ mask;
+ else
+ _crc_reg = (_crc_reg >> 1);
+
+ return _crc_reg;
+}
+
+int GlyphIntensity(GLYPH glyph)
+{
+ return (int)((glyph & 0x7f00) >> 8);
+}
+
+GLYPH DarkenGlyph(GLYPH glyph)
+{
+ int intensity = GlyphIntensity(glyph);
+
+ if(intensity > 0)
+ return GLYPH_REDRAW | ((intensity - 1) << 8) | (glyph & 0x00FF);
+ else
+ return glyph;
+}
+
+GLYPH RandomGlyph(int intensity)
+{
+ return GLYPH_REDRAW | (intensity << 8) | (crc_rand() % NUM_GLYPHS);
+}
+
+void RedrawBlip(GLYPH *glypharr, int blippos)
+{
+ glypharr[blippos+0] |= GLYPH_REDRAW;
+ glypharr[blippos+1] |= GLYPH_REDRAW;
+ glypharr[blippos+8] |= GLYPH_REDRAW;
+ glypharr[blippos+9] |= GLYPH_REDRAW;
+}
+
+void ScrollMatrixColumn(MATRIX_COLUMN *col)
+{
+ int y;
+ GLYPH lastglyph = 0;
+ GLYPH thisglyph;
+
+ // wait until we are allowed to scroll
+ if(col->started == FALSE)
+ {
+ if(--col->countdown <= 0)
+ col->started = TRUE;
+
+ return;
+ }
+
+ // "seed" the glyph-run
+ lastglyph = col->state ? (GLYPH)0 : (GLYPH)(MAX_INTENSITY << 8);
+
+ //
+ // loop over the entire length of the column, looking for changes
+ // in intensity/darkness. This change signifies the start/end
+ // of a run of glyphs.
+ //
+ for(y = 0; y < col->length; y++)
+ {
+ thisglyph = col->glyph[y];
+
+ // bottom-most part of "run". Insert a new character (glyph)
+ // at the end to lengthen the run down the screen..gives the
+ // impression that the run is "falling" down the screen
+ if(GlyphIntensity(thisglyph) < GlyphIntensity(lastglyph) &&
+ GlyphIntensity(thisglyph) == 0)
+ {
+ col->glyph[y] = RandomGlyph(MAX_INTENSITY - 1);
+ y++;
+ }
+ // top-most part of "run". Delete a character off the top by
+ // darkening the glyph until it eventually disappears (turns black).
+ // this gives the effect that the run as dropped downwards
+ else if(GlyphIntensity(thisglyph) > GlyphIntensity(lastglyph))
+ {
+ col->glyph[y] = DarkenGlyph(thisglyph);
+
+ // if we've just darkened the last bit, skip on so
+ // the whole run doesn't go dark
+ if(GlyphIntensity(thisglyph) == MAX_INTENSITY - 1)
+ y++;
+ }
+
+ lastglyph = col->glyph[y];
+ }
+
+ // change state from blanks <-> runs when the current run as expired
+ if(--col->runlen <= 0)
+ {
+ if(col->state ^= 1)
+ col->runlen = crc_rand() % (3 * DENSITY/2) + DENSITY_MIN;
+ else
+ col->runlen = crc_rand() % (DENSITY_MAX+1-DENSITY) + (DENSITY_MIN*2);
+ }
+
+ //
+ // make a "blip" run down this column at double-speed
+ //
+
+ // mark current blip as redraw so it gets "erased"
+ if(col->blippos >= 0 && col->blippos < col->length)
+ RedrawBlip(col->glyph, col->blippos);
+
+ // advance down screen at double-speed
+ col->blippos += 2;
+
+ // if the blip gets to the end of a run, start it again (for a random
+ // length so that the blips never get synched together)
+ if(col->blippos >= col->bliplen)
+ {
+ col->bliplen = col->length + crc_rand() % 50;
+ col->blippos = 0;
+ }
+
+ // now redraw blip at new position
+ if(col->blippos >= 0 && col->blippos < col->length)
+ RedrawBlip(col->glyph, col->blippos);
+
+}
+
+//
+// randomly change a small collection glyphs in a column
+//
+void RandomMatrixColumn(MATRIX_COLUMN *col)
+{
+ int i, y;
+
+ for(i = 1, y = 0; i < 16; i++)
+ {
+ // find a run
+ while(GlyphIntensity(col->glyph[y]) < MAX_INTENSITY-1 && y < col->length)
+ y++;
+
+ if(y >= col->length)
+ break;
+
+ col->glyph[y] = (col->glyph[y] & 0xff00) | (crc_rand() % NUM_GLYPHS);
+ col->glyph[y] |= GLYPH_REDRAW;
+
+ y += crc_rand() % 10;
+ }
+}
+
+void DrawGlyph(MATRIX *matrix, HDC hdc, int xpos, int ypos, GLYPH glyph)
+{
+ int intensity = GlyphIntensity(glyph);
+ int glyphidx = glyph & 0xff;
+
+ BitBlt(hdc, xpos, ypos, GLYPH_WIDTH, GLYPH_HEIGHT, matrix->hdcBitmap,
+ glyphidx * GLYPH_WIDTH, intensity * GLYPH_HEIGHT, SRCCOPY);
+}
+
+void RedrawMatrixColumn(MATRIX_COLUMN *col, MATRIX *matrix, HDC hdc, int xpos)
+{
+ int y;
+
+ // loop down the length of the column redrawing only what needs doing
+ for(y = 0; y < col->length; y++)
+ {
+ GLYPH glyph = col->glyph[y];
+
+ // does this glyph (character) need to be redrawn?
+ if(glyph & GLYPH_REDRAW)
+ {
+ if((y == col->blippos+0 || y == col->blippos+1 ||
+ y == col->blippos+8 || y == col->blippos+9) &&
+ GlyphIntensity(glyph) >= MAX_INTENSITY-1)
+ glyph |= MAX_INTENSITY << 8;
+
+ DrawGlyph(matrix, hdc, xpos, y * GLYPH_HEIGHT, glyph);
+
+ // clear redraw state
+ col->glyph[y] &= ~GLYPH_REDRAW;
+ }
+ }
+}
+
+void DecodeMatrix(HWND hwnd, MATRIX *matrix)
+{
+ int x;
+ HDC hdc = GetDC(hwnd);
+
+ for(x = 0; x < matrix->numcols; x++)
+ {
+ RandomMatrixColumn(&matrix->column[x]);
+ ScrollMatrixColumn(&matrix->column[x]);
+ RedrawMatrixColumn(&matrix->column[x], matrix, hdc, x * GLYPH_WIDTH);
+ }
+
+ if(matrix->message)
+ DoMatrixMessage(hdc, matrix);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+//
+// Allocate matrix structures
+//
+MATRIX *CreateMatrix(HWND hwnd, int width, int height)
+{
+ MATRIX *matrix;
+ HDC hdc;
+ int x, y;
+
+ int rows = height / GLYPH_HEIGHT + 1;
+ int cols = width / GLYPH_WIDTH + 1;
+
+ // allocate matrix!
+ if((matrix = malloc(sizeof(MATRIX) + sizeof(MATRIX_COLUMN) * cols)) == 0)
+ return 0;
+
+ matrix->numcols = cols;
+ matrix->numrows = rows;
+ matrix->width = width;
+ matrix->height = height;
+
+ for(x = 0; x < cols; x++)
+ {
+ matrix->column[x].length = rows;
+ matrix->column[x].started = FALSE;
+ matrix->column[x].countdown = crc_rand() % 100;
+ matrix->column[x].state = crc_rand() % 2;
+ matrix->column[x].runlen = crc_rand() % 20 + 3;
+
+ matrix->column[x].glyph = malloc(sizeof(GLYPH) * (rows+16));
+
+ for(y = 0; y < rows; y++)
+ matrix->column[x].glyph[y] = 0;//;
+ }
+
+ // Load bitmap!!
+ hdc = GetDC(NULL);
+ matrix->hbmBitmap = LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BITMAP1));
+ matrix->hdcBitmap = CreateCompatibleDC(hdc);
+ SelectObject(matrix->hdcBitmap, matrix->hbmBitmap);
+ ReleaseDC(NULL, hdc);
+
+ // Create a message for this window...only if we are
+ // screen-saving (not if in preview mode)
+ if(GetParent(hwnd) == 0)
+ matrix->message = InitMatrixMessage(hwnd, matrix->numcols, matrix->numrows);
+ else
+ matrix->message = 0;
+
+ return matrix;
+}
+
+//
+// Free up matrix structures
+//
+void DestroyMatrix(MATRIX *matrix)
+{
+ int x;
+
+ // free the matrix columns
+ for(x = 0; x < matrix->numcols; x++)
+ free(matrix->column[x].glyph);
+
+ DeleteDC(matrix->hdcBitmap);
+ DeleteObject(matrix->hbmBitmap);
+
+ // now delete the matrix!
+ free(matrix);
+}
+
+MATRIX *GetMatrix(HWND hwnd)
+{
+ return (MATRIX *)GetWindowLong(hwnd, 0);
+}
+
+void SetMatrix(HWND hwnd, MATRIX *matrix)
+{
+ SetWindowLong(hwnd, 0, (LONG)matrix);
+}
+
+//
+// Window procedure for one matrix (1 per screen)
+//
+LRESULT WINAPI MatrixWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static POINT ptLast;
+ static POINT ptCursor;
+ static BOOL fFirstTime = TRUE;
+
+ MATRIX *matrix = GetMatrix(hwnd);
+
+ switch(msg)
+ {
+ // window creation
+ case WM_NCCREATE:
+
+ // create the matrix based on how big this window is
+ matrix = CreateMatrix(hwnd, ((CREATESTRUCT *)lParam)->cx, ((CREATESTRUCT *)lParam)->cy);
+
+ // failed to allocate? stop window creation!
+ if(matrix == 0)
+ return FALSE;
+
+ SetMatrix(hwnd, matrix);
+
+ // start off an animation timer
+ SetTimer(hwnd, 0xdeadbeef, ((SPEED_MAX - g_nMatrixSpeed) + SPEED_MIN) * 10, 0);
+
+ return TRUE;
+
+ // window being destroyed, cleanup
+ case WM_NCDESTROY:
+ DestroyMatrix(matrix);
+ PostQuitMessage(0);
+ return 0;
+
+ // animation timer has gone off, redraw the matrix!
+ case WM_TIMER:
+ DecodeMatrix(hwnd, matrix);
+ return 0;
+
+ // break out of screen-saver if any keyboard activity
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ return 0;
+
+ // break out of screen-saver if any mouse activity
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_MOUSEMOVE:
+
+ // If we've got a parent then we must be a preview
+ if(GetParent(hwnd) != 0)
+ return 0;
+
+ if(fFirstTime)
+ {
+ GetCursorPos(&ptLast);
+ fFirstTime = FALSE;
+ }
+
+ GetCursorPos(&ptCursor);
+
+ // if the mouse has moved more than 3 pixels then exit
+ if(abs(ptCursor.x - ptLast.x) >= 3 || abs(ptCursor.y - ptLast.y) >= 3)
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+
+ ptLast = ptCursor;
+
+ return 0;
+
+ // someone wants to close us...see if it's ok
+ case WM_CLOSE:
+
+ if(VerifyPassword(hwnd))
+ {
+ KillTimer(hwnd, 0xdeadbeef);
+ DestroyWindow(hwnd);
+ }
+
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+HWND CreateScreenSaveWnd(HWND hwndParent, RECT *rect)
+{
+ DWORD dwStyle = hwndParent ? WS_CHILD : WS_POPUP;
+
+#ifdef _DEBUG
+ DWORD dwStyleEx = 0;
+#else
+ DWORD dwStyleEx = WS_EX_TOPMOST;
+#endif
+
+ if(hwndParent)
+ GetClientRect(hwndParent, rect);
+
+ return CreateWindowEx( dwStyleEx,
+ APPNAME,
+ 0,
+ WS_VISIBLE | dwStyle,
+ rect->left,
+ rect->top,
+ rect->right - rect->left,
+ rect->bottom - rect->top,
+ hwndParent,
+ 0,
+ GetModuleHandle(0),
+ 0
+ );
+}
+
+//
+// Initialize class for matrix window
+//
+void InitScreenSaveClass(BOOL fPreview)
+{
+ WNDCLASSEX wcx;
+
+ wcx.cbSize = sizeof(WNDCLASSEX);
+ wcx.style = 0;
+ wcx.lpfnWndProc = MatrixWndProc;
+ wcx.cbClsExtra = 0;
+ wcx.cbWndExtra = sizeof(MATRIX *);
+ wcx.hInstance = GetModuleHandle(0);
+ wcx.hIcon = 0;
+ wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wcx.lpszMenuName = 0;
+ wcx.lpszClassName = APPNAME;
+ wcx.hIconSm = 0;
+
+ if(fPreview)
+ wcx.hCursor = LoadCursor(0, IDC_ARROW);
+ else
+ wcx.hCursor = LoadCursor(wcx.hInstance, MAKEINTRESOURCE(IDC_BLANKCURSOR));
+
+ // initialize the crc register used for "random" number generation
+ _crc_reg = (WORD)GetTickCount();
+
+ RegisterClassEx(&wcx);
+}