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 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
);
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
);
178 next
= (next
+ 1) % DEMOS
;
179 fn
[next
](INIT
, backcv
);
181 else if(frame
== next_transition
+ TRANSITION_FRAMES
)
183 fn
[demo
](FREE
, frontcv
);
186 next_transition
= frame
+ DEMO_FRAMES
;
187 tmode
= cucul_rand(0, TRANSITION_COUNT
);
191 fn
[next
](UPDATE
, backcv
);
195 /* Render main demo's canvas */
196 fn
[demo
](RENDER
, frontcv
);
198 /* If a transition is on its way, render it */
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
);
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
);
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
);
231 void transition(cucul_canvas_t
*mask
, int tmode
, int completed
)
233 static float const star
[] =
243 -0.997000, -0.244000,
246 static float star_rot
[sizeof(star
)/sizeof(*star
)];
249 static float const square
[] =
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
;
267 case TRANSITION_SQUARE
:
268 /* Compute rotated coordinates */
269 for(i
= 0; i
< (sizeof(square
) / sizeof(*square
)) / 2; i
++)
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
);
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
, '#');
291 case TRANSITION_STAR
:
292 /* Compute rotated coordinates */
293 for(i
= 0; i
< (sizeof(star
) / sizeof(*star
)) / 2; i
++)
298 star_rot
[i
* 2] = x
* cos(angle
) - y
* sin(angle
);
299 star_rot
[i
* 2 + 1] = y
* cos(angle
) + x
* sin(angle
);
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, '#')
320 case TRANSITION_CIRCLE
:
321 cucul_fill_ellipse(mask
, w2
, h2
, mulx
, muly
, '#');
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];
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;
369 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
370 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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);
396 cucul_dither_bitmap(cv
, 0, 0,
397 cucul_get_canvas_width(cv
),
398 cucul_get_canvas_height(cv
),
404 cucul_free_dither(dither
);
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);
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
++)
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)
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
;
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 */
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));
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
,
489 angle
= (frame
+ angleoff
) % 360;
491 /* Crop the palette */
492 for(n
= CROPBALL
; n
< 255; n
++)
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;
526 memset(screen
, 0, XSIZ
* YSIZ
);
528 for(n
= 0; n
< METABALLS
; n
++)
529 draw_ball(screen
, x
[n
], y
[n
]);
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
));
541 cucul_free_dither(cucul_dither
);
546 static void create_ball(void)
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
)
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
];
578 b
+= XSIZ
- METASIZE
;
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
;
598 static unsigned int red
[256], green
[256], blue
[256], alpha
[256];
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
)
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
;
632 screen
= malloc(XSIZ
* YSIZ
* sizeof(uint8_t));
633 dither
= cucul_create_dither(8, XSIZ
, YSIZ
, XSIZ
, 0, 0, 0, 0);
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
);
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
);
661 cucul_dither_bitmap(cv
, 0, 0,
662 cucul_get_canvas_width(cv
),
663 cucul_get_canvas_height(cv
),
669 cucul_free_dither(dither
);
674 static void put_disc(uint8_t *screen
, int x
, int y
)
676 char *src
= ((char*)disc
) + (DISCSIZ
/ 2 - x
) + (DISCSIZ
/ 2 - y
) * DISCSIZ
;
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)
694 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) - y
),
696 memset(disc
+ (DISCSIZ
/ 2) - x
+ DISCSIZ
* ((DISCSIZ
/ 2) + y
- 1),
705 void matrix(enum action action
, cucul_canvas_t
*cv
)
709 int x
, y
, speed
, len
;
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');
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
;
743 drop
[i
].x
= cucul_rand(0, 1000);
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
++)
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
++)
768 else if(j
< drop
[i
].len
/ 4)
769 fg
= CUCUL_LIGHTGREEN
;
770 else if(j
< drop
[i
].len
* 4 / 5)
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
]);