From f31c43015d15487d5fb129877dc0d87bcb60f5c9 Mon Sep 17 00:00:00 2001 From: bakkeby Date: Wed, 10 Mar 2021 18:09:47 +0100 Subject: [PATCH] Adding sixel support ref. #7 --- Makefile | 5 +- README.md | 5 + config.mk | 2 +- patch/sixel_st.c | 18 ++ patch/sixel_st.h | 1 + patch/sixel_x.c | 14 ++ patch/st_include.c | 3 + patch/st_include.h | 3 + patch/x_include.c | 3 + patches.def.h | 20 ++ sixel.c | 616 +++++++++++++++++++++++++++++++++++++++++++++ sixel.h | 58 +++++ sixel_hls.c | 115 +++++++++ sixel_hls.h | 7 + st.c | 178 ++++++++++--- st.h | 178 ++++++++++++- x.c | 190 ++++++-------- 17 files changed, 1260 insertions(+), 156 deletions(-) create mode 100644 patch/sixel_st.c create mode 100644 patch/sixel_st.h create mode 100644 patch/sixel_x.c create mode 100644 sixel.c create mode 100644 sixel.h create mode 100644 sixel_hls.c create mode 100644 sixel_hls.h diff --git a/Makefile b/Makefile index 42dfc10..73f2ed0 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,10 @@ include config.mk #LIGATURES_C = hb.c #LIGATURES_H = hb.h -SRC = st.c x.c $(LIGATURES_C) +# Uncomment this for the SIXEL patch / SIXEL_PATCH +#SIXEL_C = sixel.c sixel_hls.c + +SRC = st.c x.c $(LIGATURES_C) $(SIXEL_C) OBJ = $(SRC:.c=.o) all: options st diff --git a/README.md b/README.md index ec7225a..84e1dd4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: +2021-03-10 - Added sixel support + 2021-02-26 - Added the dynamic cursor color patch 2021-02-15 - Added the alpha gradient patch @@ -145,6 +147,9 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - [scrollback](https://st.suckless.org/patches/scrollback/) - allows you scroll back through terminal output using keyboard shortcuts or mousewheel + - sixel + - this patch adds SIXEL graphics support + - st-embedder - this patch allows clients to embed into the st window and can be useful if you tend to start X applications from the terminal - the behavior is similar to Plan 9 where applications can take over windows diff --git a/config.mk b/config.mk index f49fc6b..88e47eb 100644 --- a/config.mk +++ b/config.mk @@ -13,7 +13,7 @@ X11LIB = /usr/X11R6/lib PKG_CONFIG = pkg-config # Uncomment this for the alpha patch / ALPHA_PATCH -XRENDER = -lXrender +#XRENDER = -lXrender # Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH #XCURSOR = -lXcursor diff --git a/patch/sixel_st.c b/patch/sixel_st.c new file mode 100644 index 0000000..10b0b98 --- /dev/null +++ b/patch/sixel_st.c @@ -0,0 +1,18 @@ +sixel_state_t sixel_st; + +void +dcshandle(void) +{ + switch (csiescseq.mode[0]) { + default: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case 'q': /* DECSIXEL */ + if (sixel_parser_init(&sixel_st, 0, 0 << 16 | 0 << 8 | 0, 1, win.cw, win.ch) != 0) + perror("sixel_parser_init() failed"); + term.mode |= MODE_SIXEL; + break; + } +} \ No newline at end of file diff --git a/patch/sixel_st.h b/patch/sixel_st.h new file mode 100644 index 0000000..0d4febc --- /dev/null +++ b/patch/sixel_st.h @@ -0,0 +1 @@ +static void dcshandle(void); \ No newline at end of file diff --git a/patch/sixel_x.c b/patch/sixel_x.c new file mode 100644 index 0000000..0f74f53 --- /dev/null +++ b/patch/sixel_x.c @@ -0,0 +1,14 @@ +void +delete_image(ImageList *im) +{ + if (im->prev) + im->prev->next = im->next; + else + term.images = im->next; + if (im->next) + im->next->prev = im->prev; + if (im->pixmap) + XFreePixmap(xw.dpy, (Drawable)im->pixmap); + free(im->pixels); + free(im); +} \ No newline at end of file diff --git a/patch/st_include.c b/patch/st_include.c index 99e831c..6f51d14 100644 --- a/patch/st_include.c +++ b/patch/st_include.c @@ -19,4 +19,7 @@ #endif #if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH #include "scrollback.c" +#endif +#if SIXEL_PATCH +#include "sixel_st.c" #endif \ No newline at end of file diff --git a/patch/st_include.h b/patch/st_include.h index 6ba2ea1..9c9aadd 100644 --- a/patch/st_include.h +++ b/patch/st_include.h @@ -19,4 +19,7 @@ #endif #if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH #include "scrollback.h" +#endif +#if SIXEL_PATCH +#include "sixel_st.h" #endif \ No newline at end of file diff --git a/patch/x_include.c b/patch/x_include.c index a462aa5..f30784e 100644 --- a/patch/x_include.c +++ b/patch/x_include.c @@ -20,6 +20,9 @@ #if RIGHTCLICKTOPLUMB_PATCH #include "rightclicktoplumb_x.c" #endif +#if SIXEL_PATCH +#include "sixel_x.c" +#endif #if ST_EMBEDDER_PATCH #include "st_embedder_x.c" #endif diff --git a/patches.def.h b/patches.def.h index 62f70dc..2789516 100644 --- a/patches.def.h +++ b/patches.def.h @@ -208,6 +208,26 @@ */ #define SINGLE_DRAWABLE_BUFFER_PATCH 0 +/* This patch adds SIXEL graphics support for st. + * Note that patch/sixel.c/sixel_hls.c come from mintty, licensed under GPL. + * Known issues: + * - Entering clear causes all sixels to be deleted from scrollback. + * - Rendering sixel graphics may cause unusual cursor placement, this is + * not specific to this variant of st - the same issue is present in + * the xterm implementation. This is likely an issue of sixel height + * not being detected correctly. + * - If combined with the alpha patch sixel graphics disappear (become white) + * when transparent and rendered against a white background. This is believed + * to be related to how the sixel graphics use RGB colors instead of RGBA. + * A pull request or instructions for how to properly add alpha support for + * sixel graphics would be very welcome. + * + * Note that you need to uncomment the corresponding lines in Makefile when including this patch. + * + * https://gist.github.com/saitoha/70e0fdf22e3e8f63ce937c7f7da71809 + */ +#define SIXEL_PATCH 0 + /* This patch allows clients to embed into the st window and is useful if you tend to * start X applications from the terminal. For example: * diff --git a/sixel.c b/sixel.c new file mode 100644 index 0000000..7bfe598 --- /dev/null +++ b/sixel.c @@ -0,0 +1,616 @@ +// sixel.c (part of mintty) +// originally written by kmiya@cluti (https://github.com/saitoha/sixel/blob/master/fromsixel.c) +// Licensed under the terms of the GNU General Public License v3 or later. + +#include +#include /* memcpy */ + +#include "sixel.h" +#include "sixel_hls.h" + +#define SIXEL_RGB(r, g, b) ((r) + ((g) << 8) + ((b) << 16)) +#define SIXEL_PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m)) +#define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100)) + +static sixel_color_t const sixel_default_color_table[] = { + SIXEL_XRGB( 0, 0, 0), /* 0 Black */ + SIXEL_XRGB(20, 20, 80), /* 1 Blue */ + SIXEL_XRGB(80, 13, 13), /* 2 Red */ + SIXEL_XRGB(20, 80, 20), /* 3 Green */ + SIXEL_XRGB(80, 20, 80), /* 4 Magenta */ + SIXEL_XRGB(20, 80, 80), /* 5 Cyan */ + SIXEL_XRGB(80, 80, 20), /* 6 Yellow */ + SIXEL_XRGB(53, 53, 53), /* 7 Gray 50% */ + SIXEL_XRGB(26, 26, 26), /* 8 Gray 25% */ + SIXEL_XRGB(33, 33, 60), /* 9 Blue* */ + SIXEL_XRGB(60, 26, 26), /* 10 Red* */ + SIXEL_XRGB(33, 60, 33), /* 11 Green* */ + SIXEL_XRGB(60, 33, 60), /* 12 Magenta* */ + SIXEL_XRGB(33, 60, 60), /* 13 Cyan* */ + SIXEL_XRGB(60, 60, 33), /* 14 Yellow* */ + SIXEL_XRGB(80, 80, 80), /* 15 Gray 75% */ +}; + +static int +set_default_color(sixel_image_t *image) +{ + int i; + int n; + int r; + int g; + int b; + + /* palette initialization */ + for (n = 1; n < 17; n++) { + image->palette[n] = sixel_default_color_table[n - 1]; + } + + /* colors 17-232 are a 6x6x6 color cube */ + for (r = 0; r < 6; r++) { + for (g = 0; g < 6; g++) { + for (b = 0; b < 6; b++) { + image->palette[n++] = SIXEL_RGB(r * 51, g * 51, b * 51); + } + } + } + + /* colors 233-256 are a grayscale ramp, intentionally leaving out */ + for (i = 0; i < 24; i++) { + image->palette[n++] = SIXEL_RGB(i * 11, i * 11, i * 11); + } + + for (; n < DECSIXEL_PALETTE_MAX; n++) { + image->palette[n] = SIXEL_RGB(255, 255, 255); + } + + return (0); +} + +static int +sixel_image_init( + sixel_image_t *image, + int width, + int height, + int fgcolor, + int bgcolor, + int use_private_register) +{ + int status = (-1); + size_t size; + + size = (size_t)(width * height) * sizeof(sixel_color_no_t); + image->width = width; + image->height = height; + image->data = (sixel_color_no_t *)malloc(size); + image->ncolors = 2; + image->use_private_register = use_private_register; + + if (image->data == NULL) { + status = (-1); + goto end; + } + memset(image->data, 0, size); + + image->palette[0] = bgcolor; + + if (image->use_private_register) + image->palette[1] = fgcolor; + + image->palette_modified = 0; + + status = (0); + +end: + return status; +} + + +static int +image_buffer_resize( + sixel_image_t *image, + int width, + int height) +{ + int status = (-1); + size_t size; + sixel_color_no_t *alt_buffer; + int n; + int min_height; + + size = (size_t)(width * height) * sizeof(sixel_color_no_t); + alt_buffer = (sixel_color_no_t *)malloc(size); + if (alt_buffer == NULL) { + /* free source image */ + free(image->data); + image->data = NULL; + status = (-1); + goto end; + } + + min_height = height > image->height ? image->height: height; + if (width > image->width) { /* if width is extended */ + for (n = 0; n < min_height; ++n) { + /* copy from source image */ + memcpy(alt_buffer + width * n, + image->data + image->width * n, + (size_t)image->width * sizeof(sixel_color_no_t)); + /* fill extended area with background color */ + memset(alt_buffer + width * n + image->width, + 0, + (size_t)(width - image->width) * sizeof(sixel_color_no_t)); + } + } else { + for (n = 0; n < min_height; ++n) { + /* copy from source image */ + memcpy(alt_buffer + width * n, + image->data + image->width * n, + (size_t)width * sizeof(sixel_color_no_t)); + } + } + + if (height > image->height) { /* if height is extended */ + /* fill extended area with background color */ + memset(alt_buffer + width * image->height, + 0, + (size_t)(width * (height - image->height)) * sizeof(sixel_color_no_t)); + } + + /* free source image */ + free(image->data); + + image->data = alt_buffer; + image->width = width; + image->height = height; + + status = (0); + +end: + return status; +} + +static void +sixel_image_deinit(sixel_image_t *image) +{ + free(image->data); + image->data = NULL; +} + +int +sixel_parser_init(sixel_state_t *st, + sixel_color_t fgcolor, sixel_color_t bgcolor, + unsigned char use_private_register, + int cell_width, int cell_height) +{ + int status = (-1); + + st->state = PS_DECSIXEL; + st->pos_x = 0; + st->pos_y = 0; + st->max_x = 0; + st->max_y = 0; + st->attributed_pan = 2; + st->attributed_pad = 1; + st->attributed_ph = 0; + st->attributed_pv = 0; + st->repeat_count = 1; + st->color_index = 16; + st->grid_width = cell_width; + st->grid_height = cell_height; + st->nparams = 0; + st->param = 0; + + /* buffer initialization */ + status = sixel_image_init(&st->image, 1, 1, fgcolor, bgcolor, use_private_register); + + return status; +} + +int +sixel_parser_set_default_color(sixel_state_t *st) +{ + return set_default_color(&st->image); +} + +int +sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels) +{ + int status = (-1); + int sx; + int sy; + sixel_image_t *image = &st->image; + int x, y; + sixel_color_no_t *src; + unsigned char *dst; + int color; + + if (++st->max_x < st->attributed_ph) + st->max_x = st->attributed_ph; + + if (++st->max_y < st->attributed_pv) + st->max_y = st->attributed_pv; + + sx = (st->max_x + st->grid_width - 1) / st->grid_width * st->grid_width; + sy = (st->max_y + st->grid_height - 1) / st->grid_height * st->grid_height; + + if (image->width > sx || image->height > sy) { + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + + if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) { + status = set_default_color(image); + if (status < 0) + goto end; + } + + src = st->image.data; + dst = pixels; + for (y = 0; y < st->image.height; ++y) { + for (x = 0; x < st->image.width; ++x) { + color = st->image.palette[*src++]; + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + dst++; /* a */ + } + /* fill right padding with bgcolor */ + for (; x < st->image.width; ++x) { + color = st->image.palette[0]; /* bgcolor */ + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + dst++; /* a */ + } + } + /* fill bottom padding with bgcolor */ + for (; y < st->image.height; ++y) { + for (x = 0; x < st->image.width; ++x) { + color = st->image.palette[0]; /* bgcolor */ + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + dst++; /* a */ + } + } + + status = (0); + +end: + return status; +} + +/* convert sixel data into indexed pixel bytes and palette data */ +int +sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) +{ + int status = (-1); + int n; + int i; + int x; + int y; + int bits; + int sixel_vertical_mask; + int sx; + int sy; + int c; + int pos; + unsigned char *p0 = p; + sixel_image_t *image = &st->image; + + if (! image->data) + goto end; + + while (p < p0 + len) { + switch (st->state) { + case PS_ESC: + goto end; + + case PS_DECSIXEL: + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '"': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGRA; + p++; + break; + case '!': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGRI; + p++; + break; + case '#': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGCI; + p++; + break; + case '$': + /* DECGCR Graphics Carriage Return */ + st->pos_x = 0; + p++; + break; + case '-': + /* DECGNL Graphics Next Line */ + st->pos_x = 0; + if (st->pos_y < DECSIXEL_HEIGHT_MAX - 5 - 6) + st->pos_y += 6; + else + st->pos_y = DECSIXEL_HEIGHT_MAX + 1; + p++; + break; + default: + if (*p >= '?' && *p <= '~') { /* sixel characters */ + if ((image->width < (st->pos_x + st->repeat_count) || image->height < (st->pos_y + 6)) + && image->width < DECSIXEL_WIDTH_MAX && image->height < DECSIXEL_HEIGHT_MAX) { + sx = image->width * 2; + sy = image->height * 2; + while (sx < (st->pos_x + st->repeat_count) || sy < (st->pos_y + 6)) { + sx *= 2; + sy *= 2; + } + + if (sx > DECSIXEL_WIDTH_MAX) + sx = DECSIXEL_WIDTH_MAX; + if (sy > DECSIXEL_HEIGHT_MAX) + sy = DECSIXEL_HEIGHT_MAX; + + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + + if (st->color_index > image->ncolors) + image->ncolors = st->color_index; + + if (st->pos_x + st->repeat_count > image->width) + st->repeat_count = image->width - st->pos_x; + + if (st->repeat_count > 0 && st->pos_y - 5 < image->height) { + bits = *p - '?'; + if (bits != 0) { + sixel_vertical_mask = 0x01; + if (st->repeat_count <= 1) { + for (i = 0; i < 6; i++) { + if ((bits & sixel_vertical_mask) != 0) { + pos = image->width * (st->pos_y + i) + st->pos_x; + image->data[pos] = st->color_index; + if (st->max_x < st->pos_x) + st->max_x = st->pos_x; + if (st->max_y < (st->pos_y + i)) + st->max_y = st->pos_y + i; + } + sixel_vertical_mask <<= 1; + } + } else { + /* st->repeat_count > 1 */ + for (i = 0; i < 6; i++) { + if ((bits & sixel_vertical_mask) != 0) { + c = sixel_vertical_mask << 1; + for (n = 1; (i + n) < 6; n++) { + if ((bits & c) == 0) + break; + c <<= 1; + } + for (y = st->pos_y + i; y < st->pos_y + i + n; ++y) { + for (x = st->pos_x; x < st->pos_x + st->repeat_count; ++x) + image->data[image->width * y + x] = st->color_index; + } + if (st->max_x < (st->pos_x + st->repeat_count - 1)) + st->max_x = st->pos_x + st->repeat_count - 1; + if (st->max_y < (st->pos_y + i + n - 1)) + st->max_y = st->pos_y + i + n - 1; + i += (n - 1); + sixel_vertical_mask <<= (n - 1); + } + sixel_vertical_mask <<= 1; + } + } + } + } + if (st->repeat_count > 0) + st->pos_x += st->repeat_count; + st->repeat_count = 1; + } + p++; + break; + } + break; + + case PS_DECGRA: + /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + case ';': + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + p++; + break; + default: + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + if (st->nparams > 0) + st->attributed_pad = st->params[0]; + if (st->nparams > 1) + st->attributed_pan = st->params[1]; + if (st->nparams > 2 && st->params[2] > 0) + st->attributed_ph = st->params[2]; + if (st->nparams > 3 && st->params[3] > 0) + st->attributed_pv = st->params[3]; + + if (st->attributed_pan <= 0) + st->attributed_pan = 1; + if (st->attributed_pad <= 0) + st->attributed_pad = 1; + + if (image->width < st->attributed_ph || + image->height < st->attributed_pv) { + sx = st->attributed_ph; + if (image->width > st->attributed_ph) + sx = image->width; + + sy = st->attributed_pv; + if (image->height > st->attributed_pv) + sy = image->height; + + sx = (sx + st->grid_width - 1) / st->grid_width * st->grid_width; + sy = (sy + st->grid_height - 1) / st->grid_height * st->grid_height; + + if (sx > DECSIXEL_WIDTH_MAX) + sx = DECSIXEL_WIDTH_MAX; + if (sy > DECSIXEL_HEIGHT_MAX) + sy = DECSIXEL_HEIGHT_MAX; + + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + st->state = PS_DECSIXEL; + st->param = 0; + st->nparams = 0; + } + break; + + case PS_DECGRI: + /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + default: + st->repeat_count = st->param; + if (st->repeat_count == 0) + st->repeat_count = 1; + st->state = PS_DECSIXEL; + st->param = 0; + st->nparams = 0; + break; + } + break; + + case PS_DECGCI: + /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + case ';': + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + p++; + break; + default: + st->state = PS_DECSIXEL; + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + + if (st->nparams > 0) { + st->color_index = 1 + st->params[0]; /* offset 1(background color) added */ + if (st->color_index < 0) + st->color_index = 0; + else if (st->color_index >= DECSIXEL_PALETTE_MAX) + st->color_index = DECSIXEL_PALETTE_MAX - 1; + } + + if (st->nparams > 4) { + st->image.palette_modified = 1; + if (st->params[1] == 1) { + /* HLS */ + if (st->params[2] > 360) + st->params[2] = 360; + if (st->params[3] > 100) + st->params[3] = 100; + if (st->params[4] > 100) + st->params[4] = 100; + image->palette[st->color_index] + = hls_to_rgb(st->params[2], st->params[3], st->params[4]); + } else if (st->params[1] == 2) { + /* RGB */ + if (st->params[2] > 100) + st->params[2] = 100; + if (st->params[3] > 100) + st->params[3] = 100; + if (st->params[4] > 100) + st->params[4] = 100; + image->palette[st->color_index] + = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]); + } + } + break; + } + break; + default: + break; + } + } + + status = (0); + +end: + return status; +} + +void +sixel_parser_deinit(sixel_state_t *st) +{ + if (st) + sixel_image_deinit(&st->image); +} diff --git a/sixel.h b/sixel.h new file mode 100644 index 0000000..8a05c44 --- /dev/null +++ b/sixel.h @@ -0,0 +1,58 @@ +#ifndef SIXEL_H +#define SIXEL_H + +#define DECSIXEL_PARAMS_MAX 16 +#define DECSIXEL_PALETTE_MAX 1024 +#define DECSIXEL_PARAMVALUE_MAX 65535 +#define DECSIXEL_WIDTH_MAX 4096 +#define DECSIXEL_HEIGHT_MAX 4096 + +typedef unsigned short sixel_color_no_t; +typedef unsigned int sixel_color_t; + +typedef struct sixel_image_buffer { + sixel_color_no_t *data; + int width; + int height; + sixel_color_t palette[DECSIXEL_PALETTE_MAX]; + sixel_color_no_t ncolors; + int palette_modified; + int use_private_register; +} sixel_image_t; + +typedef enum parse_state { + PS_ESC = 1, /* ESC */ + PS_DECSIXEL = 2, /* DECSIXEL body part ", $, -, ? ... ~ */ + PS_DECGRA = 3, /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ + PS_DECGRI = 4, /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + PS_DECGCI = 5, /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ +} parse_state_t; + +typedef struct parser_context { + parse_state_t state; + int pos_x; + int pos_y; + int max_x; + int max_y; + int attributed_pan; + int attributed_pad; + int attributed_ph; + int attributed_pv; + int repeat_count; + int color_index; + int bgindex; + int grid_width; + int grid_height; + int param; + int nparams; + int params[DECSIXEL_PARAMS_MAX]; + sixel_image_t image; +} sixel_state_t; + +int sixel_parser_init(sixel_state_t *st, sixel_color_t fgcolor, sixel_color_t bgcolor, unsigned char use_private_register, int cell_width, int cell_height); +int sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len); +int sixel_parser_set_default_color(sixel_state_t *st); +int sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels); +void sixel_parser_deinit(sixel_state_t *st); + +#endif diff --git a/sixel_hls.c b/sixel_hls.c new file mode 100644 index 0000000..4f157b2 --- /dev/null +++ b/sixel_hls.c @@ -0,0 +1,115 @@ +// 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. + +#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); +} diff --git a/sixel_hls.h b/sixel_hls.h new file mode 100644 index 0000000..6176589 --- /dev/null +++ b/sixel_hls.h @@ -0,0 +1,7 @@ +/* + * Primary color hues: + * blue: 0 degrees + * red: 120 degrees + * green: 240 degrees + */ +int hls_to_rgb(int hue, int lum, int sat); diff --git a/st.c b/st.c index 491cc54..1cdafc1 100644 --- a/st.c +++ b/st.c @@ -25,6 +25,10 @@ #include #endif // KEYBOARDSELECT_PATCH +#if SIXEL_PATCH +#include "sixel.h" +#endif // SIXEL_PATCH + #if defined(__linux) #include #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) @@ -59,6 +63,9 @@ enum term_mode { MODE_ECHO = 1 << 4, MODE_PRINT = 1 << 5, MODE_UTF8 = 1 << 6, + #if SIXEL_PATCH + MODE_SIXEL = 1 << 7, + #endif // SIXEL_PATCH }; enum cursor_movement { @@ -90,15 +97,11 @@ enum escape_state { ESC_STR_END = 16, /* a final string was encountered */ ESC_TEST = 32, /* Enter in test mode */ ESC_UTF8 = 64, + #if SIXEL_PATCH + ESC_DCS =128, + #endif // SIXEL_PATCH }; -typedef struct { - Glyph attr; /* current char attributes */ - int x; - int y; - char state; -} TCursor; - typedef struct { int mode; int type; @@ -117,32 +120,6 @@ typedef struct { int alt; } Selection; -/* Internal representation of the screen */ -typedef struct { - int row; /* nb row */ - int col; /* nb col */ - Line *line; /* screen */ - Line *alt; /* alternate screen */ - #if SCROLLBACK_PATCH - Line hist[HISTSIZE]; /* history buffer */ - int histi; /* history index */ - int scr; /* scroll back */ - #endif // SCROLLBACK_PATCH - int *dirty; /* dirtyness of lines */ - TCursor c; /* cursor */ - int ocx; /* old cursor col */ - int ocy; /* old cursor row */ - int top; /* top scroll limit */ - int bot; /* bottom scroll limit */ - int mode; /* terminal mode flags */ - int esc; /* escape state flags */ - char trantbl[4]; /* charset table translation */ - int charset; /* current charset */ - int icharset; /* selected charset for sequence */ - int *tabs; - Rune lastc; /* last printed char outside of sequence, 0 if control */ -} Term; - /* CSI Escape sequence structs */ /* ESC '[' [[ [] [;]] []] */ typedef struct { @@ -217,9 +194,6 @@ static void tdefutf8(char); static int32_t tdefcolor(int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); - -static void drawregion(int, int, int, int); - static void selnormalize(void); static void selscroll(int, int); static void selsnap(int *, int *, int); @@ -235,7 +209,6 @@ static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -static Term term; static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; @@ -245,6 +218,9 @@ static int cmdfd; static int csdfd; #endif // EXTERNALPIPEIN_PATCH static pid_t pid; +#if SIXEL_PATCH +sixel_state_t sixel_st; +#endif // SIXEL_PATCH static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; @@ -1116,6 +1092,9 @@ void treset(void) { uint i; + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH term.c = (TCursor){{ .mode = ATTR_NULL, @@ -1138,6 +1117,10 @@ treset(void) tclearregion(0, 0, term.col-1, term.row-1); tswapscreen(); } + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) + im->should_delete = 1; + #endif // SIXEL_PATCH } void @@ -1152,9 +1135,16 @@ void tswapscreen(void) { Line *tmp = term.line; + #if SIXEL_PATCH + ImageList *im = term.images; + #endif // SIXEL_PATCH term.line = term.alt; term.alt = tmp; + #if SIXEL_PATCH + term.images = term.images_alt; + term.images_alt = im; + #endif // SIXEL_PATCH term.mode ^= MODE_ALTSCREEN; tfulldirt(); } @@ -1168,6 +1158,9 @@ tscrolldown(int orig, int n) { int i; Line temp; + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH LIMIT(n, 0, term.bot-orig+1); @@ -1189,6 +1182,15 @@ tscrolldown(int orig, int n) term.line[i-n] = temp; } + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) { + if (im->y < term.bot) + im->y += n; + if (im->y > term.bot) + im->should_delete = 1; + } + #endif // SIXEL_PATCH + #if SCROLLBACK_PATCH if (term.scr == 0) selscroll(orig, n); @@ -1206,6 +1208,9 @@ tscrollup(int orig, int n) { int i; Line temp; + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH LIMIT(n, 0, term.bot-orig+1); @@ -1230,6 +1235,15 @@ tscrollup(int orig, int n) term.line[i+n] = temp; } + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) { + if (im->y+im->height/win.ch > term.top) + im->y -= n; + if (im->y+im->height/win.ch < term.top) + im->should_delete = 1; + } + #endif // SIXEL_PATCH + #if SCROLLBACK_PATCH if (term.scr == 0) selscroll(orig, -n); @@ -2033,6 +2047,10 @@ strhandle(void) { char *p = NULL, *dec; int j, narg, par; + #if SIXEL_PATCH + ImageList *new_image; + int i; + #endif // SIXEL_PATCH term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -2093,6 +2111,41 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ + #if SIXEL_PATCH + if (IS_SET(MODE_SIXEL)) { + term.mode &= ~MODE_SIXEL; + new_image = malloc(sizeof(ImageList)); + memset(new_image, 0, sizeof(ImageList)); + new_image->x = term.c.x; + new_image->y = term.c.y; + new_image->width = sixel_st.image.width; + new_image->height = sixel_st.image.height; + new_image->pixels = malloc(new_image->width * new_image->height * 4); + if (sixel_parser_finalize(&sixel_st, new_image->pixels) != 0) { + perror("sixel_parser_finalize() failed"); + sixel_parser_deinit(&sixel_st); + return; + } + sixel_parser_deinit(&sixel_st); + if (term.images) { + ImageList *im; + for (im = term.images; im->next;) + im = im->next; + im->next = new_image; + new_image->prev = im; + } else { + term.images = new_image; + } + for (i = 0; i < (sixel_st.image.height + win.ch-1)/win.ch; ++i) { + int x; + tclearregion(term.c.x, term.c.y, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw, term.c.y); + for (x = term.c.x; x < MIN(term.col, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw); x++) + term.line[term.c.y][x].mode |= ATTR_SIXEL; + tnewline(1); + } + } + return; + #endif // SIXEL_PATCH case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2286,9 +2339,16 @@ tdectest(char c) void tstrsequence(uchar c) { + #if SIXEL_PATCH + strreset(); + #endif // SIXEL_PATCH + switch (c) { case 0x90: /* DCS -- Device Control String */ c = 'P'; + #if SIXEL_PATCH + term.esc |= ESC_DCS; + #endif // SIXEL_PATCH break; case 0x9f: /* APC -- Application Program Command */ c = '_'; @@ -2300,7 +2360,9 @@ tstrsequence(uchar c) c = ']'; break; } + #if !SIXEL_PATCH strreset(); + #endif // SIXEL_PATCH strescseq.type = c; term.esc |= ESC_STR; } @@ -2420,6 +2482,9 @@ eschandle(uchar ascii) term.esc |= ESC_UTF8; return 0; case 'P': /* DCS -- Device Control String */ + #if SIXEL_PATCH + term.esc |= ESC_DCS; + #endif // SIXEL_PATCH case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ case ']': /* OSC -- Operating System Command */ @@ -2506,7 +2571,12 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (u < 127 || !IS_SET(MODE_UTF8)) { + #if SIXEL_PATCH + if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) + #else + if (u < 127 || !IS_SET(MODE_UTF8)) + #endif // SIXEL_PATCH + { c[0] = u; width = len = 1; } else { @@ -2527,11 +2597,25 @@ tputc(Rune u) if (term.esc & ESC_STR) { if (u == '\a' || u == 030 || u == 032 || u == 033 || ISCONTROLC1(u)) { + #if SIXEL_PATCH + term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); + #else term.esc &= ~(ESC_START|ESC_STR); + #endif // SIXEL_PATCH term.esc |= ESC_STR_END; goto check_control_code; } + #if SIXEL_PATCH + if (IS_SET(MODE_SIXEL)) { + if (sixel_parser_parse(&sixel_st, (unsigned char *)&u, 1) != 0) + perror("sixel_parser_parse() failed"); + return; + } + if (term.esc & ESC_DCS) + goto check_control_code; + #endif // SIXEL_PATCH + if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends @@ -2582,6 +2666,17 @@ check_control_code: csihandle(); } return; + #if SIXEL_PATCH + } else if (term.esc & ESC_DCS) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + csiparse(); + dcshandle(); + } + return; + #endif // SIXEL_PATCH } else if (term.esc & ESC_UTF8) { tdefutf8(u); } else if (term.esc & ESC_ALTCHARSET) { @@ -2643,7 +2738,12 @@ twrite(const char *buf, int buflen, int show_ctrl) int n; for (n = 0; n < buflen; n += charsize) { - if (IS_SET(MODE_UTF8)) { + #if SIXEL_PATCH + if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) + #else + if (IS_SET(MODE_UTF8)) + #endif // SIXEL_PATCH + { /* process a complete utf8 char */ charsize = utf8decode(buf + n, &u, buflen - n); if (charsize == 0) diff --git a/st.h b/st.h index 2d4ce23..d3fc5c5 100644 --- a/st.h +++ b/st.h @@ -2,6 +2,12 @@ #include #include +#include +#include +#include +#include +#include +#include #include "patches.h" /* macros */ @@ -42,15 +48,29 @@ enum glyph_attribute { ATTR_WDUMMY = 1 << 10, #if BOXDRAW_PATCH ATTR_BOXDRAW = 1 << 11, + #endif // BOXDRAW_PATCH #if LIGATURES_PATCH ATTR_LIGA = 1 << 12, #endif // LIGATURES_PATCH - #elif LIGATURES_PATCH - ATTR_LIGA = 1 << 11, - #endif // BOXDRAW_PATCH + #if SIXEL_PATCH + ATTR_SIXEL = 1 << 13, + #endif // SIXEL_PATCH ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; +#if SIXEL_PATCH +typedef struct _ImageList { + struct _ImageList *next, *prev; + unsigned char *pixels; + void *pixmap; + int width; + int height; + int x; + int y; + int should_delete; +} ImageList; +#endif // SIXEL_PATCH + #if WIDE_GLYPHS_PATCH enum drawing_mode { DRAW_NONE = 0, @@ -82,6 +102,10 @@ typedef unsigned short ushort; typedef uint_least32_t Rune; +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + #define Glyph Glyph_ typedef struct { Rune u; /* character code */ @@ -92,6 +116,43 @@ typedef struct { typedef Glyph *Line; +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + #if SCROLLBACK_PATCH + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + #endif // SCROLLBACK_PATCH + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + #if SIXEL_PATCH + ImageList *images; /* sixel images */ + ImageList *images_alt; /* sixel images for alternate screen */ + #endif // SIXEL_PATCH + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + typedef union { int i; uint ui; @@ -100,9 +161,114 @@ typedef union { const char *s; } Arg; +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + #if ANYSIZE_PATCH + int hborderpx, vborderpx; + #endif // ANYSIZE_PATCH + int ch; /* char height */ + int cw; /* char width */ + #if VERTCENTER_PATCH + int cyo; /* char y offset */ + #endif // VERTCENTER_PATCH + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + #if HIDECURSOR_PATCH + /* Here, we use the term *pointer* to differentiate the cursor + * one sees when hovering the mouse over the terminal from, e.g., + * a green rectangle where text would be entered. */ + Cursor vpointer, bpointer; /* visible and hidden pointers */ + int pointerisvisible; + #endif // HIDECURSOR_PATCH + int scr; + int isfixed; /* is fixed geometry? */ + #if ALPHA_PATCH + int depth; /* bit depth */ + #endif // ALPHA_PATCH + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + void die(const char *, ...); void redraw(void); void draw(void); +void drawregion(int, int, int, int); void printscreen(const Arg *); void printsel(const Arg *); @@ -165,3 +331,9 @@ extern const int boxdraw, boxdraw_bold, boxdraw_braille; #if ALPHA_PATCH extern float alpha; #endif // ALPHA_PATCH + +extern DC dc; +extern XWindow xw; +extern XSelection xsel; +extern TermWindow win; +extern Term term; \ No newline at end of file diff --git a/x.c b/x.c index d694c8e..fdd16a5 100644 --- a/x.c +++ b/x.c @@ -27,31 +27,6 @@ char *argv0; #include #endif // THEMED_CURSOR_PATCH -/* types used in config.h */ -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; - -typedef struct { - uint mod; - uint button; - void (*func)(const Arg *); - const Arg arg; - uint release; -} MouseShortcut; - -typedef struct { - KeySym k; - uint mask; - char *s; - /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ - signed char appkey; /* application keypad */ - signed char appcursor; /* application cursor */ -} Key; - /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 @@ -83,89 +58,6 @@ static void zoomreset(const Arg *); #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) -typedef XftDraw *Draw; -typedef XftColor Color; -typedef XftGlyphFontSpec GlyphFontSpec; - -/* Purely graphic info */ -typedef struct { - int tw, th; /* tty width and height */ - int w, h; /* window width and height */ - #if ANYSIZE_PATCH - int hborderpx, vborderpx; - #endif // ANYSIZE_PATCH - int ch; /* char height */ - int cw; /* char width */ - #if VERTCENTER_PATCH - int cyo; /* char y offset */ - #endif // VERTCENTER_PATCH - int mode; /* window state/mode flags */ - int cursor; /* cursor style */ -} TermWindow; - -typedef struct { - Display *dpy; - Colormap cmap; - Window win; - Drawable buf; - GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - Atom xembed, wmdeletewin, netwmname, netwmpid; - struct { - XIM xim; - XIC xic; - XPoint spot; - XVaNestedList spotlist; - } ime; - Draw draw; - Visual *vis; - XSetWindowAttributes attrs; - #if HIDECURSOR_PATCH - /* Here, we use the term *pointer* to differentiate the cursor - * one sees when hovering the mouse over the terminal from, e.g., - * a green rectangle where text would be entered. */ - Cursor vpointer, bpointer; /* visible and hidden pointers */ - int pointerisvisible; - #endif // HIDECURSOR_PATCH - int scr; - int isfixed; /* is fixed geometry? */ - #if ALPHA_PATCH - int depth; /* bit depth */ - #endif // ALPHA_PATCH - int l, t; /* left and top offset */ - int gm; /* geometry mask */ -} XWindow; - -typedef struct { - Atom xtarget; - char *primary, *clipboard; - struct timespec tclick1; - struct timespec tclick2; -} XSelection; - -/* Font structure */ -#define Font Font_ -typedef struct { - int height; - int width; - int ascent; - int descent; - int badslant; - int badweight; - short lbearing; - short rbearing; - XftFont *match; - FcFontSet *set; - FcPattern *pattern; -} Font; - -/* Drawing Context */ -typedef struct { - Color *col; - size_t collen; - Font font, bfont, ifont, ibfont; - GC gc; -} DC; - static inline ushort sixd_to_16bit(int); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); #if WIDE_GLYPHS_PATCH @@ -250,10 +142,11 @@ static void (*handler[LASTEvent])(XEvent *) = { }; /* Globals */ -static DC dc; -static XWindow xw; -static XSelection xsel; -static TermWindow win; +Term term; +DC dc; +XWindow xw; +XSelection xsel; +TermWindow win; /* Font Ring Cache */ enum { @@ -2096,6 +1989,16 @@ xdrawline(Line line, int x1, int y1, int x2) void xfinishdraw(void) { + #if SIXEL_PATCH + ImageList *im; + int x, y; + int n = 0; + int nlimit = 256; + XRectangle *rects = NULL; + XGCValues gcvalues; + GC gc; + #endif // SIXEL_PATCH + #if VISUALBELL_3_PATCH if (vbellmode == 3 && win.vbellset) xdrawvbell(); @@ -2107,6 +2010,69 @@ xfinishdraw(void) XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg].pixel); + + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) { + if (term.images == NULL) { + /* last image was deleted, bail out */ + break; + } + + if (im->should_delete) { + delete_image(im); + + /* prevent the next iteration from accessing an invalid image pointer */ + im = term.images; + if (im == NULL) { + break; + } else { + continue; + } + } + + if (!im->pixmap) { + im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, im->width, im->height, + #if ALPHA_PATCH + xw.depth + #else + DefaultDepth(xw.dpy, xw.scr) + #endif // ALPHA_PATCH + ); + XImage ximage = { + .format = ZPixmap, + .data = (char *)im->pixels, + .width = im->width, + .height = im->height, + .xoffset = 0, + .byte_order = LSBFirst, + .bitmap_bit_order = MSBFirst, + .bits_per_pixel = 32, + .bytes_per_line = im->width * 4, + .bitmap_unit = 32, + .bitmap_pad = 32, + #if ALPHA_PATCH + .depth = xw.depth + #else + .depth = 24 + #endif // ALPHA_PATCH + }; + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, im->width, im->height); + free(im->pixels); + im->pixels = NULL; + } + + n = 0; + memset(&gcvalues, 0, sizeof(gcvalues)); + gc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues); + + XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, im->width, im->height, borderpx + im->x * win.cw, borderpx + im->y * win.ch); + XFreeGC(xw.dpy, gc); + + } + + free(rects); + drawregion(0, 0, term.col, term.row); + #endif // SIXEL_PATCH } void