rvis | Minimal terminal colorizer. |
| download: https://git.y1.nz/archives/rvis.tar.gz | |
| README | Files | Log | Refs |
commit 053a145fdb3b359b94faf50b0fdd741cf5bcef3e parent f36f081af10a20eaeadce82f1f49f3d0cd0c14ea Author: Emma Weaver <emma@y1.nz> Date: Wed, 17 Jun 2026 10:27:56 -0500 Demoted unnecessary fork to pthread, improved config, cleaned up Diffstat:
| M | Makefile | 2 | +- |
| M | README | 7 | +++---- |
| M | config.h | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
| M | config.mk | 2 | +- |
| M | main.c | 104 | +++++++++++++++++++++++++++++++++++-------------------------------------------- |
| M | term.c | 18 | +++++++++--------- |
| M | vt.c | 29 | +++++++++++++++++++++-------- |
| M | vt.h | 8 | ++++---- |
8 files changed, 137 insertions(+), 101 deletions(-)
diff --git a/Makefile b/Makefile @@ -5,7 +5,7 @@ all: rvis rvis: main.c term.o vt.o config.h $(CC) main.c term.o vt.o -o rvis -vt.o: vt.c vt.h vt-config.h +vt.o: vt.c vt.h $(CC) -c vt.c -o vt.o term.o: term.c term.h diff --git a/README b/README @@ -11,11 +11,10 @@ RVIS - Minimal terminal colorizer by emma@y1.nz -This is a kind of silly project. That applies terminal colors +This is a kind of silly project. It applies terminal colors where there wouldn't otherwise be any. You can write whatever -kind of output processing you want in config.h. Go nuts! +kind of output processing you want in config.h. Have fun :) TODO: -[ ] preserve "normal" color codes (so for example git status correctly shows red and green) -[ ] listen for custom escape sequences, to switch between different modes? +[ ] preserve "normal" color codes/boldnesses [ ] clean up code diff --git a/config.h b/config.h @@ -1,39 +1,75 @@ - +#define COLOR_RESET 0 +#define COLOR_WHITE 1 +#define COLOR_RED 2 +#define COLOR_GREEN 3 +#define COLOR_BROWN 4 +#define COLOR_BLUE 5 +#define COLOR_MAGENTA 6 +#define COLOR_CYAN 7 +#define COLOR_BLACK 8 +#define COLOR_BOLD_WHITE 9 +#define COLOR_BOLD_RED 10 +#define COLOR_BOLD_GREEN 11 +#define COLOR_BOLD_BROWN 12 +#define COLOR_BOLD_BLUE 13 +#define COLOR_BOLD_MAGENTA 14 +#define COLOR_BOLD_CYAN 15 +#define COLOR_BOLD_BLACK 16 char *group_colors[] = { - "\033[0;37m", /* black */ + "\033[0m", /* reset */ + + "\033[0;30m", /* white */ + "\033[0;31m", /* red */ + "\033[0;32m", /* green */ + "\033[0;33m", /* brown */ "\033[0;34m", /* blue */ - "\033[1;35m", /* magenta */ + "\033[0;35m", /* magenta */ + "\033[0;36m", /* cyan */ + "\033[0;37m", /* black */ + + "\033[1;30m", /* white */ + "\033[1;31m", /* red */ "\033[1;32m", /* green */ + "\033[1;33m", /* brown */ + "\033[1;34m", /* blue */ + "\033[1;35m", /* magenta */ + "\033[1;36m", /* cyan */ + "\033[1;37m", /* black */ }; /* color by type */ int -get_char_group4(char ch) +get_char_group(char ch) { - if ('a' <= ch && ch <= 'z') return 1; - if ('A' <= ch && ch <= 'Z') return 2; - if ('0' <= ch && ch <= '9') return 3; + if ('a' <= ch && ch <= 'z') return COLOR_BOLD_MAGENTA; + if ('A' <= ch && ch <= 'Z') return COLOR_MAGENTA; + if ('0' <= ch && ch <= '9') return COLOR_BLUE; return 0; } /* color in order */ int counter = 0; int -get_char_group3(char ch) +get_char_group2(char ch) { - return counter = (counter+1) % 5; + counter = (counter+1) % 2; + if (counter == 0) return COLOR_BOLD_MAGENTA; + if (counter == 1) return COLOR_MAGENTA; + return COLOR_BLACK; } -/* really goofy multiline comment coloring */ -int is_ml_comment = 0; -int is_sl_comment = 0; +/* coloring my git diffs BY FORCE. (dumb lol) */ +int is_add = 0, is_sub = 0; char previous = (char) 0; int -get_char_group(char ch) +get_char_group3(char ch) { - if (previous == '/' && ch == '*') is_ml_comment = 1; - if (previous == '*' && ch == '/') is_ml_comment = 0; + if (previous == '\n' && ch == '-') { is_add = 0; is_sub = 1; } + else if (previous == '\n' && ch == '+') { is_add = 1; is_sub = 0; } + else if (ch == '\n') { is_add = is_sub = 0; } previous = ch; - return is_ml_comment ? 1 : 0; + if (is_add) return 3; + if (is_sub) return 4; + return 0; } diff --git a/config.mk b/config.mk @@ -1,2 +1,2 @@ -CC=c99 -std=c99 -Wpedantic +CC=c99 -std=c99 -Wpedantic -pthread diff --git a/main.c b/main.c @@ -4,27 +4,27 @@ #include <stdlib.h> /* exit */ #include <signal.h> /* signal, SIGCHLD, SIG_IGN, SIG_DFL */ #include <termios.h> /* termios, various flags, tc(set/get)attr */ +#include <pthread.h> /* for pthread_create */ #include "config.h" /* get_char_group, group_colors */ #include "vt.h" /* ParserState, handle */ #include "term.h" /* get_root_size */ +pid_t pty_pid; +pthread_t pt; +struct termios original_termios; + void handle_pty_child(int pid) { if (pid != 0) return; - signal(SIGCHLD, SIG_DFL); execl("/bin/sh", "/bin/sh", NULL); - printf("\033[31m" "Child: exited." "\033[39m" "\n"); - fflush(stdout); - exit(0); } -FILE *global_shout; void -print_to_shout(ParserState *state) +pass(ParserState *state) { - fputc(state->current_char, global_shout); + fputc(state->current_char, stdout); } int prev_group = -1; @@ -38,39 +38,31 @@ colorize(ParserState *state) } void -handle_output_child(int pid, FILE *shout) +colorize_sometimes(ParserState *state) { - if (pid != 0) return; - - ParserState state; - ParserCallbacks cbs; - - global_shout = stdout; - init_parser_state(&state); - - /* pass control codes to shout */ - init_parser_callbacks(&cbs, print_to_shout); - /* but colorize visible characters */ - cbs.print = colorize; + /* the parser calls "execute" rather than "print" on some whitespace chars. */ + if (state->current_char == '\n') { + colorize(state); + return; + } - /* DECCRM: display control codes */ - fprintf(stdout, "\033[3h"); + pass(state); +} - /* RIS (?): clear screen */ - fprintf(stdout, "\033[2J"); +void * +process_input(void *arg) +{ + FILE *shin = (FILE *) arg; - char ch; + /* read stdin, send it to shin */ for (;;) { - char ch = (char) fgetc(shout); + char ch = (char) fgetc(stdin); if (ch == EOF) break; - - handle(&cbs, &state, ch); - fflush(stdout); + fputc(ch, shin); + fflush(shin); } - exit(0); } -struct termios original_termios; void set_termios_raw_mode(FILE *fd) { @@ -84,37 +76,40 @@ set_termios_raw_mode(FILE *fd) tcsetattr(fileno(fd), TCSANOW, &t); } -pid_t pty_pid, out_pid; void -kill_children(int sig) +process_output(FILE *shout) { - kill(pty_pid, SIGKILL); - kill(out_pid, SIGKILL); - - /* restore previous termios */ - tcsetattr(fileno(stdin), TCSANOW, &original_termios); + ParserState *state; + ParserCallbacks cbs; + state = init_parser_state(); + init_parser_callbacks(&cbs, pass); + cbs.print = colorize; + cbs.execute = colorize_sometimes; /* clear screen */ fprintf(stdout, "\033[2J"); - fflush(stdout); - exit(0); + char ch; + for (;;) { + char ch = (char) fgetc(shout); + if (ch == EOF) break; + handle(&cbs, state, ch); + fflush(stdout); + } } int main() { int amaster; - int w = 30, h = 20; + int w, h; get_root_size(&w, &h); struct winsize pty_size = { .ws_row = h, .ws_col = w }; - // signal(SIGCHLD, SIG_IGN); set_termios_raw_mode(stdin); /* FORK! */ pty_pid = forkpty(&amaster, NULL, NULL, &pty_size); handle_pty_child(pty_pid); - FILE *shout = fdopen(amaster, "r"); FILE *shin = fdopen(amaster, "w"); if (shin == NULL || shout == NULL) { @@ -122,21 +117,14 @@ main() { return 1; } - /* FORK! (again) */ - out_pid = fork(); - - /* handle child */ - handle_output_child(out_pid, shout); + /* split off thread for input handling */ + pthread_create(&pt, NULL, process_input, shin); + pthread_detach(pt); - /* set parent to quit when any child terminates */ - signal(SIGCHLD, kill_children); + /* process output on the main thread. Returns after shout closes */ + process_output(shout); - /* read stdin, send it to shin */ - for (;;) { - char ch = (char) fgetc(stdin); - if (ch == EOF) break; - fputc(ch, shin); - fflush(shin); - } - return 0; + pthread_kill(pt, SIGTERM); + tcsetattr(fileno(stdin), TCSANOW, &original_termios); + exit(0); } diff --git a/term.c b/term.c @@ -9,21 +9,21 @@ int get_root_size(int *x, int *y) { - struct termios original, changed; + struct termios termios0, termios1; char response[RESPONSE_SIZE] = ""; int index = 0; int ch = 0; int result; - tcgetattr(STDIN_FILENO, &original); - changed = original; - changed.c_lflag &= ~(ICANON | ECHO); - changed.c_cc[VMIN] = 1; - changed.c_cc[VTIME] = 0; - tcsetattr(STDIN_FILENO, TCSANOW, &changed); + tcgetattr(STDIN_FILENO, &termios0); + termios1 = termios0; + termios1.c_lflag &= ~(ICANON | ECHO); + termios1.c_cc[VMIN] = 1; + termios1.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &termios1); /* move down/right until end of screen */ - printf("\033[9999;9999H"); + printf("\033[9999B" "\033[9999C"); fflush(stdout); /* query cursor position */ @@ -43,7 +43,7 @@ get_root_size(int *x, int *y) /* move cursor to top-left of screen */ printf("\033[1;1H"); - // tcsetattr(STDIN_FILENO, TCSANOW, &original); + tcsetattr(STDIN_FILENO, TCSANOW, &termios0); return result == 2; } diff --git a/vt.c b/vt.c @@ -1,6 +1,7 @@ /* See vt.h for documentation. */ #include <stdio.h> /* for printf in init_callbacks */ +#include <stdlib.h> /* for malloc/free */ #include "vt.h" @@ -20,22 +21,34 @@ #define SOS_PM_APC_STRING 12 #define OSC_STRING 13 -void -init_parser_state(ParserState *state) +const char buffer[1000]; + +ParserState * +init_parser_state() { + ParserState *state = malloc(sizeof(ParserState)); state->node = GROUND; - // TODO allocate buffers? + state->intermediate_char_count = 0; + state->intermediate_chars = buffer; + return state; +} + +void +free_parser_state(ParserState *state) +{ + free((void *) state->intermediate_chars); + free((void *) state); } void init_parser_callbacks(ParserCallbacks *cbs, void (*pass)(ParserState *)) { /* set all callbacks to pass by default */ - cbs->ignore = cbs->print = cbs->execute = - cbs->clear = cbs->collect = cbs->param = - cbs->esc_dispatch = cbs->csi_dispatch = cbs->hook = - cbs->put = cbs->unhook = cbs->osc_start = - cbs->osc_put = cbs->osc_end = cbs->unhandled = pass; + cbs->ignore = cbs->print = cbs->execute = + cbs->clear = cbs->collect = cbs->param = + cbs->esc_dispatch = cbs->csi_dispatch = cbs->hook = + cbs->put = cbs->unhook = cbs->osc_start = + cbs->osc_put = cbs->osc_end = cbs->unhandled = pass; } /* state machine actions (just for shortening's sake) */ diff --git a/vt.h b/vt.h @@ -24,9 +24,8 @@ typedef struct _ps { int node; /* other parser state, accumulated strings, etc */ - // TODO what *exactly* goes here? :( - char *private_flag; - char *intermediate_chars; + int intermediate_char_count; + const char *intermediate_chars; } ParserState; // TODO typedef the void(ParserState *state) callback type @@ -54,6 +53,7 @@ typedef struct _pcbs { void handle(ParserCallbacks *callbacks, ParserState *state, char ch); /* initialize structs */ -void init_parser_state(ParserState *state); +ParserState *init_parser_state(); +void free_parser_state(ParserState *state); void init_parser_callbacks(ParserCallbacks *cbs, void (*pass)(ParserState *));
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.