4cb385b8b3101c2a1454b4ced2cc2e816664dd1f
[reactos.git] / modules / rosapps / applications / screensavers / matrix / matrix.c
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 *)GetWindowLong(hwnd, 0);
292 }
293
294 void SetMatrix(HWND hwnd, MATRIX *matrix)
295 {
296 SetWindowLong(hwnd, 0, (LONG)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 }