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;
81 void handle_sigterm(int __attribute__((unused
)) signum
)
83 abort_requested
= true;
88 bool vlock_save(void **ctx_ptr
)
96 int nullfd
= open("/dev/null", O_RDONLY
);
101 (void) dup2(nullfd
, STDIN_FILENO
);
105 (void) signal(SIGTERM
, handle_sigterm
);
106 (void) signal(SIGABRT
, SIG_DFL
);
107 (void) signal(SIGUSR1
, SIG_DFL
);
108 (void) signal(SIGUSR2
, SIG_DFL
);
122 bool vlock_save_abort(void **ctx_ptr
)
124 if (*ctx_ptr
!= NULL
) {
137 static caca_display_t
*dp
;
138 static cucul_canvas_t
*frontcv
, *backcv
, *mask
;
140 int demo
, next
= -1, next_transition
= DEMO_FRAMES
;
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
);
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
);
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
);
189 next
= (next
+ 1) % DEMOS
;
190 fn
[next
](INIT
, backcv
);
192 else if(frame
== next_transition
+ TRANSITION_FRAMES
)
194 fn
[demo
](FREE
, frontcv
);
197 next_transition
= frame
+ DEMO_FRAMES
;
198 tmode
= cucul_rand(0, TRANSITION_COUNT
);
202 fn
[next
](UPDATE
, backcv
);
206 /* Render main demo's canvas */
207 fn
[demo
](RENDER
, frontcv
);
209 /* If a transition is on its way, render it */
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
);
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
);
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
);
242 void transition(cucul_canvas_t
*mask
, int tmode
, int completed
)
244 static float const star
[] =
254 -0.997000, -0.244000,
257 static float star_rot
[sizeof(star
)/sizeof(*star
)];
260 static float const square
[] =
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
;
278 case TRANSITION_SQUARE
:
279 /* Compute rotated coordinates */
280 for(i
= 0; i
< (sizeof(square
) / sizeof(*square
)) / 2; i
++)
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
);
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
, '#');
302 case TRANSITION_STAR
:
303 /* Compute rotated coordinates */
304 for(i
= 0; i
< (sizeof(star
) / sizeof(*star
)) / 2; i
++)
309 star_rot
[i
* 2] = x
* cos(angle
) - y
* sin(angle
);
310 star_rot
[i
* 2 + 1] = y
* cos(angle
) + x
* sin(angle
);
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, '#')
331 case TRANSITION_CIRCLE
:
332 cucul_fill_ellipse(mask
, w2
, h2
, mulx
, muly
, '#');
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];
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;
380 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
381 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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);
407 cucul_dither_bitmap(cv
, 0, 0,
408 cucul_get_canvas_width(cv
),
409 cucul_get_canvas_height(cv
),
415 cucul_free_dither(dither
);
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);
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
++)
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)
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
;
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 */
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));
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
,
500 angle
= (frame
+ angleoff
) % 360;
502 /* Crop the palette */
503 for(n
= CROPBALL
; n
< 255; n
++)
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;
537 memset(screen
, 0, XSIZ
* YSIZ
);
539 for(n
= 0; n
< METABALLS
; n
++)
540 draw_ball(screen
, x
[n
], y
[n
]);
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
));
552 cucul_free_dither(cucul_dither
);
557 static void create_ball(void)
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
)
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
];
589 b
+= XSIZ
- METASIZE
;
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
;
609 static unsigned int red
[256], green
[256], blue
[256], alpha
[256];
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
)
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
;
643 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
644 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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
);
672 cucul_dither_bitmap(cv
, 0, 0,
673 cucul_get_canvas_width(cv
),
674 cucul_get_canvas_height(cv
),
680 cucul_free_dither(dither
);
685 static void put_disc(uint8_t *screen
, int x
, int y
)
687 char *src
= ((char*)disc
) + (DISCSIZ
/ 2 - x
) + (DISCSIZ
/ 2 - y
) * DISCSIZ
;
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)
705 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) - y
),
707 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) + y
- 1),
716 void matrix(enum action action
, cucul_canvas_t
*cv
)
720 int x
, y
, speed
, len
;
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');
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
;
754 drop
[i
].x
= cucul_rand(0, 1000);
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
++)
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
++)
779 else if(j
< drop
[i
].len
/ 4)
780 fg
= CUCUL_LIGHTGREEN
;
781 else if(j
< drop
[i
].len
* 4 / 5)
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
]);