2020-02-21 21:53:23 +01:00
|
|
|
|
#include "sixel.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
2020-06-06 13:59:46 +02:00
|
|
|
|
#include <limits.h>
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "sixel"
|
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
|
#include "log.h"
|
2021-01-15 20:39:45 +00:00
|
|
|
|
#include "debug.h"
|
2020-02-21 21:53:23 +01:00
|
|
|
|
#include "render.h"
|
2020-11-15 19:45:33 +01:00
|
|
|
|
#include "hsl.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
|
#include "util.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
|
#include "xmalloc.h"
|
2021-01-14 21:30:06 +00:00
|
|
|
|
#include "xsnprintf.h"
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
|
|
|
|
|
static size_t count;
|
|
|
|
|
|
|
2020-06-10 18:36:54 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_fini(struct terminal *term)
|
|
|
|
|
|
{
|
2021-02-16 19:37:49 +01:00
|
|
|
|
free(term->sixel.private_palette);
|
|
|
|
|
|
free(term->sixel.shared_palette);
|
2020-06-10 18:36:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-10 18:52:53 +02:00
|
|
|
|
static uint32_t
|
|
|
|
|
|
color_with_alpha(const struct terminal *term, uint32_t color)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint16_t alpha = color == term->colors.bg ? term->colors.alpha : 0xffff;
|
|
|
|
|
|
return (alpha / 256u) << 24 | color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
|
void
|
|
|
|
|
|
sixel_init(struct terminal *term)
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term->sixel.image.data == NULL);
|
|
|
|
|
|
xassert(term->sixel.palette_size <= SIXEL_MAX_COLORS);
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
2020-02-22 10:54:52 +01:00
|
|
|
|
term->sixel.pos = (struct coord){0, 0};
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.color_idx = 0;
|
|
|
|
|
|
term->sixel.max_col = 0;
|
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
|
memset(term->sixel.params, 0, sizeof(term->sixel.params));
|
2020-08-08 20:34:30 +01:00
|
|
|
|
term->sixel.image.data = xmalloc(1 * 6 * sizeof(term->sixel.image.data[0]));
|
2020-02-22 10:46:35 +01:00
|
|
|
|
term->sixel.image.width = 1;
|
|
|
|
|
|
term->sixel.image.height = 6;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2021-02-16 19:37:49 +01:00
|
|
|
|
/* TODO: default palette */
|
|
|
|
|
|
|
|
|
|
|
|
if (term->sixel.use_private_palette) {
|
2021-02-17 21:58:36 +01:00
|
|
|
|
xassert(term->sixel.private_palette == NULL);
|
|
|
|
|
|
term->sixel.private_palette = xcalloc(
|
|
|
|
|
|
term->sixel.palette_size, sizeof(term->sixel.private_palette[0]));
|
2021-02-16 19:37:49 +01:00
|
|
|
|
term->sixel.palette = term->sixel.private_palette;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (term->sixel.shared_palette == NULL) {
|
|
|
|
|
|
term->sixel.shared_palette = xcalloc(
|
|
|
|
|
|
term->sixel.palette_size, sizeof(term->sixel.shared_palette[0]));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/* Shared palette - do *not* reset palette for new sixels */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term->sixel.palette = term->sixel.shared_palette;
|
2020-06-10 18:36:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
|
for (size_t i = 0; i < 1 * 6; i++)
|
2020-06-10 18:52:53 +02:00
|
|
|
|
term->sixel.image.data[i] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
|
count = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 00:23:19 +01:00
|
|
|
|
void
|
|
|
|
|
|
sixel_destroy(struct sixel *sixel)
|
|
|
|
|
|
{
|
|
|
|
|
|
pixman_image_unref(sixel->pix);
|
|
|
|
|
|
free(sixel->data);
|
|
|
|
|
|
|
|
|
|
|
|
sixel->pix = NULL;
|
|
|
|
|
|
sixel->data = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-29 21:53:29 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_destroy_all(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
tll_foreach(term->normal.sixel_images, it)
|
|
|
|
|
|
sixel_destroy(&it->item);
|
|
|
|
|
|
tll_foreach(term->alt.sixel_images, it)
|
|
|
|
|
|
sixel_destroy(&it->item);
|
|
|
|
|
|
tll_free(term->normal.sixel_images);
|
|
|
|
|
|
tll_free(term->alt.sixel_images);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-24 18:39:14 +01:00
|
|
|
|
static void
|
|
|
|
|
|
sixel_erase(struct terminal *term, struct sixel *sixel)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < sixel->rows; i++) {
|
|
|
|
|
|
int r = (sixel->pos.row + i) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
|
|
struct row *row = term->grid->rows[r];
|
2020-03-06 19:11:57 +01:00
|
|
|
|
if (row == NULL) {
|
|
|
|
|
|
/* A resize/reflow may cause row to now be unallocated */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-24 18:39:14 +01:00
|
|
|
|
row->dirty = true;
|
|
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < term->grid->num_cols; c++)
|
|
|
|
|
|
row->cells[c].attrs.clean = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sixel_destroy(sixel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:28:53 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Calculates the scrollback relative row number, given an absolute row number.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The scrollback relative row number 0 is the *first*, and *oldest*
|
|
|
|
|
|
* row in the scrollback history (and thus the *first* row to be
|
|
|
|
|
|
* scrolled out). Thus, a higher number means further *down* in the
|
|
|
|
|
|
* scrollback, with the *highest* number being at the bottom of the
|
|
|
|
|
|
* screen, where new input appears.
|
|
|
|
|
|
*/
|
2020-06-29 22:01:02 +02:00
|
|
|
|
static int
|
|
|
|
|
|
rebase_row(const struct terminal *term, int abs_row)
|
2020-06-28 08:37:25 +02:00
|
|
|
|
{
|
2020-06-29 22:01:02 +02:00
|
|
|
|
int scrollback_start = term->grid->offset + term->rows;
|
|
|
|
|
|
int rebased_row = abs_row - scrollback_start + term->grid->num_rows;
|
2020-06-28 09:16:43 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
rebased_row &= term->grid->num_rows - 1;
|
|
|
|
|
|
return rebased_row;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:28:53 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Verify the sixels are sorted correctly.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The sixels are sorted on their *end* row, in descending order. This
|
|
|
|
|
|
* invariant means the most recent sixels appear first in the list.
|
|
|
|
|
|
*/
|
2020-10-04 19:29:48 +02:00
|
|
|
|
static void
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_list_order(const struct terminal *term)
|
2020-06-29 22:01:02 +02:00
|
|
|
|
{
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
int prev_row = INT_MAX;
|
2020-07-16 08:10:56 +02:00
|
|
|
|
int prev_col = -1;
|
|
|
|
|
|
int prev_col_count = 0;
|
2020-06-28 09:16:43 +02:00
|
|
|
|
|
2020-10-04 19:10:54 +02:00
|
|
|
|
/* To aid debugging */
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
|
2020-06-28 09:16:43 +02:00
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
2020-06-29 22:01:02 +02:00
|
|
|
|
int row = rebase_row(term, it->item.pos.row + it->item.rows - 1);
|
2020-07-16 08:10:56 +02:00
|
|
|
|
int col = it->item.pos.col;
|
|
|
|
|
|
int col_count = it->item.cols;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(row <= prev_row);
|
2020-07-16 08:10:56 +02:00
|
|
|
|
|
|
|
|
|
|
if (row == prev_row) {
|
|
|
|
|
|
/* Allowed to be on the same row only if their columns
|
|
|
|
|
|
* don't overlap */
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(col + col_count <= prev_col ||
|
2020-07-16 08:10:56 +02:00
|
|
|
|
prev_col + prev_col_count <= col);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
prev_row = row;
|
2020-07-16 08:10:56 +02:00
|
|
|
|
prev_col = col;
|
|
|
|
|
|
prev_col_count = col_count;
|
2020-10-04 19:10:54 +02:00
|
|
|
|
idx++;
|
2020-06-29 22:01:02 +02:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:28:53 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Verifies there aren't any sixels that cross the scrollback
|
|
|
|
|
|
* wrap-around. This invariant means a sixel's absolute row numbers
|
|
|
|
|
|
* are strictly increasing.
|
|
|
|
|
|
*/
|
2020-10-04 19:29:48 +02:00
|
|
|
|
static void
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_no_wraparound_crossover(const struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
const struct sixel *six = &it->item;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(six->pos.row >= 0);
|
|
|
|
|
|
xassert(six->pos.row < term->grid->num_rows);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
|
2020-10-04 13:10:06 +02:00
|
|
|
|
int end = (six->pos.row + six->rows - 1) & (term->grid->num_rows - 1);
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(end >= six->pos.row);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:29:24 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Verify there aren't any sixels that cross the scrollback end. This
|
|
|
|
|
|
* invariant means a sixel's rebased row numbers are strictly
|
|
|
|
|
|
* increasing.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
|
|
|
verify_scrollback_consistency(const struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
const struct sixel *six = &it->item;
|
|
|
|
|
|
|
|
|
|
|
|
int last_row = -1;
|
|
|
|
|
|
for (int i = 0; i < six->rows; i++) {
|
|
|
|
|
|
int row_no = rebase_row(term, six->pos.row + i);
|
|
|
|
|
|
|
|
|
|
|
|
if (last_row != -1)
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(last_row < row_no);
|
2020-10-05 18:29:24 +02:00
|
|
|
|
|
|
|
|
|
|
last_row = row_no;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:28:53 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Verifies no sixel overlap with any other sixels.
|
|
|
|
|
|
*/
|
2020-10-04 19:29:48 +02:00
|
|
|
|
static void
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_no_overlap(const struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
const struct sixel *six1 = &it->item;
|
|
|
|
|
|
|
|
|
|
|
|
pixman_region32_t rect1;
|
|
|
|
|
|
pixman_region32_init_rect(
|
|
|
|
|
|
&rect1, six1->pos.col, six1->pos.row, six1->cols, six1->rows);
|
|
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it2) {
|
|
|
|
|
|
const struct sixel *six2 = &it2->item;
|
|
|
|
|
|
|
|
|
|
|
|
if (six1 == six2)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
pixman_region32_t rect2;
|
|
|
|
|
|
pixman_region32_init_rect(
|
|
|
|
|
|
&rect2, six2->pos.col,
|
|
|
|
|
|
six2->pos.row, six2->cols, six2->rows);
|
|
|
|
|
|
|
|
|
|
|
|
pixman_region32_t intersection;
|
2020-10-04 13:32:29 +02:00
|
|
|
|
pixman_region32_init(&intersection);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
pixman_region32_intersect(&intersection, &rect1, &rect2);
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(!pixman_region32_not_empty(&intersection));
|
2020-10-04 13:32:53 +02:00
|
|
|
|
|
|
|
|
|
|
pixman_region32_fini(&intersection);
|
|
|
|
|
|
pixman_region32_fini(&rect2);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
}
|
2020-10-04 13:32:53 +02:00
|
|
|
|
|
|
|
|
|
|
pixman_region32_fini(&rect1);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 19:29:48 +02:00
|
|
|
|
static void
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_sixels(const struct terminal *term)
|
|
|
|
|
|
{
|
2020-10-04 19:29:48 +02:00
|
|
|
|
verify_no_wraparound_crossover(term);
|
2020-10-05 18:29:24 +02:00
|
|
|
|
verify_scrollback_consistency(term);
|
2020-10-04 19:29:48 +02:00
|
|
|
|
verify_no_overlap(term);
|
|
|
|
|
|
verify_list_order(term);
|
2020-10-04 11:00:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
static void
|
|
|
|
|
|
sixel_insert(struct terminal *term, struct sixel sixel)
|
|
|
|
|
|
{
|
|
|
|
|
|
int end_row = rebase_row(term, sixel.pos.row + sixel.rows - 1);
|
2020-06-28 09:16:43 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
if (rebase_row(term, it->item.pos.row + it->item.rows - 1) < end_row) {
|
2020-06-28 09:16:43 +02:00
|
|
|
|
tll_insert_before(term->grid->sixel_images, it, sixel);
|
2020-06-28 10:45:05 +02:00
|
|
|
|
goto out;
|
2020-06-28 09:16:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-28 08:37:25 +02:00
|
|
|
|
tll_push_back(term->grid->sixel_images, sixel);
|
2020-06-28 10:45:05 +02:00
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
|
|
|
|
|
LOG_DBG("sixel list after insertion:");
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
LOG_DBG(" rows=%d+%d", it->item.pos.row, it->item.rows);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_sixels(term);
|
2020-06-28 08:37:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_scroll_up(struct terminal *term, int rows)
|
2020-02-22 21:35:45 +01:00
|
|
|
|
{
|
2020-10-04 19:28:22 +02:00
|
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
tll_rforeach(term->grid->sixel_images, it) {
|
2020-02-24 18:39:14 +01:00
|
|
|
|
struct sixel *six = &it->item;
|
2020-06-06 13:59:46 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
int six_start = rebase_row(term, six->pos.row);
|
sixel: scroll-up: don’t break out early of loop
This function loops the list of sixels, and discards those that would
be scrolled out if the grid offset is moved forward by the specified
number of rows.
The criteria is when the rebased row value is less than the number of
rows to scroll.
A rebased row number is a zero-based number starting at the beginning
of the scrollback. Thus, when scrolling 5 rows, rows with a rebased
row number between 0-4 will be scrolled out.
For performance reasons, we used to break out of the loop as soon as a
row number *larger* than the scroll count.
This however does not work. The sixels are sorted by their *end*
row. While this works in most cases (think images outputted in the
shell in the normal screen), it doesn’t always.
In the alt screen, where applications can print images just about
anywhere, it is possible to have *any* start row number anywhere in
the sixel list. Just as long as their *end* row numbers are sorted.
For example, a huuuge sixel that covers the entire scrollback. This
sixel will naturally be first in the list (and thus sixel_scroll_up()
will visit it *last*, since it iterates the list in reverse), but
should still be destroyed when scrolling.
2020-10-04 19:11:40 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
if (six_start < rows) {
|
2020-10-04 14:03:45 +02:00
|
|
|
|
sixel_erase(term, six);
|
2020-06-29 22:01:02 +02:00
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
sixel: scroll-up: don’t break out early of loop
This function loops the list of sixels, and discards those that would
be scrolled out if the grid offset is moved forward by the specified
number of rows.
The criteria is when the rebased row value is less than the number of
rows to scroll.
A rebased row number is a zero-based number starting at the beginning
of the scrollback. Thus, when scrolling 5 rows, rows with a rebased
row number between 0-4 will be scrolled out.
For performance reasons, we used to break out of the loop as soon as a
row number *larger* than the scroll count.
This however does not work. The sixels are sorted by their *end*
row. While this works in most cases (think images outputted in the
shell in the normal screen), it doesn’t always.
In the alt screen, where applications can print images just about
anywhere, it is possible to have *any* start row number anywhere in
the sixel list. Just as long as their *end* row numbers are sorted.
For example, a huuuge sixel that covers the entire scrollback. This
sixel will naturally be first in the list (and thus sixel_scroll_up()
will visit it *last*, since it iterates the list in reverse), but
should still be destroyed when scrolling.
2020-10-04 19:11:40 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Unfortunately, we cannot break here.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The sixels are sorted on their *end* row. This means
|
|
|
|
|
|
* there may be a sixel with a top row that will be
|
|
|
|
|
|
* scrolled out *anywhere* in the list (think of a huuuuge
|
|
|
|
|
|
* sixel that covers the entire scrollback)
|
|
|
|
|
|
*/
|
|
|
|
|
|
//break;
|
|
|
|
|
|
}
|
2020-02-22 21:35:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_sixels(term);
|
2020-06-06 13:59:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_scroll_down(struct terminal *term, int rows)
|
2020-02-24 18:39:14 +01:00
|
|
|
|
{
|
2020-10-04 19:28:22 +02:00
|
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term->grid->num_rows >= rows);
|
2020-02-24 18:39:14 +01:00
|
|
|
|
|
2020-03-13 18:44:23 +01:00
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
2020-02-24 18:39:14 +01:00
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
int six_end = rebase_row(term, six->pos.row + six->rows - 1);
|
|
|
|
|
|
if (six_end >= term->grid->num_rows - rows) {
|
2020-10-04 14:03:45 +02:00
|
|
|
|
sixel_erase(term, six);
|
2020-03-13 18:44:23 +01:00
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
2020-06-29 22:01:02 +02:00
|
|
|
|
} else
|
|
|
|
|
|
break;
|
2020-06-27 14:43:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 11:00:50 +02:00
|
|
|
|
verify_sixels(term);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-23 21:07:12 +02:00
|
|
|
|
static void
|
2020-06-27 15:29:47 +02:00
|
|
|
|
sixel_overwrite(struct terminal *term, struct sixel *six,
|
|
|
|
|
|
int row, int col, int height, int width)
|
2020-06-23 21:07:12 +02:00
|
|
|
|
{
|
2020-10-05 18:31:25 +02:00
|
|
|
|
pixman_region32_t six_rect;
|
|
|
|
|
|
pixman_region32_init_rect(
|
|
|
|
|
|
&six_rect,
|
|
|
|
|
|
six->pos.col * term->cell_width, six->pos.row * term->cell_height,
|
|
|
|
|
|
six->width, six->height);
|
|
|
|
|
|
|
|
|
|
|
|
pixman_region32_t overwrite_rect;
|
|
|
|
|
|
pixman_region32_init_rect(
|
|
|
|
|
|
&overwrite_rect,
|
|
|
|
|
|
col * term->cell_width, row * term->cell_height,
|
|
|
|
|
|
width * term->cell_width, height * term->cell_height);
|
2020-06-23 21:07:12 +02:00
|
|
|
|
|
2020-10-05 18:31:25 +02:00
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
pixman_region32_t intersection;
|
|
|
|
|
|
pixman_region32_init(&intersection);
|
|
|
|
|
|
pixman_region32_intersect(&intersection, &six_rect, &overwrite_rect);
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(pixman_region32_not_empty(&intersection));
|
2020-10-05 18:31:25 +02:00
|
|
|
|
pixman_region32_fini(&intersection);
|
|
|
|
|
|
#endif
|
2020-06-23 21:07:12 +02:00
|
|
|
|
|
2020-10-05 18:31:25 +02:00
|
|
|
|
pixman_region32_t diff;
|
|
|
|
|
|
pixman_region32_init(&diff);
|
|
|
|
|
|
pixman_region32_subtract(&diff, &six_rect, &overwrite_rect);
|
2020-06-23 21:07:12 +02:00
|
|
|
|
|
2020-10-05 18:31:25 +02:00
|
|
|
|
pixman_region32_fini(&six_rect);
|
|
|
|
|
|
pixman_region32_fini(&overwrite_rect);
|
2020-06-27 14:26:13 +02:00
|
|
|
|
|
2020-10-05 18:31:25 +02:00
|
|
|
|
int n_rects = -1;
|
|
|
|
|
|
pixman_box32_t *boxes = pixman_region32_rectangles(&diff, &n_rects);
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n_rects; i++) {
|
|
|
|
|
|
LOG_DBG("box #%d: x1=%d, y1=%d, x2=%d, y2=%d", i,
|
|
|
|
|
|
boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2);
|
2020-06-27 14:26:13 +02:00
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(boxes[i].x1 % term->cell_width == 0);
|
|
|
|
|
|
xassert(boxes[i].y1 % term->cell_height == 0);
|
2020-10-03 22:42:34 +02:00
|
|
|
|
|
2020-10-05 18:31:25 +02:00
|
|
|
|
/* New image's position, in cells */
|
|
|
|
|
|
const int new_col = boxes[i].x1 / term->cell_width;
|
|
|
|
|
|
const int new_row = boxes[i].y1 / term->cell_height;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_row < term->grid->num_rows);
|
2020-10-05 18:31:25 +02:00
|
|
|
|
|
|
|
|
|
|
/* New image's width and height, in pixels */
|
|
|
|
|
|
const int new_width = boxes[i].x2 - boxes[i].x1;
|
|
|
|
|
|
const int new_height = boxes[i].y2 - boxes[i].y1;
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t *new_data = xmalloc(new_width * new_height * sizeof(uint32_t));
|
|
|
|
|
|
const uint32_t *old_data = six->data;
|
|
|
|
|
|
|
|
|
|
|
|
/* Pixel offsets into old image backing memory */
|
|
|
|
|
|
const int x_ofs = boxes[i].x1 - six->pos.col * term->cell_width;
|
|
|
|
|
|
const int y_ofs = boxes[i].y1 - six->pos.row * term->cell_height;
|
|
|
|
|
|
|
|
|
|
|
|
/* Copy image data, one row at a time */
|
|
|
|
|
|
for (size_t j = 0; j < new_height; j++) {
|
|
|
|
|
|
memcpy(
|
|
|
|
|
|
&new_data[(0 + j) * new_width],
|
|
|
|
|
|
&old_data[(y_ofs + j) * six->width + x_ofs],
|
|
|
|
|
|
new_width * sizeof(uint32_t));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pixman_image_t *new_pix = pixman_image_create_bits_no_clear(
|
2020-06-23 21:07:12 +02:00
|
|
|
|
PIXMAN_a8r8g8b8,
|
2020-10-05 18:31:25 +02:00
|
|
|
|
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
|
|
|
|
|
|
|
|
|
|
|
struct sixel new_six = {
|
|
|
|
|
|
.data = new_data,
|
|
|
|
|
|
.pix = new_pix,
|
|
|
|
|
|
.width = new_width,
|
|
|
|
|
|
.height = new_height,
|
|
|
|
|
|
.pos = {.col = new_col, .row = new_row},
|
|
|
|
|
|
.cols = (new_width + term->cell_width - 1) / term->cell_width,
|
|
|
|
|
|
.rows = (new_height + term->cell_height - 1) / term->cell_height,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
/* Assert we don't cross the scrollback wrap-around */
|
|
|
|
|
|
const int new_end = new_six.pos.row + new_six.rows - 1;
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_end < term->grid->num_rows);
|
2020-10-05 18:31:25 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
sixel_insert(term, new_six);
|
2020-06-23 21:07:12 +02:00
|
|
|
|
}
|
2020-10-05 18:31:25 +02:00
|
|
|
|
|
|
|
|
|
|
pixman_region32_fini(&diff);
|
2020-06-23 21:07:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
|
/* Row numbers are absolute */
|
2020-06-27 12:45:22 +02:00
|
|
|
|
static void
|
2020-06-27 15:29:47 +02:00
|
|
|
|
_sixel_overwrite_by_rectangle(
|
2020-06-27 13:56:13 +02:00
|
|
|
|
struct terminal *term, int row, int col, int height, int width)
|
2020-06-23 21:07:12 +02:00
|
|
|
|
{
|
2020-10-05 18:30:23 +02:00
|
|
|
|
verify_sixels(term);
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
pixman_region32_t overwrite_rect;
|
|
|
|
|
|
pixman_region32_init_rect(&overwrite_rect, col, row, width, height);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
|
const int start = row;
|
|
|
|
|
|
const int end = row + height - 1;
|
2020-06-23 21:07:12 +02:00
|
|
|
|
|
2020-10-04 14:05:37 +02:00
|
|
|
|
/* We should never generate scrollback wrapping sixels */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(end < term->grid->num_rows);
|
2020-10-04 14:05:37 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
const int scrollback_rel_start = rebase_row(term, start);
|
2020-06-28 19:22:23 +02:00
|
|
|
|
|
2020-10-05 18:30:23 +02:00
|
|
|
|
bool UNUSED would_have_breaked = false;
|
|
|
|
|
|
|
2020-06-23 21:07:12 +02:00
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
|
|
|
|
|
|
const int six_start = six->pos.row;
|
2020-10-04 14:05:37 +02:00
|
|
|
|
const int six_end = (six_start + six->rows - 1);
|
2020-06-29 22:01:02 +02:00
|
|
|
|
const int six_scrollback_rel_end = rebase_row(term, six_end);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
2020-10-04 14:05:37 +02:00
|
|
|
|
/* We should never generate scrollback wrapping sixels */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(six_end < term->grid->num_rows);
|
2020-10-04 14:05:37 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
if (six_scrollback_rel_end < scrollback_rel_start) {
|
2020-06-28 19:22:23 +02:00
|
|
|
|
/* All remaining sixels are *before* our rectangle */
|
2020-10-05 18:30:23 +02:00
|
|
|
|
would_have_breaked = true;
|
2020-06-28 19:22:23 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:30:23 +02:00
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
pixman_region32_t six_rect;
|
|
|
|
|
|
pixman_region32_init_rect(&six_rect, six->pos.col, six->pos.row, six->cols, six->rows);
|
|
|
|
|
|
|
|
|
|
|
|
pixman_region32_t intersection;
|
|
|
|
|
|
pixman_region32_init(&intersection);
|
|
|
|
|
|
pixman_region32_intersect(&intersection, &six_rect, &overwrite_rect);
|
|
|
|
|
|
|
|
|
|
|
|
const bool collides = pixman_region32_not_empty(&intersection);
|
|
|
|
|
|
#else
|
|
|
|
|
|
const bool UNUSED collides = false;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
|
if ((start <= six_start && end >= six_start) || /* Crosses sixel start boundary */
|
|
|
|
|
|
(start <= six_end && end >= six_end) || /* Crosses sixel end boundary */
|
|
|
|
|
|
(start >= six_start && end <= six_end)) /* Fully within sixel range */
|
2020-06-27 12:45:22 +02:00
|
|
|
|
{
|
2020-06-23 21:07:12 +02:00
|
|
|
|
const int col_start = six->pos.col;
|
2020-06-27 12:45:22 +02:00
|
|
|
|
const int col_end = six->pos.col + six->cols - 1;
|
2020-06-23 21:07:12 +02:00
|
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
|
if ((col <= col_start && col + width - 1 >= col_start) ||
|
|
|
|
|
|
(col <= col_end && col + width - 1 >= col_end) ||
|
|
|
|
|
|
(col >= col_start && col + width - 1 <= col_end))
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(!would_have_breaked);
|
2020-10-05 18:30:23 +02:00
|
|
|
|
|
2020-07-23 18:15:29 +02:00
|
|
|
|
struct sixel to_be_erased = *six;
|
2020-06-23 21:07:12 +02:00
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
2020-07-23 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
sixel_overwrite(term, &to_be_erased, start, col, height, width);
|
|
|
|
|
|
sixel_erase(term, &to_be_erased);
|
2020-10-05 18:30:23 +02:00
|
|
|
|
} else
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(!collides);
|
2020-10-05 18:30:23 +02:00
|
|
|
|
} else
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(!collides);
|
2020-10-05 18:30:23 +02:00
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
pixman_region32_fini(&intersection);
|
|
|
|
|
|
pixman_region32_fini(&six_rect);
|
|
|
|
|
|
#endif
|
2020-06-23 21:07:12 +02:00
|
|
|
|
}
|
2020-10-05 18:30:23 +02:00
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
pixman_region32_fini(&overwrite_rect);
|
|
|
|
|
|
#endif
|
2020-06-23 21:07:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
|
void
|
2020-06-27 15:29:47 +02:00
|
|
|
|
sixel_overwrite_by_rectangle(
|
2020-06-27 15:22:31 +02:00
|
|
|
|
struct terminal *term, int row, int col, int height, int width)
|
2020-06-27 13:56:13 +02:00
|
|
|
|
{
|
2020-06-27 14:43:29 +02:00
|
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
|
const int start = (term->grid->offset + row) & (term->grid->num_rows - 1);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
const int end = (start + height - 1) & (term->grid->num_rows - 1);
|
|
|
|
|
|
const bool wraps = end < start;
|
|
|
|
|
|
|
|
|
|
|
|
if (wraps) {
|
|
|
|
|
|
int rows_to_wrap_around = term->grid->num_rows - start;
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(height - rows_to_wrap_around > 0);
|
2020-06-27 15:29:47 +02:00
|
|
|
|
_sixel_overwrite_by_rectangle(term, start, col, rows_to_wrap_around, width);
|
|
|
|
|
|
_sixel_overwrite_by_rectangle(term, 0, col, height - rows_to_wrap_around, width);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
} else
|
2020-06-27 15:29:47 +02:00
|
|
|
|
_sixel_overwrite_by_rectangle(term, start, col, height, width);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
|
/* Row numbers are relative to grid offset */
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width)
|
2020-06-27 13:56:13 +02:00
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(col >= 0);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(_row >= 0);
|
|
|
|
|
|
xassert(_row < term->rows);
|
|
|
|
|
|
xassert(col >= 0);
|
|
|
|
|
|
xassert(col < term->grid->num_cols);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2020-07-07 10:42:59 +02:00
|
|
|
|
if (col + width > term->grid->num_cols)
|
|
|
|
|
|
width = term->grid->num_cols - col;
|
|
|
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
|
const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1);
|
2020-06-29 22:01:02 +02:00
|
|
|
|
const int scrollback_rel_row = rebase_row(term, row);
|
2020-06-28 14:24:30 +02:00
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
const int six_start = six->pos.row;
|
|
|
|
|
|
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
|
/* We should never generate scrollback wrapping sixels */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(six_end >= six_start);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
const int six_scrollback_rel_end = rebase_row(term, six_end);
|
2020-06-28 14:24:30 +02:00
|
|
|
|
|
2020-06-29 22:01:02 +02:00
|
|
|
|
if (six_scrollback_rel_end < scrollback_rel_row) {
|
2020-06-28 14:24:30 +02:00
|
|
|
|
/* All remaining sixels are *before* "our" row */
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
|
if (row >= six_start && row <= six_end) {
|
2020-06-27 13:56:13 +02:00
|
|
|
|
const int col_start = six->pos.col;
|
2020-06-27 15:22:31 +02:00
|
|
|
|
const int col_end = six->pos.col + six->cols - 1;
|
2020-06-27 13:56:13 +02:00
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
|
if ((col <= col_start && col + width - 1 >= col_start) ||
|
|
|
|
|
|
(col <= col_end && col + width - 1 >= col_end) ||
|
|
|
|
|
|
(col >= col_start && col + width - 1 <= col_end))
|
|
|
|
|
|
{
|
2020-07-23 18:15:29 +02:00
|
|
|
|
struct sixel to_be_erased = *six;
|
2020-06-27 13:56:13 +02:00
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
2020-07-23 18:15:29 +02:00
|
|
|
|
|
|
|
|
|
|
sixel_overwrite(term, &to_be_erased, row, col, 1, width);
|
|
|
|
|
|
sixel_erase(term, &to_be_erased);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
|
void
|
2020-06-28 11:01:19 +02:00
|
|
|
|
sixel_overwrite_at_cursor(struct terminal *term, int width)
|
2020-06-27 15:22:31 +02:00
|
|
|
|
{
|
2020-06-27 15:29:47 +02:00
|
|
|
|
sixel_overwrite_by_row(
|
2020-06-28 11:01:19 +02:00
|
|
|
|
term, term->grid->cursor.point.row, term->grid->cursor.point.col, width);
|
2020-06-23 21:07:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 13:09:24 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_cell_size_changed(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct grid *g = term->grid;
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = &term->normal;
|
|
|
|
|
|
tll_foreach(term->normal.sixel_images, it) {
|
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
six->rows = (six->height + term->cell_height - 1) / term->cell_height;
|
|
|
|
|
|
six->cols = (six->width + term->cell_width - 1) / term->cell_width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = &term->alt;
|
|
|
|
|
|
tll_foreach(term->alt.sixel_images, it) {
|
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
six->rows = (six->height + term->cell_height - 1) / term->cell_height;
|
|
|
|
|
|
six->cols = (six->width + term->cell_width - 1) / term->cell_width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = g;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 13:12:44 +02:00
|
|
|
|
void
|
|
|
|
|
|
sixel_reflow(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct grid *g = term->grid;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 2; i++) {
|
|
|
|
|
|
struct grid *grid = i == 0 ? &term->normal : &term->alt;
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = grid;
|
|
|
|
|
|
|
|
|
|
|
|
/* Need the “real” list to be empty from the beginning */
|
|
|
|
|
|
tll(struct sixel) copy = tll_init();
|
|
|
|
|
|
tll_foreach(grid->sixel_images, it)
|
|
|
|
|
|
tll_push_back(copy, it->item);
|
|
|
|
|
|
tll_free(grid->sixel_images);
|
|
|
|
|
|
|
|
|
|
|
|
tll_rforeach(copy, it) {
|
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
int start = six->pos.row;
|
|
|
|
|
|
int end = (start + six->rows - 1) & (grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (end < start) {
|
|
|
|
|
|
/* Crosses scrollback wrap-around */
|
2020-10-04 13:33:56 +02:00
|
|
|
|
/* TODO: split image */
|
2020-10-04 13:12:44 +02:00
|
|
|
|
sixel_destroy(six);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (six->rows > grid->num_rows) {
|
|
|
|
|
|
/* Image too large */
|
|
|
|
|
|
/* TODO: keep bottom part? */
|
|
|
|
|
|
sixel_destroy(six);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 18:33:50 +02:00
|
|
|
|
/* Drop sixels that now cross the current scrollback end
|
|
|
|
|
|
* border. This is similar to a sixel that have been
|
|
|
|
|
|
* scrolled out */
|
|
|
|
|
|
/* TODO: should be possible to optimize this */
|
|
|
|
|
|
bool sixel_destroyed = false;
|
|
|
|
|
|
int last_row = -1;
|
|
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < six->rows; j++) {
|
|
|
|
|
|
int row_no = rebase_row(term, six->pos.row + j);
|
|
|
|
|
|
if (last_row != -1 && last_row >= row_no) {
|
|
|
|
|
|
sixel_destroy(six);
|
|
|
|
|
|
sixel_destroyed = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
last_row = row_no;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sixel_destroyed) {
|
|
|
|
|
|
LOG_WARN("destroyed sixel that now crossed history");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 13:12:44 +02:00
|
|
|
|
/* Sixels that didn’t overlap may now do so, which isn’t
|
|
|
|
|
|
* allowed of course */
|
|
|
|
|
|
_sixel_overwrite_by_rectangle(
|
|
|
|
|
|
term, six->pos.row, six->pos.col, six->rows, six->cols);
|
|
|
|
|
|
sixel_insert(term, it->item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tll_free(copy);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term->grid = g;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
|
void
|
|
|
|
|
|
sixel_unhook(struct terminal *term)
|
|
|
|
|
|
{
|
2020-06-27 14:19:08 +02:00
|
|
|
|
int pixel_row_idx = 0;
|
|
|
|
|
|
int pixel_rows_left = term->sixel.image.height;
|
|
|
|
|
|
const int stride = term->sixel.image.width * sizeof(uint32_t);
|
|
|
|
|
|
|
2020-10-02 20:56:44 +02:00
|
|
|
|
/*
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
* When sixel scrolling is enabled (the default), sixels behave
|
|
|
|
|
|
* pretty much like normal output; the sixel starts at the current
|
|
|
|
|
|
* cursor position and the cursor is moved to a point after the
|
|
|
|
|
|
* sixel.
|
2020-10-02 20:56:44 +02:00
|
|
|
|
*
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
* Furthermore, if the sixel reaches the bottom of the scrolling
|
|
|
|
|
|
* region, the terminal content is scrolled.
|
|
|
|
|
|
*
|
|
|
|
|
|
* When scrolling is disabled, sixels always start at (0,0), the
|
|
|
|
|
|
* cursor is not moved at all, and the terminal content never
|
|
|
|
|
|
* scrolls.
|
2020-10-02 20:56:44 +02:00
|
|
|
|
*/
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
|
|
|
|
|
|
const bool do_scroll = term->sixel.scrolling;
|
|
|
|
|
|
|
|
|
|
|
|
/* Number of rows we're allowed to use.
|
|
|
|
|
|
*
|
|
|
|
|
|
* When scrolling is enabled, we always allow the entire sixel to
|
|
|
|
|
|
* be emitted.
|
|
|
|
|
|
*
|
|
|
|
|
|
* When disabled, only the number of screen rows may be used. */
|
|
|
|
|
|
int rows_avail = do_scroll
|
|
|
|
|
|
? (term->sixel.image.height + term->cell_height - 1) / term->cell_height
|
2021-02-26 14:20:00 +01:00
|
|
|
|
: term->scroll_region.end;
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
|
|
|
|
|
|
/* Initial sixel coordinates */
|
|
|
|
|
|
int start_row = do_scroll ? term->grid->cursor.point.row : 0;
|
|
|
|
|
|
const int start_col = do_scroll ? term->grid->cursor.point.col : 0;
|
2020-10-02 20:56:44 +02:00
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
|
/* We do not allow sixels to cross the scrollback wrap-around, as
|
|
|
|
|
|
* this makes intersection calculations much more complicated */
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
while (pixel_rows_left > 0 && rows_avail > 0) {
|
|
|
|
|
|
const int cur_row = (term->grid->offset + start_row) & (term->grid->num_rows - 1);
|
|
|
|
|
|
const int rows_left_until_wrap_around = term->grid->num_rows - cur_row;
|
|
|
|
|
|
const int usable_rows = min(rows_avail, rows_left_until_wrap_around);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
const int pixel_rows_avail = usable_rows * term->cell_height;
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
|
|
|
|
|
const int width = term->sixel.image.width;
|
|
|
|
|
|
const int height = min(pixel_rows_left, pixel_rows_avail);
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t *img_data;
|
|
|
|
|
|
if (pixel_row_idx == 0)
|
|
|
|
|
|
img_data = term->sixel.image.data;
|
|
|
|
|
|
else {
|
2020-08-08 20:34:30 +01:00
|
|
|
|
img_data = xmalloc(height * stride);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
memcpy(
|
|
|
|
|
|
img_data,
|
|
|
|
|
|
&((uint8_t *)term->sixel.image.data)[pixel_row_idx * stride],
|
|
|
|
|
|
height * stride);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct sixel image = {
|
|
|
|
|
|
.data = img_data,
|
|
|
|
|
|
.width = width,
|
|
|
|
|
|
.height = height,
|
|
|
|
|
|
.rows = (height + term->cell_height - 1) / term->cell_height,
|
|
|
|
|
|
.cols = (width + term->cell_width - 1) / term->cell_width,
|
2020-10-02 20:56:44 +02:00
|
|
|
|
.pos = (struct coord){start_col, cur_row},
|
2020-06-27 14:19:08 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(image.rows < term->grid->num_rows);
|
|
|
|
|
|
xassert(image.pos.row + image.rows - 1 < term->grid->num_rows);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
2020-10-02 20:54:45 +02:00
|
|
|
|
LOG_DBG("generating %dx%d pixman image at %d-%d",
|
|
|
|
|
|
image.width, image.height,
|
|
|
|
|
|
image.pos.row, image.pos.row + image.rows);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
|
|
|
|
|
image.pix = pixman_image_create_bits_no_clear(
|
|
|
|
|
|
PIXMAN_a8r8g8b8,
|
|
|
|
|
|
image.width, image.height,
|
|
|
|
|
|
img_data, stride);
|
|
|
|
|
|
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
pixel_row_idx += height;
|
|
|
|
|
|
pixel_rows_left -= height;
|
|
|
|
|
|
rows_avail -= image.rows;
|
|
|
|
|
|
|
|
|
|
|
|
/* Dirty touched cells, and scroll terminal content if necessary */
|
2020-11-13 16:54:40 +01:00
|
|
|
|
for (size_t i = 0; i < image.rows; i++) {
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
struct row *row = term->grid->rows[cur_row + i];
|
2020-11-13 16:54:40 +01:00
|
|
|
|
row->dirty = true;
|
|
|
|
|
|
|
|
|
|
|
|
for (int col = image.pos.col;
|
|
|
|
|
|
col < min(image.pos.col + image.cols, term->cols);
|
|
|
|
|
|
col++)
|
|
|
|
|
|
{
|
|
|
|
|
|
row->cells[col].attrs.clean = 0;
|
|
|
|
|
|
}
|
2021-02-16 19:11:38 +01:00
|
|
|
|
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
if (do_scroll) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Linefeed, *unless* we're on the very last row of
|
|
|
|
|
|
* the final image (not just this chunk) and private
|
|
|
|
|
|
* mode 8452 (leave cursor at the right of graphics)
|
|
|
|
|
|
* is enabled.
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (term->sixel.cursor_right_of_graphics &&
|
|
|
|
|
|
rows_avail == 0 &&
|
|
|
|
|
|
i >= image.rows - 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
term_cursor_to(
|
|
|
|
|
|
term,
|
|
|
|
|
|
term->grid->cursor.point.row,
|
|
|
|
|
|
min(image.pos.col + image.cols, term->cols - 1));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_linefeed(term);
|
|
|
|
|
|
term_carriage_return(term);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-11-13 16:54:40 +01:00
|
|
|
|
}
|
2021-02-16 19:11:38 +01:00
|
|
|
|
|
2020-10-05 18:32:44 +02:00
|
|
|
|
_sixel_overwrite_by_rectangle(
|
|
|
|
|
|
term, image.pos.row, image.pos.col, image.rows, image.cols);
|
|
|
|
|
|
|
2020-06-28 08:37:25 +02:00
|
|
|
|
sixel_insert(term, image);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
|
|
|
|
if (do_scroll)
|
|
|
|
|
|
start_row = term->grid->cursor.point.row;
|
|
|
|
|
|
else
|
|
|
|
|
|
start_row -= image.rows;
|
2020-06-27 14:19:08 +02:00
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
|
term->sixel.image.data = NULL;
|
|
|
|
|
|
term->sixel.image.width = 0;
|
|
|
|
|
|
term->sixel.image.height = 0;
|
2020-02-21 23:40:35 +01:00
|
|
|
|
term->sixel.max_col = 0;
|
2020-02-22 10:54:52 +01:00
|
|
|
|
term->sixel.pos = (struct coord){0, 0};
|
2020-10-04 13:13:40 +02:00
|
|
|
|
|
2021-02-17 21:58:36 +01:00
|
|
|
|
free(term->sixel.private_palette);
|
|
|
|
|
|
term->sixel.private_palette = NULL;
|
|
|
|
|
|
|
2020-10-04 19:17:33 +02:00
|
|
|
|
LOG_DBG("you now have %zu sixels in current grid",
|
|
|
|
|
|
tll_length(term->grid->sixel_images));
|
|
|
|
|
|
|
2020-10-04 13:13:40 +02:00
|
|
|
|
render_refresh(term);
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
resize(struct terminal *term, int new_width, int new_height)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_DBG("resizing image: %dx%d -> %dx%d",
|
|
|
|
|
|
term->sixel.image.width, term->sixel.image.height,
|
|
|
|
|
|
new_width, new_height);
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t *old_data = term->sixel.image.data;
|
|
|
|
|
|
const int old_width = term->sixel.image.width;
|
|
|
|
|
|
const int old_height = term->sixel.image.height;
|
|
|
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
|
int alloc_new_width = new_width;
|
|
|
|
|
|
int alloc_new_height = (new_height + 6 - 1) / 6 * 6;
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(alloc_new_height >= new_height);
|
|
|
|
|
|
xassert(alloc_new_height - new_height < 6);
|
2020-06-11 18:40:52 +02:00
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
|
uint32_t *new_data = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (new_width == old_width) {
|
|
|
|
|
|
/* Width (and thus stride) is the same, so we can simply
|
|
|
|
|
|
* re-alloc the existing buffer */
|
|
|
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
|
new_data = realloc(old_data, alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
2020-02-22 10:46:35 +01:00
|
|
|
|
if (new_data == NULL) {
|
|
|
|
|
|
LOG_ERRNO("failed to reallocate sixel image buffer");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_height > old_height);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
/* Width (and thus stride) change - need to allocate a new buffer */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_width > old_width);
|
2020-08-08 20:34:30 +01:00
|
|
|
|
new_data = xmalloc(alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
2020-02-22 10:46:35 +01:00
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
|
/* Copy old rows, and initialize new columns to background color */
|
2020-11-23 19:22:40 +01:00
|
|
|
|
for (int r = 0; r < min(old_height, new_height); r++) {
|
2020-02-22 10:46:35 +01:00
|
|
|
|
memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t));
|
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
|
for (int c = old_width; c < new_width; c++)
|
2020-06-10 18:52:53 +02:00
|
|
|
|
new_data[r * new_width + c] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
}
|
2020-02-22 10:46:35 +01:00
|
|
|
|
free(old_data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
|
/* Initialize new rows to background color */
|
|
|
|
|
|
for (int r = old_height; r < new_height; r++) {
|
|
|
|
|
|
for (int c = 0; c < new_width; c++)
|
2020-06-10 18:52:53 +02:00
|
|
|
|
new_data[r * new_width + c] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(new_data != NULL);
|
2020-02-22 10:46:35 +01:00
|
|
|
|
term->sixel.image.data = new_data;
|
|
|
|
|
|
term->sixel.image.width = new_width;
|
|
|
|
|
|
term->sixel.image.height = new_height;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
|
static void
|
|
|
|
|
|
sixel_add(struct terminal *term, uint32_t color, uint8_t sixel)
|
|
|
|
|
|
{
|
2020-02-22 10:46:35 +01:00
|
|
|
|
//LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color);
|
|
|
|
|
|
|
2020-11-23 20:10:55 +01:00
|
|
|
|
if (term->sixel.pos.col >= term->sixel.max_width ||
|
2021-03-06 19:41:28 +01:00
|
|
|
|
term->sixel.pos.row + 5 >= term->sixel.max_height)
|
2020-02-22 21:03:24 +01:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 10:54:52 +01:00
|
|
|
|
if (term->sixel.pos.col >= term->sixel.image.width ||
|
2021-03-06 19:41:28 +01:00
|
|
|
|
term->sixel.pos.row + 5 >= (term->sixel.image.height + 6 - 1) / 6 * 6)
|
2020-02-22 10:46:35 +01:00
|
|
|
|
{
|
2020-02-22 21:20:22 +01:00
|
|
|
|
int width = max(
|
|
|
|
|
|
term->sixel.image.width,
|
|
|
|
|
|
max(term->sixel.max_col, term->sixel.pos.col + 1));
|
|
|
|
|
|
|
|
|
|
|
|
int height = max(
|
2021-03-06 19:41:28 +01:00
|
|
|
|
term->sixel.image.height, term->sixel.pos.row + 1);
|
2020-02-22 21:20:22 +01:00
|
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
|
if (!resize(term, width, height))
|
|
|
|
|
|
return;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-06 19:44:26 +01:00
|
|
|
|
size_t ofs = term->sixel.pos.row * term->sixel.image.width;
|
|
|
|
|
|
ofs += term->sixel.pos.col;
|
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
|
for (int i = 0; i < 6; i++, sixel >>= 1) {
|
2021-03-06 19:44:26 +01:00
|
|
|
|
if (sixel & 1)
|
2021-03-06 19:49:04 +01:00
|
|
|
|
term->sixel.image.data[ofs] = color;
|
2021-03-06 19:44:26 +01:00
|
|
|
|
ofs += term->sixel.image.width;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(sixel == 0);
|
2020-02-22 10:54:52 +01:00
|
|
|
|
term->sixel.pos.col++;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
|
decsixel(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
|
case '"':
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECGRA;
|
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
|
|
term->sixel.state = SIXEL_DECGRI;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '#':
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECGCI;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.color_idx = 0;
|
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '$':
|
2020-02-22 10:54:52 +01:00
|
|
|
|
if (term->sixel.pos.col > term->sixel.max_col)
|
|
|
|
|
|
term->sixel.max_col = term->sixel.pos.col;
|
|
|
|
|
|
term->sixel.pos.col = 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '-':
|
2020-02-22 10:54:52 +01:00
|
|
|
|
if (term->sixel.pos.col > term->sixel.max_col)
|
|
|
|
|
|
term->sixel.max_col = term->sixel.pos.col;
|
2021-03-06 19:41:28 +01:00
|
|
|
|
term->sixel.pos.row += 6;
|
2020-02-22 10:54:52 +01:00
|
|
|
|
term->sixel.pos.col = 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-08-23 09:37:51 +02:00
|
|
|
|
case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
|
|
|
|
case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
|
|
|
|
|
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
|
|
|
|
|
|
case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
|
|
|
|
|
case '[': case '\\': case ']': case '^': case '_': case '`': case 'a':
|
|
|
|
|
|
case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
|
|
|
|
|
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
|
|
|
|
case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v':
|
|
|
|
|
|
case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}':
|
|
|
|
|
|
case '~':
|
2021-03-06 19:49:04 +01:00
|
|
|
|
sixel_add(
|
|
|
|
|
|
term,
|
|
|
|
|
|
color_with_alpha(
|
|
|
|
|
|
term, term->sixel.palette[term->sixel.color_idx]),
|
|
|
|
|
|
c - 63);
|
2020-02-21 21:53:23 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
case ' ':
|
|
|
|
|
|
case '\n':
|
|
|
|
|
|
case '\r':
|
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
default:
|
2020-06-09 17:34:04 +02:00
|
|
|
|
LOG_WARN("invalid sixel character: '%c' at idx=%zu", c, count);
|
2020-02-21 21:53:23 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
|
decgra(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
|
switch (c) {
|
2020-08-23 09:37:51 +02:00
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
2020-02-22 10:46:35 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
case ';':
|
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
default: {
|
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
int nparams = term->sixel.param_idx;
|
|
|
|
|
|
unsigned pan = nparams > 0 ? term->sixel.params[0] : 0;
|
|
|
|
|
|
unsigned pad = nparams > 1 ? term->sixel.params[1] : 0;
|
|
|
|
|
|
unsigned ph = nparams > 2 ? term->sixel.params[2] : 0;
|
|
|
|
|
|
unsigned pv = nparams > 3 ? term->sixel.params[3] : 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
pan = pan > 0 ? pan : 1;
|
|
|
|
|
|
pad = pad > 0 ? pad : 1;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u",
|
|
|
|
|
|
pan, pad, pan / pad, ph, pv);
|
2020-02-22 10:49:00 +01:00
|
|
|
|
|
2020-02-22 21:03:24 +01:00
|
|
|
|
if (ph >= term->sixel.image.height && pv >= term->sixel.image.width &&
|
2020-11-23 20:10:55 +01:00
|
|
|
|
ph <= term->sixel.max_height && pv <= term->sixel.max_width)
|
2020-02-22 21:03:24 +01:00
|
|
|
|
{
|
2021-03-06 15:03:47 +01:00
|
|
|
|
resize(term, ph, pv);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
|
sixel_put(term, c);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
|
decgri(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
|
switch (c) {
|
2020-08-23 09:37:51 +02:00
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2021-03-06 19:48:37 +01:00
|
|
|
|
case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
|
|
|
|
case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
|
|
|
|
|
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
|
|
|
|
|
|
case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
|
|
|
|
|
case '[': case '\\': case ']': case '^': case '_': case '`': case 'a':
|
|
|
|
|
|
case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
|
|
|
|
|
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
|
|
|
|
case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v':
|
|
|
|
|
|
case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}':
|
|
|
|
|
|
case '~': {
|
2020-02-22 11:30:30 +01:00
|
|
|
|
//LOG_DBG("repeating '%c' %u times", c, term->sixel.param);
|
2021-03-06 19:49:04 +01:00
|
|
|
|
uint32_t color = color_with_alpha(
|
|
|
|
|
|
term, term->sixel.palette[term->sixel.color_idx]);
|
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
for (unsigned i = 0; i < term->sixel.param; i++)
|
2021-03-06 19:49:04 +01:00
|
|
|
|
sixel_add(term, color, c - 63);
|
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
2021-03-06 19:48:37 +01:00
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
|
decgci(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
|
switch (c) {
|
2020-08-23 09:37:51 +02:00
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
case ';':
|
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
term->sixel.param = 0;
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
default: {
|
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
|
|
|
|
|
|
|
int nparams = term->sixel.param_idx;
|
|
|
|
|
|
|
2020-02-22 14:02:00 +01:00
|
|
|
|
if (nparams > 0)
|
|
|
|
|
|
term->sixel.color_idx = min(term->sixel.params[0], term->sixel.palette_size - 1);
|
2020-02-22 11:30:30 +01:00
|
|
|
|
|
|
|
|
|
|
if (nparams > 4) {
|
|
|
|
|
|
unsigned format = term->sixel.params[1];
|
2020-11-15 19:45:33 +01:00
|
|
|
|
int c1 = term->sixel.params[2];
|
|
|
|
|
|
int c2 = term->sixel.params[3];
|
|
|
|
|
|
int c3 = term->sixel.params[4];
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
switch (format) {
|
|
|
|
|
|
case 1: { /* HLS */
|
2020-11-15 19:45:33 +01:00
|
|
|
|
int hue = min(c1, 360);
|
|
|
|
|
|
int lum = min(c2, 100);
|
|
|
|
|
|
int sat = min(c3, 100);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Sixel’s HLS use the following primary color hues:
|
|
|
|
|
|
* blue: 0°
|
|
|
|
|
|
* red: 120°
|
|
|
|
|
|
* green: 240°
|
|
|
|
|
|
*
|
|
|
|
|
|
* While “standard” HSL uses:
|
|
|
|
|
|
* red: 0°
|
|
|
|
|
|
* green: 120°
|
|
|
|
|
|
* blue: 240°
|
|
|
|
|
|
*/
|
|
|
|
|
|
hue = (hue + 240) % 360;
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t rgb = hsl_to_rgb(hue, sat, lum);
|
|
|
|
|
|
|
2020-02-22 21:18:55 +01:00
|
|
|
|
LOG_DBG("setting palette #%d = HLS %hhu/%hhu/%hhu (0x%06x)",
|
2020-11-15 19:45:33 +01:00
|
|
|
|
term->sixel.color_idx, hue, lum, sat, rgb);
|
|
|
|
|
|
|
2020-02-22 21:18:55 +01:00
|
|
|
|
term->sixel.palette[term->sixel.color_idx] = rgb;
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 2: { /* RGB */
|
2020-11-15 19:45:33 +01:00
|
|
|
|
uint8_t r = 255 * min(c1, 100) / 100;
|
|
|
|
|
|
uint8_t g = 255 * min(c2, 100) / 100;
|
|
|
|
|
|
uint8_t b = 255 * min(c3, 100) / 100;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
|
|
|
|
|
LOG_DBG("setting palette #%d = RGB %hhu/%hhu/%hhu",
|
|
|
|
|
|
term->sixel.color_idx, r, g, b);
|
|
|
|
|
|
|
|
|
|
|
|
term->sixel.palette[term->sixel.color_idx] = r << 16 | g << 8 | b;
|
2020-02-22 11:30:30 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
|
sixel_put(term, c);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_put(struct terminal *term, uint8_t c)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (term->sixel.state) {
|
2020-02-22 11:30:30 +01:00
|
|
|
|
case SIXEL_DECSIXEL: decsixel(term, c); break;
|
|
|
|
|
|
case SIXEL_DECGRA: decgra(term, c); break;
|
|
|
|
|
|
case SIXEL_DECGRI: decgri(term, c); break;
|
|
|
|
|
|
case SIXEL_DECGCI: decgci(term, c); break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
2020-02-22 11:30:30 +01:00
|
|
|
|
|
|
|
|
|
|
count++;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
}
|
2020-02-22 14:02:00 +01:00
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_colors_report_current(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
char reply[24];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
LOG_DBG("query response for current color count: %u", term->sixel.palette_size);
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_colors_reset(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_DBG("sixel palette size reset to %u", SIXEL_MAX_COLORS);
|
2020-06-10 18:36:54 +02:00
|
|
|
|
|
|
|
|
|
|
free(term->sixel.palette);
|
|
|
|
|
|
term->sixel.palette = NULL;
|
|
|
|
|
|
|
2020-02-24 18:42:04 +01:00
|
|
|
|
term->sixel.palette_size = SIXEL_MAX_COLORS;
|
|
|
|
|
|
sixel_colors_report_current(term);
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_colors_set(struct terminal *term, unsigned count)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned new_palette_size = min(max(2, count), SIXEL_MAX_COLORS);
|
|
|
|
|
|
LOG_DBG("sixel palette size set to %u", new_palette_size);
|
2020-02-24 18:42:04 +01:00
|
|
|
|
|
2020-06-10 18:36:54 +02:00
|
|
|
|
free(term->sixel.palette);
|
|
|
|
|
|
term->sixel.palette = NULL;
|
|
|
|
|
|
|
2020-02-24 18:42:04 +01:00
|
|
|
|
term->sixel.palette_size = new_palette_size;
|
|
|
|
|
|
sixel_colors_report_current(term);
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_colors_report_max(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
char reply[24];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
LOG_DBG("query response for max color count: %u", SIXEL_MAX_COLORS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_geometry_report_current(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS",
|
2021-01-14 14:41:34 +01:00
|
|
|
|
min(term->cols * term->cell_width, term->sixel.max_width),
|
|
|
|
|
|
min(term->rows * term->cell_height, term->sixel.max_height));
|
2021-01-14 21:30:06 +00:00
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
|
|
|
|
|
|
LOG_DBG("query response for current sixel geometry: %ux%u",
|
2020-11-23 20:10:55 +01:00
|
|
|
|
term->sixel.max_width, term->sixel.max_height);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_geometry_reset(struct terminal *term)
|
|
|
|
|
|
{
|
2020-11-23 20:10:55 +01:00
|
|
|
|
LOG_DBG("sixel geometry reset to %ux%u", SIXEL_MAX_WIDTH, SIXEL_MAX_HEIGHT);
|
|
|
|
|
|
term->sixel.max_width = SIXEL_MAX_WIDTH;
|
|
|
|
|
|
term->sixel.max_height = SIXEL_MAX_HEIGHT;
|
2020-02-24 18:42:04 +01:00
|
|
|
|
sixel_geometry_report_current(term);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_geometry_set(struct terminal *term, unsigned width, unsigned height)
|
|
|
|
|
|
{
|
2020-02-24 18:42:04 +01:00
|
|
|
|
LOG_DBG("sixel geometry set to %ux%u", width, height);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
term->sixel.max_width = width;
|
|
|
|
|
|
term->sixel.max_height = height;
|
2020-02-24 18:42:04 +01:00
|
|
|
|
sixel_geometry_report_current(term);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
sixel_geometry_report_max(struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned max_width = term->cols * term->cell_width;
|
|
|
|
|
|
unsigned max_height = term->rows * term->cell_height;
|
|
|
|
|
|
|
|
|
|
|
|
char reply[64];
|
2021-01-14 21:30:06 +00:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
2020-02-22 21:03:24 +01:00
|
|
|
|
|
|
|
|
|
|
LOG_DBG("query response for max sixel geometry: %ux%u",
|
|
|
|
|
|
max_width, max_height);
|
2020-02-22 14:02:00 +01:00
|
|
|
|
}
|