summaryrefslogtreecommitdiffstats
path: root/video/out/vo_tct.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vo_tct.c')
-rw-r--r--video/out/vo_tct.c222
1 files changed, 150 insertions, 72 deletions
diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c
index b0965e14cc..293f884544 100644
--- a/video/out/vo_tct.c
+++ b/video/out/vo_tct.c
@@ -28,6 +28,7 @@
#include "options/m_config.h"
#include "config.h"
#include "osdep/terminal.h"
+#include "osdep/io.h"
#include "vo.h"
#include "sub/osd.h"
#include "video/sws_utils.h"
@@ -37,52 +38,50 @@
#define ALGO_PLAIN 1
#define ALGO_HALF_BLOCKS 2
-#define ESC_HIDE_CURSOR "\033[?25l"
-#define ESC_RESTORE_CURSOR "\033[?25h"
-#define ESC_CLEAR_SCREEN "\033[2J"
-#define ESC_CLEAR_COLORS "\033[0m"
-#define ESC_GOTOXY "\033[%d;%df"
-#define ESC_COLOR_BG "\033[48;2;%d;%d;%dm"
-#define ESC_COLOR_FG "\033[38;2;%d;%d;%dm"
-#define ESC_COLOR256_BG "\033[48;5;%dm"
-#define ESC_COLOR256_FG "\033[38;5;%dm"
+
#define DEFAULT_WIDTH 80
#define DEFAULT_HEIGHT 25
+static const bstr TERM_ESC_CLEAR_COLORS = bstr0_lit("\033[0m");
+static const bstr TERM_ESC_COLOR256_BG = bstr0_lit("\033[48;5");
+static const bstr TERM_ESC_COLOR256_FG = bstr0_lit("\033[38;5");
+static const bstr TERM_ESC_COLOR24BIT_BG = bstr0_lit("\033[48;2");
+static const bstr TERM_ESC_COLOR24BIT_FG = bstr0_lit("\033[38;2");
+
+static const bstr UNICODE_LOWER_HALF_BLOCK = bstr0_lit("\xe2\x96\x84");
+
+#define WRITE_STR(str) fwrite((str), strlen(str), 1, stdout)
+
+enum vo_tct_buffering {
+ VO_TCT_BUFFER_PIXEL,
+ VO_TCT_BUFFER_LINE,
+ VO_TCT_BUFFER_FRAME
+};
+
struct vo_tct_opts {
int algo;
+ int buffering;
int width; // 0 -> default
int height; // 0 -> default
- int term256; // 0 -> true color
+ bool term256; // 0 -> true color
};
-#define OPT_BASE_STRUCT struct vo_tct_opts
-static const struct m_sub_options vo_tct_conf = {
- .opts = (const m_option_t[]) {
- {"vo-tct-algo", OPT_CHOICE(algo,
- {"plain", ALGO_PLAIN},
- {"half-blocks", ALGO_HALF_BLOCKS})},
- {"vo-tct-width", OPT_INT(width)},
- {"vo-tct-height", OPT_INT(height)},
- {"vo-tct-256", OPT_FLAG(term256)},
- {0}
- },
- .defaults = &(const struct vo_tct_opts) {
- .algo = ALGO_HALF_BLOCKS,
- },
- .size = sizeof(struct vo_tct_opts),
+struct lut_item {
+ char str[4];
+ uint8_t width;
};
struct priv {
- struct vo_tct_opts *opts;
+ struct vo_tct_opts opts;
size_t buffer_size;
- char *buffer;
int swidth;
int sheight;
struct mp_image *frame;
struct mp_rect src;
struct mp_rect dst;
struct mp_sws_context *sws;
+ bstr frame_buf;
+ struct lut_item lut[256];
};
// Convert RGB24 to xterm-256 8-bit value
@@ -114,39 +113,65 @@ static int rgb_to_x256(uint8_t r, uint8_t g, uint8_t b)
return color_err <= gray_err ? 16 + color_index() : 232 + gray_index;
}
-static void write_plain(
+static void print_seq3(bstr *frame, struct lut_item *lut, bstr prefix,
+ uint8_t r, uint8_t g, uint8_t b)
+{
+ bstr_xappend(NULL, frame, prefix);
+ bstr_xappend(NULL, frame, (bstr){ lut[r].str, lut[r].width });
+ bstr_xappend(NULL, frame, (bstr){ lut[g].str, lut[g].width });
+ bstr_xappend(NULL, frame, (bstr){ lut[b].str, lut[b].width });
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit("m"));
+}
+
+static void print_seq1(bstr *frame, struct lut_item *lut, bstr prefix, uint8_t c)
+{
+ bstr_xappend(NULL, frame, prefix);
+ bstr_xappend(NULL, frame, (bstr){ lut[c].str, lut[c].width });
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit("m"));
+}
+
+static void print_buffer(bstr *frame)
+{
+ fwrite(frame->start, frame->len, 1, stdout);
+ frame->len = 0;
+}
+
+static void write_plain(bstr *frame,
const int dwidth, const int dheight,
const int swidth, const int sheight,
const unsigned char *source, const int source_stride,
- bool term256)
+ bool term256, struct lut_item *lut, enum vo_tct_buffering buffering)
{
assert(source);
const int tx = (dwidth - swidth) / 2;
const int ty = (dheight - sheight) / 2;
for (int y = 0; y < sheight; y++) {
const unsigned char *row = source + y * source_stride;
- printf(ESC_GOTOXY, ty + y, tx);
+ bstr_xappend_asprintf(NULL, frame, TERM_ESC_GOTO_YX, ty + y, tx);
for (int x = 0; x < swidth; x++) {
unsigned char b = *row++;
unsigned char g = *row++;
unsigned char r = *row++;
if (term256) {
- printf(ESC_COLOR256_BG, rgb_to_x256(r, g, b));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r, g, b));
} else {
- printf(ESC_COLOR_BG, r, g, b);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_BG, r, g, b);
}
- printf(" ");
+ bstr_xappend(NULL, frame, (bstr)bstr0_lit(" "));
+ if (buffering <= VO_TCT_BUFFER_PIXEL)
+ print_buffer(frame);
}
- printf(ESC_CLEAR_COLORS);
+ bstr_xappend(NULL, frame, TERM_ESC_CLEAR_COLORS);
+ if (buffering <= VO_TCT_BUFFER_LINE)
+ print_buffer(frame);
}
- printf("\n");
}
-static void write_half_blocks(
+static void write_half_blocks(bstr *frame,
const int dwidth, const int dheight,
const int swidth, const int sheight,
unsigned char *source, int source_stride,
- bool term256)
+ bool term256, struct lut_item *lut, enum vo_tct_buffering buffering)
{
assert(source);
const int tx = (dwidth - swidth) / 2;
@@ -154,7 +179,7 @@ static void write_half_blocks(
for (int y = 0; y < sheight * 2; y += 2) {
const unsigned char *row_up = source + y * source_stride;
const unsigned char *row_down = source + (y + 1) * source_stride;
- printf(ESC_GOTOXY, ty + y / 2, tx);
+ bstr_xappend_asprintf(NULL, frame, TERM_ESC_GOTO_YX, ty + y / 2, tx);
for (int x = 0; x < swidth; x++) {
unsigned char b_up = *row_up++;
unsigned char g_up = *row_up++;
@@ -163,17 +188,20 @@ static void write_half_blocks(
unsigned char g_down = *row_down++;
unsigned char r_down = *row_down++;
if (term256) {
- printf(ESC_COLOR256_BG, rgb_to_x256(r_up, g_up, b_up));
- printf(ESC_COLOR256_FG, rgb_to_x256(r_down, g_down, b_down));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_BG, rgb_to_x256(r_up, g_up, b_up));
+ print_seq1(frame, lut, TERM_ESC_COLOR256_FG, rgb_to_x256(r_down, g_down, b_down));
} else {
- printf(ESC_COLOR_BG, r_up, g_up, b_up);
- printf(ESC_COLOR_FG, r_down, g_down, b_down);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_BG, r_up, g_up, b_up);
+ print_seq3(frame, lut, TERM_ESC_COLOR24BIT_FG, r_down, g_down, b_down);
}
- printf("\xe2\x96\x84"); // UTF8 bytes of U+2584 (lower half block)
+ bstr_xappend(NULL, frame, UNICODE_LOWER_HALF_BLOCK);
+ if (buffering <= VO_TCT_BUFFER_PIXEL)
+ print_buffer(frame);
}
- printf(ESC_CLEAR_COLORS);
+ bstr_xappend(NULL, frame, TERM_ESC_CLEAR_COLORS);
+ if (buffering <= VO_TCT_BUFFER_LINE)
+ print_buffer(frame);
}
- printf("\n");
}
static void get_win_size(struct vo *vo, int *out_width, int *out_height) {
@@ -183,10 +211,10 @@ static void get_win_size(struct vo *vo, int *out_width, int *out_height) {
terminal_get_size(out_width, out_height);
- if (p->opts->width > 0)
- *out_width = p->opts->width;
- if (p->opts->height > 0)
- *out_height = p->opts->height;
+ if (p->opts.width > 0)
+ *out_width = p->opts.width;
+ if (p->opts.height > 0)
+ *out_height = p->opts.height;
}
static int reconfig(struct vo *vo, struct mp_image_params *params)
@@ -200,9 +228,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
p->swidth = p->dst.x1 - p->dst.x0;
p->sheight = p->dst.y1 - p->dst.y0;
- if (p->buffer)
- free(p->buffer);
-
p->sws->src = *params;
p->sws->dst = (struct mp_image_params) {
.imgfmt = IMGFMT,
@@ -212,7 +237,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
.p_h = 1,
};
- const int mul = (p->opts->algo == ALGO_PLAIN ? 1 : 2);
+ const int mul = (p->opts.algo == ALGO_PLAIN ? 1 : 2);
+ if (p->frame)
+ talloc_free(p->frame);
p->frame = mp_image_alloc(IMGFMT, p->swidth, p->sheight * mul);
if (!p->frame)
return -1;
@@ -220,46 +247,63 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (mp_sws_reinit(p->sws) < 0)
return -1;
- printf(ESC_HIDE_CURSOR);
- printf(ESC_CLEAR_SCREEN);
+ WRITE_STR(TERM_ESC_CLEAR_SCREEN);
+
vo->want_redraw = true;
return 0;
}
-static void draw_image(struct vo *vo, mp_image_t *mpi)
+static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
struct priv *p = vo->priv;
- struct mp_image src = *mpi;
+ struct mp_image *src = frame->current;
+ if (!src)
+ return;
// XXX: pan, crop etc.
- mp_sws_scale(p->sws, p->frame, &src);
- talloc_free(mpi);
+ mp_sws_scale(p->sws, p->frame, src);
}
static void flip_page(struct vo *vo)
{
struct priv *p = vo->priv;
- if (p->opts->algo == ALGO_PLAIN) {
- write_plain(
+
+ int width, height;
+ get_win_size(vo, &width, &height);
+
+ if (vo->dwidth != width || vo->dheight != height)
+ reconfig(vo, vo->params);
+
+ WRITE_STR(TERM_ESC_SYNC_UPDATE_BEGIN);
+
+ p->frame_buf.len = 0;
+ if (p->opts.algo == ALGO_PLAIN) {
+ write_plain(&p->frame_buf,
vo->dwidth, vo->dheight, p->swidth, p->sheight,
p->frame->planes[0], p->frame->stride[0],
- p->opts->term256);
+ p->opts.term256, p->lut, p->opts.buffering);
} else {
- write_half_blocks(
+ write_half_blocks(&p->frame_buf,
vo->dwidth, vo->dheight, p->swidth, p->sheight,
p->frame->planes[0], p->frame->stride[0],
- p->opts->term256);
+ p->opts.term256, p->lut, p->opts.buffering);
}
+
+ bstr_xappend(NULL, &p->frame_buf, (bstr)bstr0_lit("\n"));
+ if (p->opts.buffering <= VO_TCT_BUFFER_FRAME)
+ print_buffer(&p->frame_buf);
+
+ WRITE_STR(TERM_ESC_SYNC_UPDATE_END);
fflush(stdout);
}
static void uninit(struct vo *vo)
{
- printf(ESC_RESTORE_CURSOR);
- printf(ESC_CLEAR_SCREEN);
- printf(ESC_GOTOXY, 0, 0);
+ WRITE_STR(TERM_ESC_RESTORE_CURSOR);
+ terminal_set_mouse_input(false);
+ WRITE_STR(TERM_ESC_NORMAL_SCREEN);
struct priv *p = vo->priv;
- if (p->buffer)
- talloc_free(p->buffer);
+ talloc_free(p->frame);
+ talloc_free(p->frame_buf.start);
}
static int preinit(struct vo *vo)
@@ -269,10 +313,25 @@ static int preinit(struct vo *vo)
vo->monitor_par = vo->opts->monitor_pixel_aspect * 2;
struct priv *p = vo->priv;
- p->opts = mp_get_config_group(vo, vo->global, &vo_tct_conf);
p->sws = mp_sws_alloc(vo);
p->sws->log = vo->log;
mp_sws_enable_cmdline_opts(p->sws, vo->global);
+
+ for (int i = 0; i < MP_ARRAY_SIZE(p->lut); ++i) {
+ char* out = p->lut[i].str;
+ *out++ = ';';
+ if (i >= 100)
+ *out++ = '0' + (i / 100);
+ if (i >= 10)
+ *out++ = '0' + ((i / 10) % 10);
+ *out++ = '0' + (i % 10);
+ p->lut[i].width = out - p->lut[i].str;
+ }
+
+ WRITE_STR(TERM_ESC_HIDE_CURSOR);
+ terminal_set_mouse_input(true);
+ WRITE_STR(TERM_ESC_ALT_SCREEN);
+
return 0;
}
@@ -286,6 +345,8 @@ static int control(struct vo *vo, uint32_t request, void *data)
return VO_NOTIMPL;
}
+#define OPT_BASE_STRUCT struct priv
+
const struct vo_driver video_out_tct = {
.name = "tct",
.description = "true-color terminals",
@@ -293,9 +354,26 @@ const struct vo_driver video_out_tct = {
.query_format = query_format,
.reconfig = reconfig,
.control = control,
- .draw_image = draw_image,
+ .draw_frame = draw_frame,
.flip_page = flip_page,
.uninit = uninit,
.priv_size = sizeof(struct priv),
- .global_opts = &vo_tct_conf,
+ .priv_defaults = &(const struct priv) {
+ .opts.algo = ALGO_HALF_BLOCKS,
+ .opts.buffering = VO_TCT_BUFFER_LINE,
+ },
+ .options = (const m_option_t[]) {
+ {"algo", OPT_CHOICE(opts.algo,
+ {"plain", ALGO_PLAIN},
+ {"half-blocks", ALGO_HALF_BLOCKS})},
+ {"width", OPT_INT(opts.width)},
+ {"height", OPT_INT(opts.height)},
+ {"256", OPT_BOOL(opts.term256)},
+ {"buffering", OPT_CHOICE(opts.buffering,
+ {"pixel", VO_TCT_BUFFER_PIXEL},
+ {"line", VO_TCT_BUFFER_LINE},
+ {"frame", VO_TCT_BUFFER_FRAME})},
+ {0}
+ },
+ .options_prefix = "vo-tct",
};