src/script.c: check return value of create_child
[vlock.git] / modules / caca.c
blobf544446315ad2d1d12d7bfdcf897ab08235780da
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;
79 static pid_t pid;
81 void handle_sigterm(int __attribute__((unused)) signum)
83 abort_requested = true;
86 int caca_main(void);
88 bool vlock_save(void **ctx_ptr)
90 initscr();
92 pid = fork();
94 if (pid == 0) {
95 /* Child. */
96 int nullfd = open("/dev/null", O_RDONLY);
98 if (nullfd < 0)
99 _exit(1);
101 (void) dup2(nullfd, STDIN_FILENO);
103 close_all_fds();
105 (void) signal(SIGTERM, handle_sigterm);
106 (void) signal(SIGABRT, SIG_DFL);
107 (void) signal(SIGUSR1, SIG_DFL);
108 (void) signal(SIGUSR2, SIG_DFL);
110 setgid(getgid());
111 setuid(getuid());
113 _exit(caca_main());
116 if (pid > 0)
117 *ctx_ptr = &pid;
119 return pid > 0;
122 bool vlock_save_abort(void **ctx_ptr)
124 if (*ctx_ptr != NULL) {
125 ensure_death(pid);
126 curs_set(1);
127 refresh();
128 endwin();
129 *ctx_ptr = NULL;
132 return true;
135 int caca_main(void)
137 static caca_display_t *dp;
138 static cucul_canvas_t *frontcv, *backcv, *mask;
140 int demo, next = -1, next_transition = DEMO_FRAMES;
141 unsigned int i;
142 int tmode = cucul_rand(0, TRANSITION_COUNT);
144 /* Set up two canvases, a mask, and attach a display to the front one */
145 frontcv = cucul_create_canvas(0, 0);
146 backcv = cucul_create_canvas(0, 0);
147 mask = cucul_create_canvas(0, 0);
149 (void) setenv("CACA_DRIVER", "ncurses", 1);
150 dp = caca_create_display(frontcv);
152 if(!dp)
153 return 1;
155 cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv),
156 cucul_get_canvas_height(frontcv));
157 cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv),
158 cucul_get_canvas_height(frontcv));
160 caca_set_display_time(dp, 20000);
162 /* Initialise all demos' lookup tables */
163 for(i = 0; i < DEMOS; i++)
164 fn[i](PREPARE, frontcv);
166 /* Choose a demo at random */
167 demo = cucul_rand(0, DEMOS);
168 fn[demo](INIT, frontcv);
170 for(;;)
172 if (abort_requested)
173 goto end;
175 /* Resize the spare canvas, just in case the main one changed */
176 cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv),
177 cucul_get_canvas_height(frontcv));
178 cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv),
179 cucul_get_canvas_height(frontcv));
181 /* Update demo's data */
182 fn[demo](UPDATE, frontcv);
184 /* Handle transitions */
185 if(frame == next_transition)
187 next = cucul_rand(0, DEMOS);
188 if(next == demo)
189 next = (next + 1) % DEMOS;
190 fn[next](INIT, backcv);
192 else if(frame == next_transition + TRANSITION_FRAMES)
194 fn[demo](FREE, frontcv);
195 demo = next;
196 next = -1;
197 next_transition = frame + DEMO_FRAMES;
198 tmode = cucul_rand(0, TRANSITION_COUNT);
201 if(next != -1)
202 fn[next](UPDATE, backcv);
204 frame++;
206 /* Render main demo's canvas */
207 fn[demo](RENDER, frontcv);
209 /* If a transition is on its way, render it */
210 if(next != -1)
212 fn[next](RENDER, backcv);
213 cucul_set_color_ansi(mask, CUCUL_LIGHTGRAY, CUCUL_BLACK);
214 cucul_clear_canvas(mask);
215 cucul_set_color_ansi(mask, CUCUL_WHITE, CUCUL_WHITE);
216 transition(mask, tmode,
217 100 * (frame - next_transition) / TRANSITION_FRAMES);
218 cucul_blit(frontcv, 0, 0, backcv, mask);
221 cucul_set_color_ansi(frontcv, CUCUL_WHITE, CUCUL_BLUE);
222 if(frame < 100)
223 cucul_put_str(frontcv, cucul_get_canvas_width(frontcv) - 30,
224 cucul_get_canvas_height(frontcv) - 2,
225 " -=[ Powered by libcaca ]=- ");
226 caca_refresh_display(dp);
228 end:
229 if(next != -1)
230 fn[next](FREE, frontcv);
231 fn[demo](FREE, frontcv);
233 caca_free_display(dp);
234 cucul_free_canvas(mask);
235 cucul_free_canvas(backcv);
236 cucul_free_canvas(frontcv);
238 return 0;
241 /* Transitions */
242 void transition(cucul_canvas_t *mask, int tmode, int completed)
244 static float const star[] =
246 0.000000, -1.000000,
247 0.308000, -0.349000,
248 0.992000, -0.244000,
249 0.500000, 0.266000,
250 0.632000, 0.998000,
251 0.008000, 0.659000,
252 -0.601000, 0.995000,
253 -0.496000, 0.275000,
254 -0.997000, -0.244000,
255 -0.313000, -0.349000
257 static float star_rot[sizeof(star)/sizeof(*star)];
260 static float const square[] =
262 -1, -1,
263 1, -1,
264 1, 1,
265 -1, 1
267 static float square_rot[sizeof(square)/sizeof(*square)];
269 float mulx = 0.0075f * completed * cucul_get_canvas_width(mask);
270 float muly = 0.0075f * completed * cucul_get_canvas_height(mask);
271 int w2 = cucul_get_canvas_width(mask) / 2;
272 int h2 = cucul_get_canvas_height(mask) / 2;
273 float angle = (0.0075f * completed * 360) * 3.14 / 180, x, y;
274 unsigned int i;
276 switch(tmode)
278 case TRANSITION_SQUARE:
279 /* Compute rotated coordinates */
280 for(i = 0; i < (sizeof(square) / sizeof(*square)) / 2; i++)
282 x = square[i * 2];
283 y = square[i * 2 + 1];
285 square_rot[i * 2] = x * cos(angle) - y * sin(angle);
286 square_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
289 mulx *= 1.8;
290 muly *= 1.8;
291 cucul_fill_triangle(mask,
292 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
293 square_rot[1*2] * mulx + w2, square_rot[1*2+1] * muly + h2, \
294 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, '#');
295 cucul_fill_triangle(mask,
296 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
297 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, \
298 square_rot[3*2] * mulx + w2, square_rot[3*2+1] * muly + h2, '#');
299 break;
302 case TRANSITION_STAR:
303 /* Compute rotated coordinates */
304 for(i = 0; i < (sizeof(star) / sizeof(*star)) / 2; i++)
306 x = star[i * 2];
307 y = star[i * 2 + 1];
309 star_rot[i * 2] = x * cos(angle) - y * sin(angle);
310 star_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
313 mulx *= 1.8;
314 muly *= 1.8;
316 #define DO_TRI(a, b, c) \
317 cucul_fill_triangle(mask, \
318 star_rot[(a)*2] * mulx + w2, star_rot[(a)*2+1] * muly + h2, \
319 star_rot[(b)*2] * mulx + w2, star_rot[(b)*2+1] * muly + h2, \
320 star_rot[(c)*2] * mulx + w2, star_rot[(c)*2+1] * muly + h2, '#')
321 DO_TRI(0, 1, 9);
322 DO_TRI(1, 2, 3);
323 DO_TRI(3, 4, 5);
324 DO_TRI(5, 6, 7);
325 DO_TRI(7, 8, 9);
326 DO_TRI(9, 1, 5);
327 DO_TRI(9, 5, 7);
328 DO_TRI(1, 3, 5);
329 break;
331 case TRANSITION_CIRCLE:
332 cucul_fill_ellipse(mask, w2, h2, mulx, muly, '#');
333 break;
338 /* The plasma effect */
339 #define TABLEX (XSIZ * 2)
340 #define TABLEY (YSIZ * 2)
341 static uint8_t table[TABLEX * TABLEY];
343 static void do_plasma(uint8_t *,
344 double, double, double, double, double, double);
346 void plasma(enum action action, cucul_canvas_t *cv)
348 static cucul_dither_t *dither;
349 static uint8_t *screen;
350 static unsigned int red[256], green[256], blue[256], alpha[256];
351 static double r[3], R[6];
353 int i, x, y;
355 switch(action)
357 case PREPARE:
358 /* Fill various tables */
359 for(i = 0 ; i < 256; i++)
360 red[i] = green[i] = blue[i] = alpha[i] = 0;
362 for(i = 0; i < 3; i++)
363 r[i] = (double)(cucul_rand(1, 1000)) / 60000 * M_PI;
365 for(i = 0; i < 6; i++)
366 R[i] = (double)(cucul_rand(1, 1000)) / 10000;
368 for(y = 0 ; y < TABLEY ; y++)
369 for(x = 0 ; x < TABLEX ; x++)
371 double tmp = (((double)((x - (TABLEX / 2)) * (x - (TABLEX / 2))
372 + (y - (TABLEX / 2)) * (y - (TABLEX / 2))))
373 * (M_PI / (TABLEX * TABLEX + TABLEY * TABLEY)));
375 table[x + y * TABLEX] = (1.0 + sin(12.0 * sqrt(tmp))) * 256 / 6;
377 break;
379 case INIT:
380 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
381 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
382 break;
384 case UPDATE:
385 for(i = 0 ; i < 256; i++)
387 double z = ((double)i) / 256 * 6 * M_PI;
389 red[i] = (1.0 + sin(z + r[1] * frame)) / 2 * 0xfff;
390 blue[i] = (1.0 + cos(z + r[0] * (frame + 100))) / 2 * 0xfff;
391 green[i] = (1.0 + cos(z + r[2] * (frame + 200))) / 2 * 0xfff;
394 /* Set the palette */
395 cucul_set_dither_palette(dither, red, green, blue, alpha);
397 do_plasma(screen,
398 (1.0 + sin(((double)frame) * R[0])) / 2,
399 (1.0 + sin(((double)frame) * R[1])) / 2,
400 (1.0 + sin(((double)frame) * R[2])) / 2,
401 (1.0 + sin(((double)frame) * R[3])) / 2,
402 (1.0 + sin(((double)frame) * R[4])) / 2,
403 (1.0 + sin(((double)frame) * R[5])) / 2);
404 break;
406 case RENDER:
407 cucul_dither_bitmap(cv, 0, 0,
408 cucul_get_canvas_width(cv),
409 cucul_get_canvas_height(cv),
410 dither, screen);
411 break;
413 case FREE:
414 free(screen);
415 cucul_free_dither(dither);
416 break;
420 static void do_plasma(uint8_t *pixels, double x_1, double y_1,
421 double x_2, double y_2, double x_3, double y_3)
423 unsigned int X1 = x_1 * (TABLEX / 2),
424 Y1 = y_1 * (TABLEY / 2),
425 X2 = x_2 * (TABLEX / 2),
426 Y2 = y_2 * (TABLEY / 2),
427 X3 = x_3 * (TABLEX / 2),
428 Y3 = y_3 * (TABLEY / 2);
429 unsigned int y;
430 uint8_t * t1 = table + X1 + Y1 * TABLEX,
431 * t2 = table + X2 + Y2 * TABLEX,
432 * t3 = table + X3 + Y3 * TABLEX;
434 for(y = 0; y < YSIZ; y++)
436 unsigned int x;
437 uint8_t * tmp = pixels + y * YSIZ;
438 unsigned int ty = y * TABLEX, tmax = ty + XSIZ;
439 for(x = 0; ty < tmax; ty++, tmp++)
440 tmp[0] = t1[ty] + t2[ty] + t3[ty];
444 /* The metaball effect */
445 #define METASIZE (XSIZ/2)
446 #define METABALLS 12
447 #define CROPBALL 200 /* Colour index where to crop balls */
448 static uint8_t metaball[METASIZE * METASIZE];
450 static void create_ball(void);
451 static void draw_ball(uint8_t *, unsigned int, unsigned int);
453 void metaballs(enum action action, cucul_canvas_t *cv)
455 static cucul_dither_t *cucul_dither;
456 static uint8_t *screen;
457 static unsigned int r[256], g[256], b[256], a[256];
458 static float dd[METABALLS], di[METABALLS], dj[METABALLS], dk[METABALLS];
459 static unsigned int x[METABALLS], y[METABALLS];
460 static float i = 10.0, j = 17.0, k = 11.0;
461 static double offset[360 + 80];
462 static unsigned int angleoff;
464 int n, angle;
466 switch(action)
468 case PREPARE:
469 /* Make the palette eatable by libcaca */
470 for(n = 0; n < 256; n++)
471 r[n] = g[n] = b[n] = a[n] = 0x0;
472 r[255] = g[255] = b[255] = 0xfff;
474 /* Generate ball sprite */
475 create_ball();
477 for(n = 0; n < METABALLS; n++)
479 dd[n] = cucul_rand(0, 100);
480 di[n] = (float)cucul_rand(500, 4000) / 6000.0;
481 dj[n] = (float)cucul_rand(500, 4000) / 6000.0;
482 dk[n] = (float)cucul_rand(500, 4000) / 6000.0;
485 angleoff = cucul_rand(0, 360);
487 for(n = 0; n < 360 + 80; n++)
488 offset[n] = 1.0 + sin((double)(n * M_PI / 60));
489 break;
491 case INIT:
492 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
493 /* Create a libcucul dither smaller than our pixel buffer, so that we
494 * display only the interesting part of it */
495 cucul_dither = cucul_create_dither(8, XSIZ - METASIZE, YSIZ - METASIZE,
496 XSIZ, 0, 0, 0, 0);
497 break;
499 case UPDATE:
500 angle = (frame + angleoff) % 360;
502 /* Crop the palette */
503 for(n = CROPBALL; n < 255; n++)
505 int t1, t2, t3;
506 double c1 = offset[angle];
507 double c2 = offset[angle + 40];
508 double c3 = offset[angle + 80];
510 t1 = n < 0x40 ? 0 : n < 0xc0 ? (n - 0x40) * 0x20 : 0xfff;
511 t2 = n < 0xe0 ? 0 : (n - 0xe0) * 0x80;
512 t3 = n < 0x40 ? n * 0x40 : 0xfff;
514 r[n] = (c1 * t1 + c2 * t2 + c3 * t3) / 4;
515 g[n] = (c1 * t2 + c2 * t3 + c3 * t1) / 4;
516 b[n] = (c1 * t3 + c2 * t1 + c3 * t2) / 4;
519 /* Set the palette */
520 cucul_set_dither_palette(cucul_dither, r, g, b, a);
522 /* Silly paths for our balls */
523 for(n = 0; n < METABALLS; n++)
525 float u = di[n] * i + dj[n] * j + dk[n] * sin(di[n] * k);
526 float v = dd[n] + di[n] * j + dj[n] * k + dk[n] * sin(dk[n] * i);
527 u = sin(i + u * 2.1) * (1.0 + sin(u));
528 v = sin(j + v * 1.9) * (1.0 + sin(v));
529 x[n] = (XSIZ - METASIZE) / 2 + u * (XSIZ - METASIZE) / 4;
530 y[n] = (YSIZ - METASIZE) / 2 + v * (YSIZ - METASIZE) / 4;
533 i += 0.011;
534 j += 0.017;
535 k += 0.019;
537 memset(screen, 0, XSIZ * YSIZ);
539 for(n = 0; n < METABALLS; n++)
540 draw_ball(screen, x[n], y[n]);
541 break;
543 case RENDER:
544 cucul_dither_bitmap(cv, 0, 0,
545 cucul_get_canvas_width(cv),
546 cucul_get_canvas_height(cv),
547 cucul_dither, screen + (METASIZE / 2) * (1 + XSIZ));
548 break;
550 case FREE:
551 free(screen);
552 cucul_free_dither(cucul_dither);
553 break;
557 static void create_ball(void)
559 int x, y;
560 float distance;
562 for(y = 0; y < METASIZE; y++)
563 for(x = 0; x < METASIZE; x++)
565 distance = ((METASIZE/2) - x) * ((METASIZE/2) - x)
566 + ((METASIZE/2) - y) * ((METASIZE/2) - y);
567 distance = sqrt(distance) * 64 / METASIZE;
568 metaball[x + y * METASIZE] = distance > 15 ? 0 : (255 - distance) * 15;
572 static void draw_ball(uint8_t *screen, unsigned int bx, unsigned int by)
574 unsigned int color;
575 unsigned int i, e = 0;
576 unsigned int b = (by * XSIZ) + bx;
578 for(i = 0; i < METASIZE * METASIZE; i++)
580 color = screen[b] + metaball[i];
582 if(color > 255)
583 color = 255;
585 screen[b] = color;
586 if(e == METASIZE)
588 e = 0;
589 b += XSIZ - METASIZE;
591 b++;
592 e++;
596 /* The moiré effect */
597 #define DISCSIZ (XSIZ*2)
598 #define DISCTHICKNESS (XSIZ*15/40)
599 static uint8_t disc[DISCSIZ * DISCSIZ];
601 static void put_disc(uint8_t *, int, int);
602 static void draw_line(int, int, char);
604 void moire(enum action action, cucul_canvas_t *cv)
606 static cucul_dither_t *dither;
607 static uint8_t *screen;
608 static float d[6];
609 static unsigned int red[256], green[256], blue[256], alpha[256];
611 int i, x, y;
613 switch(action)
615 case PREPARE:
616 /* Fill various tables */
617 for(i = 0 ; i < 256; i++)
618 red[i] = green[i] = blue[i] = alpha[i] = 0;
620 for(i = 0; i < 6; i++)
621 d[i] = ((float)cucul_rand(50, 70)) / 1000.0;
623 red[0] = green[0] = blue[0] = 0x777;
624 red[1] = green[1] = blue[1] = 0xfff;
626 /* Fill the circle */
627 for(i = DISCSIZ * 2; i > 0; i -= DISCTHICKNESS)
629 int t, dx, dy;
631 for(t = 0, dx = 0, dy = i; dx <= dy; dx++)
633 draw_line(dx / 3, dy / 3, (i / DISCTHICKNESS) % 2);
634 draw_line(dy / 3, dx / 3, (i / DISCTHICKNESS) % 2);
636 t += t > 0 ? dx - dy-- : dx;
640 break;
642 case INIT:
643 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
644 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
645 break;
647 case UPDATE:
648 memset(screen, 0, XSIZ * YSIZ);
650 /* Set the palette */
651 red[0] = 0.5 * (1 + sin(d[0] * (frame + 1000))) * 0xfff;
652 green[0] = 0.5 * (1 + cos(d[1] * frame)) * 0xfff;
653 blue[0] = 0.5 * (1 + cos(d[2] * (frame + 3000))) * 0xfff;
655 red[1] = 0.5 * (1 + sin(d[3] * (frame + 2000))) * 0xfff;
656 green[1] = 0.5 * (1 + cos(d[4] * frame + 5.0)) * 0xfff;
657 blue[1] = 0.5 * (1 + cos(d[5] * (frame + 4000))) * 0xfff;
659 cucul_set_dither_palette(dither, red, green, blue, alpha);
661 /* Draw circles */
662 x = cos(d[0] * (frame + 1000)) * 128.0 + (XSIZ / 2);
663 y = sin(0.11 * frame) * 128.0 + (YSIZ / 2);
664 put_disc(screen, x, y);
666 x = cos(0.13 * frame + 2.0) * 64.0 + (XSIZ / 2);
667 y = sin(d[1] * (frame + 2000)) * 64.0 + (YSIZ / 2);
668 put_disc(screen, x, y);
669 break;
671 case RENDER:
672 cucul_dither_bitmap(cv, 0, 0,
673 cucul_get_canvas_width(cv),
674 cucul_get_canvas_height(cv),
675 dither, screen);
676 break;
678 case FREE:
679 free(screen);
680 cucul_free_dither(dither);
681 break;
685 static void put_disc(uint8_t *screen, int x, int y)
687 char *src = ((char*)disc) + (DISCSIZ / 2 - x) + (DISCSIZ / 2 - y) * DISCSIZ;
688 int i, j;
690 for(j = 0; j < YSIZ; j++)
691 for(i = 0; i < XSIZ; i++)
693 screen[i + XSIZ * j] ^= src[i + DISCSIZ * j];
697 static void draw_line(int x, int y, char color)
699 if(x == 0 || y == 0 || y > DISCSIZ / 2)
700 return;
702 if(x > DISCSIZ / 2)
703 x = DISCSIZ / 2;
705 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) - y),
706 color, 2 * x - 1);
707 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) + y - 1),
708 color, 2 * x - 1);
711 /* Matrix effect */
712 #define MAXDROPS 500
713 #define MINLEN 15
714 #define MAXLEN 30
716 void matrix(enum action action, cucul_canvas_t *cv)
718 static struct drop
720 int x, y, speed, len;
721 char str[MAXLEN];
723 drop[MAXDROPS];
725 int w, h, i, j;
727 switch(action)
729 case PREPARE:
730 for(i = 0; i < MAXDROPS; i++)
732 drop[i].x = cucul_rand(0, 1000);
733 drop[i].y = cucul_rand(0, 1000);
734 drop[i].speed = 5 + cucul_rand(0, 30);
735 drop[i].len = MINLEN + cucul_rand(0, (MAXLEN - MINLEN));
736 for(j = 0; j < MAXLEN; j++)
737 drop[i].str[j] = cucul_rand('0', 'z');
739 break;
741 case INIT:
742 break;
744 case UPDATE:
745 w = cucul_get_canvas_width(cv);
746 h = cucul_get_canvas_height(cv);
748 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
750 drop[i].y += drop[i].speed;
751 if(drop[i].y > 1000)
753 drop[i].y -= 1000;
754 drop[i].x = cucul_rand(0, 1000);
757 break;
759 case RENDER:
760 w = cucul_get_canvas_width(cv);
761 h = cucul_get_canvas_height(cv);
763 cucul_set_color_ansi(cv, CUCUL_BLACK, CUCUL_BLACK);
764 cucul_clear_canvas(cv);
766 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
768 int x, y;
770 x = drop[i].x * w / 1000 / 2 * 2;
771 y = drop[i].y * (h + MAXLEN) / 1000;
773 for(j = 0; j < drop[i].len; j++)
775 unsigned int fg;
777 if(j < 2)
778 fg = CUCUL_WHITE;
779 else if(j < drop[i].len / 4)
780 fg = CUCUL_LIGHTGREEN;
781 else if(j < drop[i].len * 4 / 5)
782 fg = CUCUL_GREEN;
783 else
784 fg = CUCUL_DARKGRAY;
785 cucul_set_color_ansi(cv, fg, CUCUL_BLACK);
787 cucul_put_char(cv, x, y - j,
788 drop[i].str[(y - j) % drop[i].len]);
791 break;
793 case FREE:
794 break;