diff --git a/examples/sixdraw.c b/examples/sixdraw.c index 7b754a0..6599327 100644 --- a/examples/sixdraw.c +++ b/examples/sixdraw.c @@ -29,10 +29,12 @@ # include "config.h" #endif // HAVE_CONFIG_H +#include #include #include #include #include +#include #include #include @@ -46,17 +48,19 @@ # define DEFAULT_TIMEOUT_MILLIS 500 #endif // !DEFAULT_TIMEOUT_MILLIS -#define DECRQM_SET 1 -#define DECRQM_RST 2 +#define DECRQM_UNREC 0 +#define DECRQM_SET 1 +#define DECRQM_RST 2 #define DECTCEM 25 +#define DECSDM 80 #define BTN_EVENT_TRACKING 1002 #define SGR_MOUSE_PIXELMODE 1016 #define ALT_SCRBUF 1049 #define SIXEL_SEQ_HEAD CTLSEQS_DCS "0;1q\"1;1;" -static char sixel_seq[2048] = SIXEL_SEQ_HEAD; +static char sixel_seq[4096] = SIXEL_SEQ_HEAD; struct sixdraw_ctx { struct termios termios; @@ -70,7 +74,9 @@ struct sixdraw_ctx { bool hide_cursor; bool alt_scrbuf; bool btnev_tracking; + bool legacy_xterm; bool sgr_pixelmode; + bool sixel_scroll; int in_fd; int out_fd; int timeout; @@ -100,11 +106,6 @@ terminate(struct sixdraw_ctx *ctx) ctlseqs_matcher_free(ctx->matcher); ctlseqs_reader_free(ctx->reader); - // Restore cursor status. - if (ctx->hide_cursor) { - fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), DECTCEM); - } - // Restore normal screen buffer. if (ctx->alt_scrbuf) { fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), ALT_SCRBUF); @@ -118,6 +119,16 @@ terminate(struct sixdraw_ctx *ctx) fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), SGR_MOUSE_PIXELMODE); } + // Restore cursor status. + if (ctx->hide_cursor) { + fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), DECTCEM); + } + + // Restore original sixel mode. + if (ctx->sixel_scroll) { + fprintf(ctx->out_file, ctx->legacy_xterm ? CTLSEQS_DECRST("%d") : CTLSEQS_DECSET("%d"), DECSDM); + } + // Restore original terminal modes. if (ctx->has_termios) { tcsetattr(ctx->in_fd, TCSANOW, &ctx->termios); @@ -157,13 +168,41 @@ decrqm(struct sixdraw_ctx *ctx, unsigned mode, char const *name) print_error(ctx, "failed to get %s status", name); return false; } - if (result[0].num != mode || (result[1].num != DECRQM_SET && result[1].num != DECRQM_RST)) { + if (result[0].num != mode || result[1].num == DECRQM_UNREC) { print_error(ctx, "%s status not recognizable", name); return false; } return true; } +static long +xtversion(struct sixdraw_ctx *ctx) { + ssize_t retval; + union ctlseqs_value *result = ctx->result; + fprintf(ctx->out_file, CTLSEQS_XTVERSION()); + do { + retval = ctlseqs_read(ctx->reader, ctx->matcher, ctx->timeout); + } while (retval == CTLSEQS_PARTIAL); + if (retval != 3) { + // Terminal emulator does not recognize XTVERSION; + return -1; + } + char const *xtversion = result[1].str; + if (result[0].len < sizeof("XTerm(")) { + return -2; + } + if (strncmp(xtversion, "XTerm(", sizeof("XTerm(") - 1) != 0) { + // Terminal emulator is not XTerm. + return -2; + } + errno = 0; + long version_num = strtol(xtversion + (sizeof("XTerm(") - 1), NULL, 10); + if (errno) { + return -3; + } + return version_num; +} + static void print_sixel_dot(struct sixdraw_ctx *ctx, unsigned x, unsigned y) { @@ -225,7 +264,8 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv) char const *patterns[] = { CTLSEQS_RESP_PRIMARY_DA(CTLSEQS_PH_NUMS), CTLSEQS_RESP_DECRQM(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), - CTLSEQS_RESP_SGR_MOUSE(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, "M") + CTLSEQS_RESP_SGR_MOUSE(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, "M"), + CTLSEQS_RESP_XTVERSION(CTLSEQS_PH_STR), }; struct ctlseqs_matcher_options matcher_options = { .patterns = patterns, @@ -330,6 +370,25 @@ prepare(struct sixdraw_ctx *ctx) fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), DECTCEM); } + // Check terminal name and version. + long xterm_version = xtversion(ctx); + if (xterm_version >= 0 && xterm_version < 369) { + ctx->legacy_xterm = true; + } else { + ctx->legacy_xterm = false; + } + + // Enable sixel scrolling. + if (!decrqm(ctx, DECSDM, "sixel scrolling")) { + return false; + } + // Before patch #369, XTerm implemented DECSDM incorrectly. + // See https://invisible-island.net/xterm/xterm.log.html#xterm_369 + ctx->sixel_scroll = result[1].num == (ctx->legacy_xterm ? DECRQM_RST : DECRQM_SET); + if (ctx->sixel_scroll) { + fprintf(ctx->out_file, ctx->legacy_xterm ? CTLSEQS_DECSET("%d") : CTLSEQS_DECRST("%d"), DECSDM); + } + // Enable button event tracking mode. if (!decrqm(ctx, BTN_EVENT_TRACKING, "button event tracking mode")) { return false;