Created
April 10, 2019 17:30
-
-
Save ashafq/217b9c7a90e2d1008c594a486109edb1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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