2006-11-08 11:47:44 +00:00
|
|
|
//
|
|
|
|
// 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);
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
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
|
2007-10-19 23:21:45 +00:00
|
|
|
if(GlyphIntensity(thisglyph) < GlyphIntensity(lastglyph) &&
|
2006-11-08 11:47:44 +00:00
|
|
|
GlyphIntensity(thisglyph) == 0)
|
|
|
|
{
|
|
|
|
col->glyph[y] = RandomGlyph(MAX_INTENSITY - 1);
|
|
|
|
y++;
|
|
|
|
}
|
|
|
|
// top-most part of "run". Delete a character off the top by
|
2007-10-19 23:21:45 +00:00
|
|
|
// darkening the glyph until it eventually disappears (turns black).
|
2006-11-08 11:47:44 +00:00
|
|
|
// this gives the effect that the run as dropped downwards
|
|
|
|
else if(GlyphIntensity(thisglyph) > GlyphIntensity(lastglyph))
|
|
|
|
{
|
|
|
|
col->glyph[y] = DarkenGlyph(thisglyph);
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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)
|
|
|
|
{
|
2007-10-19 23:21:45 +00:00
|
|
|
if(col->state ^= 1)
|
2006-11-08 11:47:44 +00:00
|
|
|
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;
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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
|
2007-10-19 23:21:45 +00:00
|
|
|
while(GlyphIntensity(col->glyph[y]) < MAX_INTENSITY-1 && y < col->length)
|
2006-11-08 11:47:44 +00:00
|
|
|
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 ||
|
2007-10-19 23:21:45 +00:00
|
|
|
y == col->blippos+8 || y == col->blippos+9) &&
|
2006-11-08 11:47:44 +00:00
|
|
|
GlyphIntensity(glyph) >= MAX_INTENSITY-1)
|
|
|
|
glyph |= MAX_INTENSITY << 8;
|
|
|
|
|
|
|
|
DrawGlyph(matrix, hdc, xpos, y * GLYPH_HEIGHT, glyph);
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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++)
|
|
|
|
{
|
2007-10-19 23:21:45 +00:00
|
|
|
RandomMatrixColumn(&matrix->column[x]);
|
2006-11-08 11:47:44 +00:00
|
|
|
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;//;
|
|
|
|
}
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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)
|
|
|
|
{
|
2018-02-27 21:48:32 +00:00
|
|
|
return (MATRIX *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetMatrix(HWND hwnd, MATRIX *matrix)
|
|
|
|
{
|
2018-02-27 21:48:32 +00:00
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)matrix);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
2007-09-08 19:04:47 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
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:
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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);
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
// 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;
|
2007-10-19 23:21:45 +00:00
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
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);
|
|
|
|
|
2007-10-19 23:21:45 +00:00
|
|
|
return CreateWindowEx( dwStyleEx,
|
|
|
|
APPNAME,
|
|
|
|
0,
|
|
|
|
WS_VISIBLE | dwStyle,
|
|
|
|
rect->left,
|
2006-11-08 11:47:44 +00:00
|
|
|
rect->top,
|
|
|
|
rect->right - rect->left,
|
|
|
|
rect->bottom - rect->top,
|
2007-10-19 23:21:45 +00:00
|
|
|
hwndParent,
|
2006-11-08 11:47:44 +00:00
|
|
|
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;
|
2007-09-08 19:04:47 +00:00
|
|
|
wcx.hIconSm = 0;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|