From 1cbe732b6edf67551ba8dfb75997e978f9f36fb1 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 7 Jan 2021 21:43:43 +0800 Subject: [PATCH] Update sixdraw. --- examples/sixdraw.c | 103 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/examples/sixdraw.c b/examples/sixdraw.c index c32a552..69d28ac 100644 --- a/examples/sixdraw.c +++ b/examples/sixdraw.c @@ -1,9 +1,13 @@ /** * sixdraw.c - draw lines on your terminal * - * Requires sixel graphics and DEC locator support to run on your terminal. + * Requires sixel graphics and 1016 mouse mode to run on your terminal. * These features are not widely supported. To save yourself from trouble, - * use a latest version of XTerm or mlterm. + * use a latest version of XTerm or mintty. + * + * Before 1016 mode was introduced in XTerm patch #359, it is also possible + * to report mouse position in pixels using DEC locator (which is also rarely + * implemented). However, it is not used in this example. * * Copyright (C) 2020 CismonX * @@ -44,8 +48,12 @@ #define DECRQM_SET 1 #define DECRQM_RST 2 -#define DECTCEM 25u -#define ALT_SCRBUF 1049u + +#define DECTCEM 25u +#define SGR_MOUSE_PIXELMODE 1016u +#define ALT_SCRBUF 1049u + +#define SIXEL_SEQ_HEAD CTLSEQS_DCS "0;1q\"1;1;" struct sixdraw_ctx { struct termios termios; @@ -56,13 +64,15 @@ struct sixdraw_ctx { bool has_termios; bool show_cursor; bool normal_scrbuf; + bool sgr_pixelmode; int in_fd; int out_fd; int timeout; - int rows; - int cols; - int ch_width; - int ch_height; + unsigned rows; + unsigned cols; + unsigned ch_width; + unsigned ch_height; + unsigned line_color; }; static inline void @@ -117,6 +127,10 @@ terminate(struct sixdraw_ctx *ctx) if (ctx->has_termios) { tcsetattr(ctx->in_fd, TCSANOW, &ctx->termios); } + + if (!ctx->sgr_pixelmode) { + dprintf(ctx->out_fd, CTLSEQS_DECRST("%u"), SGR_MOUSE_PIXELMODE); + } } static bool @@ -158,14 +172,56 @@ decrqm(struct sixdraw_ctx *ctx, unsigned mode_id, char const *name) return true; } +static inline void +get_sixel_color(unsigned rgb888, unsigned *red, unsigned *green, unsigned *blue) +{ + *red = ((rgb888 >> 16) & 0xFF) * 100 / 255; + *green = ((rgb888 >> 8) & 0xFF) * 100 / 255; + *blue = ((rgb888 >> 0) & 0xFF) * 100 / 255; +} + +static inline char * +get_sixel_buffer() +{ + static char sixel_seq_buffer[2048] = SIXEL_SEQ_HEAD; + return sixel_seq_buffer; +} + +static void +print_sixel_dot(struct sixdraw_ctx *ctx, unsigned x, unsigned y) +{ + char *sixel_seq = get_sixel_buffer(); + size_t offset = sizeof(SIXEL_SEQ_HEAD) - 1; + offset += sprintf(sixel_seq + offset, "%d;%d", ctx->ch_width, ctx->ch_height); + + // Select color + unsigned red, green, blue; + get_sixel_color(ctx->line_color, &red, &green, &blue); + offset += sprintf(sixel_seq + offset, "#0;2;%d;%d;%d#0", red, green, blue); + + // Move cursor + unsigned row = y / ctx->ch_height + 1; + unsigned col = x / ctx->ch_width + 1; + dprintf(ctx->out_fd, CTLSEQS_CUP("%d", "%d"), row, col); + + // Print dot + row = y % ctx->ch_height; + col = x % ctx->ch_width; + offset += sprintf(sixel_seq + offset, "%.*s", row / 6, "----------------"); + offset += sprintf(sixel_seq + offset, "!%u?%u", col, (1 << row % 6) + 0x3F); + + write(ctx->out_fd, sixel_seq, offset); +} + static bool init(struct sixdraw_ctx *ctx, int argc, char **argv) { *ctx = (struct sixdraw_ctx) { - .prog_name = argc > 0 ? argv[0] : "sixdraw", - .in_fd = STDIN_FILENO, - .out_fd = STDOUT_FILENO, - .timeout = DEFAULT_TIMEOUT_MILLIS, + .prog_name = argc > 0 ? argv[0] : "sixdraw", + .in_fd = STDIN_FILENO, + .out_fd = STDOUT_FILENO, + .timeout = DEFAULT_TIMEOUT_MILLIS, + .line_color = 0x00FF00 }; // Process command line arguments. @@ -175,6 +231,9 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv) case 't': ctx->timeout = atoi(optarg); break; + case 'c': + ctx->line_color = strtoul(optarg, NULL, 16); + break; case '?': default: return false; @@ -277,42 +336,40 @@ prepare(struct sixdraw_ctx *ctx) return false; } bool has_sixel = false; - bool has_dec_locator = false; union ctlseqs_value *result = ctx->result; for (size_t i = 1; i <= result[0].len; ++i) { if (result[i].num == 4) { has_sixel = true; - } else if (result[i].num == 29) { - has_dec_locator = true; } } if (!has_sixel) { print_error(ctx, "terminal does not support sixel graphics"); return false; } - if (!has_dec_locator) { - print_error(ctx, "terminal does not support DEC locator mode"); + + // Switch into SGR mouse pixel mode. + if (!decrqm(ctx, SGR_MOUSE_PIXELMODE, "SGR mouse pixel mode")) { return false; } + ctx->sgr_pixelmode = result[1].num == DECRQM_SET; + if (!ctx->sgr_pixelmode) { + dprintf(ctx->out_fd, CTLSEQS_DECSET("%u"), SGR_MOUSE_PIXELMODE); + } - // Get current cursor status. + // Hide cursor. if (!decrqm(ctx, DECTCEM, "cursor")) { return false; } ctx->show_cursor = result[1].num == DECRQM_SET; - - // Hide cursor. if (ctx->show_cursor) { dprintf(ctx->out_fd, CTLSEQS_DECRST("%u"), DECTCEM); } - // Get current screen buffer status. + // Switch to alternate screen buffer. if (!decrqm(ctx, ALT_SCRBUF, "screen buffer")) { return false; } ctx->normal_scrbuf = result[1].num == DECRQM_RST; - - // Switch to alternate screen buffer. if (ctx->normal_scrbuf) { dprintf(ctx->out_fd, CTLSEQS_DECSET("%u"), ALT_SCRBUF); }