modules/caca.c: use create_child function
[vlock.git] / modules / caca.c
blob32a7c6b4b97d52dec3ad54f15055320dcfc7e070
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 int caca_main(void);
87 bool vlock_save(void **ctx_ptr)
89 static struct child_process child = {
90 .function = (void (*)(void *))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 int caca_main(void)
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 caca_set_display_time(dp, 20000);
151 /* Initialise all demos' lookup tables */
152 for(i = 0; i < DEMOS; i++)
153 fn[i](PREPARE, frontcv);
155 /* Choose a demo at random */
156 demo = cucul_rand(0, DEMOS);
157 fn[demo](INIT, frontcv);
159 for(;;)
161 if (abort_requested)
162 goto end;
164 /* Resize the spare canvas, just in case the main one changed */
165 cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv),
166 cucul_get_canvas_height(frontcv));
167 cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv),
168 cucul_get_canvas_height(frontcv));
170 /* Update demo's data */
171 fn[demo](UPDATE, frontcv);
173 /* Handle transitions */
174 if(frame == next_transition)
176 next = cucul_rand(0, DEMOS);
177 if(next == demo)
178 next = (next + 1) % DEMOS;
179 fn[next](INIT, backcv);
181 else if(frame == next_transition + TRANSITION_FRAMES)
183 fn[demo](FREE, frontcv);
184 demo = next;
185 next = -1;
186 next_transition = frame + DEMO_FRAMES;
187 tmode = cucul_rand(0, TRANSITION_COUNT);
190 if(next != -1)
191 fn[next](UPDATE, backcv);
193 frame++;
195 /* Render main demo's canvas */
196 fn[demo](RENDER, frontcv);
198 /* If a transition is on its way, render it */
199 if(next != -1)
201 fn[next](RENDER, backcv);
202 cucul_set_color_ansi(mask, CUCUL_LIGHTGRAY, CUCUL_BLACK);
203 cucul_clear_canvas(mask);
204 cucul_set_color_ansi(mask, CUCUL_WHITE, CUCUL_WHITE);
205 transition(mask, tmode,
206 100 * (frame - next_transition) / TRANSITION_FRAMES);
207 cucul_blit(frontcv, 0, 0, backcv, mask);
210 cucul_set_color_ansi(frontcv, CUCUL_WHITE, CUCUL_BLUE);
211 if(frame < 100)
212 cucul_put_str(frontcv, cucul_get_canvas_width(frontcv) - 30,
213 cucul_get_canvas_height(frontcv) - 2,
214 " -=[ Powered by libcaca ]=- ");
215 caca_refresh_display(dp);
217 end:
218 if(next != -1)
219 fn[next](FREE, frontcv);
220 fn[demo](FREE, frontcv);
222 caca_free_display(dp);
223 cucul_free_canvas(mask);
224 cucul_free_canvas(backcv);
225 cucul_free_canvas(frontcv);
227 return 0;
230 /* Transitions */
231 void transition(cucul_canvas_t *mask, int tmode, int completed)
233 static float const star[] =
235 0.000000, -1.000000,
236 0.308000, -0.349000,
237 0.992000, -0.244000,
238 0.500000, 0.266000,
239 0.632000, 0.998000,
240 0.008000, 0.659000,
241 -0.601000, 0.995000,
242 -0.496000, 0.275000,
243 -0.997000, -0.244000,
244 -0.313000, -0.349000
246 static float star_rot[sizeof(star)/sizeof(*star)];
249 static float const square[] =
251 -1, -1,
252 1, -1,
253 1, 1,
254 -1, 1
256 static float square_rot[sizeof(square)/sizeof(*square)];
258 float mulx = 0.0075f * completed * cucul_get_canvas_width(mask);
259 float muly = 0.0075f * completed * cucul_get_canvas_height(mask);
260 int w2 = cucul_get_canvas_width(mask) / 2;
261 int h2 = cucul_get_canvas_height(mask) / 2;
262 float angle = (0.0075f * completed * 360) * 3.14 / 180, x, y;
263 unsigned int i;
265 switch(tmode)
267 case TRANSITION_SQUARE:
268 /* Compute rotated coordinates */
269 for(i = 0; i < (sizeof(square) / sizeof(*square)) / 2; i++)
271 x = square[i * 2];
272 y = square[i * 2 + 1];
274 square_rot[i * 2] = x * cos(angle) - y * sin(angle);
275 square_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
278 mulx *= 1.8;
279 muly *= 1.8;
280 cucul_fill_triangle(mask,
281 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
282 square_rot[1*2] * mulx + w2, square_rot[1*2+1] * muly + h2, \
283 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, '#');
284 cucul_fill_triangle(mask,
285 square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \
286 square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, \
287 square_rot[3*2] * mulx + w2, square_rot[3*2+1] * muly + h2, '#');
288 break;
291 case TRANSITION_STAR:
292 /* Compute rotated coordinates */
293 for(i = 0; i < (sizeof(star) / sizeof(*star)) / 2; i++)
295 x = star[i * 2];
296 y = star[i * 2 + 1];
298 star_rot[i * 2] = x * cos(angle) - y * sin(angle);
299 star_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle);
302 mulx *= 1.8;
303 muly *= 1.8;
305 #define DO_TRI(a, b, c) \
306 cucul_fill_triangle(mask, \
307 star_rot[(a)*2] * mulx + w2, star_rot[(a)*2+1] * muly + h2, \
308 star_rot[(b)*2] * mulx + w2, star_rot[(b)*2+1] * muly + h2, \
309 star_rot[(c)*2] * mulx + w2, star_rot[(c)*2+1] * muly + h2, '#')
310 DO_TRI(0, 1, 9);
311 DO_TRI(1, 2, 3);
312 DO_TRI(3, 4, 5);
313 DO_TRI(5, 6, 7);
314 DO_TRI(7, 8, 9);
315 DO_TRI(9, 1, 5);
316 DO_TRI(9, 5, 7);
317 DO_TRI(1, 3, 5);
318 break;
320 case TRANSITION_CIRCLE:
321 cucul_fill_ellipse(mask, w2, h2, mulx, muly, '#');
322 break;
327 /* The plasma effect */
328 #define TABLEX (XSIZ * 2)
329 #define TABLEY (YSIZ * 2)
330 static uint8_t table[TABLEX * TABLEY];
332 static void do_plasma(uint8_t *,
333 double, double, double, double, double, double);
335 void plasma(enum action action, cucul_canvas_t *cv)
337 static cucul_dither_t *dither;
338 static uint8_t *screen;
339 static unsigned int red[256], green[256], blue[256], alpha[256];
340 static double r[3], R[6];
342 int i, x, y;
344 switch(action)
346 case PREPARE:
347 /* Fill various tables */
348 for(i = 0 ; i < 256; i++)
349 red[i] = green[i] = blue[i] = alpha[i] = 0;
351 for(i = 0; i < 3; i++)
352 r[i] = (double)(cucul_rand(1, 1000)) / 60000 * M_PI;
354 for(i = 0; i < 6; i++)
355 R[i] = (double)(cucul_rand(1, 1000)) / 10000;
357 for(y = 0 ; y < TABLEY ; y++)
358 for(x = 0 ; x < TABLEX ; x++)
360 double tmp = (((double)((x - (TABLEX / 2)) * (x - (TABLEX / 2))
361 + (y - (TABLEX / 2)) * (y - (TABLEX / 2))))
362 * (M_PI / (TABLEX * TABLEX + TABLEY * TABLEY)));
364 table[x + y * TABLEX] = (1.0 + sin(12.0 * sqrt(tmp))) * 256 / 6;
366 break;
368 case INIT:
369 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
370 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
371 break;
373 case UPDATE:
374 for(i = 0 ; i < 256; i++)
376 double z = ((double)i) / 256 * 6 * M_PI;
378 red[i] = (1.0 + sin(z + r[1] * frame)) / 2 * 0xfff;
379 blue[i] = (1.0 + cos(z + r[0] * (frame + 100))) / 2 * 0xfff;
380 green[i] = (1.0 + cos(z + r[2] * (frame + 200))) / 2 * 0xfff;
383 /* Set the palette */
384 cucul_set_dither_palette(dither, red, green, blue, alpha);
386 do_plasma(screen,
387 (1.0 + sin(((double)frame) * R[0])) / 2,
388 (1.0 + sin(((double)frame) * R[1])) / 2,
389 (1.0 + sin(((double)frame) * R[2])) / 2,
390 (1.0 + sin(((double)frame) * R[3])) / 2,
391 (1.0 + sin(((double)frame) * R[4])) / 2,
392 (1.0 + sin(((double)frame) * R[5])) / 2);
393 break;
395 case RENDER:
396 cucul_dither_bitmap(cv, 0, 0,
397 cucul_get_canvas_width(cv),
398 cucul_get_canvas_height(cv),
399 dither, screen);
400 break;
402 case FREE:
403 free(screen);
404 cucul_free_dither(dither);
405 break;
409 static void do_plasma(uint8_t *pixels, double x_1, double y_1,
410 double x_2, double y_2, double x_3, double y_3)
412 unsigned int X1 = x_1 * (TABLEX / 2),
413 Y1 = y_1 * (TABLEY / 2),
414 X2 = x_2 * (TABLEX / 2),
415 Y2 = y_2 * (TABLEY / 2),
416 X3 = x_3 * (TABLEX / 2),
417 Y3 = y_3 * (TABLEY / 2);
418 unsigned int y;
419 uint8_t * t1 = table + X1 + Y1 * TABLEX,
420 * t2 = table + X2 + Y2 * TABLEX,
421 * t3 = table + X3 + Y3 * TABLEX;
423 for(y = 0; y < YSIZ; y++)
425 unsigned int x;
426 uint8_t * tmp = pixels + y * YSIZ;
427 unsigned int ty = y * TABLEX, tmax = ty + XSIZ;
428 for(x = 0; ty < tmax; ty++, tmp++)
429 tmp[0] = t1[ty] + t2[ty] + t3[ty];
433 /* The metaball effect */
434 #define METASIZE (XSIZ/2)
435 #define METABALLS 12
436 #define CROPBALL 200 /* Colour index where to crop balls */
437 static uint8_t metaball[METASIZE * METASIZE];
439 static void create_ball(void);
440 static void draw_ball(uint8_t *, unsigned int, unsigned int);
442 void metaballs(enum action action, cucul_canvas_t *cv)
444 static cucul_dither_t *cucul_dither;
445 static uint8_t *screen;
446 static unsigned int r[256], g[256], b[256], a[256];
447 static float dd[METABALLS], di[METABALLS], dj[METABALLS], dk[METABALLS];
448 static unsigned int x[METABALLS], y[METABALLS];
449 static float i = 10.0, j = 17.0, k = 11.0;
450 static double offset[360 + 80];
451 static unsigned int angleoff;
453 int n, angle;
455 switch(action)
457 case PREPARE:
458 /* Make the palette eatable by libcaca */
459 for(n = 0; n < 256; n++)
460 r[n] = g[n] = b[n] = a[n] = 0x0;
461 r[255] = g[255] = b[255] = 0xfff;
463 /* Generate ball sprite */
464 create_ball();
466 for(n = 0; n < METABALLS; n++)
468 dd[n] = cucul_rand(0, 100);
469 di[n] = (float)cucul_rand(500, 4000) / 6000.0;
470 dj[n] = (float)cucul_rand(500, 4000) / 6000.0;
471 dk[n] = (float)cucul_rand(500, 4000) / 6000.0;
474 angleoff = cucul_rand(0, 360);
476 for(n = 0; n < 360 + 80; n++)
477 offset[n] = 1.0 + sin((double)(n * M_PI / 60));
478 break;
480 case INIT:
481 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
482 /* Create a libcucul dither smaller than our pixel buffer, so that we
483 * display only the interesting part of it */
484 cucul_dither = cucul_create_dither(8, XSIZ - METASIZE, YSIZ - METASIZE,
485 XSIZ, 0, 0, 0, 0);
486 break;
488 case UPDATE:
489 angle = (frame + angleoff) % 360;
491 /* Crop the palette */
492 for(n = CROPBALL; n < 255; n++)
494 int t1, t2, t3;
495 double c1 = offset[angle];
496 double c2 = offset[angle + 40];
497 double c3 = offset[angle + 80];
499 t1 = n < 0x40 ? 0 : n < 0xc0 ? (n - 0x40) * 0x20 : 0xfff;
500 t2 = n < 0xe0 ? 0 : (n - 0xe0) * 0x80;
501 t3 = n < 0x40 ? n * 0x40 : 0xfff;
503 r[n] = (c1 * t1 + c2 * t2 + c3 * t3) / 4;
504 g[n] = (c1 * t2 + c2 * t3 + c3 * t1) / 4;
505 b[n] = (c1 * t3 + c2 * t1 + c3 * t2) / 4;
508 /* Set the palette */
509 cucul_set_dither_palette(cucul_dither, r, g, b, a);
511 /* Silly paths for our balls */
512 for(n = 0; n < METABALLS; n++)
514 float u = di[n] * i + dj[n] * j + dk[n] * sin(di[n] * k);
515 float v = dd[n] + di[n] * j + dj[n] * k + dk[n] * sin(dk[n] * i);
516 u = sin(i + u * 2.1) * (1.0 + sin(u));
517 v = sin(j + v * 1.9) * (1.0 + sin(v));
518 x[n] = (XSIZ - METASIZE) / 2 + u * (XSIZ - METASIZE) / 4;
519 y[n] = (YSIZ - METASIZE) / 2 + v * (YSIZ - METASIZE) / 4;
522 i += 0.011;
523 j += 0.017;
524 k += 0.019;
526 memset(screen, 0, XSIZ * YSIZ);
528 for(n = 0; n < METABALLS; n++)
529 draw_ball(screen, x[n], y[n]);
530 break;
532 case RENDER:
533 cucul_dither_bitmap(cv, 0, 0,
534 cucul_get_canvas_width(cv),
535 cucul_get_canvas_height(cv),
536 cucul_dither, screen + (METASIZE / 2) * (1 + XSIZ));
537 break;
539 case FREE:
540 free(screen);
541 cucul_free_dither(cucul_dither);
542 break;
546 static void create_ball(void)
548 int x, y;
549 float distance;
551 for(y = 0; y < METASIZE; y++)
552 for(x = 0; x < METASIZE; x++)
554 distance = ((METASIZE/2) - x) * ((METASIZE/2) - x)
555 + ((METASIZE/2) - y) * ((METASIZE/2) - y);
556 distance = sqrt(distance) * 64 / METASIZE;
557 metaball[x + y * METASIZE] = distance > 15 ? 0 : (255 - distance) * 15;
561 static void draw_ball(uint8_t *screen, unsigned int bx, unsigned int by)
563 unsigned int color;
564 unsigned int i, e = 0;
565 unsigned int b = (by * XSIZ) + bx;
567 for(i = 0; i < METASIZE * METASIZE; i++)
569 color = screen[b] + metaball[i];
571 if(color > 255)
572 color = 255;
574 screen[b] = color;
575 if(e == METASIZE)
577 e = 0;
578 b += XSIZ - METASIZE;
580 b++;
581 e++;
585 /* The moiré effect */
586 #define DISCSIZ (XSIZ*2)
587 #define DISCTHICKNESS (XSIZ*15/40)
588 static uint8_t disc[DISCSIZ * DISCSIZ];
590 static void put_disc(uint8_t *, int, int);
591 static void draw_line(int, int, char);
593 void moire(enum action action, cucul_canvas_t *cv)
595 static cucul_dither_t *dither;
596 static uint8_t *screen;
597 static float d[6];
598 static unsigned int red[256], green[256], blue[256], alpha[256];
600 int i, x, y;
602 switch(action)
604 case PREPARE:
605 /* Fill various tables */
606 for(i = 0 ; i < 256; i++)
607 red[i] = green[i] = blue[i] = alpha[i] = 0;
609 for(i = 0; i < 6; i++)
610 d[i] = ((float)cucul_rand(50, 70)) / 1000.0;
612 red[0] = green[0] = blue[0] = 0x777;
613 red[1] = green[1] = blue[1] = 0xfff;
615 /* Fill the circle */
616 for(i = DISCSIZ * 2; i > 0; i -= DISCTHICKNESS)
618 int t, dx, dy;
620 for(t = 0, dx = 0, dy = i; dx <= dy; dx++)
622 draw_line(dx / 3, dy / 3, (i / DISCTHICKNESS) % 2);
623 draw_line(dy / 3, dx / 3, (i / DISCTHICKNESS) % 2);
625 t += t > 0 ? dx - dy-- : dx;
629 break;
631 case INIT:
632 screen = malloc(XSIZ * YSIZ * sizeof(uint8_t));
633 dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0);
634 break;
636 case UPDATE:
637 memset(screen, 0, XSIZ * YSIZ);
639 /* Set the palette */
640 red[0] = 0.5 * (1 + sin(d[0] * (frame + 1000))) * 0xfff;
641 green[0] = 0.5 * (1 + cos(d[1] * frame)) * 0xfff;
642 blue[0] = 0.5 * (1 + cos(d[2] * (frame + 3000))) * 0xfff;
644 red[1] = 0.5 * (1 + sin(d[3] * (frame + 2000))) * 0xfff;
645 green[1] = 0.5 * (1 + cos(d[4] * frame + 5.0)) * 0xfff;
646 blue[1] = 0.5 * (1 + cos(d[5] * (frame + 4000))) * 0xfff;
648 cucul_set_dither_palette(dither, red, green, blue, alpha);
650 /* Draw circles */
651 x = cos(d[0] * (frame + 1000)) * 128.0 + (XSIZ / 2);
652 y = sin(0.11 * frame) * 128.0 + (YSIZ / 2);
653 put_disc(screen, x, y);
655 x = cos(0.13 * frame + 2.0) * 64.0 + (XSIZ / 2);
656 y = sin(d[1] * (frame + 2000)) * 64.0 + (YSIZ / 2);
657 put_disc(screen, x, y);
658 break;
660 case RENDER:
661 cucul_dither_bitmap(cv, 0, 0,
662 cucul_get_canvas_width(cv),
663 cucul_get_canvas_height(cv),
664 dither, screen);
665 break;
667 case FREE:
668 free(screen);
669 cucul_free_dither(dither);
670 break;
674 static void put_disc(uint8_t *screen, int x, int y)
676 char *src = ((char*)disc) + (DISCSIZ / 2 - x) + (DISCSIZ / 2 - y) * DISCSIZ;
677 int i, j;
679 for(j = 0; j < YSIZ; j++)
680 for(i = 0; i < XSIZ; i++)
682 screen[i + XSIZ * j] ^= src[i + DISCSIZ * j];
686 static void draw_line(int x, int y, char color)
688 if(x == 0 || y == 0 || y > DISCSIZ / 2)
689 return;
691 if(x > DISCSIZ / 2)
692 x = DISCSIZ / 2;
694 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) - y),
695 color, 2 * x - 1);
696 memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) + y - 1),
697 color, 2 * x - 1);
700 /* Matrix effect */
701 #define MAXDROPS 500
702 #define MINLEN 15
703 #define MAXLEN 30
705 void matrix(enum action action, cucul_canvas_t *cv)
707 static struct drop
709 int x, y, speed, len;
710 char str[MAXLEN];
712 drop[MAXDROPS];
714 int w, h, i, j;
716 switch(action)
718 case PREPARE:
719 for(i = 0; i < MAXDROPS; i++)
721 drop[i].x = cucul_rand(0, 1000);
722 drop[i].y = cucul_rand(0, 1000);
723 drop[i].speed = 5 + cucul_rand(0, 30);
724 drop[i].len = MINLEN + cucul_rand(0, (MAXLEN - MINLEN));
725 for(j = 0; j < MAXLEN; j++)
726 drop[i].str[j] = cucul_rand('0', 'z');
728 break;
730 case INIT:
731 break;
733 case UPDATE:
734 w = cucul_get_canvas_width(cv);
735 h = cucul_get_canvas_height(cv);
737 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
739 drop[i].y += drop[i].speed;
740 if(drop[i].y > 1000)
742 drop[i].y -= 1000;
743 drop[i].x = cucul_rand(0, 1000);
746 break;
748 case RENDER:
749 w = cucul_get_canvas_width(cv);
750 h = cucul_get_canvas_height(cv);
752 cucul_set_color_ansi(cv, CUCUL_BLACK, CUCUL_BLACK);
753 cucul_clear_canvas(cv);
755 for(i = 0; i < MAXDROPS && i < (w * h / 32); i++)
757 int x, y;
759 x = drop[i].x * w / 1000 / 2 * 2;
760 y = drop[i].y * (h + MAXLEN) / 1000;
762 for(j = 0; j < drop[i].len; j++)
764 unsigned int fg;
766 if(j < 2)
767 fg = CUCUL_WHITE;
768 else if(j < drop[i].len / 4)
769 fg = CUCUL_LIGHTGREEN;
770 else if(j < drop[i].len * 4 / 5)
771 fg = CUCUL_GREEN;
772 else
773 fg = CUCUL_DARKGRAY;
774 cucul_set_color_ansi(cv, fg, CUCUL_BLACK);
776 cucul_put_char(cv, x, y - j,
777 drop[i].str[(y - j) % drop[i].len]);
780 break;
782 case FREE:
783 break;