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>
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.
30 # define M_PI 3.14159265358979323846
33 #include <sys/types.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
*) =
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 */
76 /* Global variables */
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
,
92 .stdin_fd
= REDIRECT_DEV_NULL
,
93 .stdout_fd
= NO_REDIRECT
,
94 .stderr_fd
= NO_REDIRECT
,
97 /* Initialize ncurses. */
100 if (!create_child(&child
))
108 bool vlock_save_abort(void **ctx_ptr
)
110 struct child_process
*child
= *ctx_ptr
;
113 ensure_death(child
->pid
);
114 /* Restore sane terminal and uninitialize ncurses. */
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
;
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
);
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
);
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
);
179 next
= (next
+ 1) % DEMOS
;
180 fn
[next
](INIT
, backcv
);
182 else if(frame
== next_transition
+ TRANSITION_FRAMES
)
184 fn
[demo
](FREE
, frontcv
);
187 next_transition
= frame
+ DEMO_FRAMES
;
188 tmode
= cucul_rand(0, TRANSITION_COUNT
);
192 fn
[next
](UPDATE
, backcv
);
196 /* Render main demo's canvas */
197 fn
[demo
](RENDER
, frontcv
);
199 /* If a transition is on its way, render it */
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
);
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
);
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
);
232 void transition(cucul_canvas_t
*mask
, int tmode
, int completed
)
234 static float const star
[] =
244 -0.997000, -0.244000,
247 static float star_rot
[sizeof(star
)/sizeof(*star
)];
250 static float const square
[] =
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
;
268 case TRANSITION_SQUARE
:
269 /* Compute rotated coordinates */
270 for(i
= 0; i
< (sizeof(square
) / sizeof(*square
)) / 2; i
++)
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
);
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
, '#');
292 case TRANSITION_STAR
:
293 /* Compute rotated coordinates */
294 for(i
= 0; i
< (sizeof(star
) / sizeof(*star
)) / 2; i
++)
299 star_rot
[i
* 2] = x
* cos(angle
) - y
* sin(angle
);
300 star_rot
[i
* 2 + 1] = y
* cos(angle
) + x
* sin(angle
);
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, '#')
321 case TRANSITION_CIRCLE
:
322 cucul_fill_ellipse(mask
, w2
, h2
, mulx
, muly
, '#');
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];
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;
370 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
371 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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);
397 cucul_dither_bitmap(cv
, 0, 0,
398 cucul_get_canvas_width(cv
),
399 cucul_get_canvas_height(cv
),
405 cucul_free_dither(dither
);
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);
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
++)
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)
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
;
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 */
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));
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
,
490 angle
= (frame
+ angleoff
) % 360;
492 /* Crop the palette */
493 for(n
= CROPBALL
; n
< 255; n
++)
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;
527 memset(screen
, 0, XSIZ
* YSIZ
);
529 for(n
= 0; n
< METABALLS
; n
++)
530 draw_ball(screen
, x
[n
], y
[n
]);
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
));
542 cucul_free_dither(cucul_dither
);
547 static void create_ball(void)
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
)
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
];
579 b
+= XSIZ
- METASIZE
;
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
;
599 static unsigned int red
[256], green
[256], blue
[256], alpha
[256];
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
)
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
;
633 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
634 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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
);
662 cucul_dither_bitmap(cv
, 0, 0,
663 cucul_get_canvas_width(cv
),
664 cucul_get_canvas_height(cv
),
670 cucul_free_dither(dither
);
675 static void put_disc(uint8_t *screen
, int x
, int y
)
677 char *src
= ((char*)disc
) + (DISCSIZ
/ 2 - x
) + (DISCSIZ
/ 2 - y
) * DISCSIZ
;
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)
695 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) - y
),
697 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) + y
- 1),
706 void matrix(enum action action
, cucul_canvas_t
*cv
)
710 int x
, y
, speed
, len
;
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');
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
;
744 drop
[i
].x
= cucul_rand(0, 1000);
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
++)
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
++)
769 else if(j
< drop
[i
].len
/ 4)
770 fg
= CUCUL_LIGHTGREEN
;
771 else if(j
< drop
[i
].len
* 4 / 5)
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
]);