/** * Example program for DEC Locator Mode integration * * Hayaki Saito * * I declared this program is in Public Domain (CC0 - "No Rights Reserved"), * This file is offered AS-IS, without any warranty. * */ #include #include #include #include #include #include #include #include struct canvas { int width; int height; unsigned char *buf; int x0; int y0; int x1; int y1; }; static int canvas_init(struct canvas *c, int width, int height) { c->width = width; c->height = height; c->x1 = (-1); c->y1 = (-1); c->x0 = (-1); c->y0 = (-1); c->buf = calloc(1, width * height); return 0; } static void canvas_deinit(struct canvas *c) { free(c->buf); } static void canvas_point(struct canvas *c, int x, int y) { c->x1 = x; c->y1 = y; } static void canvas_sync(struct canvas *c) { c->x0 = c->x1; c->y0 = c->y1; } static int wait_stdin(int usec) { fd_set rfds; struct timeval tv; tv.tv_sec = usec / 1000000; tv.tv_usec = usec % 1000000; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); return select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv); } static int scroll_on_demand(int pixelheight) { struct winsize size = {0, 0, 0, 0}; int row = 0; int col = 0; int cellheight; int scroll; ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); if (size.ws_ypixel <= 0) { printf("\033[H\0337"); return 0; } /* request cursor position report */ printf("\033[6n"); if (wait_stdin(1000 * 1000) < 0) /* wait 1 sec */ goto err; if (scanf("\033[%d;%dR", &row, &col) != 2) goto err; cellheight = pixelheight * size.ws_row / size.ws_ypixel + 1; scroll = cellheight + row - size.ws_row; printf("\033[%dS\033[%dA", scroll, scroll); printf("\0337"); return size.ws_ypixel / size.ws_row * (row - (scroll < 0 ? 0: scroll) - 1); err: printf("\033[H\0337"); return 0; } static int tty_raw(struct termios *old_termios) { struct termios new_termios; int ret; ret = tcgetattr(STDIN_FILENO, old_termios); if (ret < 0) return ret; memcpy(&new_termios, old_termios, sizeof(*old_termios)); new_termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); new_termios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); new_termios.c_cflag &= ~(CSIZE | PARENB); new_termios.c_cflag |= CS8; new_termios.c_oflag &= ~(OPOST); new_termios.c_cc[VMIN] = 1; new_termios.c_cc[VTIME] = 0; ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios); if (ret < 0) return ret; printf("\033[?25l\0338"); return 0; } static void tty_restore(struct termios *old_termios) { (void) tcsetattr(STDIN_FILENO, TCSADRAIN, old_termios); } static int sixel_write(char *data, int size, void *priv) { return fwrite(data, 1, size, (FILE *)priv); } static int draw_line(struct canvas const *c) { int dx; int dy; int sx; int sy; int err; int err2; int x0; int y0; int x1; int y1; if (c->x0 < 0 || c->y0 < 0 || c->x0 >= c->width || c->y0 >= c->height) return 1; x0 = c->x0; y0 = c->y0; x1 = c->x1; y1 = c->y1; if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x1 >= c->width) x1 = c->width - 1; if (y1 >= c->height) y1 = c->height - 1; if (x0 == x1 && y0 == y1) return 1; dx = x0 > x1 ? x0 - x1 : x1 - x0; dy = y0 > y1 ? y0 - y1 : y1 - y0; sx = x0 < x1 ? 1 : -1; sy = y0 < y1 ? 1 : -1; err = dx - dy; for (;;) { c->buf[c->width * y0 + x0] = 0xff; if (x0 == x1 && y0 == y1) break; err2 = 2 * err; if (err2 > - dy) { err -= dy; x0 += sx; } if (err2 < dx) { err += dx; y0 += sy; } } return 0; } enum { STATE_GROUND = 0, STATE_ESC = 1, STATE_ESC_INTERMEDIATE = 2, STATE_CSI_PARAMETER = 3, STATE_CSI_INTERMEDIATE = 4, STATE_SS = 5, STATE_OSC = 6, STATE_STR = 7 }; int main(int argc, char **argv) { SIXELSTATUS status = SIXEL_FALSE; struct canvas c; sixel_output_t *output = NULL; sixel_dither_t *dither = NULL; struct termios old_termios; unsigned char ibuf[256]; unsigned char *p; int n; unsigned int params[1024]; int param = 0; int params_idx = 0; int ibytes; int state; int offset; printf("\033[?1;3;256S" "\033[?8452h" "\033[1;1'z"); (void) tty_raw(&old_termios); if (canvas_init(&c, 640, 480) != 0) return (-1); offset = scroll_on_demand(c.height); status = sixel_output_new(&output, sixel_write, stdout, NULL); if (SIXEL_FAILED(status)) goto end; dither = sixel_dither_get(SIXEL_BUILTIN_G8); sixel_dither_set_pixelformat(dither, SIXEL_PIXELFORMAT_G8); status = sixel_encode(c.buf, c.width, c.height, 0, dither, output); if (SIXEL_FAILED(status)) goto end; for (state = STATE_GROUND;;) { printf("\033['|"); fflush(stdout); if (wait_stdin(10000) < 0) goto end; n = read(STDIN_FILENO, ibuf, sizeof(ibuf)); for (p = ibuf; p < ibuf + n; ++p) { switch (state) { case STATE_GROUND: switch (*p) { case 0x03: /* */ if (wait_stdin(10000) >= 0) n = read(STDIN_FILENO, ibuf, sizeof(ibuf)); goto end; break; case 0x1b: /* ESC */ state = STATE_ESC; break; default: /* ignore */ break; } break; case STATE_ESC: /* * - ISO-6429 independent escape sequense * * ESC F * * - ISO-2022 designation sequence * * ESC I ... I F */ switch (*p) { case 0x5b: /* [ */ ibytes = 0; param = 0; params_idx = 0; state = STATE_CSI_PARAMETER; break; case 0x5d: /* ] */ state = STATE_OSC; break; case 0x4e ... 0x4f: /* N / O */ state = STATE_SS; break; case 0x50: /* P(DCS) */ case 0x58: /* X(SOS) */ case 0x5e ... 0x5f: /* ^(PM) / _(APC) */ state = STATE_STR; break; case 0x1b: /* ESC */ state = STATE_ESC; break; case 0x18: case 0x1a: state = STATE_GROUND; break; case 0x00 ... 0x17: case 0x19: case 0x1c ... 0x1f: case 0x7f: /* ignore */ break; case 0x20 ... 0x2f: /* SP to / */ ibytes = ibytes << 8 | *p; state = STATE_ESC_INTERMEDIATE; break; default: state = STATE_GROUND; break; } break; case STATE_CSI_PARAMETER: /* * parse control sequence * * CSI P ... P I ... I F * ^ */ switch (*p) { case 0x00 ... 0x17: case 0x19: case 0x1c ... 0x1f: case 0x7f: /* ignore */ break; case 0x18: /* CAN */ case 0x1a: /* SUB */ state = STATE_GROUND; break; case 0x1b: /* ESC */ state = STATE_ESC; break; case 0x20 ... 0x2f: /* intermediate, SP to / */ params[params_idx++] = param; param = 0; ibytes = ibytes << 8 | *p; state = STATE_CSI_INTERMEDIATE; break; case 0x30 ... 0x39: /* parameter, 0 to 9 */ param = param * 10 + *p - 0x30; break; case 0x3a: /* separator, : */ ibytes = ibytes << 8; break; case 0x3b: /* separator, : to ; */ params[params_idx++] = param; param = 0; break; case 0x3c ... 0x3f: /* parameter, < to ? */ ibytes = ibytes << 8 | *p; break; case 0x40 ... 0x4f: /* Final byte, @ to ~ */ default: state = STATE_GROUND; break; } break; case STATE_CSI_INTERMEDIATE: /* * parse control sequence * * CSI P ... P I ... I F * ^ */ switch (*p) { case 0x00 ... 0x17: case 0x19: case 0x1c ... 0x1f: case 0x7f: /* ignore */ break; case 0x18: case 0x1a: state = STATE_GROUND; break; case 0x1b: /* ESC */ state = STATE_ESC; break; case 0x20 ... 0x2f: /* intermediate, SP to / */ ibytes = ibytes << 8 | *p; break; case 0x30 ... 0x3f: state = STATE_GROUND; break; case 0x40 ... 0x7e: /* Final byte, @ to ~ */ ibytes = ibytes << 8 | *p; state = STATE_GROUND; if (ibytes == ('&' << 8 | 'w') && params_idx >= 4) { canvas_point(&c, params[3], params[2] - offset); if (params[1] == 4 && draw_line(&c) == 0) { printf("\0338"); status = sixel_encode(c.buf, c.width, c.height, 0, dither, output); if (SIXEL_FAILED(status)) { goto end; } } canvas_sync(&c); } break; default: state = STATE_GROUND; break; } break; case STATE_ESC_INTERMEDIATE: switch (*p) { case 0x00 ... 0x17: case 0x19: case 0x1c ... 0x1f: case 0x7f: /* ignore */ break; case 0x18: case 0x1a: state = STATE_GROUND; break; case 0x1b: /* ESC */ state = STATE_ESC; break; case 0x20 ... 0x2f: /* SP to / */ state = STATE_ESC_INTERMEDIATE; break; case 0x30 ... 0x3f: /* 0 to ~, Final byte */ state = STATE_GROUND; break; } break; case STATE_OSC: switch (*p) { case 0x07: case 0x18: case 0x1a: state = STATE_GROUND; break; case 0x1b: state = STATE_ESC; break; default: break; } break; case STATE_STR: switch (*p) { case 0x18: case 0x1a: state = STATE_GROUND; break; case 0x1b: state = STATE_ESC; break; default: break; } break; case STATE_SS: switch (*p) { case 0x1b: state = STATE_ESC; break; default: state = STATE_GROUND; break; } break; } } } end: printf("\033[?25h"); fflush(stdout); tty_restore(&old_termios); canvas_deinit(&c); printf("\n"); return 0; } /* EOF */