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:
M | nyancat.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;
}