mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-17 22:05:22 -05:00
Merge branch 'sixel-graphics'
This commit is contained in:
commit
3a24777e45
13 changed files with 849 additions and 6 deletions
|
|
@ -37,6 +37,10 @@
|
|||
* Scrollback search
|
||||
* Color emoji support
|
||||
* "Server" mode (one master process, many windows)
|
||||
* [Sixel image support](https://en.wikipedia.org/wiki/Sixel)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Non-features
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ cmd_scrollback_up(struct terminal *term, int rows)
|
|||
new_view = end + 1;
|
||||
}
|
||||
|
||||
while (term->grid->rows[new_view] == NULL)
|
||||
new_view = (new_view + 1) % term->grid->num_rows;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
for (int r = 0; r < term->rows; r++)
|
||||
assert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL);
|
||||
|
|
|
|||
29
csi.c
29
csi.c
|
|
@ -15,6 +15,7 @@
|
|||
#include "grid.h"
|
||||
#include "vt.h"
|
||||
#include "selection.h"
|
||||
#include "sixel.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
|
|
@ -372,7 +373,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
* - 28 Rectangular editing.
|
||||
* - 29 ANSI text locator (i.e., DEC Locator mode).
|
||||
*/
|
||||
const char *reply = "\033[?62;6;15;17;22c";
|
||||
const char *reply = "\033[?62;4;6;15;17;22c";
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
break;
|
||||
}
|
||||
|
|
@ -1227,6 +1228,32 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
}
|
||||
break;
|
||||
|
||||
case 'S': {
|
||||
unsigned target = vt_param_get(term, 0, 0);
|
||||
unsigned operation = vt_param_get(term, 1, 0);
|
||||
|
||||
switch (target) {
|
||||
case 1:
|
||||
switch (operation) {
|
||||
case 1: sixel_colors_report_current(term); break;
|
||||
case 2: sixel_colors_reset(term); break;
|
||||
case 3: sixel_colors_set(term, vt_param_get(term, 2, 0)); break;
|
||||
case 4: sixel_colors_report_max(term);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
switch (operation) {
|
||||
case 1: sixel_geometry_report_current(term); break;
|
||||
case 2: sixel_geometry_reset(term); break;
|
||||
case 3: sixel_geometry_set(term, vt_param_get(term, 2, 0), vt_param_get(term, 3, 0)); break;
|
||||
case 4: sixel_geometry_report_max(term);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNHANDLED();
|
||||
break;
|
||||
|
|
|
|||
23
dcs.c
23
dcs.c
|
|
@ -3,6 +3,7 @@
|
|||
#define LOG_MODULE "dcs"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "sixel.h"
|
||||
#include "vt.h"
|
||||
|
||||
static void
|
||||
|
|
@ -35,9 +36,20 @@ dcs_hook(struct terminal *term, uint8_t final)
|
|||
|
||||
assert(term->vt.dcs.data == NULL);
|
||||
assert(term->vt.dcs.size == 0);
|
||||
assert(term->vt.dcs.put_handler == NULL);
|
||||
assert(term->vt.dcs.unhook_handler == NULL);
|
||||
|
||||
switch (term->vt.private[0]) {
|
||||
case 0:
|
||||
switch (final) {
|
||||
case 'q':
|
||||
sixel_init(term);
|
||||
term->vt.dcs.put_handler = &sixel_put;
|
||||
term->vt.dcs.unhook_handler = &sixel_unhook;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
switch (final) {
|
||||
case 's':
|
||||
|
|
@ -75,9 +87,13 @@ void
|
|||
dcs_put(struct terminal *term, uint8_t c)
|
||||
{
|
||||
LOG_DBG("PUT: %c", c);
|
||||
if (!ensure_size(term, term->vt.dcs.idx + 1))
|
||||
return;
|
||||
term->vt.dcs.data[term->vt.dcs.idx++] = c;
|
||||
if (term->vt.dcs.put_handler != NULL)
|
||||
term->vt.dcs.put_handler(term, c);
|
||||
else {
|
||||
if (!ensure_size(term, term->vt.dcs.idx + 1))
|
||||
return;
|
||||
term->vt.dcs.data[term->vt.dcs.idx++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -87,6 +103,7 @@ dcs_unhook(struct terminal *term)
|
|||
term->vt.dcs.unhook_handler(term);
|
||||
|
||||
term->vt.dcs.unhook_handler = NULL;
|
||||
term->vt.dcs.put_handler = NULL;
|
||||
|
||||
free(term->vt.dcs.data);
|
||||
term->vt.dcs.data = NULL;
|
||||
|
|
|
|||
BIN
doc/sixel-wow.png
Normal file
BIN
doc/sixel-wow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
|
|
@ -117,6 +117,8 @@ executable(
|
|||
'selection.c', 'selection.h',
|
||||
'server.c', 'server.h',
|
||||
'shm.c', 'shm.h',
|
||||
'sixel.c', 'sixel.h',
|
||||
'sixel-hls.c', 'sixel-hls.h',
|
||||
'slave.c', 'slave.h',
|
||||
'terminal.c', 'terminal.h',
|
||||
'tokenize.c', 'tokenize.h',
|
||||
|
|
|
|||
75
render.c
75
render.c
|
|
@ -519,6 +519,79 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_sixel(struct terminal *term, pixman_image_t *pix,
|
||||
const struct sixel *sixel)
|
||||
{
|
||||
int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1);
|
||||
int first_visible_row = -1;
|
||||
|
||||
for (size_t i = sixel->pos.row; i < sixel->pos.row + sixel->rows; i++) {
|
||||
int row = i & (term->grid->num_rows - 1);
|
||||
|
||||
if (view_end >= term->grid->view) {
|
||||
/* Not wrapped */
|
||||
if (row >= term->grid->view && row <= view_end) {
|
||||
first_visible_row = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Wrapped */
|
||||
if (row >= term->grid->view || row <= view_end) {
|
||||
first_visible_row = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_visible_row < 0)
|
||||
return;
|
||||
|
||||
/* First visible (0 based) row of the image */
|
||||
const int first_img_row = first_visible_row - sixel->pos.row;
|
||||
|
||||
/* Map first visible line to current grid view */
|
||||
const int row = first_visible_row & (term->grid->num_rows - 1);
|
||||
const int view_aligned =
|
||||
(row - term->grid->view + term->grid->num_rows) & (term->grid->num_rows - 1);
|
||||
|
||||
/* Translate row/column to x/y pixel values */
|
||||
const int x = term->x_margin + sixel->pos.col * term->cell_width;
|
||||
const int y = max(
|
||||
term->y_margin, term->y_margin + view_aligned * term->cell_height);
|
||||
|
||||
/* Width/height, in pixels - and don't touch the window margins */
|
||||
const int width = min(sixel->width, term->width - x - term->x_margin);
|
||||
const int height = min(
|
||||
sixel->height - first_img_row * term->cell_height,
|
||||
term->height - y - term->y_margin);
|
||||
|
||||
/* Verify we're not stepping outside the grid */
|
||||
assert(x >= term->x_margin);
|
||||
assert(y >= term->y_margin);
|
||||
assert(x + width <= term->width - term->x_margin);
|
||||
assert(y + height <= term->height - term->y_margin);
|
||||
|
||||
pixman_image_composite(
|
||||
PIXMAN_OP_SRC,
|
||||
sixel->pix,
|
||||
NULL,
|
||||
pix,
|
||||
0, first_img_row * term->cell_height,
|
||||
0, 0,
|
||||
x, y,
|
||||
width, height);
|
||||
|
||||
wl_surface_damage_buffer(term->window->surface, x, y, width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
render_sixel_images(struct terminal *term, pixman_image_t *pix)
|
||||
{
|
||||
tll_foreach(term->sixel_images, it)
|
||||
render_sixel(term, pix, &it->item);
|
||||
}
|
||||
|
||||
static void
|
||||
render_row(struct terminal *term, pixman_image_t *pix, struct row *row, int row_no)
|
||||
{
|
||||
|
|
@ -816,6 +889,8 @@ grid_render(struct terminal *term)
|
|||
cols_updated * term->cell_width, term->cell_height);
|
||||
}
|
||||
|
||||
render_sixel_images(term, pix);
|
||||
|
||||
if (term->flash.active) {
|
||||
/* Note: alpha is pre-computed in each color component */
|
||||
/* TODO: dim while searching */
|
||||
|
|
|
|||
117
sixel-hls.c
Normal file
117
sixel-hls.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// sixel.c (part of mintty)
|
||||
// this function is derived from a part of graphics.c
|
||||
// in Xterm pl#310 originally written by Ross Combs.
|
||||
//
|
||||
// Copyright 2013,2014 by Ross Combs
|
||||
//
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name(s) of the above copyright
|
||||
// holders shall not be used in advertising or otherwise to promote the
|
||||
// sale, use or other dealings in this Software without prior written
|
||||
// authorization.
|
||||
|
||||
#include "sixel-hls.h"
|
||||
|
||||
#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) + (b))
|
||||
|
||||
int
|
||||
hls_to_rgb(int hue, int lum, int sat)
|
||||
{
|
||||
double hs = (hue + 240) % 360;
|
||||
double hv = hs / 360.0;
|
||||
double lv = lum / 100.0;
|
||||
double sv = sat / 100.0;
|
||||
double c, x, m, c2;
|
||||
double r1, g1, b1;
|
||||
int r, g, b;
|
||||
int hpi;
|
||||
|
||||
if (sat == 0) {
|
||||
r = g = b = lum * 255 / 100;
|
||||
return SIXEL_RGB(r, g, b);
|
||||
}
|
||||
|
||||
if ((c2 = ((2.0 * lv) - 1.0)) < 0.0) {
|
||||
c2 = -c2;
|
||||
}
|
||||
c = (1.0 - c2) * sv;
|
||||
hpi = (int) (hv * 6.0);
|
||||
x = (hpi & 1) ? c : 0.0;
|
||||
m = lv - 0.5 * c;
|
||||
|
||||
switch (hpi) {
|
||||
case 0:
|
||||
r1 = c;
|
||||
g1 = x;
|
||||
b1 = 0.0;
|
||||
break;
|
||||
case 1:
|
||||
r1 = x;
|
||||
g1 = c;
|
||||
b1 = 0.0;
|
||||
break;
|
||||
case 2:
|
||||
r1 = 0.0;
|
||||
g1 = c;
|
||||
b1 = x;
|
||||
break;
|
||||
case 3:
|
||||
r1 = 0.0;
|
||||
g1 = x;
|
||||
b1 = c;
|
||||
break;
|
||||
case 4:
|
||||
r1 = x;
|
||||
g1 = 0.0;
|
||||
b1 = c;
|
||||
break;
|
||||
case 5:
|
||||
r1 = c;
|
||||
g1 = 0.0;
|
||||
b1 = x;
|
||||
break;
|
||||
default:
|
||||
return SIXEL_RGB(255, 255, 255);
|
||||
}
|
||||
|
||||
r = (int) ((r1 + m) * 100.0 + 0.5);
|
||||
g = (int) ((g1 + m) * 100.0 + 0.5);
|
||||
b = (int) ((b1 + m) * 100.0 + 0.5);
|
||||
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
} else if (r > 100) {
|
||||
r = 100;
|
||||
}
|
||||
if (g < 0) {
|
||||
g = 0;
|
||||
} else if (g > 100) {
|
||||
g = 100;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = 0;
|
||||
} else if (b > 100) {
|
||||
b = 100;
|
||||
}
|
||||
return SIXEL_RGB(r * 255 / 100, g * 255 / 100, b * 255 / 100);
|
||||
}
|
||||
9
sixel-hls.h
Normal file
9
sixel-hls.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* Primary color hues:
|
||||
* blue: 0 degrees
|
||||
* red: 120 degrees
|
||||
* green: 240 degrees
|
||||
*/
|
||||
int hls_to_rgb(int hue, int lum, int sat);
|
||||
482
sixel.c
Normal file
482
sixel.c
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
#include "sixel.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define LOG_MODULE "sixel"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "render.h"
|
||||
#include "sixel-hls.h"
|
||||
|
||||
#define ALEN(v) (sizeof(v) / sizeof(v[0]))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
|
||||
static size_t count;
|
||||
|
||||
void
|
||||
sixel_init(struct terminal *term)
|
||||
{
|
||||
assert(term->sixel.palette == NULL);
|
||||
assert(term->sixel.image.data == NULL);
|
||||
assert(term->sixel.palette_size <= SIXEL_MAX_COLORS);
|
||||
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
term->sixel.pos = (struct coord){0, 0};
|
||||
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));
|
||||
term->sixel.palette = calloc(term->sixel.palette_size, sizeof(term->sixel.palette[0]));
|
||||
term->sixel.image.data = malloc(1 * 6 * sizeof(term->sixel.image.data[0]));
|
||||
term->sixel.image.width = 1;
|
||||
term->sixel.image.height = 6;
|
||||
|
||||
for (size_t i = 0; i < 1 * 6; i++)
|
||||
term->sixel.image.data[i] = term->colors.alpha / 256 << 24 | term->colors.bg;
|
||||
|
||||
count = 0;
|
||||
|
||||
/* TODO: default palette */
|
||||
}
|
||||
|
||||
void
|
||||
sixel_destroy(struct sixel *sixel)
|
||||
{
|
||||
pixman_image_unref(sixel->pix);
|
||||
free(sixel->data);
|
||||
|
||||
sixel->pix = NULL;
|
||||
sixel->data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
sixel_purge_at_cursor(struct terminal *term)
|
||||
{
|
||||
const int row = term->grid->offset + term->cursor.point.row;
|
||||
|
||||
tll_foreach(term->sixel_images, it) {
|
||||
const int start = it->item.pos.row;
|
||||
const int end = start + it->item.rows;
|
||||
|
||||
if (row >= start && row < end) {
|
||||
sixel_destroy(&it->item);
|
||||
tll_remove(term->sixel_images, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sixel_unhook(struct terminal *term)
|
||||
{
|
||||
free(term->sixel.palette);
|
||||
term->sixel.palette = NULL;
|
||||
|
||||
sixel_purge_at_cursor(term);
|
||||
|
||||
struct sixel image = {
|
||||
.data = term->sixel.image.data,
|
||||
.width = term->sixel.image.width,
|
||||
.height = term->sixel.image.height,
|
||||
.rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height,
|
||||
.pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row},
|
||||
};
|
||||
|
||||
LOG_DBG("generating %dx%d pixman image", image.width, image.height);
|
||||
|
||||
image.pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8,
|
||||
image.width, image.height,
|
||||
term->sixel.image.data,
|
||||
term->sixel.image.width * sizeof(uint32_t));
|
||||
|
||||
tll_push_back(term->sixel_images, image);
|
||||
|
||||
term->sixel.image.data = NULL;
|
||||
term->sixel.image.width = 0;
|
||||
term->sixel.image.height = 0;
|
||||
term->sixel.max_col = 0;
|
||||
term->sixel.pos = (struct coord){0, 0};
|
||||
|
||||
for (size_t i = 0; i < image.rows; i++)
|
||||
term_linefeed(term);
|
||||
term_formfeed(term);
|
||||
render_refresh(term);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
max_width(const struct terminal *term)
|
||||
{
|
||||
/* foot extension - treat 0 to mean current terminal size */
|
||||
return term->sixel.max_width == 0
|
||||
? term->cols * term->cell_width
|
||||
: term->sixel.max_width;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
max_height(const struct terminal *term)
|
||||
{
|
||||
/* foot extension - treat 0 to mean current terminal size */
|
||||
return term->sixel.max_height == 0
|
||||
? term->rows * term->cell_height
|
||||
: term->sixel.max_height;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(new_width >= old_width);
|
||||
assert(new_height >= old_height);
|
||||
|
||||
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 */
|
||||
|
||||
new_data = realloc(old_data, new_width * new_height * sizeof(uint32_t));
|
||||
if (new_data == NULL) {
|
||||
LOG_ERRNO("failed to reallocate sixel image buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(new_height > old_height);
|
||||
|
||||
} else {
|
||||
/* Width (and thus stride) change - need to allocate a new buffer */
|
||||
assert(new_width > old_width);
|
||||
new_data = malloc(new_width * new_height * sizeof(uint32_t));
|
||||
|
||||
/* Copy old rows, and initialize new columns to background color */
|
||||
for (int r = 0; r < old_height; r++) {
|
||||
memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t));
|
||||
|
||||
for (int c = old_width; c < new_width; c++)
|
||||
new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->colors.bg;
|
||||
}
|
||||
free(old_data);
|
||||
}
|
||||
|
||||
/* Initialize new rows to background color */
|
||||
for (int r = old_height; r < new_height; r++) {
|
||||
for (int c = 0; c < new_width; c++)
|
||||
new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->colors.bg;
|
||||
}
|
||||
|
||||
assert(new_data != NULL);
|
||||
term->sixel.image.data = new_data;
|
||||
term->sixel.image.width = new_width;
|
||||
term->sixel.image.height = new_height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_add(struct terminal *term, uint32_t color, uint8_t sixel)
|
||||
{
|
||||
//LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color);
|
||||
|
||||
if (term->sixel.pos.col >= max_width(term) ||
|
||||
term->sixel.pos.row * 6 + 5 >= max_height(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (term->sixel.pos.col >= term->sixel.image.width ||
|
||||
term->sixel.pos.row * 6 + 5 >= term->sixel.image.height)
|
||||
{
|
||||
int width = max(
|
||||
term->sixel.image.width,
|
||||
max(term->sixel.max_col, term->sixel.pos.col + 1));
|
||||
|
||||
int height = max(
|
||||
term->sixel.image.height,
|
||||
(term->sixel.pos.row + 1) * 6);
|
||||
|
||||
resize(term, width, height);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++, sixel >>= 1) {
|
||||
if (sixel & 1) {
|
||||
size_t pixel_row = term->sixel.pos.row * 6 + i;
|
||||
size_t stride = term->sixel.image.width;
|
||||
size_t idx = pixel_row * stride + term->sixel.pos.col;
|
||||
term->sixel.image.data[idx] = term->colors.alpha / 256 << 24 | color;
|
||||
}
|
||||
}
|
||||
|
||||
assert(sixel == 0);
|
||||
term->sixel.pos.col++;
|
||||
}
|
||||
|
||||
static void
|
||||
decsixel(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '"':
|
||||
term->sixel.state = SIXEL_DECGRA;
|
||||
term->sixel.param = 0;
|
||||
term->sixel.param_idx = 0;
|
||||
break;
|
||||
|
||||
case '!':
|
||||
term->sixel.state = SIXEL_DECGRI;
|
||||
term->sixel.param = 0;
|
||||
term->sixel.param_idx = 0;
|
||||
break;
|
||||
|
||||
case '#':
|
||||
term->sixel.state = SIXEL_DECGCI;
|
||||
term->sixel.color_idx = 0;
|
||||
term->sixel.param = 0;
|
||||
term->sixel.param_idx = 0;
|
||||
break;
|
||||
|
||||
case '$':
|
||||
if (term->sixel.pos.col > term->sixel.max_col)
|
||||
term->sixel.max_col = term->sixel.pos.col;
|
||||
term->sixel.pos.col = 0;
|
||||
break;
|
||||
|
||||
case '-':
|
||||
if (term->sixel.pos.col > term->sixel.max_col)
|
||||
term->sixel.max_col = term->sixel.pos.col;
|
||||
term->sixel.pos.row++;
|
||||
term->sixel.pos.col = 0;
|
||||
break;
|
||||
|
||||
case '?'...'~':
|
||||
sixel_add(term, term->sixel.palette[term->sixel.color_idx], c - 63);
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_WARN("invalid sixel charactwer: '%c' at idx=%zu", c, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decgra(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0'...'9':
|
||||
term->sixel.param *= 10;
|
||||
term->sixel.param += c - '0';
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
pan = pan > 0 ? pan : 1;
|
||||
pad = pad > 0 ? pad : 1;
|
||||
|
||||
LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u",
|
||||
pan, pad, pan / pad, ph, pv);
|
||||
|
||||
if (ph >= term->sixel.image.height && pv >= term->sixel.image.width &&
|
||||
ph <= max_height(term) && pv <= max_width(term))
|
||||
{
|
||||
resize(term, ph, pv);
|
||||
}
|
||||
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
sixel_put(term, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decgri(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0'...'9':
|
||||
term->sixel.param *= 10;
|
||||
term->sixel.param += c - '0';
|
||||
break;
|
||||
|
||||
default:
|
||||
//LOG_DBG("repeating '%c' %u times", c, term->sixel.param);
|
||||
for (unsigned i = 0; i < term->sixel.param; i++)
|
||||
decsixel(term, c);
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decgci(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0'...'9':
|
||||
term->sixel.param *= 10;
|
||||
term->sixel.param += c - '0';
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (nparams > 0)
|
||||
term->sixel.color_idx = min(term->sixel.params[0], term->sixel.palette_size - 1);
|
||||
|
||||
if (nparams > 4) {
|
||||
unsigned format = term->sixel.params[1];
|
||||
unsigned c1 = term->sixel.params[2];
|
||||
unsigned c2 = term->sixel.params[3];
|
||||
unsigned c3 = term->sixel.params[4];
|
||||
|
||||
switch (format) {
|
||||
case 1: { /* HLS */
|
||||
uint32_t rgb = hls_to_rgb(c1, c2, c3);
|
||||
LOG_DBG("setting palette #%d = HLS %hhu/%hhu/%hhu (0x%06x)",
|
||||
term->sixel.color_idx, c1, c2, c3, rgb);
|
||||
term->sixel.palette[term->sixel.color_idx] = rgb;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: { /* RGB */
|
||||
uint8_t r = 255 * c1 / 100;
|
||||
uint8_t g = 255 * c2 / 100;
|
||||
uint8_t b = 255 * c3 / 100;
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
sixel_put(term, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sixel_put(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (term->sixel.state) {
|
||||
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;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
void
|
||||
sixel_colors_report_current(struct terminal *term)
|
||||
{
|
||||
char reply[24];
|
||||
snprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size);
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
LOG_DBG("query response for current color count: %u", term->sixel.palette_size);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_colors_reset(struct terminal *term)
|
||||
{
|
||||
term->sixel.palette_size = SIXEL_MAX_COLORS;
|
||||
LOG_DBG("sixel palette size reset to %u", SIXEL_MAX_COLORS);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_colors_set(struct terminal *term, unsigned count)
|
||||
{
|
||||
unsigned new_palette_size = min(max(2, count), SIXEL_MAX_COLORS);
|
||||
term->sixel.palette_size = new_palette_size;
|
||||
LOG_DBG("sixel palette size set to %u", new_palette_size);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_colors_report_max(struct terminal *term)
|
||||
{
|
||||
char reply[24];
|
||||
snprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS);
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
LOG_DBG("query response for max color count: %u", SIXEL_MAX_COLORS);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_geometry_report_current(struct terminal *term)
|
||||
{
|
||||
char reply[64];
|
||||
snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS",
|
||||
max_width(term), max_height(term));
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
|
||||
LOG_DBG("query response for current sixel geometry: %ux%u",
|
||||
max_width(term), max_height(term));
|
||||
}
|
||||
|
||||
void
|
||||
sixel_geometry_reset(struct terminal *term)
|
||||
{
|
||||
term->sixel.max_width = 0;
|
||||
term->sixel.max_height = 0;
|
||||
LOG_DBG("sixel geometry reset to %ux%u", max_width(term), max_height(term));
|
||||
}
|
||||
|
||||
void
|
||||
sixel_geometry_set(struct terminal *term, unsigned width, unsigned height)
|
||||
{
|
||||
term->sixel.max_width = width;
|
||||
term->sixel.max_height = height;
|
||||
LOG_DBG("sixel geometry set to %ux%u",
|
||||
term->sixel.max_width, term->sixel.max_height);
|
||||
}
|
||||
|
||||
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];
|
||||
snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height);
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
|
||||
LOG_DBG("query response for max sixel geometry: %ux%u",
|
||||
max_width, max_height);
|
||||
}
|
||||
22
sixel.h
Normal file
22
sixel.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "terminal.h"
|
||||
|
||||
#define SIXEL_MAX_COLORS 1024u
|
||||
|
||||
void sixel_init(struct terminal *term);
|
||||
void sixel_put(struct terminal *term, uint8_t c);
|
||||
void sixel_unhook(struct terminal *term);
|
||||
|
||||
void sixel_destroy(struct sixel *sixel);
|
||||
void sixel_purge_at_cursor(struct terminal *term);
|
||||
|
||||
void sixel_colors_report_current(struct terminal *term);
|
||||
void sixel_colors_reset(struct terminal *term);
|
||||
void sixel_colors_set(struct terminal *term, unsigned count);
|
||||
void sixel_colors_report_max(struct terminal *term);
|
||||
|
||||
void sixel_geometry_report_current(struct terminal *term);
|
||||
void sixel_geometry_reset(struct terminal *term);
|
||||
void sixel_geometry_set(struct terminal *term, unsigned width, unsigned height);
|
||||
void sixel_geometry_report_max(struct terminal *term);
|
||||
48
terminal.c
48
terminal.c
|
|
@ -19,12 +19,13 @@
|
|||
#include "log.h"
|
||||
|
||||
#include "async.h"
|
||||
#include "config.h"
|
||||
#include "grid.h"
|
||||
#include "render.h"
|
||||
#include "vt.h"
|
||||
#include "selection.h"
|
||||
#include "config.h"
|
||||
#include "sixel.h"
|
||||
#include "slave.h"
|
||||
#include "vt.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
|
@ -740,6 +741,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
|||
.lower_fd = delay_lower_fd,
|
||||
.upper_fd = delay_upper_fd,
|
||||
},
|
||||
.sixel = {
|
||||
.palette_size = SIXEL_MAX_COLORS,
|
||||
},
|
||||
.sixel_images = tll_init(),
|
||||
.hold_at_exit = conf->hold_at_exit,
|
||||
.shutdown_cb = shutdown_cb,
|
||||
.shutdown_data = shutdown_data,
|
||||
|
|
@ -993,6 +998,10 @@ term_destroy(struct terminal *term)
|
|||
tll_free(term->ptmx_buffer);
|
||||
tll_free(term->tab_stops);
|
||||
|
||||
tll_foreach(term->sixel_images, it)
|
||||
sixel_destroy(&it->item);
|
||||
tll_free(term->sixel_images);
|
||||
|
||||
free(term->foot_exe);
|
||||
free(term->cwd);
|
||||
|
||||
|
|
@ -1115,6 +1124,10 @@ term_reset(struct terminal *term, bool hard)
|
|||
term->meta.esc_prefix = true;
|
||||
term->meta.eight_bit = true;
|
||||
|
||||
tll_foreach(term->sixel_images, it)
|
||||
sixel_destroy(&it->item);
|
||||
tll_free(term->sixel_images);
|
||||
|
||||
if (!hard)
|
||||
return;
|
||||
|
||||
|
|
@ -1521,6 +1534,20 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
|||
erase_line(term, grid_row_and_alloc(term->grid, r));
|
||||
if (selection_on_row_in_view(term, r))
|
||||
selection_cancel(term);
|
||||
|
||||
|
||||
tll_foreach(term->sixel_images, it) {
|
||||
/* Make it simple - remove the entire image if it starts
|
||||
* getting scrolled out */
|
||||
|
||||
int img_top_row = it->item.pos.row & (term->grid->num_rows - 1);
|
||||
int new_row = (term->grid->offset + r) & (term->grid->num_rows - 1);
|
||||
|
||||
if (img_top_row == new_row) {
|
||||
sixel_destroy(&it->item);
|
||||
tll_remove(term->sixel_images, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||
|
|
@ -1572,6 +1599,21 @@ term_scroll_reverse_partial(struct terminal *term,
|
|||
erase_line(term, grid_row_and_alloc(term->grid, r));
|
||||
if (selection_on_row_in_view(term, r))
|
||||
selection_cancel(term);
|
||||
|
||||
tll_foreach(term->sixel_images, it) {
|
||||
/* Make it simple - remove the entire image if it starts
|
||||
* getting scrolled out */
|
||||
|
||||
/* TODO: untested */
|
||||
|
||||
int img_bottom_row = (it->item.pos.row + it->item.rows) & (term->grid->num_rows - 1);
|
||||
int new_row = (term->grid->offset + r) & (term->grid->num_rows - 1);
|
||||
|
||||
if (img_bottom_row == new_row) {
|
||||
sixel_destroy(&it->item);
|
||||
tll_remove(term->sixel_images, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
|
||||
|
|
@ -2086,6 +2128,8 @@ term_print(struct terminal *term, wchar_t wc, int width)
|
|||
print_linewrap(term);
|
||||
print_insert(term, width);
|
||||
|
||||
sixel_purge_at_cursor(term);
|
||||
|
||||
/* *Must* get current cell *after* linewrap+insert */
|
||||
struct row *row = term->grid->cur_row;
|
||||
struct cell *cell = &row->cells[term->cursor.point.col];
|
||||
|
|
|
|||
41
terminal.h
41
terminal.h
|
|
@ -131,6 +131,7 @@ struct vt {
|
|||
uint8_t *data;
|
||||
size_t size;
|
||||
size_t idx;
|
||||
void (*put_handler)(struct terminal *term, uint8_t c);
|
||||
void (*unhook_handler)(struct terminal *term);
|
||||
} dcs;
|
||||
struct attributes attrs;
|
||||
|
|
@ -174,6 +175,15 @@ struct ptmx_buffer {
|
|||
size_t idx;
|
||||
};
|
||||
|
||||
struct sixel {
|
||||
void *data;
|
||||
pixman_image_t *pix;
|
||||
int width;
|
||||
int height;
|
||||
int rows;
|
||||
struct coord pos;
|
||||
};
|
||||
|
||||
struct terminal {
|
||||
struct fdm *fdm;
|
||||
const struct config *conf;
|
||||
|
|
@ -344,6 +354,37 @@ struct terminal {
|
|||
int upper_fd;
|
||||
} delayed_render_timer;
|
||||
|
||||
struct {
|
||||
enum {
|
||||
SIXEL_DECSIXEL, /* DECSIXEL body part ", $, -, ? ... ~ */
|
||||
SIXEL_DECGRA, /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
|
||||
SIXEL_DECGRI, /* DECGRI Graphics Repeat Introducer ! Pn Ch */
|
||||
SIXEL_DECGCI, /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
|
||||
} state;
|
||||
|
||||
struct coord pos; /* Current sixel coordinate */
|
||||
int color_idx; /* Current palette index */
|
||||
int max_col; /* Largest column index we've seen (aka the image width) */
|
||||
uint32_t *palette; /* Color palette */
|
||||
|
||||
struct {
|
||||
uint32_t *data; /* Raw image data, in ARGB */
|
||||
int width; /* Image width, in pixels */
|
||||
int height; /* Image height, in pixels */
|
||||
} image;
|
||||
|
||||
unsigned params[5]; /* Collected parmaeters, for RASTER, COLOR_SPEC */
|
||||
unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */
|
||||
unsigned param_idx; /* Parameters seen */
|
||||
|
||||
/* Application configurable */
|
||||
unsigned palette_size; /* Number of colors in palette */
|
||||
unsigned max_width; /* Maximum image width, in pixels */
|
||||
unsigned max_height; /* Maximum image height, in pixels */
|
||||
} sixel;
|
||||
|
||||
tll(struct sixel) sixel_images;
|
||||
|
||||
bool hold_at_exit;
|
||||
bool is_shutting_down;
|
||||
void (*shutdown_cb)(void *data, int exit_code);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue