Skip to content

Instantly share code, notes, and snippets.

@ashafq
Created April 10, 2019 17:30
Show Gist options
  • Select an option

  • Save ashafq/217b9c7a90e2d1008c594a486109edb1 to your computer and use it in GitHub Desktop.

Select an option

Save ashafq/217b9c7a90e2d1008c594a486109edb1 to your computer and use it in GitHub Desktop.
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#define M_TAU (2.0 * M_PI)
#define CHAR_PER_COLOR (20)
#define CHAR_END (5)
#define die(error_code, ...) \
do { \
fprintf(stderr, "[ERROR] " __VA_ARGS__); \
exit(error_code); \
} while (0)
/* Rainbow context */
struct rctx
{
double phase;
double dphase;
};
enum RAINBOW_ERR_CODE
{
NO_ERROR = 0,
DEST_BUF_TOO_SMALL = 100,
};
typedef uint32_t rgb_t;
#define RED(x) ((x) & 0xFFU)
#define GREEN(x) (((x) >> 8U) & 0xFFU)
#define BLUE(x) (((x) >> 16U) & 0xFFU)
#define COMPOSE_RGB(R, G, B) \
((R) & 0xFFU) | \
(((G) & 0xFFU) << 8U) | \
(((B) & 0xFFU) << 16U)
static int get_win_size(int fd, struct winsize *win);
static uint8_t dtou8(double x);
static void calc_rainbow(struct rctx *ctx, rgb_t *rc, size_t len);
static int rgb_ansi_esc_seq(char *buf, size_t bufsize, rgb_t *rc, size_t rclen);
static struct ctx
{
char *scrbuf;
rgb_t *colbuf;
} prog_ctx = {0};
static void free_up_memory(void)
{
free(prog_ctx.scrbuf);
free(prog_ctx.colbuf);
}
int main()
{
struct winsize win = {0};
struct rctx rainbow = {0};
size_t colsize = 0, rowsize = 0, scrbufsize = 0;
char *scrbuf = NULL;
rgb_t *colbuf = NULL;
if (get_win_size(STDIN_FILENO, &win))
{
die(EXIT_FAILURE, "get_win_size failed\n");
}
rowsize = win.ws_row;
colsize = win.ws_col;
scrbufsize = (win.ws_col * CHAR_PER_COLOR) + CHAR_END;
rainbow.phase = 0.0;
rainbow.dphase = M_TAU / (1.05 * colsize);
colbuf = calloc(colsize, sizeof(rgb_t));
if (colbuf == NULL)
{
die(EXIT_FAILURE, "allocating memory (colbuf)\n");
}
scrbuf = calloc(scrbufsize, sizeof(char));
if (scrbuf == NULL)
{
die(EXIT_FAILURE, "allocating memory (scrbuf)\n");
}
// Some housekeeping stuff
prog_ctx.scrbuf = scrbuf;
prog_ctx.colbuf = colbuf;
atexit(free_up_memory);
for/*ever*/ (;;)
{
calc_rainbow(&rainbow, colbuf, colsize);
if (rgb_ansi_esc_seq(scrbuf, scrbufsize, colbuf, colsize))
{
die(EXIT_FAILURE, "Error drawing line\n");
}
write(STDOUT_FILENO, "\x1b[2J", 4);
for (size_t i = 0; i < (rowsize - 1); ++i)
write(STDOUT_FILENO, scrbuf, scrbufsize);
usleep(100000);
}
free(colbuf);
free(scrbuf);
return 0;
}
static uint8_t dtou8(double x)
{
double y = 127.0 * x + 128.0;
if (y >= 255.0)
return 255;
if (y <= 0)
return 0;
return (uint8_t)(y);
}
static void calc_rainbow(struct rctx *ctx, rgb_t *rc, size_t len)
{
const double RED_PHASE = 0.0;
const double BLUE_PHASE = (1.0 / 3.0) * M_TAU;
const double GREEN_PHASE = (2.0 / 3.0) * M_TAU;
double phase = ctx->phase;
double dphase = ctx->dphase;
for (size_t i = 0; i < len; ++i)
{
double red = sin(phase + RED_PHASE);
double green = sin(phase + GREEN_PHASE);
double blue = sin(phase + BLUE_PHASE);
rgb_t rcolor = COMPOSE_RGB(dtou8(red), dtou8(green), dtou8(blue));
rc[i] = rcolor;
phase = fmod(phase + dphase, M_TAU);
}
ctx->phase = phase;
}
static int rgb_ansi_esc_seq(char *buf, size_t bufsize, rgb_t *rc, size_t rclen)
{
const char *fmt = "\x1b[48;2;%d;%d;%dm ";
size_t offset = 0;
size_t written = 0;
memset(buf, 0, bufsize);
for (size_t i = 0; i < rclen; ++i)
{
rgb_t x = rc[i];
written = sprintf(buf + offset, fmt, RED(x), GREEN(x), BLUE(x));
offset += written;
if (offset >= bufsize)
return DEST_BUF_TOO_SMALL;
}
// Reset the terminal at the end
written = sprintf(buf + offset, "\x1b[0m\n");
offset += written;
if (offset >= bufsize)
return DEST_BUF_TOO_SMALL;
return NO_ERROR;
}
static int get_win_size(int fd, struct winsize *win)
{
int err = ioctl(fd, TIOCGWINSZ, (char *) win);
return err;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment