nyancat

Unnamed repository; edit this file 'description' to name the repository.
git clone https://git.inz.fi/nyancat
Log | Files | Refs | README

commit 3c4ea4c02aa74682db94a754d11a39a949d4396f
parent 91955c57b52bb619ecd3894eaa663fd49da0a181
Author: Santtu Lakkala <inz@inz.fi>
Date:   Wed, 22 Jul 2020 16:34:50 +0300

Add animations

Use a background process to feed the terminal palette changes to create
nifty little color animations.

Diffstat:
Mnyancat.c | 280++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 255 insertions(+), 25 deletions(-)

diff --git a/nyancat.c b/nyancat.c @@ -12,6 +12,9 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#define _POSIX_C_SOURCE 199309L + #include <stdarg.h> #include <stdlib.h> #include <stdio.h> @@ -19,6 +22,10 @@ #include <stdbool.h> #include <stdint.h> +#include <sys/types.h> +#include <signal.h> +#include <time.h> + #include <math.h> #include <fcntl.h> @@ -32,6 +39,11 @@ #include <unicode/utf8.h> #define PI 3.1415926535898 +#define N(a) ((sizeof(a) / sizeof(*(a)))) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define CLAMP(a, b, c) MIN(MAX(a, b), c) +#define GRADSTEPS 16 struct lolcat { int x; @@ -52,22 +64,17 @@ struct lolcat { bool was_reverse; bool was_underline; bool was_bgset; - enum { TRUECOLOR, INDEXED256, INDEXED16 } mode; + bool xterm_workaround; + enum { TRUECOLOR, INDEXED256, INDEXED16, + NYANANIMATE, GRADANIMATE, STOP } mode; enum { NONE, FCOLOR, FI256, FR, FG, FB, BCOLOR, BI256, BR, BG, BB } cstate; - void (*write)(const char *buffer, size_t buflen, void *data); + bool (*write)(const char *buffer, size_t buflen, void *data); void *write_data; }; -int nyan(int x, int y, bool transpose) -{ - if (transpose) - return sin(y * PI / 18) * 18 + x * 18; - return sin(x * PI / 18) * 18 + y * 18; -} - int rainbow(float freq, int i) { return ((int)(sin(freq * i + 0) * 127 + 128)) << 16 | @@ -75,6 +82,92 @@ int rainbow(float freq, int i) ((int)(sin(freq * i + 4 * PI / 3) * 127 + 128)); } +const uint32_t animation_colors[] = { + 0xff0000, + 0xffaf00, + 0xffff00, + 0x87ff00, + 0x0087ff, + 0x0000af +}; + +size_t alter_palette(char *buffer, int index, uint32_t color) +{ + return sprintf(buffer, "\x1b]4;%d;rgb:%02x/%02x/%02x\x1b\\", + index, + (unsigned)(color >> 16), + (unsigned)((color >> 8) & 0xff), + (unsigned)(color & 0xff)); +} + +bool animate_gradpalette(struct lolcat *lc, int offset) +{ + size_t i; + + for (i = 0; i < GRADSTEPS; i++) { + char buffer[128]; + if (!lc->write(buffer, + alter_palette(buffer, + 16 + (i + offset) % GRADSTEPS, + rainbow(PI * 2 / GRADSTEPS, i)), + lc->write_data)) + return false; + } + + if (lc->xterm_workaround) + lc->write("\x1b[s\x1b[u", 6, lc->write_data); + + return true; +} + +bool animate_palette(struct lolcat *lc, bool up) +{ + size_t i; + + for (i = 0; i < N(animation_colors); i++) { + char buffer[128]; + if (!lc->write(buffer, + alter_palette(buffer, + 16 + + (3 * i + N(animation_colors) * + 3 - 1 - !!up) % + (N(animation_colors) * 3), + animation_colors[i]), + lc->write_data) || + !lc->write(buffer, + alter_palette(buffer, + 16 + + (3 * i + 1 + !!up) + % (N(animation_colors) * 3), + animation_colors[i]), + lc->write_data)) + return false; + } + + return true; +} + +void prepare_palette(struct lolcat *lc) +{ + size_t i; + + for (i = 0; i < N(animation_colors); i++) { + char buffer[128]; + lc->write(buffer, alter_palette(buffer, 3 * i + 16, + animation_colors[i]), + lc->write_data); + } + + animate_palette(lc, false); +} + +int nyan(int x, int y, bool transpose) +{ + if (transpose) + return sin(y * PI / 18) * 18 + x * 18; + return sin(x * PI / 18) * 18 + y * 18; +} + static size_t strnspn_printable(const char *str, size_t len) { size_t rv = 0; @@ -138,7 +231,7 @@ static void strtok_foreach_int(const char *str, int len, char delim, strtok_foreach(str, len, delim, _strtok_fi_cb, &fidata); } -struct lolcat *lolcat_init(void (*write_cb)(const char *buffer, size_t buflen, +struct lolcat *lolcat_init(bool (*write_cb)(const char *buffer, size_t buflen, void *data), void *cbdata) { struct lolcat *rv = calloc(1, sizeof(struct lolcat)); @@ -152,6 +245,21 @@ struct lolcat *lolcat_init(void (*write_cb)(const char *buffer, size_t buflen, return rv; } +static char *strcpy_alter(char *to, const char *from, char f, char t) +{ + char *w = to; + while (*w) + w++; + for (; *from; from++) { + if (*from == f) + *w++ = t; + else + *w++ = *from; + } + + return to; +} + static inline int _bold(int color) { if (!color) @@ -308,9 +416,6 @@ static int _lc_arg_m(int arg, void *data) return 0; } -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define CLAMP(a, b, c) MIN(MAX(a, b), c) static int _add(int a, int b, double amount) { int r = (a >> 16) + ((b >> 16) - 0xc0) * amount; @@ -388,16 +493,31 @@ static void _lc_colorize(struct lolcat *lc, const char *u8_char, size_t len) return; } - if (lc->bold) - col = _bold(lc->fg); - else - col = lc->fg; - col = _add(rainbow(0.03, nyan(lc->x, lc->y, lc->transpose)), col, 0.5); + if (lc->mode == NYANANIMATE) { + if (lc->transpose) + col = 16 + (((lc->x / 2 % N(animation_colors)) * 3) + + (lc->x & 1) * + (1 + (((lc->y + 25) / 30) & 1))); + else + col = 16 + (((lc->y / 2 % N(animation_colors)) * 3) + + (lc->y & 1) * + (1 + (((lc->x + 25) / 30) & 1))); + } else if (lc->mode == GRADANIMATE) { + col = 16 + (lc->x + lc->y) % GRADSTEPS; + } else { + if (lc->bold) + col = _bold(lc->fg); + else + col = lc->fg; + col = _add(rainbow(0.03, + nyan(lc->x, lc->y, lc->transpose)), + col, 0.5); - if (lc->mode == INDEXED16) - col = _index16(col, &lc->bold); - else if (lc->mode == INDEXED256) - col = _index256(col); + if (lc->mode == INDEXED16) + col = _index16(col, &lc->bold); + else if (lc->mode == INDEXED256) + col = _index256(col); + } if ((lc->was_bold && !lc->bold) || (lc->was_reverse && !lc->reverse) || @@ -445,7 +565,9 @@ static void _lc_colorize(struct lolcat *lc, const char *u8_char, size_t len) if (lc->mode == INDEXED16) bw += sprintf(bw, "3%dm", col); - else if (lc->mode == INDEXED256) + else if (lc->mode == INDEXED256 || + lc->mode == GRADANIMATE || + lc->mode == NYANANIMATE) bw += sprintf(bw, "38;5;%dm", col); else @@ -621,6 +743,16 @@ ssize_t lc_process(struct lolcat *lc, const char *buffer, size_t len) i += dlen + 2; continue; } + if (buffer[i + dlen + 1] == '\x1b') { + if (dlen + i + 2 == len) + return ip; + if (buffer[i + dlen + 2] == '\\') + lc->write(buffer + ip, + dlen + 4, + lc->write_data); + i += dlen + 3; + continue; + } } if (buffer[i] == '>') { lc->write(buffer + ip, 2, lc->write_data); @@ -680,9 +812,9 @@ ssize_t lc_process(struct lolcat *lc, const char *buffer, size_t len) return i; } -void _write(const char *data, size_t len, void *user_data) +bool _write(const char *data, size_t len, void *user_data) { - fwrite(data, 1, len, user_data); + return fwrite(data, 1, len, user_data) == len; } void lolcat_set_size(struct lolcat *lc, int w, int h) @@ -699,11 +831,21 @@ static void usage(const char *binname) " -1 16 color output\n" " -2 256 color output\n" " -b Escape color codes with \\[ \\] for bash prompt\n" + " -n Animate the rainbowaves\n" + " -g Animate rainbow gradient\n" + " -s Stop animation\n" " -v Make rainbowaves vertical\n", binname); exit(0); } +static bool hupped = false; +static void sig_hup(int signum) +{ + (void)signum; + hupped = true; +} + int main(int argc, char **argv) { struct lolcat *lc = lolcat_init(_write, stdout); @@ -739,6 +881,15 @@ int main(int argc, char **argv) case '2': lc->mode = INDEXED256; break; + case 'n': + lc->mode = NYANANIMATE; + break; + case 'g': + lc->mode = GRADANIMATE; + break; + case 's': + lc->mode = STOP; + break; case 'v': lc->transpose = true; break; @@ -753,6 +904,83 @@ int main(int argc, char **argv) } } + + if ((lc->mode == GRADANIMATE || lc->mode == NYANANIMATE || + lc->mode == STOP) && isatty(STDERR_FILENO)) { + char buffer[8192]; + const char *tty = ttyname(STDERR_FILENO); + strcpy_alter(buffer + + sprintf(buffer, "/tmp/.nyancat-%u-", getuid()), + tty, '/', '_'); + FILE *pf = fopen(buffer, "r"); + long unsigned pid = 0; + const char *term; + + if ((term = getenv("TERM"))) + lc->xterm_workaround = !strncmp(term, "xterm", 5); + + if (pf) { + fscanf(pf, " %lu", &pid); + fclose(pf); + } + + if (lc->mode == STOP) { + if (pid) + kill(pid, SIGHUP); + exit(0); + } else if (!pid || kill(pid, 0)) { + if ((pid = fork())) { + pf = fopen(buffer, "w"); + if (pf) { + fprintf(pf, "%lu\n", pid); + fclose(pf); + } + } else { + size_t n = 0; + if (lc->mode == NYANANIMATE) + prepare_palette(lc); + else + animate_gradpalette(lc, 0); + fclose(stderr); + fclose(stdout); + fclose(stdin); + + signal(SIGHUP, sig_hup); + signal(SIGINT, sig_hup); + + while (!hupped) { + nanosleep(&(struct timespec){ + .tv_sec = lc->mode == + NYANANIMATE ? 1 : 0, + .tv_nsec = lc->mode == + NYANANIMATE ? 0 : 100000000 }, + NULL); + lc->write_data = fopen(tty, "w"); + if (!lc->write_data) + break; + if (lc->mode == NYANANIMATE) { + if (!animate_palette(lc, + n ^= 1)) + break; + } else { + if (!animate_gradpalette( + lc, + n = (n + 1) % + GRADSTEPS)) + break; + } + if (fflush(lc->write_data)) + break; + fclose(lc->write_data); + } + + close(STDOUT_FILENO); + unlink(buffer); + exit(0); + } + } + } + if (i >= argc) { tcgetattr(STDIN_FILENO, &old_tio); @@ -816,6 +1044,8 @@ int main(int argc, char **argv) fprintf(stdout, "\\[\x1b[0m\\]"); else fprintf(stdout, "\x1b[0m"); + fflush(stdout); + return 0; }