You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
asciinema.org/src/terminal.c

311 lines
7.3 KiB
C

#include <stdio.h>
#include <libtsm.h>
#include <string.h>
#define READ_BUF_SIZE 16 * 1024
static int RGB_LEVELS[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
struct tsm_screen_attr last_attr;
struct tsm_screen_attr *last_attr_ptr = &last_attr;
static int u8_wc_to_utf8(char *dest, uint32_t ch) {
if (ch < 0x80) {
dest[0] = (char)ch;
return 1;
}
if (ch < 0x800) {
dest[0] = (ch >> 6) | 0xC0;
dest[1] = (ch & 0x3F) | 0x80;
return 2;
}
if (ch < 0x10000) {
dest[0] = (ch >> 12) | 0xE0;
dest[1] = ((ch >> 6) & 0x3F) | 0x80;
dest[2] = (ch & 0x3F) | 0x80;
return 3;
}
if (ch < 0x110000) {
dest[0] = (ch >> 18) | 0xF0;
dest[1] = ((ch >> 12) & 0x3F) | 0x80;
dest[2] = ((ch >> 6) & 0x3F) | 0x80;
dest[3] = (ch & 0x3F) | 0x80;
return 4;
}
return 0;
}
void check_err(int err, const char *message) {
if (err) {
printf("%s\n", message);
exit(1);
}
}
void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) {}
int prepare_cb(struct tsm_screen *con, void *data) {
int *line = (int *)data;
*line = -1;
printf("[");
return 0;
}
void print_char(const uint32_t *ch) {
char str[5];
int size;
if (*ch == 0) {
printf(" ");
} else if (*ch == '"') {
printf("\\\"");
} else if (*ch == '\\') {
printf("\\\\");
} else {
size = u8_wc_to_utf8(str, *ch);
if (size > 0) {
str[size] = 0;
printf("%s", str);
} else {
printf("?");
}
}
}
int get_rgb_index(int value) {
int i;
for (i=0; i<6; i++) {
if (RGB_LEVELS[i] == value) return i;
}
return -1;
}
int get_rgb_color(int r, int g, int b) {
if (r == g && g == b && (r - 8) % 10 == 0) {
return 232 + (r - 8) / 10;
} else {
return 16 + get_rgb_index(r) * 36 + get_rgb_index(g) * 6 + get_rgb_index(b);
}
}
int get_fg(const struct tsm_screen_attr *attr) {
if (attr->fccode == -1) {
return get_rgb_color(attr->fr, attr->fg, attr->fb);
} else if (attr->fccode >= 0 && attr->fccode < 16) {
return attr->fccode;
} else {
return -1;
}
}
int get_bg(const struct tsm_screen_attr *attr) {
if (attr->bccode == -1) {
return get_rgb_color(attr->br, attr->bg, attr->bb);
} else if (attr->bccode >= 0 && attr->bccode < 16) {
return attr->bccode;
} else {
return -1;
}
}
void print_attr(const struct tsm_screen_attr *attr) {
bool comma_needed = false;
int color_code;
printf("{");
color_code = get_fg(attr);
if (color_code != -1) {
printf("\"fg\":%d", color_code);
comma_needed = true;
}
color_code = get_bg(attr);
if (color_code != -1) {
if (comma_needed) printf(",");
printf("\"bg\":%d", color_code);
comma_needed = true;
}
if (attr->bold) {
if (comma_needed) printf(",");
printf("\"bold\":true");
comma_needed = true;
}
if (attr->underline) {
if (comma_needed) printf(",");
printf("\"underline\":true");
comma_needed = true;
}
if (attr->inverse) {
if (comma_needed) printf(",");
printf("\"inverse\":true");
comma_needed = true;
}
if (attr->blink) {
if (comma_needed) printf(",");
printf("\"blink\":true");
comma_needed = true;
}
printf("}");
}
bool attr_eq(struct tsm_screen_attr *attr1, const struct tsm_screen_attr *attr2) {
return attr1->fccode == attr2->fccode &&
attr1->bccode == attr2->bccode &&
attr1->fr == attr2->fr &&
attr1->fg == attr2->fg &&
attr1->fb == attr2->fb &&
attr1->br == attr2->br &&
attr1->bg == attr2->bg &&
attr1->bb == attr2->bb &&
attr1->bold == attr2->bold &&
attr1->underline == attr2->underline &&
attr1->inverse == attr2->inverse &&
attr1->blink == attr2->blink;
}
void attr_cp(const struct tsm_screen_attr *src, struct tsm_screen_attr *dst) {
memcpy((void *)dst, (const void *)src, sizeof(last_attr));
}
void close_cell() {
printf("\",");
print_attr(last_attr_ptr);
printf("]");
}
void open_cell(const struct tsm_screen_attr *attr) {
printf("[\"");
attr_cp(attr, last_attr_ptr);
}
int draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len,
unsigned int width, unsigned int posx, unsigned int posy,
const struct tsm_screen_attr *attr, tsm_age_t age, void *data) {
int *line = (int *)data;
if (((signed int)posy) > *line) {
if (*line >= 0) {
close_cell();
printf("],"); // close line
}
printf("["); // open line
open_cell(attr);
}
*line = posy;
if (width == 0) return 0;
if (!(attr_eq(last_attr_ptr, attr))) {
close_cell();
printf(",");
open_cell(attr);
}
print_char(ch);
return 0;
}
int render_cb(struct tsm_screen *con, void *data) {
int *line = (int *)data;
if (*line >= 0) {
close_cell();
printf("]"); // close line
}
printf("]\n");
return 0;
}
int main(int argc, char *argv[]) {
int err;
int i;
struct tsm_screen *screen;
struct tsm_vte *vte;
int width, height;
char *line = NULL;
size_t size;
unsigned int n;
char *buffer = (char *) malloc(READ_BUF_SIZE);
int line_n, cursor_x, cursor_y;
char *cursor_visible;
unsigned int flags;
int m, read;
width = atoi(argv[1]);
height = atoi(argv[2]);
err = tsm_screen_new(&screen, NULL, NULL);
check_err(err, "can't create screen");
err = tsm_screen_resize(screen, width, height);
check_err(err, "can't resize screen");
err = tsm_vte_new(&vte, screen, write_cb, NULL, NULL, NULL);
check_err(err, "can't create vte");
for (;;) {
if (getline(&line, &size, stdin) == -1) break;
char action = line[0];
switch(action) {
case 'd':
if (getline(&line, &size, stdin) == -1) break;
n = atoi(line);
while (n > 0) {
if (n > READ_BUF_SIZE) {
m = READ_BUF_SIZE;
} else {
m = n;
}
read = fread(buffer, 1, m, stdin);
tsm_vte_input(vte, buffer, read);
n = n - read;
}
break;
case 'p':
//tsm_screen_draw(screen, prepare_cb, draw_cb, render_cb, &line_n);
prepare_cb(screen, &line_n);
tsm_screen_draw(screen, draw_cb, &line_n);
render_cb(screen, &line_n);
break;
case 'c':
cursor_x = tsm_screen_get_cursor_x(screen);
cursor_y = tsm_screen_get_cursor_y(screen);
flags = tsm_screen_get_flags(screen);
if (!(flags & TSM_SCREEN_HIDE_CURSOR)) {
cursor_visible = "true";
} else {
cursor_visible = "false";
}
printf("{\"x\":%d,\"y\":%d,\"visible\":%s}\n", cursor_x, cursor_y, cursor_visible);
break;
}
fflush(stdout);
}
return 0;
}