2020-11-17 06:24:43 +00:00
|
|
|
/**
|
|
|
|
* sixdraw.c - draw lines on your terminal
|
|
|
|
*
|
2021-01-07 13:43:43 +00:00
|
|
|
* Requires sixel graphics and 1016 mouse mode to run on your terminal.
|
2020-11-17 06:24:43 +00:00
|
|
|
* These features are not widely supported. To save yourself from trouble,
|
2021-01-07 13:43:43 +00:00
|
|
|
* 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.
|
2020-11-17 06:24:43 +00:00
|
|
|
*
|
2021-01-10 17:53:38 +00:00
|
|
|
* Copyright (C) 2020,2021 CismonX <admin@cismon.net>
|
2020-11-17 06:24:43 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif // HAVE_CONFIG_H
|
|
|
|
|
2020-12-22 13:51:01 +00:00
|
|
|
#include <stdarg.h>
|
2020-11-17 06:24:43 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
2021-01-21 16:08:05 +00:00
|
|
|
#include <getopt.h>
|
2020-11-17 06:24:43 +00:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <ctlseqs.h>
|
|
|
|
|
2021-01-06 13:31:25 +00:00
|
|
|
#ifndef DEFAULT_TIMEOUT_MILLIS
|
|
|
|
# define DEFAULT_TIMEOUT_MILLIS 500
|
|
|
|
#endif // !DEFAULT_TIMEOUT_MILLIS
|
2020-11-17 06:24:43 +00:00
|
|
|
|
2021-01-06 13:31:25 +00:00
|
|
|
#define DECRQM_SET 1
|
|
|
|
#define DECRQM_RST 2
|
2021-01-07 13:43:43 +00:00
|
|
|
|
2021-01-08 00:00:49 +00:00
|
|
|
#define DECTCEM 25
|
|
|
|
#define BTN_EVENT_TRACKING 1002
|
|
|
|
#define SGR_MOUSE_PIXELMODE 1016
|
|
|
|
#define ALT_SCRBUF 1049
|
2021-01-07 13:43:43 +00:00
|
|
|
|
|
|
|
#define SIXEL_SEQ_HEAD CTLSEQS_DCS "0;1q\"1;1;"
|
2020-12-22 13:51:01 +00:00
|
|
|
|
2021-01-10 20:58:39 +00:00
|
|
|
static char sixel_seq[2048] = SIXEL_SEQ_HEAD;
|
|
|
|
|
2020-11-17 06:24:43 +00:00
|
|
|
struct sixdraw_ctx {
|
|
|
|
struct termios termios;
|
2020-12-24 10:21:03 +00:00
|
|
|
union ctlseqs_value result[64];
|
2020-12-01 11:42:13 +00:00
|
|
|
char const *prog_name;
|
2021-01-22 04:39:41 +00:00
|
|
|
FILE* out_file;
|
|
|
|
FILE* err_file;
|
2020-11-17 06:24:43 +00:00
|
|
|
struct ctlseqs_matcher *matcher;
|
|
|
|
struct ctlseqs_reader *reader;
|
|
|
|
bool has_termios;
|
2021-01-08 00:00:49 +00:00
|
|
|
bool hide_cursor;
|
|
|
|
bool alt_scrbuf;
|
|
|
|
bool btnev_tracking;
|
2021-01-07 13:43:43 +00:00
|
|
|
bool sgr_pixelmode;
|
2020-11-17 06:24:43 +00:00
|
|
|
int in_fd;
|
|
|
|
int out_fd;
|
|
|
|
int timeout;
|
2021-01-10 20:58:39 +00:00
|
|
|
unsigned rows;
|
|
|
|
unsigned canvas_width;
|
|
|
|
unsigned canvas_height;
|
2021-01-07 13:43:43 +00:00
|
|
|
unsigned ch_width;
|
|
|
|
unsigned ch_height;
|
|
|
|
unsigned line_color;
|
2021-01-10 20:58:39 +00:00
|
|
|
unsigned sixel_init_size;
|
2020-11-17 06:24:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline void
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(struct sixdraw_ctx const *ctx, char const *format, ...)
|
2020-11-17 06:24:43 +00:00
|
|
|
{
|
2020-12-22 13:51:01 +00:00
|
|
|
char msg[1024];
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
vsnprintf(msg, 1024, format, args);
|
|
|
|
va_end(args);
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->err_file, "%s: [error] %s.\n", ctx->prog_name, msg);
|
2020-11-17 06:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-01-06 13:31:25 +00:00
|
|
|
terminate(struct sixdraw_ctx *ctx)
|
2020-11-17 06:24:43 +00:00
|
|
|
{
|
|
|
|
ctlseqs_matcher_free(ctx->matcher);
|
|
|
|
ctlseqs_reader_free(ctx->reader);
|
|
|
|
|
2020-12-22 13:51:01 +00:00
|
|
|
// Restore cursor status.
|
2021-01-08 00:00:49 +00:00
|
|
|
if (ctx->hide_cursor) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), DECTCEM);
|
2020-12-22 13:51:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Restore normal screen buffer.
|
2021-01-08 00:00:49 +00:00
|
|
|
if (ctx->alt_scrbuf) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), ALT_SCRBUF);
|
2021-01-08 00:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Restore original mouse modes.
|
|
|
|
if (ctx->btnev_tracking) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), BTN_EVENT_TRACKING);
|
2021-01-08 00:00:49 +00:00
|
|
|
}
|
|
|
|
if (ctx->sgr_pixelmode) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), SGR_MOUSE_PIXELMODE);
|
2020-12-22 13:51:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Restore original terminal modes.
|
2020-11-17 06:24:43 +00:00
|
|
|
if (ctx->has_termios) {
|
|
|
|
tcsetattr(ctx->in_fd, TCSANOW, &ctx->termios);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2021-01-06 13:31:25 +00:00
|
|
|
get_winsize(struct sixdraw_ctx *ctx)
|
2020-11-17 06:24:43 +00:00
|
|
|
{
|
|
|
|
struct winsize ws = { 0 };
|
|
|
|
if (ioctl(ctx->in_fd, TIOCGWINSZ, &ws) != 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get terminal window size");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ws.ws_xpixel == 0 || ws.ws_ypixel == 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get terminal window size (in pixels)");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-10 20:58:39 +00:00
|
|
|
ctx->rows = ws.ws_row;
|
2020-11-17 06:24:43 +00:00
|
|
|
ctx->ch_width = ws.ws_xpixel / ws.ws_col;
|
|
|
|
ctx->ch_height = ws.ws_ypixel / ws.ws_row;
|
2021-01-31 18:01:10 +00:00
|
|
|
ctx->canvas_width = ctx->ch_width * ws.ws_col;
|
|
|
|
ctx->canvas_height = ctx->ch_height * ws.ws_row - ctx->ch_height * 2;
|
2020-11-17 06:24:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-22 13:51:01 +00:00
|
|
|
static bool
|
2021-01-08 00:00:49 +00:00
|
|
|
decrqm(struct sixdraw_ctx *ctx, unsigned mode, char const *name)
|
2020-12-22 13:51:01 +00:00
|
|
|
{
|
2020-12-24 10:21:03 +00:00
|
|
|
ssize_t retval;
|
|
|
|
union ctlseqs_value *result = ctx->result;
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECRQM("%d"), mode);
|
2020-12-22 13:51:01 +00:00
|
|
|
do {
|
2020-12-24 10:21:03 +00:00
|
|
|
retval = ctlseqs_read(ctx->reader, ctx->matcher, ctx->timeout);
|
|
|
|
} while (retval == CTLSEQS_PARTIAL);
|
|
|
|
if (retval != 1) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get %s status", name);
|
2020-12-22 13:51:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-08 00:00:49 +00:00
|
|
|
if (result[0].num != mode || (result[1].num != DECRQM_SET && result[1].num != DECRQM_RST)) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "%s status not recognizable", name);
|
2020-12-22 13:51:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-07 13:43:43 +00:00
|
|
|
static void
|
|
|
|
print_sixel_dot(struct sixdraw_ctx *ctx, unsigned x, unsigned y)
|
|
|
|
{
|
2021-01-10 20:58:39 +00:00
|
|
|
if (x >= ctx->canvas_width || y >= ctx->canvas_height) {
|
|
|
|
return;
|
|
|
|
}
|
2021-01-07 13:43:43 +00:00
|
|
|
|
2021-01-08 00:21:28 +00:00
|
|
|
// Move cursor.
|
2021-01-07 13:43:43 +00:00
|
|
|
unsigned row = y / ctx->ch_height + 1;
|
|
|
|
unsigned col = x / ctx->ch_width + 1;
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_CUP("%d", "%d"), row, col);
|
2021-01-07 13:43:43 +00:00
|
|
|
|
2021-01-10 20:58:39 +00:00
|
|
|
// Build sixel sequence.
|
2021-01-07 13:43:43 +00:00
|
|
|
row = y % ctx->ch_height;
|
|
|
|
col = x % ctx->ch_width;
|
2021-01-10 20:58:39 +00:00
|
|
|
unsigned seq_size = ctx->sixel_init_size;
|
|
|
|
seq_size += sprintf(sixel_seq + seq_size, "%.*s!%u?%c" CTLSEQS_ST,
|
|
|
|
row / 6, "------------------------", col, (1 << row % 6) + 0x3F);
|
2021-01-07 13:43:43 +00:00
|
|
|
|
2021-01-22 04:39:41 +00:00
|
|
|
fwrite(sixel_seq, seq_size, 1, ctx->out_file);
|
2021-01-07 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 06:24:43 +00:00
|
|
|
static bool
|
2021-01-06 13:31:25 +00:00
|
|
|
init(struct sixdraw_ctx *ctx, int argc, char **argv)
|
2020-11-17 06:24:43 +00:00
|
|
|
{
|
|
|
|
*ctx = (struct sixdraw_ctx) {
|
2021-01-07 13:43:43 +00:00
|
|
|
.prog_name = argc > 0 ? argv[0] : "sixdraw",
|
2021-01-22 04:39:41 +00:00
|
|
|
.out_file = stdout,
|
|
|
|
.err_file = stderr,
|
2021-01-07 13:43:43 +00:00
|
|
|
.in_fd = STDIN_FILENO,
|
|
|
|
.out_fd = STDOUT_FILENO,
|
|
|
|
.timeout = DEFAULT_TIMEOUT_MILLIS,
|
|
|
|
.line_color = 0x00FF00
|
2020-11-17 06:24:43 +00:00
|
|
|
};
|
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Process command line arguments.
|
2020-11-17 06:24:43 +00:00
|
|
|
int opt;
|
2021-01-08 00:00:49 +00:00
|
|
|
while (-1 != (opt = getopt(argc, argv, "t:c:"))) {
|
2020-11-17 06:24:43 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 't':
|
|
|
|
ctx->timeout = atoi(optarg);
|
|
|
|
break;
|
2021-01-07 13:43:43 +00:00
|
|
|
case 'c':
|
|
|
|
ctx->line_color = strtoul(optarg, NULL, 16);
|
|
|
|
break;
|
2020-11-17 06:24:43 +00:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Initialize control sequence matcher.
|
2020-11-17 06:24:43 +00:00
|
|
|
ctx->matcher = ctlseqs_matcher_init();
|
|
|
|
if (ctx->matcher == NULL) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to initialize matcher");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
char const *patterns[] = {
|
|
|
|
CTLSEQS_RESP_PRIMARY_DA(CTLSEQS_PH_NUMS),
|
2021-01-08 00:00:49 +00:00
|
|
|
CTLSEQS_RESP_DECRQM(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
|
|
|
|
CTLSEQS_RESP_SGR_MOUSE(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, "M")
|
2020-11-17 06:24:43 +00:00
|
|
|
};
|
2020-12-11 04:47:14 +00:00
|
|
|
struct ctlseqs_matcher_options matcher_options = {
|
2020-11-17 06:24:43 +00:00
|
|
|
.patterns = patterns,
|
|
|
|
.npatterns = sizeof(patterns) / sizeof(char const *),
|
|
|
|
};
|
2020-12-11 04:47:14 +00:00
|
|
|
if (ctlseqs_matcher_config(ctx->matcher, &matcher_options) != 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to set matcher options");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Initialize control sequence reader.
|
2020-11-17 06:24:43 +00:00
|
|
|
ctx->reader = ctlseqs_reader_init();
|
|
|
|
if (ctx->reader == NULL) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to initialize reader");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-12-11 04:47:14 +00:00
|
|
|
struct ctlseqs_reader_options reader_options = {
|
2020-12-24 10:21:03 +00:00
|
|
|
.result = ctx->result,
|
2020-11-17 06:24:43 +00:00
|
|
|
.fd = ctx->in_fd,
|
2020-12-18 10:37:47 +00:00
|
|
|
.maxlen = 4096,
|
2020-11-17 06:24:43 +00:00
|
|
|
};
|
2020-12-11 04:47:14 +00:00
|
|
|
if (ctlseqs_reader_config(ctx->reader, &reader_options) != CTLSEQS_OK) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to set reader options");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2021-01-06 13:31:25 +00:00
|
|
|
prepare(struct sixdraw_ctx *ctx)
|
2020-11-17 06:24:43 +00:00
|
|
|
{
|
2020-12-18 10:37:47 +00:00
|
|
|
// Check whether we're running on a terminal.
|
2020-11-17 06:24:43 +00:00
|
|
|
if (!isatty(ctx->in_fd) || !isatty(ctx->out_fd)) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "this program can only run in a terminal");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Set terminal to noncanonical mode.
|
2020-11-17 06:24:43 +00:00
|
|
|
if (tcgetattr(ctx->in_fd, &ctx->termios) != 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get terminal attributes");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-12-15 12:49:45 +00:00
|
|
|
struct termios termios = ctx->termios;
|
|
|
|
termios.c_cc[VMIN] = 0;
|
|
|
|
termios.c_cc[VTIME] = 0;
|
2020-11-17 06:24:43 +00:00
|
|
|
termios.c_lflag &= ~(ICANON | ISIG | ECHO);
|
|
|
|
if (tcsetattr(ctx->in_fd, TCSANOW, &termios) != 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to set terminal attributes");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ctx->has_termios = true;
|
|
|
|
|
2021-01-22 04:39:41 +00:00
|
|
|
// Set STDIN flags to nonblocking, and disable output buffering.
|
2020-11-17 06:24:43 +00:00
|
|
|
int flags = fcntl(ctx->in_fd, F_GETFL);
|
|
|
|
if (flags == -1) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get file status flags");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fcntl(ctx->in_fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to set file status flags");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-22 04:39:41 +00:00
|
|
|
if (setvbuf(stdout, NULL, _IONBF, 0) != 0) {
|
|
|
|
print_error(ctx, "failed to disable output buffering");
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-17 06:24:43 +00:00
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Get initial terminal window size.
|
2021-01-06 13:31:25 +00:00
|
|
|
if (!get_winsize(ctx)) {
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:37:47 +00:00
|
|
|
// Check terminal support for sixel graphics and DEC locator.
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_PRIMARY_DA());
|
2020-12-24 10:21:03 +00:00
|
|
|
ssize_t retval;
|
2020-12-18 10:37:47 +00:00
|
|
|
do {
|
2020-12-24 10:21:03 +00:00
|
|
|
retval = ctlseqs_read(ctx->reader, ctx->matcher, ctx->timeout);
|
|
|
|
} while (retval == CTLSEQS_PARTIAL);
|
|
|
|
if (retval != 0) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "failed to get terminal features");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool has_sixel = false;
|
2020-12-24 10:21:03 +00:00
|
|
|
union ctlseqs_value *result = ctx->result;
|
2020-12-25 11:19:54 +00:00
|
|
|
for (size_t i = 1; i <= result[0].len; ++i) {
|
2020-12-24 10:21:03 +00:00
|
|
|
if (result[i].num == 4) {
|
2020-11-17 06:24:43 +00:00
|
|
|
has_sixel = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!has_sixel) {
|
2021-01-06 13:31:25 +00:00
|
|
|
print_error(ctx, "terminal does not support sixel graphics");
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-07 13:43:43 +00:00
|
|
|
|
|
|
|
// Hide cursor.
|
2021-01-06 13:31:25 +00:00
|
|
|
if (!decrqm(ctx, DECTCEM, "cursor")) {
|
2020-11-17 06:24:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-08 00:00:49 +00:00
|
|
|
ctx->hide_cursor = result[1].num == DECRQM_SET;
|
|
|
|
if (ctx->hide_cursor) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECRST("%d"), DECTCEM);
|
2021-01-08 00:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable button event tracking mode.
|
|
|
|
if (!decrqm(ctx, BTN_EVENT_TRACKING, "button event tracking mode")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ctx->btnev_tracking = result[1].num == DECRQM_RST;
|
|
|
|
if (ctx->btnev_tracking) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), BTN_EVENT_TRACKING);
|
2021-01-08 00:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable SGR mouse pixel mode.
|
|
|
|
if (!decrqm(ctx, SGR_MOUSE_PIXELMODE, "SGR mouse pixel mode")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ctx->sgr_pixelmode = result[1].num == DECRQM_RST;
|
|
|
|
if (ctx->sgr_pixelmode) {
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), SGR_MOUSE_PIXELMODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch to alternate screen buffer.
|
|
|
|
if (!decrqm(ctx, ALT_SCRBUF, "screen buffer")) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ctx->alt_scrbuf = result[1].num == DECRQM_RST;
|
|
|
|
if (ctx->alt_scrbuf) {
|
|
|
|
fprintf(ctx->out_file, CTLSEQS_DECSET("%d"), ALT_SCRBUF);
|
2020-12-22 13:51:01 +00:00
|
|
|
}
|
2020-11-17 06:24:43 +00:00
|
|
|
|
2021-01-10 20:58:39 +00:00
|
|
|
// Build the immutable part of sixel sequence.
|
|
|
|
ctx->sixel_init_size = sizeof(SIXEL_SEQ_HEAD) - 1;
|
|
|
|
ctx->sixel_init_size += sprintf(sixel_seq + ctx->sixel_init_size, "%u;%u#0;2;%d;%d;%d#0",
|
|
|
|
ctx->ch_width, ctx->ch_height, ((ctx->line_color >> 16) & 0xFF) * 100 / 0xFF,
|
|
|
|
((ctx->line_color >> 8) & 0xFF) * 100 / 0xFF, ((ctx->line_color >> 0) & 0xFF) * 100 / 0xFF);
|
|
|
|
|
2020-11-17 06:24:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-08 00:00:49 +00:00
|
|
|
static bool
|
|
|
|
draw(struct sixdraw_ctx *ctx)
|
|
|
|
{
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_CUP("%d", "1") "Canvas size: %ux%u. Line color: #%06X.",
|
2021-01-10 20:58:39 +00:00
|
|
|
ctx->rows - 1, ctx->canvas_width, ctx->canvas_height, ctx->line_color);
|
2021-01-22 04:39:41 +00:00
|
|
|
fprintf(ctx->out_file, CTLSEQS_CUP("%d", "1") "Usage: Draw with mouse. Press Ctrl+C to exit.", ctx->rows);
|
2021-01-10 20:58:39 +00:00
|
|
|
|
2021-01-08 00:00:49 +00:00
|
|
|
union ctlseqs_value *result = ctx->result;
|
|
|
|
while (true) {
|
|
|
|
switch (ctlseqs_read(ctx->reader, ctx->matcher, -1)) {
|
|
|
|
case 2: // CTLSEQS_RESP_SGR_MOUSE
|
2021-01-31 18:01:10 +00:00
|
|
|
// Pixel coordinates start at 1 instead of 0.
|
2021-01-10 20:58:39 +00:00
|
|
|
print_sixel_dot(ctx, result[1].num - 1, result[2].num - 1);
|
2021-01-08 00:00:49 +00:00
|
|
|
break;
|
|
|
|
case CTLSEQS_NOSEQ:
|
|
|
|
// Press Ctrl+C to exit.
|
|
|
|
if (result[1].str[0] == 0x03) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTLSEQS_ERROR:
|
|
|
|
case CTLSEQS_NOMEM:
|
|
|
|
case CTLSEQS_EOF:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 06:24:43 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2021-01-08 00:00:49 +00:00
|
|
|
int status;
|
2020-11-17 06:24:43 +00:00
|
|
|
struct sixdraw_ctx ctx;
|
2021-01-08 00:00:49 +00:00
|
|
|
if (init(&ctx, argc, argv) && prepare(&ctx)) {
|
|
|
|
status = draw(&ctx) ? 0 : -1;
|
|
|
|
} else {
|
2020-11-17 06:24:43 +00:00
|
|
|
status = -1;
|
|
|
|
}
|
2021-01-06 13:31:25 +00:00
|
|
|
terminate(&ctx);
|
2020-11-17 06:24:43 +00:00
|
|
|
return status;
|
|
|
|
}
|