Merge branch 'canoe' into vlock-2
[vlock.git] / modules / caca.c
blobbbf3bca85717204f3ed673731d1b90c14896c306
1 /* caca.c -- a screen saving plugin for vlock,
2 * the VT locking program for linux
4 * This file consists mostly of the code from cacademo from libcaca. Only
5 * minor changes were necessary to fit it into vlock's module architecture.
6 * These changes are copyright (C) 2007 Frank Benkstein.
8 * cacademo various demo effects for libcaca
9 * Copyright (c) 1998 Michele Bini <mibin@tin.it>
10 * 2003-2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
11 * 2004-2006 Sam Hocevar <sam@zoy.org>
12 * All Rights Reserved
14 * $Id: cacademo.c 1130 2007-06-28 12:49:28Z sam $
16 * This program is free software. It comes without any warranty, to
17 * the extent permitted by applicable law. You can redistribute it
18 * and/or modify it under the terms of the Do What The Fuck You Want
19 * To Public License, Version 2, as published by Sam Hocevar. See
20 * http://sam.zoy.org/wtfpl/COPYING for more details.
23 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 #ifndef M_PI
30 # define M_PI 3.14159265358979323846
31 #endif
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <signal.h>
38 #include <ncurses.h>
40 #include <cucul.h>
41 #include <caca.h>
43 #include "process.h"
45 #include "vlock_plugin.h"
47 enum action { PREPARE, INIT, UPDATE, RENDER, FREE };
49 void transition(cucul_canvas_t *, int, int);
50 void plasma(enum action, cucul_canvas_t *);
51 void metaballs(enum action, cucul_canvas_t *);
52 void moire(enum action, cucul_canvas_t *);
53 void matrix(enum action, cucul_canvas_t *);
55 void (*fn[])(enum action, cucul_canvas_t *) =
57 plasma,
58 metaballs,
59 moire,
60 matrix,
62 #define DEMOS (sizeof(fn)/sizeof(*fn))
64 #define DEMO_FRAMES cucul_rand(500, 1000)
65 #define TRANSITION_FRAMES 40
67 #define TRANSITION_COUNT 3
68 #define TRANSITION_CIRCLE 0
69 #define TRANSITION_STAR 1
70 #define TRANSITION_SQUARE 2
72 /* Common macros for dither-based demos */
73 #define XSIZ 256
74 #define YSIZ 256
76 /* Global variables */
77 static int frame = 0;
78 static bool abort_requested = false;
80 void handle_sigterm(int __attribute__((unused)) signum)
82 abort_requested = true;
85 static int caca_main(void *argument);
87 bool vlock_save(void **ctx_ptr)
89 static struct child_process child = {
90 .function = caca_main,
91 .argument = NULL,
92 .stdin_fd = REDIRECT_DEV_NULL,
93 .stdout_fd = NO_REDIRECT,
94 .stderr_fd = NO_REDIRECT,
97 /* Initialize ncurses. */
98 initscr();
100 if (!create_child(&child))
101 return false;
103 *ctx_ptr = &child;
105 return true;
108 bool vlock_save_abort(void **ctx_ptr)
110 struct child_process *child = *ctx_ptr;
112 if (child != NULL) {
113 ensure_death(child->pid);
114 /* Restore sane terminal and uninitialize ncurses. */
115 curs_set(1);
116 refresh();
117 endwin();
118 *ctx_ptr = NULL;
121 return true;
124 static int caca_main(void __attribute__((unused)) *argument)
126 static caca_display_t *dp;
127 static cucul_canvas_t *frontcv, *backcv, *mask;
129 int demo, next = -1, next_transition = DEMO_FRAMES;
130 unsigned int i;
131 int tmode = cucul_rand(0, TRANSITION_COUNT);
133 /* Set up two canvases, a mask, and attach a display to the front one */
134 frontcv = cucul_create_canvas(0, 0);
135 backcv = cucul_create_canvas(0, 0);
136 mask = cucul_create_canvas(0, 0);
138 (void) setenv("CACA_DRIVER", "ncurses", 1);
139 dp = caca_create_display(frontcv);
141 if(!dp)
142 return 1;
144 cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv),
145 cucul_get_canvas_height(frontcv));
146 cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv),
147 cucul_get_canvas_height(frontcv));
149 /* Set refresh delay. 40ms corresponds to 25 FPS. */
150 caca_set_display_time(dp, 40000);
152 /* Initialise all demos' lookup tables */
153 for(i = 0; i < DEMOS; i++)
154 fn[i](PREPARE, frontcv);
156 /* Choose a demo at random */
157 demo = cucul_rand(0, DEMOS);
158 fn[demo](INIT, frontcv);
160 for(;;)
162 if (abort_requested)
163 goto end;
165 /* Resize the spare canvas, just in case the main one changed */
166 cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv),
167 cucul_get_canvas_height(frontcv));
168 cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv),
169 cucul_get_canvas_height(frontcv));
171 /* Update demo's data */
172 fn[demo](UPDATE, frontcv);
174 /* Handle transitions */
175 if(frame == next_transition)
177 next = cucul_rand(0, DEMOS);
178 if(next == demo)
179 next = (next + 1) % DEMOS;
180 fn[next](INIT, backcv);
182 else if(frame == next_transition + TRANSITION_FRAMES)
184 fn[demo](FREE, frontcv);
185 demo = next;
186 next = -1;
187 next_transition = frame + DEMO_FRAMES;
188 tmode = cucul_rand(0, TRANSITION_COUNT);
191 if(next != -1)
192 fn[next](UPDATE, backcv);
194 frame++;
196 /* Render main demo's canvas */
197 fn[demo](RENDER, frontcv);
199 /* If a transition is on its way, render it */
200 if(next != -1)
202 fn[next](RENDER, backcv);
203 cucul_set_color_ansi(mask, CUCUL_LIGHTGRAY, CUCUL_BLACK);
204 cucul_clear_canvas(mask);
205 cucul_set_color_ansi(mask, CUCUL_WHITE, CUCUL_WHITE);
206 transition(mask, tmode,
207 100 * (frame - next_transition) / TRANSITION_FRAMES);
208 cucul_blit(frontcv, 0, 0, backcv, mask);
211 cucul_set_color_ansi(frontcv, CUCUL_WHITE, CUCUL_BLUE);
212 if(frame < 100)
213 cucul_put_str(frontcv, cucul_get_canvas_width(frontcv) - 30,
214 cucul_get_canvas_height(frontcv) - 2,
215 " -=[ Powered by libcaca ]=- ");
216 caca_refresh_display(dp);
218 end:
219 if(next != -1)
220 fn[next](FREE, frontcv);
221 fn[demo](FREE, frontcv);
223 caca_free_display(dp);
224 cucul_free_canvas(mask);
225 cucul_free_canvas(backcv);
226 cucul_free_canvas(frontcv);
228 return 0;
231 /* Transitions */
232 void transition(cucul_canvas_t *mask, int tmode, int completed)
234 static float const star[] =
236 0.000000, -1.000000,
237 0.308000, -0.349000,
238 0.992000, -0.244000,
239 0.500000, 0.266000,
240 0.632000, 0.998000,
241 0.008000, 0.659000,
242 -0.601000, 0.995000,
243 -0.496000, 0.275000,
244 -0.997000, -0.244000,
245 -0.313000, -0.349000
247 static float star_rot[sizeof(star)/sizeof(*star)];
250 static float const square[] =
252 -1, -1,
253 1, -1,
254 1, 1,
255 -1, 1
257 static float square_rot[sizeof(square)/sizeof(*square)];
259 float mulx = 0.0075f * completed * cucul_get_canvas_width(mask);
260 float muly = 0.0075f * completed * cucul_get_canvas_height(mask);
261 int w2 = cucul_get_canvas_width(mask) / 2;
262 int h2 = cucul_get_canvas_height(mask) / 2;
263 float angle = (0.0075f * completed * 360) * 3.14 / 180, x, y;
264 unsigned int i;
266 switch(tmode)
268 case TRANSITION_SQUARE:
269 /* Compute rotated coordinates */
270 for(i = 0; i < (sizeof(square) / sizeof(*square)) / 2; i++)
272 x = square[i * 2];
273 y = square[i * 2 + 1];
275 square_rot[i * 2] = x * cos(angle) - y * sin(angle);
276 square_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
279 mulx *= 1.8;
280 muly *= 1.8;
281 cucul_fill_triangle(mask,
282 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
283 square_rot[1*2] * mulx + w2, square_rot[1*2+1] * muly + h2, \
284 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, '#');
285 cucul_fill_triangle(mask,
286 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
287 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, \
288 square_rot[3*2] * mulx + w2, square_rot[3*2+1] * muly + h2, '#');
289 break;
292 case TRANSITION_STAR:
293 /* Compute rotated coordinates */
294 for(i = 0; i < (sizeof(star) / sizeof(*star)) / 2; i++)
296 x = star[i * 2];
297 y = star[i * 2 + 1];
299 star_rot[i * 2] = x * cos(angle) - y * sin(angle);
300 star_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
303 mulx *= 1.8;
304 muly *= 1.8;
306 #define DO_TRI(a, b, c) \
307 cucul_fill_triangle(mask, \
308 star_rot[(a)*2] * mulx + w2, star_rot[(a)*2+1] * muly + h2, \
309 star_rot[(b)*2] * mulx + w2, star_rot[(b)*2+1] * muly + h2, \
310 star_rot[(c)*2] * mulx + w2, star_rot[(c)*2+1] * muly + h2, '#')
311 DO_TRI(0, 1, 9);
312 DO_TRI(1, 2, 3);
313 DO_TRI(3, 4, 5);
314 DO_TRI(5, 6, 7);
315 DO_TRI(7, 8, 9);
316 DO_TRI(9, 1, 5);
317 DO_TRI(9, 5, 7);
318 DO_TRI(1, 3, 5);
319 break;
321 case TRANSITION_CIRCLE:
322 cucul_fill_ellipse(mask, w2, h2, mulx, muly, '#');
323 break;
328 /* The plasma effect */
329 #define TABLEX (XSIZ * 2)
330 #define TABLEY (YSIZ * 2)
331 static uint8_t table[TABLEX * TABLEY];
333 static void do_plasma(uint8_t *,
334 double, double, double, double, double, double);
336 void plasma(enum action action, cucul_canvas_t *cv)
338 static cucul_dither_t *dither;
339 static uint8_t *screen;
340 static unsigned int red[256], green[256], blue[256], alpha[256];
341 static double r[3], R[6];
343 int i, x, y;
345 switch(action)
347 case PREPARE:
348 /* Fill various tables */
349 for(i = 0 ; i < 256; i++)
350 red[i] = green[i] = blue[i] = alpha[i] = 0;
352 for(i = 0; i < 3; i++)
353 r[i] = (double)(cucul_rand(1, 1000)) / 60000 * M_PI;
355 for(i = 0; i < 6; i++)
356 R[i] = (double)(cucul_rand(1, 1000)) / 10000;
358 for(y = 0 ; y < TABLEY ; y++)
359 for(x = 0 ; x < TABLEX ; x++)
361 double tmp = (((double)((x - (TABLEX / 2)) * (x - (TABLEX / 2))
362 + (y - (TABLEX / 2)) * (y - (TABLEX / 2))))
363 * (M_PI / (TABLEX * TABLEX + TABLEY * TABLEY)));
365 table[x + y * TABLEX] = (1.0 + sin(12.0 * sqrt(tmp))) * 256 / 6;
367 break;
369 case INIT:
370 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
371 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
372 break;
374 case UPDATE:
375 for(i = 0 ; i < 256; i++)
377 double z = ((double)i) / 256 * 6 * M_PI;
379 red[i] = (1.0 + sin(z + r[1] * frame)) / 2 * 0xfff;
380 blue[i] = (1.0 + cos(z + r[0] * (frame + 100))) / 2 * 0xfff;
381 green[i] = (1.0 + cos(z + r[2] * (frame + 200))) / 2 * 0xfff;
384 /* Set the palette */
385 cucul_set_dither_palette(dither, red, green, blue, alpha);
387 do_plasma(screen,
388 (1.0 + sin(((double)frame) * R[0])) / 2,
389 (1.0 + sin(((double)frame) * R[1])) / 2,
390 (1.0 + sin(((double)frame) * R[2])) / 2,
391 (1.0 + sin(((double)frame) * R[3])) / 2,
392 (1.0 + sin(((double)frame) * R[4])) / 2,
393 (1.0 + sin(((double)frame) * R[5])) / 2);
394 break;
396 case RENDER:
397 cucul_dither_bitmap(cv, 0, 0,
398 cucul_get_canvas_width(cv),
399 cucul_get_canvas_height(cv),
400 dither, screen);
401 break;
403 case FREE:
404 free(screen);
405 cucul_free_dither(dither);
406 break;
410 static void do_plasma(uint8_t *pixels, double x_1, double y_1,
411 double x_2, double y_2, double x_3, double y_3)
413 unsigned int X1 = x_1 * (TABLEX / 2),
414 Y1 = y_1 * (TABLEY / 2),
415 X2 = x_2 * (TABLEX / 2),
416 Y2 = y_2 * (TABLEY / 2),
417 X3 = x_3 * (TABLEX / 2),
418 Y3 = y_3 * (TABLEY / 2);
419 unsigned int y;
420 uint8_t * t1 = table + X1 + Y1 * TABLEX,
421 * t2 = table + X2 + Y2 * TABLEX,
422 * t3 = table + X3 + Y3 * TABLEX;
424 for(y = 0; y < YSIZ; y++)
426 unsigned int x;
427 uint8_t * tmp = pixels + y * YSIZ;
428 unsigned int ty = y * TABLEX, tmax = ty + XSIZ;
429 for(x = 0; ty < tmax; ty++, tmp++)
430 tmp[0] = t1[ty] + t2[ty] + t3[ty];
434 /* The metaball effect */
435 #define METASIZE (XSIZ/2)
436 #define METABALLS 12
437 #define CROPBALL 200 /* Colour index where to crop balls */
438 static uint8_t metaball[METASIZE * METASIZE];
440 static void create_ball(void);
441 static void draw_ball(uint8_t *, unsigned int, unsigned int);
443 void metaballs(enum action action, cucul_canvas_t *cv)
445 static cucul_dither_t *cucul_dither;
446 static uint8_t *screen;
447 static unsigned int r[256], g[256], b[256], a[256];
448 static float dd[METABALLS], di[METABALLS], dj[METABALLS], dk[METABALLS];
449 static unsigned int x[METABALLS], y[METABALLS];
450 static float i = 10.0, j = 17.0, k = 11.0;
451 static double offset[360 + 80];
452 static unsigned int angleoff;
454 int n, angle;
456 switch(action)
458 case PREPARE:
459 /* Make the palette eatable by libcaca */
460 for(n = 0; n < 256; n++)
461 r[n] = g[n] = b[n] = a[n] = 0x0;
462 r[255] = g[255] = b[255] = 0xfff;
464 /* Generate ball sprite */
465 create_ball();
467 for(n = 0; n < METABALLS; n++)
469 dd[n] = cucul_rand(0, 100);
470 di[n] = (float)cucul_rand(500, 4000) / 6000.0;
471 dj[n] = (float)cucul_rand(500, 4000) / 6000.0;
472 dk[n] = (float)cucul_rand(500, 4000) / 6000.0;
475 angleoff = cucul_rand(0, 360);
477 for(n = 0; n < 360 + 80; n++)
478 offset[n] = 1.0 + sin((double)(n * M_PI / 60));
479 break;
481 case INIT:
482 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
483 /* Create a libcucul dither smaller than our pixel buffer, so that we
484 * display only the interesting part of it */
485 cucul_dither = cucul_create_dither(8, XSIZ - METASIZE, YSIZ - METASIZE,
486 XSIZ, 0, 0, 0, 0);
487 break;
489 case UPDATE:
490 angle = (frame + angleoff) % 360;
492 /* Crop the palette */
493 for(n = CROPBALL; n < 255; n++)
495 int t1, t2, t3;
496 double c1 = offset[angle];
497 double c2 = offset[angle + 40];
498 double c3 = offset[angle + 80];
500 t1 = n < 0x40 ? 0 : n < 0xc0 ? (n - 0x40) * 0x20 : 0xfff;
501 t2 = n < 0xe0 ? 0 : (n - 0xe0) * 0x80;
502 t3 = n < 0x40 ? n * 0x40 : 0xfff;
504 r[n] = (c1 * t1 + c2 * t2 + c3 * t3) / 4;
505 g[n] = (c1 * t2 + c2 * t3 + c3 * t1) / 4;
506 b[n] = (c1 * t3 + c2 * t1 + c3 * t2) / 4;
509 /* Set the palette */
510 cucul_set_dither_palette(cucul_dither, r, g, b, a);
512 /* Silly paths for our balls */
513 for(n = 0; n < METABALLS; n++)
515 float u = di[n] * i + dj[n] * j + dk[n] * sin(di[n] * k);
516 float v = dd[n] + di[n] * j + dj[n] * k + dk[n] * sin(dk[n] * i);
517 u = sin(i + u * 2.1) * (1.0 + sin(u));
518 v = sin(j + v * 1.9) * (1.0 + sin(v));
519 x[n] = (XSIZ - METASIZE) / 2 + u * (XSIZ - METASIZE) / 4;
520 y[n] = (YSIZ - METASIZE) / 2 + v * (YSIZ - METASIZE) / 4;
523 i += 0.011;
524 j += 0.017;
525 k += 0.019;
527 memset(screen, 0, XSIZ * YSIZ);
529 for(n = 0; n < METABALLS; n++)
530 draw_ball(screen, x[n], y[n]);
531 break;
533 case RENDER:
534 cucul_dither_bitmap(cv, 0, 0,
535 cucul_get_canvas_width(cv),
536 cucul_get_canvas_height(cv),
537 cucul_dither, screen + (METASIZE / 2) * (1 + XSIZ));
538 break;
540 case FREE:
541 free(screen);
542 cucul_free_dither(cucul_dither);
543 break;
547 static void create_ball(void)
549 int x, y;
550 float distance;
552 for(y = 0; y < METASIZE; y++)
553 for(x = 0; x < METASIZE; x++)
555 distance = ((METASIZE/2) - x) * ((METASIZE/2) - x)
556 + ((METASIZE/2) - y) * ((METASIZE/2) - y);
557 distance = sqrt(distance) * 64 / METASIZE;
558 metaball[x + y * METASIZE] = distance > 15 ? 0 : (255 - distance) * 15;
562 static void draw_ball(uint8_t *screen, unsigned int bx, unsigned int by)
564 unsigned int color;
565 unsigned int i, e = 0;
566 unsigned int b = (by * XSIZ) + bx;
568 for(i = 0; i < METASIZE * METASIZE; i++)
570 color = screen[b] + metaball[i];
572 if(color > 255)
573 color = 255;
575 screen[b] = color;
576 if(e == METASIZE)
578 e = 0;
579 b += XSIZ - METASIZE;
581 b++;
582 e++;
586 /* The moiré effect */
587 #define DISCSIZ (XSIZ*2)
588 #define DISCTHICKNESS (XSIZ*15/40)
589 static uint8_t disc[DISCSIZ * DISCSIZ];
591 static void put_disc(uint8_t *, int, int);
592 static void draw_line(int, int, char);
594 void moire(enum action action, cucul_canvas_t *cv)
596 static cucul_dither_t *dither;
597 static uint8_t *screen;
598 static float d[6];
599 static unsigned int red[256], green[256], blue[256], alpha[256];
601 int i, x, y;
603 switch(action)
605 case PREPARE:
606 /* Fill various tables */
607 for(i = 0 ; i < 256; i++)
608 red[i] = green[i] = blue[i] = alpha[i] = 0;
610 for(i = 0; i < 6; i++)
611 d[i] = ((float)cucul_rand(50, 70)) / 1000.0;
613 red[0] = green[0] = blue[0] = 0x777;
614 red[1] = green[1] = blue[1] = 0xfff;
616 /* Fill the circle */
617 for(i = DISCSIZ * 2; i > 0; i -= DISCTHICKNESS)
619 int t, dx, dy;
621 for(t = 0, dx = 0, dy = i; dx <= dy; dx++)
623 draw_line(dx / 3, dy / 3, (i / DISCTHICKNESS) % 2);
624 draw_line(dy / 3, dx / 3, (i / DISCTHICKNESS) % 2);
626 t += t > 0 ? dx - dy-- : dx;
630 break;
632 case INIT:
633 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
634 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
635 break;
637 case UPDATE:
638 memset(screen, 0, XSIZ * YSIZ);
640 /* Set the palette */
641 red[0] = 0.5 * (1 + sin(d[0] * (frame + 1000))) * 0xfff;
642 green[0] = 0.5 * (1 + cos(d[1] * frame)) * 0xfff;
643 blue[0] = 0.5 * (1 + cos(d[2] * (frame + 3000))) * 0xfff;
645 red[1] = 0.5 * (1 + sin(d[3] * (frame + 2000))) * 0xfff;
646 green[1] = 0.5 * (1 + cos(d[4] * frame + 5.0)) * 0xfff;
647 blue[1] = 0.5 * (1 + cos(d[5] * (frame + 4000))) * 0xfff;
649 cucul_set_dither_palette(dither, red, green, blue, alpha);
651 /* Draw circles */
652 x = cos(d[0] * (frame + 1000)) * 128.0 + (XSIZ / 2);
653 y = sin(0.11 * frame) * 128.0 + (YSIZ / 2);
654 put_disc(screen, x, y);
656 x = cos(0.13 * frame + 2.0) * 64.0 + (XSIZ / 2);
657 y = sin(d[1] * (frame + 2000)) * 64.0 + (YSIZ / 2);
658 put_disc(screen, x, y);
659 break;
661 case RENDER:
662 cucul_dither_bitmap(cv, 0, 0,
663 cucul_get_canvas_width(cv),
664 cucul_get_canvas_height(cv),
665 dither, screen);
666 break;
668 case FREE:
669 free(screen);
670 cucul_free_dither(dither);
671 break;
675 static void put_disc(uint8_t *screen, int x, int y)
677 char *src = ((char*)disc) + (DISCSIZ / 2 - x) + (DISCSIZ / 2 - y) * DISCSIZ;
678 int i, j;
680 for(j = 0; j < YSIZ; j++)
681 for(i = 0; i < XSIZ; i++)
683 screen[i + XSIZ * j] ^= src[i + DISCSIZ * j];
687 static void draw_line(int x, int y, char color)
689 if(x == 0 || y == 0 || y > DISCSIZ / 2)
690 return;
692 if(x > DISCSIZ / 2)
693 x = DISCSIZ / 2;
695 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) - y),
696 color, 2 * x - 1);
697 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) + y - 1),
698 color, 2 * x - 1);
701 /* Matrix effect */
702 #define MAXDROPS 500
703 #define MINLEN 15
704 #define MAXLEN 30
706 void matrix(enum action action, cucul_canvas_t *cv)
708 static struct drop
710 int x, y, speed, len;
711 char str[MAXLEN];
713 drop[MAXDROPS];
715 int w, h, i, j;
717 switch(action)
719 case PREPARE:
720 for(i = 0; i < MAXDROPS; i++)
722 drop[i].x = cucul_rand(0, 1000);
723 drop[i].y = cucul_rand(0, 1000);
724 drop[i].speed = 5 + cucul_rand(0, 30);
725 drop[i].len = MINLEN + cucul_rand(0, (MAXLEN - MINLEN));
726 for(j = 0; j < MAXLEN; j++)
727 drop[i].str[j] = cucul_rand('0', 'z');
729 break;
731 case INIT:
732 break;
734 case UPDATE:
735 w = cucul_get_canvas_width(cv);
736 h = cucul_get_canvas_height(cv);
738 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
740 drop[i].y += drop[i].speed;
741 if(drop[i].y > 1000)
743 drop[i].y -= 1000;
744 drop[i].x = cucul_rand(0, 1000);
747 break;
749 case RENDER:
750 w = cucul_get_canvas_width(cv);
751 h = cucul_get_canvas_height(cv);
753 cucul_set_color_ansi(cv, CUCUL_BLACK, CUCUL_BLACK);
754 cucul_clear_canvas(cv);
756 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
758 int x, y;
760 x = drop[i].x * w / 1000 / 2 * 2;
761 y = drop[i].y * (h + MAXLEN) / 1000;
763 for(j = 0; j < drop[i].len; j++)
765 unsigned int fg;
767 if(j < 2)
768 fg = CUCUL_WHITE;
769 else if(j < drop[i].len / 4)
770 fg = CUCUL_LIGHTGREEN;
771 else if(j < drop[i].len * 4 / 5)
772 fg = CUCUL_GREEN;
773 else
774 fg = CUCUL_DARKGRAY;
775 cucul_set_color_ansi(cv, fg, CUCUL_BLACK);
777 cucul_put_char(cv, x, y - j,
778 drop[i].str[(y - j) % drop[i].len]);
781 break;
783 case FREE:
784 break;