1 // 2 // matrix.c 3 // 4 // Matrix-window implementation 5 // 6 #include <windows.h> 7 #include <windowsx.h> 8 #include <tchar.h> 9 #include "globals.h" 10 #include "message.h" 11 #include "matrix.h" 12 #include "resource.h" 13 14 void DoMatrixMessage(HDC hdc, MATRIX *matrix); 15 16 // pseudo-random number generator, based on 16bit CRC algorithm 17 static WORD _crc_reg = 0; 18 int crc_rand() 19 { 20 const WORD mask = 0xb400; 21 22 if(_crc_reg & 1) 23 _crc_reg = (_crc_reg >> 1) ^ mask; 24 else 25 _crc_reg = (_crc_reg >> 1); 26 27 return _crc_reg; 28 } 29 30 int GlyphIntensity(GLYPH glyph) 31 { 32 return (int)((glyph & 0x7f00) >> 8); 33 } 34 35 GLYPH DarkenGlyph(GLYPH glyph) 36 { 37 int intensity = GlyphIntensity(glyph); 38 39 if(intensity > 0) 40 return GLYPH_REDRAW | ((intensity - 1) << 8) | (glyph & 0x00FF); 41 else 42 return glyph; 43 } 44 45 GLYPH RandomGlyph(int intensity) 46 { 47 return GLYPH_REDRAW | (intensity << 8) | (crc_rand() % NUM_GLYPHS); 48 } 49 50 void RedrawBlip(GLYPH *glypharr, int blippos) 51 { 52 glypharr[blippos+0] |= GLYPH_REDRAW; 53 glypharr[blippos+1] |= GLYPH_REDRAW; 54 glypharr[blippos+8] |= GLYPH_REDRAW; 55 glypharr[blippos+9] |= GLYPH_REDRAW; 56 } 57 58 void ScrollMatrixColumn(MATRIX_COLUMN *col) 59 { 60 int y; 61 GLYPH lastglyph = 0; 62 GLYPH thisglyph; 63 64 // wait until we are allowed to scroll 65 if(col->started == FALSE) 66 { 67 if(--col->countdown <= 0) 68 col->started = TRUE; 69 70 return; 71 } 72 73 // "seed" the glyph-run 74 lastglyph = col->state ? (GLYPH)0 : (GLYPH)(MAX_INTENSITY << 8); 75 76 // 77 // loop over the entire length of the column, looking for changes 78 // in intensity/darkness. This change signifies the start/end 79 // of a run of glyphs. 80 // 81 for(y = 0; y < col->length; y++) 82 { 83 thisglyph = col->glyph[y]; 84 85 // bottom-most part of "run". Insert a new character (glyph) 86 // at the end to lengthen the run down the screen..gives the 87 // impression that the run is "falling" down the screen 88 if(GlyphIntensity(thisglyph) < GlyphIntensity(lastglyph) && 89 GlyphIntensity(thisglyph) == 0) 90 { 91 col->glyph[y] = RandomGlyph(MAX_INTENSITY - 1); 92 y++; 93 } 94 // top-most part of "run". Delete a character off the top by 95 // darkening the glyph until it eventually disappears (turns black). 96 // this gives the effect that the run as dropped downwards 97 else if(GlyphIntensity(thisglyph) > GlyphIntensity(lastglyph)) 98 { 99 col->glyph[y] = DarkenGlyph(thisglyph); 100 101 // if we've just darkened the last bit, skip on so 102 // the whole run doesn't go dark 103 if(GlyphIntensity(thisglyph) == MAX_INTENSITY - 1) 104 y++; 105 } 106 107 lastglyph = col->glyph[y]; 108 } 109 110 // change state from blanks <-> runs when the current run as expired 111 if(--col->runlen <= 0) 112 { 113 if(col->state ^= 1) 114 col->runlen = crc_rand() % (3 * DENSITY/2) + DENSITY_MIN; 115 else 116 col->runlen = crc_rand() % (DENSITY_MAX+1-DENSITY) + (DENSITY_MIN*2); 117 } 118 119 // 120 // make a "blip" run down this column at double-speed 121 // 122 123 // mark current blip as redraw so it gets "erased" 124 if(col->blippos >= 0 && col->blippos < col->length) 125 RedrawBlip(col->glyph, col->blippos); 126 127 // advance down screen at double-speed 128 col->blippos += 2; 129 130 // if the blip gets to the end of a run, start it again (for a random 131 // length so that the blips never get synched together) 132 if(col->blippos >= col->bliplen) 133 { 134 col->bliplen = col->length + crc_rand() % 50; 135 col->blippos = 0; 136 } 137 138 // now redraw blip at new position 139 if(col->blippos >= 0 && col->blippos < col->length) 140 RedrawBlip(col->glyph, col->blippos); 141 142 } 143 144 // 145 // randomly change a small collection glyphs in a column 146 // 147 void RandomMatrixColumn(MATRIX_COLUMN *col) 148 { 149 int i, y; 150 151 for(i = 1, y = 0; i < 16; i++) 152 { 153 // find a run 154 while(GlyphIntensity(col->glyph[y]) < MAX_INTENSITY-1 && y < col->length) 155 y++; 156 157 if(y >= col->length) 158 break; 159 160 col->glyph[y] = (col->glyph[y] & 0xff00) | (crc_rand() % NUM_GLYPHS); 161 col->glyph[y] |= GLYPH_REDRAW; 162 163 y += crc_rand() % 10; 164 } 165 } 166 167 void DrawGlyph(MATRIX *matrix, HDC hdc, int xpos, int ypos, GLYPH glyph) 168 { 169 int intensity = GlyphIntensity(glyph); 170 int glyphidx = glyph & 0xff; 171 172 BitBlt(hdc, xpos, ypos, GLYPH_WIDTH, GLYPH_HEIGHT, matrix->hdcBitmap, 173 glyphidx * GLYPH_WIDTH, intensity * GLYPH_HEIGHT, SRCCOPY); 174 } 175 176 void RedrawMatrixColumn(MATRIX_COLUMN *col, MATRIX *matrix, HDC hdc, int xpos) 177 { 178 int y; 179 180 // loop down the length of the column redrawing only what needs doing 181 for(y = 0; y < col->length; y++) 182 { 183 GLYPH glyph = col->glyph[y]; 184 185 // does this glyph (character) need to be redrawn? 186 if(glyph & GLYPH_REDRAW) 187 { 188 if((y == col->blippos+0 || y == col->blippos+1 || 189 y == col->blippos+8 || y == col->blippos+9) && 190 GlyphIntensity(glyph) >= MAX_INTENSITY-1) 191 glyph |= MAX_INTENSITY << 8; 192 193 DrawGlyph(matrix, hdc, xpos, y * GLYPH_HEIGHT, glyph); 194 195 // clear redraw state 196 col->glyph[y] &= ~GLYPH_REDRAW; 197 } 198 } 199 } 200 201 void DecodeMatrix(HWND hwnd, MATRIX *matrix) 202 { 203 int x; 204 HDC hdc = GetDC(hwnd); 205 206 for(x = 0; x < matrix->numcols; x++) 207 { 208 RandomMatrixColumn(&matrix->column[x]); 209 ScrollMatrixColumn(&matrix->column[x]); 210 RedrawMatrixColumn(&matrix->column[x], matrix, hdc, x * GLYPH_WIDTH); 211 } 212 213 if(matrix->message) 214 DoMatrixMessage(hdc, matrix); 215 216 ReleaseDC(hwnd, hdc); 217 } 218 219 // 220 // Allocate matrix structures 221 // 222 MATRIX *CreateMatrix(HWND hwnd, int width, int height) 223 { 224 MATRIX *matrix; 225 HDC hdc; 226 int x, y; 227 228 int rows = height / GLYPH_HEIGHT + 1; 229 int cols = width / GLYPH_WIDTH + 1; 230 231 // allocate matrix! 232 if((matrix = malloc(sizeof(MATRIX) + sizeof(MATRIX_COLUMN) * cols)) == 0) 233 return 0; 234 235 matrix->numcols = cols; 236 matrix->numrows = rows; 237 matrix->width = width; 238 matrix->height = height; 239 240 for(x = 0; x < cols; x++) 241 { 242 matrix->column[x].length = rows; 243 matrix->column[x].started = FALSE; 244 matrix->column[x].countdown = crc_rand() % 100; 245 matrix->column[x].state = crc_rand() % 2; 246 matrix->column[x].runlen = crc_rand() % 20 + 3; 247 248 matrix->column[x].glyph = malloc(sizeof(GLYPH) * (rows+16)); 249 250 for(y = 0; y < rows; y++) 251 matrix->column[x].glyph[y] = 0;//; 252 } 253 254 // Load bitmap!! 255 hdc = GetDC(NULL); 256 matrix->hbmBitmap = LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BITMAP1)); 257 matrix->hdcBitmap = CreateCompatibleDC(hdc); 258 SelectObject(matrix->hdcBitmap, matrix->hbmBitmap); 259 ReleaseDC(NULL, hdc); 260 261 // Create a message for this window...only if we are 262 // screen-saving (not if in preview mode) 263 if(GetParent(hwnd) == 0) 264 matrix->message = InitMatrixMessage(hwnd, matrix->numcols, matrix->numrows); 265 else 266 matrix->message = 0; 267 268 return matrix; 269 } 270 271 // 272 // Free up matrix structures 273 // 274 void DestroyMatrix(MATRIX *matrix) 275 { 276 int x; 277 278 // free the matrix columns 279 for(x = 0; x < matrix->numcols; x++) 280 free(matrix->column[x].glyph); 281 282 DeleteDC(matrix->hdcBitmap); 283 DeleteObject(matrix->hbmBitmap); 284 285 // now delete the matrix! 286 free(matrix); 287 } 288 289 MATRIX *GetMatrix(HWND hwnd) 290 { 291 return (MATRIX *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 292 } 293 294 void SetMatrix(HWND hwnd, MATRIX *matrix) 295 { 296 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)matrix); 297 } 298 299 // 300 // Window procedure for one matrix (1 per screen) 301 // 302 LRESULT WINAPI MatrixWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 303 { 304 static POINT ptLast; 305 static POINT ptCursor; 306 static BOOL fFirstTime = TRUE; 307 308 MATRIX *matrix = GetMatrix(hwnd); 309 310 switch(msg) 311 { 312 // window creation 313 case WM_NCCREATE: 314 315 // create the matrix based on how big this window is 316 matrix = CreateMatrix(hwnd, ((CREATESTRUCT *)lParam)->cx, ((CREATESTRUCT *)lParam)->cy); 317 318 // failed to allocate? stop window creation! 319 if(matrix == 0) 320 return FALSE; 321 322 SetMatrix(hwnd, matrix); 323 324 // start off an animation timer 325 SetTimer(hwnd, 0xdeadbeef, ((SPEED_MAX - g_nMatrixSpeed) + SPEED_MIN) * 10, 0); 326 327 return TRUE; 328 329 // window being destroyed, cleanup 330 case WM_NCDESTROY: 331 DestroyMatrix(matrix); 332 PostQuitMessage(0); 333 return 0; 334 335 // animation timer has gone off, redraw the matrix! 336 case WM_TIMER: 337 DecodeMatrix(hwnd, matrix); 338 return 0; 339 340 // break out of screen-saver if any keyboard activity 341 case WM_KEYDOWN: 342 case WM_SYSKEYDOWN: 343 PostMessage(hwnd, WM_CLOSE, 0, 0); 344 return 0; 345 346 // break out of screen-saver if any mouse activity 347 case WM_LBUTTONDOWN: 348 case WM_LBUTTONUP: 349 case WM_RBUTTONDOWN: 350 case WM_RBUTTONUP: 351 case WM_MBUTTONDOWN: 352 case WM_MBUTTONUP: 353 case WM_MOUSEMOVE: 354 355 // If we've got a parent then we must be a preview 356 if(GetParent(hwnd) != 0) 357 return 0; 358 359 if(fFirstTime) 360 { 361 GetCursorPos(&ptLast); 362 fFirstTime = FALSE; 363 } 364 365 GetCursorPos(&ptCursor); 366 367 // if the mouse has moved more than 3 pixels then exit 368 if(abs(ptCursor.x - ptLast.x) >= 3 || abs(ptCursor.y - ptLast.y) >= 3) 369 PostMessage(hwnd, WM_CLOSE, 0, 0); 370 371 ptLast = ptCursor; 372 373 return 0; 374 375 // someone wants to close us...see if it's ok 376 case WM_CLOSE: 377 378 if(VerifyPassword(hwnd)) 379 { 380 KillTimer(hwnd, 0xdeadbeef); 381 DestroyWindow(hwnd); 382 } 383 384 return 0; 385 } 386 387 return DefWindowProc(hwnd, msg, wParam, lParam); 388 } 389 390 HWND CreateScreenSaveWnd(HWND hwndParent, RECT *rect) 391 { 392 DWORD dwStyle = hwndParent ? WS_CHILD : WS_POPUP; 393 394 #ifdef _DEBUG 395 DWORD dwStyleEx = 0; 396 #else 397 DWORD dwStyleEx = WS_EX_TOPMOST; 398 #endif 399 400 if(hwndParent) 401 GetClientRect(hwndParent, rect); 402 403 return CreateWindowEx( dwStyleEx, 404 APPNAME, 405 0, 406 WS_VISIBLE | dwStyle, 407 rect->left, 408 rect->top, 409 rect->right - rect->left, 410 rect->bottom - rect->top, 411 hwndParent, 412 0, 413 GetModuleHandle(0), 414 0 415 ); 416 } 417 418 // 419 // Initialize class for matrix window 420 // 421 void InitScreenSaveClass(BOOL fPreview) 422 { 423 WNDCLASSEX wcx; 424 425 wcx.cbSize = sizeof(WNDCLASSEX); 426 wcx.style = 0; 427 wcx.lpfnWndProc = MatrixWndProc; 428 wcx.cbClsExtra = 0; 429 wcx.cbWndExtra = sizeof(MATRIX *); 430 wcx.hInstance = GetModuleHandle(0); 431 wcx.hIcon = 0; 432 wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 433 wcx.lpszMenuName = 0; 434 wcx.lpszClassName = APPNAME; 435 wcx.hIconSm = 0; 436 437 if(fPreview) 438 wcx.hCursor = LoadCursor(0, IDC_ARROW); 439 else 440 wcx.hCursor = LoadCursor(wcx.hInstance, MAKEINTRESOURCE(IDC_BLANKCURSOR)); 441 442 // initialize the crc register used for "random" number generation 443 _crc_reg = (WORD)GetTickCount(); 444 445 RegisterClassEx(&wcx); 446 } 447