rvis | Minimal terminal colorizer. |
| download: https://git.y1.nz/archives/rvis.tar.gz | |
| README | Files | Log | Refs |
main.c
1 #include <unistd.h> /* execl */
2 #include <stdio.h> /* printf, fprintf, stdin, stdout, stderr */
3 #include <pty.h> /* forkpty */
4 #include <stdlib.h> /* exit */
5 #include <signal.h> /* signal, SIGCHLD, SIG_IGN, SIG_DFL */
6 #include <termios.h> /* termios, various flags, tc(set/get)attr */
7 #include <pthread.h> /* for pthread_create */
8
9 #include "config.h" /* get_char_group, group_colors */
10 #include "vt.h" /* ParserState, handle */
11 #include "term.h" /* get_root_size */
12
13 pid_t pty_pid;
14 pthread_t pt;
15 struct termios original_termios;
16
17 void
18 handle_pty_child(int pid)
19 {
20 if (pid != 0) return;
21 execl("/bin/sh", "/bin/sh", NULL);
22 }
23
24 void
25 pass(ParserState *state)
26 {
27 fputc(state->current_char, stdout);
28 }
29
30 int prev_group = -1;
31 void
32 colorize(ParserState *state)
33 {
34 int group = get_char_group(state->current_char);
35 if (prev_group != group)
36 fprintf(stdout, "%s", group_colors[group]);
37 fputc(state->current_char, stdout);
38 }
39
40 void
41 colorize_sometimes(ParserState *state)
42 {
43 /* the parser calls "execute" rather than "print" on some whitespace chars. */
44 if (state->current_char == '\n') {
45 colorize(state);
46 return;
47 }
48
49 pass(state);
50 }
51
52 void *
53 process_input(void *arg)
54 {
55 FILE *shin = (FILE *) arg;
56
57 /* read stdin, send it to shin */
58 for (;;) {
59 char ch = (char) fgetc(stdin);
60 if (ch == EOF) break;
61 fputc(ch, shin);
62 fflush(shin);
63 }
64 }
65
66 void
67 set_termios_raw_mode(FILE *fd)
68 {
69 struct termios t;
70 tcgetattr(fileno(fd), &original_termios);
71 t.c_iflag |= IGNBRK;
72 t.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
73 t.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
74 t.c_cc[VMIN] = 1;
75 t.c_cc[VTIME] = 0;
76 tcsetattr(fileno(fd), TCSANOW, &t);
77 }
78
79 void
80 process_output(FILE *shout)
81 {
82 ParserState *state;
83 ParserCallbacks cbs;
84 state = init_parser_state();
85 init_parser_callbacks(&cbs, pass);
86 cbs.print = colorize;
87 cbs.execute = colorize_sometimes;
88
89 /* clear screen */
90 fprintf(stdout, "\033[2J");
91
92 char ch;
93 for (;;) {
94 char ch = (char) fgetc(shout);
95 if (ch == EOF) break;
96 handle(&cbs, state, ch);
97 fflush(stdout);
98 }
99 }
100
101 int
102 main() {
103 int amaster;
104 int w, h;
105 get_root_size(&w, &h);
106 struct winsize pty_size = { .ws_row = h, .ws_col = w };
107
108 set_termios_raw_mode(stdin);
109
110 /* FORK! */
111 pty_pid = forkpty(&amaster, NULL, NULL, &pty_size);
112 handle_pty_child(pty_pid);
113 FILE *shout = fdopen(amaster, "r");
114 FILE *shin = fdopen(amaster, "w");
115 if (shin == NULL || shout == NULL) {
116 fprintf(stderr, "Fatal: failed to open pty file" "\n");
117 return 1;
118 }
119
120 /* split off thread for input handling */
121 pthread_create(&pt, NULL, process_input, shin);
122 pthread_detach(pt);
123
124 /* process output on the main thread. Returns after shout closes */
125 process_output(shout);
126
127 pthread_kill(pt, SIGTERM);
128 tcsetattr(fileno(stdin), TCSANOW, &original_termios);
129 exit(0);
130 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.