Update sixdraw.

This commit is contained in:
CismonX 2021-01-07 21:43:43 +08:00
parent decdf5738f
commit 1cbe732b6e
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
1 changed files with 80 additions and 23 deletions

View File

@ -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 <admin@cismon.net>
*
@ -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);
}