Created
November 8, 2025 12:52
-
-
Save alek3y/0417a353558fec13435e4d3e50e72f1a to your computer and use it in GitHub Desktop.
Sixels patch for suckless st 0.9.3, based on CommandMaker/27ba97427424c06aff54d0a2a91a4ef1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/Makefile b/Makefile | |
| index 15db421..899b163 100644 | |
| --- a/Makefile | |
| +++ b/Makefile | |
| @@ -4,7 +4,7 @@ | |
| include config.mk | |
| -SRC = st.c x.c | |
| +SRC = st.c x.c $(SIXEL_C) | |
| OBJ = $(SRC:.c=.o) | |
| all: st | |
| diff --git a/config.def.h b/config.def.h | |
| index 2cd740a..e824b5d 100644 | |
| --- a/config.def.h | |
| +++ b/config.def.h | |
| @@ -23,7 +23,10 @@ char *scroll = NULL; | |
| char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; | |
| /* identification sequence returned in DA and DECID */ | |
| -char *vtiden = "\033[?6c"; | |
| +char *vtiden = "\033[?62;4c"; /* VT200 family (62) with sixel (4) */ | |
| + | |
| +/* sixel rgb byte order: LSBFirst or MSBFirst */ | |
| +int const sixelbyteorder = LSBFirst; | |
| /* Kerning / character bounding-box multipliers */ | |
| static float cwscale = 1.0; | |
| diff --git a/config.mk b/config.mk | |
| index 2fc854e..eae82cb 100644 | |
| --- a/config.mk | |
| +++ b/config.mk | |
| @@ -12,11 +12,14 @@ X11LIB = /usr/X11R6/lib | |
| PKG_CONFIG = pkg-config | |
| +SIXEL_C = sixel.c sixel_hls.c | |
| +SIXEL_LIBS = `$(PKG_CONFIG) --libs imlib2` | |
| + | |
| # includes and libs | |
| INCS = -I$(X11INC) \ | |
| `$(PKG_CONFIG) --cflags fontconfig` \ | |
| `$(PKG_CONFIG) --cflags freetype2` | |
| -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
| +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS}\ | |
| `$(PKG_CONFIG) --libs fontconfig` \ | |
| `$(PKG_CONFIG) --libs freetype2` | |
| @@ -28,8 +31,8 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
| # OpenBSD: | |
| #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
| #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
| -# `$(PKG_CONFIG) --libs fontconfig` \ | |
| -# `$(PKG_CONFIG) --libs freetype2` | |
| +# `pkg-config --libs fontconfig` \ | |
| +# `pkg-config --libs freetype2` | |
| #MANPREFIX = ${PREFIX}/man | |
| # compiler and linker | |
| diff --git a/sixel.c b/sixel.c | |
| new file mode 100644 | |
| index 0000000..fef2442 | |
| --- /dev/null | |
| +++ b/sixel.c | |
| @@ -0,0 +1,690 @@ | |
| +// 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 <stdlib.h> | |
| +#include <string.h> /* memcpy */ | |
| + | |
| +#include "st.h" | |
| +#include "win.h" | |
| +#include "sixel.h" | |
| +#include "sixel_hls.h" | |
| + | |
| +#define SIXEL_RGB(r, g, b) ((255 << 24) + ((r) << 16) + ((g) << 8) + (b)) | |
| +#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% */ | |
| +}; | |
| + | |
| +void | |
| +scroll_images(int n) { | |
| + ImageList *im, *next; | |
| + int top = 0; | |
| + | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + im->y += n; | |
| + | |
| + /* check if the current sixel has exceeded the maximum | |
| + * draw distance, and should therefore be deleted */ | |
| + if (im->y < top) { | |
| + //fprintf(stderr, "im@0x%08x exceeded maximum distance\n"); | |
| + delete_image(im); | |
| + } | |
| + } | |
| +} | |
| + | |
| +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); | |
| + if (im->clipmask) | |
| + XFreePixmap(xw.dpy, (Drawable)im->clipmask); | |
| + free(im->pixels); | |
| + free(im); | |
| +} | |
| + | |
| +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) | |
| +{ | |
| + if (image->data) | |
| + free(image->data); | |
| + image->data = NULL; | |
| +} | |
| + | |
| +int | |
| +sixel_parser_init(sixel_state_t *st, | |
| + int transparent, | |
| + 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->transparent = transparent; | |
| + 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, transparent ? 0 : 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, ImageList **newimages, int cx, int cy, int cw, int ch) | |
| +{ | |
| + sixel_image_t *image = &st->image; | |
| + int x, y; | |
| + sixel_color_no_t *src; | |
| + sixel_color_t *dst, color; | |
| + int w, h; | |
| + int i, j, cols, numimages; | |
| + char trans; | |
| + ImageList *im, *next, *tail; | |
| + | |
| + if (!image->data) | |
| + return -1; | |
| + | |
| + 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; | |
| + | |
| + if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) { | |
| + if (set_default_color(image) < 0) | |
| + return -1; | |
| + } | |
| + | |
| + w = MIN(st->max_x, image->width); | |
| + h = MIN(st->max_y, image->height); | |
| + | |
| + if ((numimages = (h + ch-1) / ch) <= 0) | |
| + return -1; | |
| + | |
| + cols = (w + cw-1) / cw; | |
| + | |
| + *newimages = NULL, tail = NULL; | |
| + for (y = 0, i = 0; i < numimages; i++) { | |
| + if ((im = malloc(sizeof(ImageList)))) { | |
| + if (!tail) { | |
| + *newimages = tail = im; | |
| + im->prev = im->next = NULL; | |
| + } else { | |
| + tail->next = im; | |
| + im->prev = tail; | |
| + im->next = NULL; | |
| + tail = im; | |
| + } | |
| + im->x = cx; | |
| + im->y = cy + i; | |
| + im->cols = cols; | |
| + im->width = w; | |
| + im->height = MIN(h - ch * i, ch); | |
| + im->pixels = malloc(im->width * im->height * 4); | |
| + im->pixmap = NULL; | |
| + im->clipmask = NULL; | |
| + im->cw = cw; | |
| + im->ch = ch; | |
| + } | |
| + if (!im || !im->pixels) { | |
| + for (im = *newimages; im; im = next) { | |
| + next = im->next; | |
| + if (im->pixels) | |
| + free(im->pixels); | |
| + free(im); | |
| + } | |
| + *newimages = NULL; | |
| + return -1; | |
| + } | |
| + dst = (sixel_color_t *)im->pixels; | |
| + for (trans = 0, j = 0; j < im->height && y < h; j++, y++) { | |
| + src = st->image.data + image->width * y; | |
| + for (x = 0; x < w; x++) { | |
| + color = st->image.palette[*src++]; | |
| + trans |= (color == 0); | |
| + *dst++ = color; | |
| + } | |
| + } | |
| + im->transparent = (st->transparent && trans); | |
| + } | |
| + | |
| + return numimages; | |
| +} | |
| + | |
| +/* convert sixel data into indexed pixel bytes and palette data */ | |
| +int | |
| +sixel_parser_parse(sixel_state_t *st, const unsigned char *p, size_t len) | |
| +{ | |
| + int n = 0; | |
| + int i; | |
| + int x; | |
| + int y; | |
| + int bits; | |
| + int sx; | |
| + int sy; | |
| + int c; | |
| + int pos; | |
| + int width; | |
| + const unsigned char *p0 = p, *p2 = p + len; | |
| + sixel_image_t *image = &st->image; | |
| + sixel_color_no_t *data, color_index; | |
| + | |
| + if (!image->data) | |
| + st->state = PS_ERROR; | |
| + | |
| + while (p < p2) { | |
| + switch (st->state) { | |
| + case PS_ESC: | |
| + goto end; | |
| + | |
| + case PS_DECSIXEL: | |
| + switch (*p) { | |
| + case '\x1b': | |
| + st->state = PS_ESC; | |
| + 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; | |
| + } | |
| + | |
| + sx = MIN(sx, DECSIXEL_WIDTH_MAX); | |
| + sy = MIN(sy, DECSIXEL_HEIGHT_MAX); | |
| + | |
| + if (image_buffer_resize(image, sx, sy) < 0) { | |
| + perror("sixel_parser_parse() failed"); | |
| + st->state = PS_ERROR; | |
| + p++; | |
| + break; | |
| + } | |
| + } | |
| + | |
| + 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) { | |
| + data = image->data + image->width * st->pos_y + st->pos_x; | |
| + width = image->width; | |
| + color_index = st->color_index; | |
| + if (st->repeat_count <= 1) { | |
| + if (bits & 0x01) | |
| + *data = color_index, n = 0; | |
| + data += width; | |
| + if (bits & 0x02) | |
| + *data = color_index, n = 1; | |
| + data += width; | |
| + if (bits & 0x04) | |
| + *data = color_index, n = 2; | |
| + data += width; | |
| + if (bits & 0x08) | |
| + *data = color_index, n = 3; | |
| + data += width; | |
| + if (bits & 0x10) | |
| + *data = color_index, n = 4; | |
| + if (bits & 0x20) | |
| + data[width] = color_index, n = 5; | |
| + if (st->max_x < st->pos_x) | |
| + st->max_x = st->pos_x; | |
| + } else { | |
| + /* st->repeat_count > 1 */ | |
| + for (i = 0; bits; bits >>= 1, i++, data += width) { | |
| + if (bits & 1) { | |
| + data[0] = color_index; | |
| + data[1] = color_index; | |
| + for (x = 2; x < st->repeat_count; x++) | |
| + data[x] = color_index; | |
| + n = i; | |
| + } | |
| + } | |
| + 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 + n)) | |
| + st->max_y = st->pos_y + n; | |
| + } | |
| + } | |
| + 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; | |
| + 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'; | |
| + st->param = MIN(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 = MAX(image->width, st->attributed_ph); | |
| + sy = MAX(image->height, st->attributed_pv); | |
| + | |
| + /* the height of the image buffer must be divisible by 6 | |
| + * to avoid unnecessary resizing of the image buffer when | |
| + * parsing the last sixel line */ | |
| + sy = (sy + 5) / 6 * 6; | |
| + | |
| + sx = MIN(sx, DECSIXEL_WIDTH_MAX); | |
| + sy = MIN(sy, DECSIXEL_HEIGHT_MAX); | |
| + | |
| + if (image_buffer_resize(image, sx, sy) < 0) { | |
| + perror("sixel_parser_parse() failed"); | |
| + st->state = PS_ERROR; | |
| + break; | |
| + } | |
| + } | |
| + 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; | |
| + 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'; | |
| + st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX); | |
| + p++; | |
| + break; | |
| + default: | |
| + st->repeat_count = MAX(st->param, 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; | |
| + 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'; | |
| + st->param = MIN(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 */ | |
| + st->params[2] = MIN(st->params[2], 360); | |
| + st->params[3] = MIN(st->params[3], 100); | |
| + st->params[4] = MIN(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 */ | |
| + st->params[2] = MIN(st->params[2], 100); | |
| + st->params[3] = MIN(st->params[3], 100); | |
| + st->params[4] = MIN(st->params[4], 100); | |
| + image->palette[st->color_index] | |
| + = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]); | |
| + } | |
| + } | |
| + break; | |
| + } | |
| + break; | |
| + | |
| + case PS_ERROR: | |
| + if (*p == '\x1b') { | |
| + st->state = PS_ESC; | |
| + goto end; | |
| + } | |
| + p++; | |
| + break; | |
| + default: | |
| + break; | |
| + } | |
| + } | |
| + | |
| +end: | |
| + return p - p0; | |
| +} | |
| + | |
| +void | |
| +sixel_parser_deinit(sixel_state_t *st) | |
| +{ | |
| + if (st) | |
| + sixel_image_deinit(&st->image); | |
| +} | |
| + | |
| +Pixmap | |
| +sixel_create_clipmask(char *pixels, int width, int height) | |
| +{ | |
| + char c, *clipdata, *dst; | |
| + int b, i, n, y, w; | |
| + int msb = (XBitmapBitOrder(xw.dpy) == MSBFirst); | |
| + sixel_color_t *src = (sixel_color_t *)pixels; | |
| + Pixmap clipmask; | |
| + | |
| + clipdata = dst = malloc((width+7)/8 * height); | |
| + if (!clipdata) | |
| + return (Pixmap)None; | |
| + | |
| + for (y = 0; y < height; y++) { | |
| + for (w = width; w > 0; w -= n) { | |
| + n = MIN(w, 8); | |
| + if (msb) { | |
| + for (b = 0x80, c = 0, i = 0; i < n; i++, b >>= 1) | |
| + c |= (*src++) ? b : 0; | |
| + } else { | |
| + for (b = 0x01, c = 0, i = 0; i < n; i++, b <<= 1) | |
| + c |= (*src++) ? b : 0; | |
| + } | |
| + *dst++ = c; | |
| + } | |
| + } | |
| + | |
| + clipmask = XCreateBitmapFromData(xw.dpy, xw.win, clipdata, width, height); | |
| + free(clipdata); | |
| + return clipmask; | |
| +} | |
| diff --git a/sixel.h b/sixel.h | |
| new file mode 100644 | |
| index 0000000..7d14f8a | |
| --- /dev/null | |
| +++ b/sixel.h | |
| @@ -0,0 +1,63 @@ | |
| +#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 */ | |
| + PS_ERROR = 6, | |
| +} 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 transparent; | |
| + 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; | |
| + | |
| +void scroll_images(int n); | |
| +void delete_image(ImageList *im); | |
| +int sixel_parser_init(sixel_state_t *st, int transparent, 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, const unsigned char *p, size_t len); | |
| +int sixel_parser_set_default_color(sixel_state_t *st); | |
| +int sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy, int cw, int ch); | |
| +void sixel_parser_deinit(sixel_state_t *st); | |
| +Pixmap sixel_create_clipmask(char *pixels, int width, int height); | |
| + | |
| +#endif | |
| diff --git a/sixel_hls.c b/sixel_hls.c | |
| new file mode 100644 | |
| index 0000000..c88241c | |
| --- /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) + ((g) << 8) + ((b) << 16) + (255 << 24)) | |
| + | |
| +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 8e57991..269d553 100644 | |
| --- a/st.c | |
| +++ b/st.c | |
| @@ -14,12 +14,15 @@ | |
| #include <sys/types.h> | |
| #include <sys/wait.h> | |
| #include <termios.h> | |
| +#include <time.h> | |
| #include <unistd.h> | |
| #include <wchar.h> | |
| #include "st.h" | |
| #include "win.h" | |
| +#include "sixel.h" | |
| + | |
| #if defined(__linux) | |
| #include <pty.h> | |
| #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) | |
| @@ -35,22 +38,27 @@ | |
| #define ESC_ARG_SIZ 16 | |
| #define STR_BUF_SIZ ESC_BUF_SIZ | |
| #define STR_ARG_SIZ ESC_ARG_SIZ | |
| +#define STR_TERM_ST "\033\\" | |
| +#define STR_TERM_BEL "\007" | |
| /* macros */ | |
| -#define IS_SET(flag) ((term.mode & (flag)) != 0) | |
| -#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) | |
| -#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | |
| -#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | |
| -#define ISDELIM(u) (u && wcschr(worddelimiters, u)) | |
| +#define IS_SET(flag) ((term.mode & (flag)) != 0) | |
| +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) | |
| +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | |
| +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | |
| +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) | |
| enum term_mode { | |
| - MODE_WRAP = 1 << 0, | |
| - MODE_INSERT = 1 << 1, | |
| - MODE_ALTSCREEN = 1 << 2, | |
| - MODE_CRLF = 1 << 3, | |
| - MODE_ECHO = 1 << 4, | |
| - MODE_PRINT = 1 << 5, | |
| - MODE_UTF8 = 1 << 6, | |
| + MODE_WRAP = 1 << 0, | |
| + MODE_INSERT = 1 << 1, | |
| + MODE_ALTSCREEN = 1 << 2, | |
| + MODE_CRLF = 1 << 3, | |
| + MODE_ECHO = 1 << 4, | |
| + MODE_PRINT = 1 << 5, | |
| + MODE_UTF8 = 1 << 6, | |
| + MODE_SIXEL = 1 << 7, | |
| + MODE_SIXEL_CUR_RT = 1 << 8, | |
| + MODE_SIXEL_SDM = 1 << 9 | |
| }; | |
| enum cursor_movement { | |
| @@ -82,15 +90,9 @@ enum escape_state { | |
| ESC_STR_END = 16, /* a final string was encountered */ | |
| ESC_TEST = 32, /* Enter in test mode */ | |
| ESC_UTF8 = 64, | |
| + ESC_DCS =128, | |
| }; | |
| -typedef struct { | |
| - Glyph attr; /* current char attributes */ | |
| - int x; | |
| - int y; | |
| - char state; | |
| -} TCursor; | |
| - | |
| typedef struct { | |
| int mode; | |
| int type; | |
| @@ -109,27 +111,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 */ | |
| - 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 '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ | |
| typedef struct { | |
| @@ -150,6 +131,7 @@ typedef struct { | |
| size_t len; /* raw string length */ | |
| char *args[STR_ARG_SIZ]; | |
| int narg; /* nb of args */ | |
| + char *term; /* terminator: ST or BEL */ | |
| } STREscape; | |
| static void execsh(char *, char **); | |
| @@ -159,6 +141,7 @@ static void ttywriteraw(const char *, size_t); | |
| static void csidump(void); | |
| static void csihandle(void); | |
| +static void dcshandle(void); | |
| static void csiparse(void); | |
| static void csireset(void); | |
| static void osc_color_response(int, int, int); | |
| @@ -174,7 +157,9 @@ static void tdumpline(int); | |
| static void tdump(void); | |
| static void tclearregion(int, int, int, int); | |
| static void tcursor(int); | |
| +static void tresetcursor(void); | |
| static void tdeletechar(int); | |
| +static void tdeleteimages(void); | |
| static void tdeleteline(int); | |
| static void tinsertblank(int); | |
| static void tinsertblankline(int); | |
| @@ -191,27 +176,24 @@ static void tsetattr(const int *, int); | |
| static void tsetchar(Rune, const Glyph *, int, int); | |
| static void tsetdirt(int, int); | |
| static void tsetscroll(int, int); | |
| +static inline void tsetsixelattr(Line line, int x1, int x2); | |
| static void tswapscreen(void); | |
| static void tsetmode(int, int, const int *, int); | |
| static int twrite(const char *, int, int); | |
| -static void tfulldirt(void); | |
| static void tcontrolcode(uchar ); | |
| static void tdectest(char ); | |
| static void tdefutf8(char); | |
| static int32_t tdefcolor(const 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); | |
| static size_t utf8decode(const char *, Rune *, size_t); | |
| -static Rune utf8decodebyte(char, size_t *); | |
| -static char utf8encodebyte(Rune, size_t); | |
| -static size_t utf8validate(Rune *, size_t); | |
| +static inline Rune utf8decodebyte(char, size_t *); | |
| +static inline char utf8encodebyte(Rune, size_t); | |
| +static inline size_t utf8validate(Rune *, size_t); | |
| static char *base64dec(const char *); | |
| static char base64dec_getc(const char **); | |
| @@ -219,19 +201,20 @@ 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; | |
| static int iofd = 1; | |
| static int cmdfd; | |
| static pid_t pid; | |
| +sixel_state_t sixel_st; | |
| static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | |
| static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | |
| static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | |
| static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | |
| + | |
| ssize_t | |
| xwrite(int fd, const char *s, size_t len) | |
| { | |
| @@ -273,7 +256,6 @@ char * | |
| xstrdup(const char *s) | |
| { | |
| char *p; | |
| - | |
| if ((p = strdup(s)) == NULL) | |
| die("strdup: %s\n", strerror(errno)); | |
| @@ -283,24 +265,27 @@ xstrdup(const char *s) | |
| size_t | |
| utf8decode(const char *c, Rune *u, size_t clen) | |
| { | |
| - size_t i, j, len, type; | |
| + size_t i, len; | |
| Rune udecoded; | |
| *u = UTF_INVALID; | |
| if (!clen) | |
| return 0; | |
| udecoded = utf8decodebyte(c[0], &len); | |
| - if (!BETWEEN(len, 1, UTF_SIZ)) | |
| + if (!BETWEEN(len, 2, UTF_SIZ)) { | |
| + *u = (len == 1) ? udecoded : UTF_INVALID; | |
| return 1; | |
| - for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { | |
| - udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | |
| - if (type != 0) | |
| - return j; | |
| } | |
| - if (j < len) | |
| + clen = MIN(clen, len); | |
| + for (i = 1; i < clen; ++i) { | |
| + if ((c[i] & 0xC0) != 0x80) | |
| + return i; | |
| + udecoded = (udecoded << 6) | (c[i] & 0x3F); | |
| + } | |
| + if (i < len) | |
| return 0; | |
| - *u = udecoded; | |
| - utf8validate(u, len); | |
| + *u = (!BETWEEN(udecoded, utfmin[len], utfmax[len]) || BETWEEN(udecoded, 0xD800, 0xDFFF)) | |
| + ? UTF_INVALID : udecoded; | |
| return len; | |
| } | |
| @@ -455,8 +440,8 @@ selextend(int col, int row, int type, int done) | |
| sel.oe.x = col; | |
| sel.oe.y = row; | |
| - selnormalize(); | |
| sel.type = type; | |
| + selnormalize(); | |
| if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) | |
| tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); | |
| @@ -485,6 +470,7 @@ selnormalize(void) | |
| /* expand selection over line breaks */ | |
| if (sel.type == SEL_RECTANGULAR) | |
| return; | |
| + | |
| i = tlinelen(sel.nb.y); | |
| if (i < sel.nb.x) | |
| sel.nb.x = i; | |
| @@ -564,15 +550,15 @@ selsnap(int *x, int *y, int direction) | |
| *x = (direction < 0) ? 0 : term.col - 1; | |
| if (direction < 0) { | |
| for (; *y > 0; *y += direction) { | |
| - if (!(term.line[*y-1][term.col-1].mode | |
| - & ATTR_WRAP)) { | |
| + if (!(term.line[*y-1][term.col-1].mode & ATTR_WRAP)) | |
| + { | |
| break; | |
| } | |
| } | |
| } else if (direction > 0) { | |
| for (; *y < term.row-1; *y += direction) { | |
| - if (!(term.line[*y][term.col-1].mode | |
| - & ATTR_WRAP)) { | |
| + if (!(term.line[*y][term.col-1].mode & ATTR_WRAP)) | |
| + { | |
| break; | |
| } | |
| } | |
| @@ -595,7 +581,8 @@ getsel(void) | |
| ptr = str = xmalloc(bufsize); | |
| /* append every set & selected glyph to the selection */ | |
| - for (y = sel.nb.y; y <= sel.ne.y; y++) { | |
| + for (y = sel.nb.y; y <= sel.ne.y; y++) | |
| + { | |
| if ((linelen = tlinelen(y)) == 0) { | |
| *ptr++ = '\n'; | |
| continue; | |
| @@ -608,6 +595,7 @@ getsel(void) | |
| gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; | |
| lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; | |
| } | |
| + | |
| last = &term.line[y][MIN(lastx, linelen-1)]; | |
| while (last >= gp && last->u == ' ') | |
| --last; | |
| @@ -628,8 +616,8 @@ getsel(void) | |
| * st. | |
| * FIXME: Fix the computer world. | |
| */ | |
| - if ((y < sel.ne.y || lastx >= linelen) && | |
| - (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) | |
| + if ((y < sel.ne.y || lastx >= linelen) | |
| + && (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) | |
| *ptr++ = '\n'; | |
| } | |
| *ptr = 0; | |
| @@ -641,9 +629,15 @@ selclear(void) | |
| { | |
| if (sel.ob.x == -1) | |
| return; | |
| + selremove(); | |
| + tsetdirt(sel.nb.y, sel.ne.y); | |
| +} | |
| + | |
| +void | |
| +selremove(void) | |
| +{ | |
| sel.mode = SEL_IDLE; | |
| sel.ob.x = -1; | |
| - tsetdirt(sel.nb.y, sel.ne.y); | |
| } | |
| void | |
| @@ -697,6 +691,7 @@ execsh(char *cmd, char **args) | |
| setenv("SHELL", sh, 1); | |
| setenv("HOME", pw->pw_dir, 1); | |
| setenv("TERM", termname, 1); | |
| + setenv("COLORTERM", "truecolor", 1); | |
| signal(SIGCHLD, SIG_DFL); | |
| signal(SIGHUP, SIG_DFL); | |
| @@ -715,17 +710,16 @@ sigchld(int a) | |
| int stat; | |
| pid_t p; | |
| - if ((p = waitpid(pid, &stat, WNOHANG)) < 0) | |
| - die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); | |
| - | |
| - if (pid != p) | |
| - return; | |
| + while ((p = waitpid(-1, &stat, WNOHANG)) > 0) { | |
| + if (p == pid) { | |
| - if (WIFEXITED(stat) && WEXITSTATUS(stat)) | |
| - die("child exited with status %d\n", WEXITSTATUS(stat)); | |
| - else if (WIFSIGNALED(stat)) | |
| - die("child terminated due to signal %d\n", WTERMSIG(stat)); | |
| - _exit(0); | |
| + if (WIFEXITED(stat) && WEXITSTATUS(stat)) | |
| + die("child exited with status %d\n", WEXITSTATUS(stat)); | |
| + else if (WIFSIGNALED(stat)) | |
| + die("child terminated due to signal %d\n", WTERMSIG(stat)); | |
| + _exit(0); | |
| + } | |
| + } | |
| } | |
| void | |
| @@ -756,6 +750,7 @@ int | |
| ttynew(const char *line, char *cmd, const char *out, char **args) | |
| { | |
| int m, s; | |
| + struct sigaction sa; | |
| if (out) { | |
| term.mode |= MODE_PRINT; | |
| @@ -794,7 +789,7 @@ ttynew(const char *line, char *cmd, const char *out, char **args) | |
| if (ioctl(s, TIOCSCTTY, NULL) < 0) | |
| die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); | |
| if (s > 2) | |
| - close(s); | |
| + close(s); | |
| #ifdef __OpenBSD__ | |
| if (pledge("stdio getpw proc exec", NULL) == -1) | |
| die("pledge\n"); | |
| @@ -808,7 +803,10 @@ ttynew(const char *line, char *cmd, const char *out, char **args) | |
| #endif | |
| close(s); | |
| cmdfd = m; | |
| - signal(SIGCHLD, sigchld); | |
| + memset(&sa, 0, sizeof(sa)); | |
| + sigemptyset(&sa.sa_mask); | |
| + sa.sa_handler = sigchld; | |
| + sigaction(SIGCHLD, &sa, NULL); | |
| break; | |
| } | |
| return cmdfd; | |
| @@ -960,6 +958,12 @@ tattrset(int attr) | |
| return 0; | |
| } | |
| +int | |
| +tisaltscr(void) | |
| +{ | |
| + return IS_SET(MODE_ALTSCREEN); | |
| +} | |
| + | |
| void | |
| tsetdirt(int top, int bot) | |
| { | |
| @@ -987,6 +991,13 @@ tsetdirtattr(int attr) | |
| } | |
| } | |
| +void | |
| +tsetsixelattr(Line line, int x1, int x2) | |
| +{ | |
| + for (; x1 <= x2; x1++) | |
| + line[x1].mode |= ATTR_SIXEL; | |
| +} | |
| + | |
| void | |
| tfulldirt(void) | |
| { | |
| @@ -1007,16 +1018,19 @@ tcursor(int mode) | |
| } | |
| } | |
| +void | |
| +tresetcursor(void) | |
| +{ | |
| + term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg }, | |
| + .x = 0, .y = 0, .state = CURSOR_DEFAULT }; | |
| +} | |
| + | |
| void | |
| treset(void) | |
| { | |
| uint i; | |
| - term.c = (TCursor){{ | |
| - .mode = ATTR_NULL, | |
| - .fg = defaultfg, | |
| - .bg = defaultbg | |
| - }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; | |
| + tresetcursor(); | |
| memset(term.tabs, 0, term.col * sizeof(*term.tabs)); | |
| for (i = tabspaces; i < term.col; i += tabspaces) | |
| @@ -1031,6 +1045,7 @@ treset(void) | |
| tmoveto(0, 0); | |
| tcursor(CURSOR_SAVE); | |
| tclearregion(0, 0, term.col-1, term.row-1); | |
| + tdeleteimages(); | |
| tswapscreen(); | |
| } | |
| } | |
| @@ -1047,9 +1062,12 @@ void | |
| tswapscreen(void) | |
| { | |
| Line *tmp = term.line; | |
| + ImageList *im = term.images; | |
| term.line = term.alt; | |
| term.alt = tmp; | |
| + term.images = term.images_alt; | |
| + term.images_alt = im; | |
| term.mode ^= MODE_ALTSCREEN; | |
| tfulldirt(); | |
| } | |
| @@ -1057,8 +1075,13 @@ tswapscreen(void) | |
| void | |
| tscrolldown(int orig, int n) | |
| { | |
| + | |
| int i; | |
| Line temp; | |
| + int bot = term.bot; | |
| + int scr = 0; | |
| + int itop = orig + scr, ibot = bot + scr; | |
| + ImageList *im, *next; | |
| LIMIT(n, 0, term.bot-orig+1); | |
| @@ -1071,14 +1094,29 @@ tscrolldown(int orig, int n) | |
| term.line[i-n] = temp; | |
| } | |
| + /* move images, if they are inside the scrolling region */ | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + if (im->y >= itop && im->y <= ibot) { | |
| + im->y += n; | |
| + if (im->y > ibot) | |
| + delete_image(im); | |
| + } | |
| + } | |
| + | |
| selscroll(orig, n); | |
| } | |
| void | |
| tscrollup(int orig, int n) | |
| { | |
| + | |
| int i; | |
| Line temp; | |
| + int bot = term.bot; | |
| + int scr = 0; | |
| + int itop = orig + scr, ibot = bot + scr; | |
| + ImageList *im, *next; | |
| LIMIT(n, 0, term.bot-orig+1); | |
| @@ -1091,6 +1129,16 @@ tscrollup(int orig, int n) | |
| term.line[i+n] = temp; | |
| } | |
| + /* move images, if they are inside the scrolling region */ | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + if (im->y >= itop && im->y <= ibot) { | |
| + im->y -= n; | |
| + if (im->y < itop) | |
| + delete_image(im); | |
| + } | |
| + } | |
| + | |
| selscroll(orig, -n); | |
| } | |
| @@ -1218,6 +1266,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) | |
| term.dirty[y] = 1; | |
| term.line[y][x] = *attr; | |
| term.line[y][x].u = u; | |
| + | |
| } | |
| void | |
| @@ -1291,11 +1340,23 @@ tinsertblankline(int n) | |
| tscrolldown(term.c.y, n); | |
| } | |
| +void | |
| +tdeleteimages(void) | |
| +{ | |
| + ImageList *im, *next; | |
| + | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + delete_image(im); | |
| + } | |
| +} | |
| + | |
| void | |
| tdeleteline(int n) | |
| { | |
| - if (BETWEEN(term.c.y, term.top, term.bot)) | |
| + if (BETWEEN(term.c.y, term.top, term.bot)) { | |
| tscrollup(term.c.y, n); | |
| + } | |
| } | |
| int32_t | |
| @@ -1475,7 +1536,8 @@ tsetscroll(int t, int b) | |
| void | |
| tsetmode(int priv, int set, const int *args, int narg) | |
| { | |
| - int alt; const int *lim; | |
| + int alt; | |
| + const int *lim; | |
| for (lim = args + narg; args < lim; ++args) { | |
| if (priv) { | |
| @@ -1546,8 +1608,7 @@ tsetmode(int priv, int set, const int *args, int narg) | |
| break; | |
| alt = IS_SET(MODE_ALTSCREEN); | |
| if (alt) { | |
| - tclearregion(0, 0, term.col-1, | |
| - term.row-1); | |
| + tclearregion(0, 0, term.col-1, term.row-1); | |
| } | |
| if (set ^ alt) /* set is always 1 or 0 */ | |
| tswapscreen(); | |
| @@ -1570,6 +1631,12 @@ tsetmode(int priv, int set, const int *args, int narg) | |
| and can be mistaken for other control | |
| codes. */ | |
| break; | |
| + case 80: /* DECSDM -- Sixel Display Mode */ | |
| + MODBIT(term.mode, set, MODE_SIXEL_SDM); | |
| + break; | |
| + case 8452: /* sixel scrolling leaves cursor to right of graphic */ | |
| + MODBIT(term.mode, set, MODE_SIXEL_CUR_RT); | |
| + break; | |
| default: | |
| fprintf(stderr, | |
| "erresc: unknown private set/reset mode %d\n", | |
| @@ -1605,8 +1672,11 @@ tsetmode(int priv, int set, const int *args, int narg) | |
| void | |
| csihandle(void) | |
| { | |
| - char buf[40]; | |
| - int len; | |
| + char buffer[40]; | |
| + int n = 0, len; | |
| + ImageList *im, *next; | |
| + int pi, pa; | |
| + int maxcol = term.col; | |
| switch (csiescseq.mode[0]) { | |
| default: | |
| @@ -1704,19 +1774,30 @@ csihandle(void) | |
| case 'J': /* ED -- Clear screen */ | |
| switch (csiescseq.arg[0]) { | |
| case 0: /* below */ | |
| - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); | |
| - if (term.c.y < term.row-1) { | |
| - tclearregion(0, term.c.y+1, term.col-1, | |
| - term.row-1); | |
| - } | |
| + tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); | |
| + if (term.c.y < term.row-1) | |
| + tclearregion(0, term.c.y+1, maxcol-1, term.row-1); | |
| break; | |
| case 1: /* above */ | |
| if (term.c.y > 0) | |
| - tclearregion(0, 0, term.col-1, term.c.y-1); | |
| + tclearregion(0, 0, maxcol-1, term.c.y-1); | |
| tclearregion(0, term.c.y, term.c.x, term.c.y); | |
| break; | |
| - case 2: /* all */ | |
| - tclearregion(0, 0, term.col-1, term.row-1); | |
| + case 2: /* screen */ | |
| + | |
| + tclearregion(0, 0, maxcol-1, term.row-1); | |
| + tdeleteimages(); | |
| + break; | |
| + case 3: /* scrollback */ | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + if (im->y < 0) | |
| + delete_image(im); | |
| + } | |
| + break; | |
| + case 6: /* sixels */ | |
| + tdeleteimages(); | |
| + tfulldirt(); | |
| break; | |
| default: | |
| goto unknown; | |
| @@ -1725,19 +1806,43 @@ csihandle(void) | |
| case 'K': /* EL -- Clear line */ | |
| switch (csiescseq.arg[0]) { | |
| case 0: /* right */ | |
| - tclearregion(term.c.x, term.c.y, term.col-1, | |
| - term.c.y); | |
| + tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); | |
| break; | |
| case 1: /* left */ | |
| tclearregion(0, term.c.y, term.c.x, term.c.y); | |
| break; | |
| case 2: /* all */ | |
| - tclearregion(0, term.c.y, term.col-1, term.c.y); | |
| + tclearregion(0, term.c.y, maxcol-1, term.c.y); | |
| break; | |
| } | |
| break; | |
| - case 'S': /* SU -- Scroll <n> line up */ | |
| - if (csiescseq.priv) break; | |
| + case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */ | |
| + if (csiescseq.priv) { | |
| + if (csiescseq.narg > 1) { | |
| + /* XTSMGRAPHICS */ | |
| + pi = csiescseq.arg[0]; | |
| + pa = csiescseq.arg[1]; | |
| + if (pi == 1 && (pa == 1 || pa == 2 || pa == 4)) { | |
| + /* number of sixel color registers */ | |
| + /* (read, reset and read the maximum value give the same response) */ | |
| + n = snprintf(buffer, sizeof buffer, "\033[?1;0;%dS", DECSIXEL_PALETTE_MAX); | |
| + ttywrite(buffer, n, 1); | |
| + break; | |
| + } else if (pi == 2 && (pa == 1 || pa == 2 || pa == 4)) { | |
| + /* sixel graphics geometry (in pixels) */ | |
| + /* (read, reset and read the maximum value give the same response) */ | |
| + n = snprintf(buffer, sizeof buffer, "\033[?2;0;%d;%dS", | |
| + MIN(term.col * win.cw, DECSIXEL_WIDTH_MAX), | |
| + MIN(term.row * win.ch, DECSIXEL_HEIGHT_MAX)); | |
| + ttywrite(buffer, n, 1); | |
| + break; | |
| + } | |
| + /* the number of color registers and sixel geometry can't be changed */ | |
| + n = snprintf(buffer, sizeof buffer, "\033[?%d;3;0S", pi); /* failure */ | |
| + ttywrite(buffer, n, 1); | |
| + } | |
| + goto unknown; | |
| + } | |
| DEFAULT(csiescseq.arg[0], 1); | |
| tscrollup(term.top, csiescseq.arg[0]); | |
| break; | |
| @@ -1785,9 +1890,9 @@ csihandle(void) | |
| ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); | |
| break; | |
| case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */ | |
| - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", | |
| + len = snprintf(buffer, sizeof(buffer), "\033[%i;%iR", | |
| term.c.y+1, term.c.x+1); | |
| - ttywrite(buf, len, 0); | |
| + ttywrite(buffer, len, 0); | |
| break; | |
| default: | |
| goto unknown; | |
| @@ -1806,6 +1911,27 @@ csihandle(void) | |
| case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ | |
| tcursor(CURSOR_SAVE); | |
| break; | |
| + case 't': /* title stack operations ; XTWINOPS */ | |
| + switch (csiescseq.arg[0]) { | |
| + case 14: /* text area size in pixels */ | |
| + if (csiescseq.narg > 1) | |
| + goto unknown; | |
| + n = snprintf(buffer, sizeof buffer, "\033[4;%d;%dt", | |
| + term.row * win.ch, term.col * win.cw); | |
| + ttywrite(buffer, n, 1); | |
| + break; | |
| + case 16: /* character cell size in pixels */ | |
| + n = snprintf(buffer, sizeof buffer, "\033[6;%d;%dt", win.ch, win.cw); | |
| + ttywrite(buffer, n, 1); | |
| + break; | |
| + case 18: /* size of the text area in characters */ | |
| + n = snprintf(buffer, sizeof buffer, "\033[8;%d;%dt", term.row, term.col); | |
| + ttywrite(buffer, n, 1); | |
| + break; | |
| + default: | |
| + goto unknown; | |
| + } | |
| + break; | |
| case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ | |
| if (csiescseq.priv) { | |
| goto unknown; | |
| @@ -1870,8 +1996,8 @@ osc_color_response(int num, int index, int is_osc4) | |
| return; | |
| } | |
| - n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", | |
| - is_osc4 ? "4;" : "", num, r, r, g, g, b, b); | |
| + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x%s", | |
| + is_osc4 ? "4;" : "", num, r, r, g, g, b, b, strescseq.term); | |
| if (n < 0 || n >= sizeof(buf)) { | |
| fprintf(stderr, "error: %s while printing %s response\n", | |
| n < 0 ? "snprintf failed" : "truncation occurred", | |
| @@ -1891,6 +2017,11 @@ strhandle(void) | |
| { defaultbg, "background" }, | |
| { defaultcs, "cursor" } | |
| }; | |
| + ImageList *im, *newimages, *next, *tail = NULL; | |
| + int i, x1, y1, x2, y2, y, numimages; | |
| + int cx, cy; | |
| + Line line; | |
| + int scr = 0; | |
| term.esc &= ~(ESC_STR_END|ESC_STR); | |
| strparse(); | |
| @@ -1924,6 +2055,8 @@ strhandle(void) | |
| } | |
| } | |
| return; | |
| + case 8: /* Clear Hyperlinks */ | |
| + return; | |
| case 10: /* set dynamic VT100 text foreground color */ | |
| case 11: /* set dynamic VT100 text background color */ | |
| case 12: /* set dynamic text cursor color */ | |
| @@ -1986,6 +2119,95 @@ strhandle(void) | |
| xsettitle(strescseq.args[0]); | |
| return; | |
| case 'P': /* DCS -- Device Control String */ | |
| + if (IS_SET(MODE_SIXEL)) { | |
| + term.mode &= ~MODE_SIXEL; | |
| + if (!sixel_st.image.data) { | |
| + sixel_parser_deinit(&sixel_st); | |
| + return; | |
| + } | |
| + cx = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.x; | |
| + cy = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.y; | |
| + if ((numimages = sixel_parser_finalize(&sixel_st, &newimages, | |
| + cx, cy + scr, win.cw, win.ch)) <= 0) { | |
| + sixel_parser_deinit(&sixel_st); | |
| + perror("sixel_parser_finalize() failed"); | |
| + return; | |
| + } | |
| + sixel_parser_deinit(&sixel_st); | |
| + x1 = newimages->x; | |
| + y1 = newimages->y; | |
| + x2 = x1 + newimages->cols; | |
| + y2 = y1 + numimages; | |
| + /* Delete the old images that are covered by the new image(s). We also need | |
| + * to check if they have already been deleted before adding the new ones. */ | |
| + if (term.images) { | |
| + char transparent[numimages]; | |
| + for (i = 0, im = newimages; im; im = im->next, i++) { | |
| + transparent[i] = im->transparent; | |
| + } | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + if (im->y >= y1 && im->y < y2) { | |
| + y = im->y - scr; | |
| + if (y >= 0 && y < term.row && term.dirty[y]) { | |
| + line = term.line[y]; | |
| + j = MIN(im->x + im->cols, term.col); | |
| + for (i = im->x; i < j; i++) { | |
| + if (line[i].mode & ATTR_SIXEL) | |
| + break; | |
| + } | |
| + if (i == j) { | |
| + delete_image(im); | |
| + continue; | |
| + } | |
| + } | |
| + if (im->x >= x1 && im->x + im->cols <= x2 && !transparent[im->y - y1]) { | |
| + delete_image(im); | |
| + continue; | |
| + } | |
| + } | |
| + tail = im; | |
| + } | |
| + } | |
| + if (tail) { | |
| + tail->next = newimages; | |
| + newimages->prev = tail; | |
| + } else { | |
| + term.images = newimages; | |
| + } | |
| + x2 = MIN(x2, term.col) - 1; | |
| + if (IS_SET(MODE_SIXEL_SDM)) { | |
| + /* Sixel display mode: put the sixel in the upper left corner of | |
| + * the screen, disable scrolling (the sixel will be truncated if | |
| + * it is too long) and do not change the cursor position. */ | |
| + for (i = 0, im = newimages; im; im = next, i++) { | |
| + next = im->next; | |
| + if (i >= term.row) { | |
| + delete_image(im); | |
| + continue; | |
| + } | |
| + im->y = i + scr; | |
| + tsetsixelattr(term.line[i], x1, x2); | |
| + term.dirty[MIN(im->y, term.row-1)] = 1; | |
| + } | |
| + } else { | |
| + for (i = 0, im = newimages; im; im = next, i++) { | |
| + next = im->next; | |
| + im->y = term.c.y + scr; | |
| + tsetsixelattr(term.line[term.c.y], x1, x2); | |
| + term.dirty[MIN(im->y, term.row-1)] = 1; | |
| + if (i < numimages-1) { | |
| + im->next = NULL; | |
| + tnewline(0); | |
| + im->next = next; | |
| + } | |
| + } | |
| + /* if mode 8452 is set, sixel scrolling leaves cursor to right of graphic */ | |
| + if (IS_SET(MODE_SIXEL_CUR_RT)) | |
| + term.c.x = MIN(term.c.x + newimages->cols, term.col-1); | |
| + } | |
| + } | |
| + return; | |
| case '_': /* APC -- Application Program Command */ | |
| case '^': /* PM -- Privacy Message */ | |
| return; | |
| @@ -2007,6 +2229,16 @@ strparse(void) | |
| if (*p == '\0') | |
| return; | |
| + /* preserve semicolons in window titles, icon names and OSC 7 sequences */ | |
| + if (strescseq.type == ']' && ( | |
| + p[0] <= '2' | |
| + ) && p[1] == ';') { | |
| + strescseq.args[strescseq.narg++] = p; | |
| + strescseq.args[strescseq.narg++] = p + 2; | |
| + p[1] = '\0'; | |
| + return; | |
| + } | |
| + | |
| while (strescseq.narg < STR_ARG_SIZ) { | |
| strescseq.args[strescseq.narg++] = p; | |
| while ((c = *p) != ';' && c != '\0') | |
| @@ -2041,7 +2273,7 @@ strdump(void) | |
| fprintf(stderr, "(%02x)", c); | |
| } | |
| } | |
| - fprintf(stderr, "ESC\\\n"); | |
| + fprintf(stderr, (strescseq.term[0] == 0x1b) ? "ESC\\\n" : "BEL\n"); | |
| } | |
| void | |
| @@ -2179,9 +2411,12 @@ tdectest(char c) | |
| void | |
| tstrsequence(uchar c) | |
| { | |
| + strreset(); | |
| + | |
| switch (c) { | |
| case 0x90: /* DCS -- Device Control String */ | |
| c = 'P'; | |
| + term.esc |= ESC_DCS; | |
| break; | |
| case 0x9f: /* APC -- Application Program Command */ | |
| c = '_'; | |
| @@ -2193,7 +2428,6 @@ tstrsequence(uchar c) | |
| c = ']'; | |
| break; | |
| } | |
| - strreset(); | |
| strescseq.type = c; | |
| term.esc |= ESC_STR; | |
| } | |
| @@ -2220,6 +2454,7 @@ tcontrolcode(uchar ascii) | |
| case '\a': /* BEL */ | |
| if (term.esc & ESC_STR_END) { | |
| /* backwards compatibility to xterm */ | |
| + strescseq.term = STR_TERM_BEL; | |
| strhandle(); | |
| } else { | |
| xbell(); | |
| @@ -2295,6 +2530,38 @@ tcontrolcode(uchar ascii) | |
| term.esc &= ~(ESC_STR_END|ESC_STR); | |
| } | |
| +void | |
| +dcshandle(void) | |
| +{ | |
| + int bgcolor, transparent; | |
| + unsigned char r, g, b, a = 255; | |
| + | |
| + switch (csiescseq.mode[0]) { | |
| + default: | |
| + unknown: | |
| + fprintf(stderr, "erresc: unknown csi "); | |
| + csidump(); | |
| + /* die(""); */ | |
| + break; | |
| + case 'q': /* DECSIXEL */ | |
| + transparent = (csiescseq.narg >= 2 && csiescseq.arg[1] == 1); | |
| + if (IS_TRUECOL(term.c.attr.bg)) { | |
| + r = term.c.attr.bg >> 16 & 255; | |
| + g = term.c.attr.bg >> 8 & 255; | |
| + b = term.c.attr.bg >> 0 & 255; | |
| + } else { | |
| + xgetcolor(term.c.attr.bg, &r, &g, &b); | |
| + if (term.c.attr.bg == defaultbg) | |
| + a = dc.col[defaultbg].pixel >> 24 & 255; | |
| + } | |
| + bgcolor = a << 24 | r << 16 | g << 8 | b; | |
| + if (sixel_parser_init(&sixel_st, transparent, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0) | |
| + perror("sixel_parser_init() failed"); | |
| + term.mode |= MODE_SIXEL; | |
| + break; | |
| + } | |
| +} | |
| + | |
| /* | |
| * returns 1 when the sequence is finished and it hasn't to read | |
| * more characters for this sequence, otherwise 0 | |
| @@ -2313,6 +2580,7 @@ eschandle(uchar ascii) | |
| term.esc |= ESC_UTF8; | |
| return 0; | |
| case 'P': /* DCS -- Device Control String */ | |
| + term.esc |= ESC_DCS; | |
| case '_': /* APC -- Application Program Command */ | |
| case '^': /* PM -- Privacy Message */ | |
| case ']': /* OSC -- Operating System Command */ | |
| @@ -2372,8 +2640,10 @@ eschandle(uchar ascii) | |
| tcursor(CURSOR_LOAD); | |
| break; | |
| case '\\': /* ST -- String Terminator */ | |
| - if (term.esc & ESC_STR_END) | |
| + if (term.esc & ESC_STR_END) { | |
| + strescseq.term = STR_TERM_ST; | |
| strhandle(); | |
| + } | |
| break; | |
| default: | |
| fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", | |
| @@ -2392,7 +2662,8 @@ tputc(Rune u) | |
| Glyph *gp; | |
| control = ISCONTROL(u); | |
| - if (u < 127 || !IS_SET(MODE_UTF8)) { | |
| + if (u < 127 || !IS_SET(MODE_UTF8)) | |
| + { | |
| c[0] = u; | |
| width = len = 1; | |
| } else { | |
| @@ -2413,11 +2684,14 @@ tputc(Rune u) | |
| if (term.esc & ESC_STR) { | |
| if (u == '\a' || u == 030 || u == 032 || u == 033 || | |
| ISCONTROLC1(u)) { | |
| - term.esc &= ~(ESC_START|ESC_STR); | |
| + term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); | |
| term.esc |= ESC_STR_END; | |
| goto check_control_code; | |
| } | |
| + if (term.esc & ESC_DCS) | |
| + goto check_control_code; | |
| + | |
| if (strescseq.len+len >= strescseq.siz) { | |
| /* | |
| * Here is a bug in terminals. If the user never sends | |
| @@ -2471,6 +2745,15 @@ check_control_code: | |
| csihandle(); | |
| } | |
| return; | |
| + } 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; | |
| } else if (term.esc & ESC_UTF8) { | |
| tdefutf8(u); | |
| } else if (term.esc & ESC_ALTCHARSET) { | |
| @@ -2489,6 +2772,7 @@ check_control_code: | |
| */ | |
| return; | |
| } | |
| + | |
| if (selected(term.c.x, term.c.y)) | |
| selclear(); | |
| @@ -2541,7 +2825,11 @@ twrite(const char *buf, int buflen, int show_ctrl) | |
| int n; | |
| for (n = 0; n < buflen; n += charsize) { | |
| - if (IS_SET(MODE_UTF8)) { | |
| + if (IS_SET(MODE_SIXEL) && sixel_st.state != PS_ESC) { | |
| + charsize = sixel_parser_parse(&sixel_st, (const unsigned char*)buf + n, buflen - n); | |
| + continue; | |
| + } else if (IS_SET(MODE_UTF8)) | |
| + { | |
| /* process a complete utf8 char */ | |
| charsize = utf8decode(buf + n, &u, buflen - n); | |
| if (charsize == 0) | |
| @@ -2568,11 +2856,13 @@ twrite(const char *buf, int buflen, int show_ctrl) | |
| void | |
| tresize(int col, int row) | |
| { | |
| - int i; | |
| + int i, j; | |
| int minrow = MIN(row, term.row); | |
| int mincol = MIN(col, term.col); | |
| int *bp; | |
| - TCursor c; | |
| + int x2; | |
| + Line line; | |
| + ImageList *im, *next; | |
| if (col < 1 || row < 1) { | |
| fprintf(stderr, | |
| @@ -2580,23 +2870,19 @@ tresize(int col, int row) | |
| return; | |
| } | |
| - /* | |
| - * slide screen to keep cursor where we expect it - | |
| - * tscrollup would work here, but we can optimize to | |
| - * memmove because we're freeing the earlier lines | |
| - */ | |
| - for (i = 0; i <= term.c.y - row; i++) { | |
| - free(term.line[i]); | |
| - free(term.alt[i]); | |
| - } | |
| - /* ensure that both src and dst are not NULL */ | |
| - if (i > 0) { | |
| - memmove(term.line, term.line + i, row * sizeof(Line)); | |
| - memmove(term.alt, term.alt + i, row * sizeof(Line)); | |
| - } | |
| - for (i += row; i < term.row; i++) { | |
| - free(term.line[i]); | |
| - free(term.alt[i]); | |
| + /* scroll both screens independently */ | |
| + if (row < term.row) { | |
| + tcursor(CURSOR_SAVE); | |
| + tsetscroll(0, term.row - 1); | |
| + for (i = 0; i < 2; i++) { | |
| + if (term.c.y >= row) { | |
| + tscrollup(0, term.c.y - row + 1); | |
| + } | |
| + for (j = row; j < term.row; j++) | |
| + free(term.line[j]); | |
| + tswapscreen(); | |
| + tcursor(CURSOR_LOAD); | |
| + } | |
| } | |
| /* resize to new height */ | |
| @@ -2616,25 +2902,27 @@ tresize(int col, int row) | |
| term.line[i] = xmalloc(col * sizeof(Glyph)); | |
| term.alt[i] = xmalloc(col * sizeof(Glyph)); | |
| } | |
| - if (col > term.col) { | |
| + if (col > term.col) | |
| + { | |
| bp = term.tabs + term.col; | |
| - | |
| memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); | |
| + | |
| while (--bp > term.tabs && !*bp) | |
| /* nothing */ ; | |
| for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) | |
| *bp = 1; | |
| } | |
| + | |
| /* update terminal size */ | |
| term.col = col; | |
| term.row = row; | |
| + | |
| /* reset scrolling region */ | |
| tsetscroll(0, row-1); | |
| - /* make use of the LIMIT in tmoveto */ | |
| - tmoveto(term.c.x, term.c.y); | |
| /* Clearing both screens (it makes dirty all lines) */ | |
| - c = term.c; | |
| for (i = 0; i < 2; i++) { | |
| + tmoveto(term.c.x, term.c.y); /* make use of the LIMIT in tmoveto */ | |
| + tcursor(CURSOR_SAVE); | |
| if (mincol < col && 0 < minrow) { | |
| tclearregion(mincol, 0, col - 1, minrow - 1); | |
| } | |
| @@ -2644,7 +2932,22 @@ tresize(int col, int row) | |
| tswapscreen(); | |
| tcursor(CURSOR_LOAD); | |
| } | |
| - term.c = c; | |
| + | |
| + /* expand images into new text cells */ | |
| + for (i = 0; i < 2; i++) { | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + if (im->y < 0 || im->y >= term.row) { | |
| + delete_image(im); | |
| + continue; | |
| + } | |
| + line = term.line[im->y]; | |
| + x2 = MIN(im->x + im->cols, col) - 1; | |
| + if (mincol < col && x2 >= mincol && im->x < col) | |
| + tsetsixelattr(line, MAX(im->x, mincol), x2); | |
| + } | |
| + tswapscreen(); | |
| + } | |
| } | |
| void | |
| @@ -2667,6 +2970,7 @@ drawregion(int x1, int y1, int x2, int y2) | |
| } | |
| } | |
| + | |
| void | |
| draw(void) | |
| { | |
| @@ -2684,6 +2988,7 @@ draw(void) | |
| cx--; | |
| drawregion(0, 0, term.col, term.row); | |
| + | |
| xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
| term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
| term.ocx = cx; | |
| diff --git a/st.h b/st.h | |
| index fd3b0d8..0d04bd8 100644 | |
| --- a/st.h | |
| +++ b/st.h | |
| @@ -1,7 +1,14 @@ | |
| /* See LICENSE for license details. */ | |
| #include <stdint.h> | |
| +#include <time.h> | |
| #include <sys/types.h> | |
| +#include <X11/Xatom.h> | |
| +#include <X11/Xlib.h> | |
| +#include <X11/cursorfont.h> | |
| +#include <X11/keysym.h> | |
| +#include <X11/Xft/Xft.h> | |
| +#include <X11/XKBlib.h> | |
| /* macros */ | |
| #define MIN(a, b) ((a) < (b) ? (a) : (b)) | |
| @@ -21,21 +28,45 @@ | |
| #define IS_TRUECOL(x) (1 << 24 & (x)) | |
| enum glyph_attribute { | |
| - ATTR_NULL = 0, | |
| - ATTR_BOLD = 1 << 0, | |
| - ATTR_FAINT = 1 << 1, | |
| - ATTR_ITALIC = 1 << 2, | |
| - ATTR_UNDERLINE = 1 << 3, | |
| - ATTR_BLINK = 1 << 4, | |
| - ATTR_REVERSE = 1 << 5, | |
| - ATTR_INVISIBLE = 1 << 6, | |
| - ATTR_STRUCK = 1 << 7, | |
| - ATTR_WRAP = 1 << 8, | |
| - ATTR_WIDE = 1 << 9, | |
| - ATTR_WDUMMY = 1 << 10, | |
| + ATTR_NULL = 0, | |
| + ATTR_SET = 1 << 0, | |
| + ATTR_BOLD = 1 << 1, | |
| + ATTR_FAINT = 1 << 2, | |
| + ATTR_ITALIC = 1 << 3, | |
| + ATTR_UNDERLINE = 1 << 4, | |
| + ATTR_BLINK = 1 << 5, | |
| + ATTR_REVERSE = 1 << 6, | |
| + ATTR_INVISIBLE = 1 << 7, | |
| + ATTR_STRUCK = 1 << 8, | |
| + ATTR_WRAP = 1 << 9, | |
| + ATTR_WIDE = 1 << 10, | |
| + ATTR_WDUMMY = 1 << 11, | |
| + ATTR_SIXEL = 1 << 16, | |
| ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
| }; | |
| +typedef struct _ImageList { | |
| + struct _ImageList *next, *prev; | |
| + unsigned char *pixels; | |
| + void *pixmap; | |
| + void *clipmask; | |
| + int width; | |
| + int height; | |
| + int x; | |
| + int y; | |
| + int cols; | |
| + int cw; | |
| + int ch; | |
| + int transparent; | |
| +} ImageList; | |
| + | |
| +/* Used to control which screen(s) keybindings and mouse shortcuts apply to. */ | |
| +enum screen { | |
| + S_PRI = -1, /* primary screen */ | |
| + S_ALL = 0, /* both primary and alt screen */ | |
| + S_ALT = 1 /* alternate screen */ | |
| +}; | |
| + | |
| enum selection_mode { | |
| SEL_IDLE = 0, | |
| SEL_EMPTY = 1, | |
| @@ -59,16 +90,50 @@ 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 */ | |
| - ushort mode; /* attribute flags */ | |
| + uint32_t mode; /* attribute flags */ | |
| uint32_t fg; /* foreground */ | |
| uint32_t bg; /* background */ | |
| } Glyph; | |
| 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 */ | |
| + 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; | |
| + ImageList *images; /* sixel images */ | |
| + ImageList *images_alt; /* sixel images for alternate screen */ | |
| + Rune lastc; /* last printed char outside of sequence, 0 if control */ | |
| +} Term; | |
| + | |
| typedef union { | |
| int i; | |
| uint ui; | |
| @@ -77,9 +142,101 @@ 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 */ | |
| + int ch; /* char height */ | |
| + int cw; /* char width */ | |
| + 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, netwmiconname, netwmpid; | |
| + struct { | |
| + XIM xim; | |
| + XIC xic; | |
| + XPoint spot; | |
| + XVaNestedList spotlist; | |
| + } ime; | |
| + Draw draw; | |
| + Visual *vis; | |
| + XSetWindowAttributes attrs; | |
| + int scr; | |
| + int isfixed; /* is fixed geometry? */ | |
| + 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; | |
| + int screen; | |
| +} Shortcut; | |
| + | |
| +typedef struct { | |
| + uint mod; | |
| + uint button; | |
| + void (*func)(const Arg *); | |
| + const Arg arg; | |
| + uint release; | |
| + int screen; | |
| +} 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 tfulldirt(void); | |
| void printscreen(const Arg *); | |
| void printsel(const Arg *); | |
| @@ -87,6 +244,7 @@ void sendbreak(const Arg *); | |
| void toggleprinter(const Arg *); | |
| int tattrset(int); | |
| +int tisaltscr(void); | |
| void tnew(int, int); | |
| void tresize(int, int); | |
| void tsetdirtattr(int); | |
| @@ -100,6 +258,7 @@ void resettitle(void); | |
| void selclear(void); | |
| void selinit(void); | |
| +void selremove(void); | |
| void selstart(int, int, int); | |
| void selextend(int, int, int, int); | |
| int selected(int, int); | |
| @@ -111,6 +270,8 @@ void *xmalloc(size_t); | |
| void *xrealloc(void *, size_t); | |
| char *xstrdup(const char *); | |
| +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); | |
| + | |
| /* config.h globals */ | |
| extern char *utmp; | |
| extern char *scroll; | |
| @@ -124,3 +285,9 @@ extern unsigned int tabspaces; | |
| extern unsigned int defaultfg; | |
| extern unsigned int defaultbg; | |
| extern unsigned int defaultcs; | |
| + | |
| +extern DC dc; | |
| +extern XWindow xw; | |
| +extern XSelection xsel; | |
| +extern TermWindow win; | |
| +extern Term term; | |
| diff --git a/x.c b/x.c | |
| index d73152b..0366726 100644 | |
| --- a/x.c | |
| +++ b/x.c | |
| @@ -20,30 +20,8 @@ char *argv0; | |
| #include "st.h" | |
| #include "win.h" | |
| -/* 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; | |
| +#include <Imlib2.h> | |
| +#include "sixel.h" | |
| /* X modifiers */ | |
| #define XK_ANY_MOD UINT_MAX | |
| @@ -55,10 +33,11 @@ static void clipcopy(const Arg *); | |
| static void clippaste(const Arg *); | |
| static void numlock(const Arg *); | |
| static void selpaste(const Arg *); | |
| +static void ttysend(const Arg *); | |
| static void zoom(const Arg *); | |
| static void zoomabs(const Arg *); | |
| static void zoomreset(const Arg *); | |
| -static void ttysend(const Arg *); | |
| + | |
| /* config.h for applying patches and the configuration. */ | |
| #include "config.h" | |
| @@ -73,77 +52,10 @@ static void ttysend(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 */ | |
| - int ch; /* char height */ | |
| - int cw; /* char width */ | |
| - 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, netwmiconname, netwmpid; | |
| - struct { | |
| - XIM xim; | |
| - XIC xic; | |
| - XPoint spot; | |
| - XVaNestedList spotlist; | |
| - } ime; | |
| - Draw draw; | |
| - Visual *vis; | |
| - XSetWindowAttributes attrs; | |
| - int scr; | |
| - int isfixed; /* is fixed geometry? */ | |
| - 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); | |
| static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); | |
| -static void xdrawglyph(Glyph, int, int); | |
| +void xdrawglyph(Glyph, int, int); | |
| static void xclear(int, int, int, int); | |
| static int xgeommasktogravity(int); | |
| static int ximopen(Display *); | |
| @@ -172,7 +84,6 @@ static void cmessage(XEvent *); | |
| static void resize(XEvent *); | |
| static void focus(XEvent *); | |
| static uint buttonmask(uint); | |
| -static int mouseaction(XEvent *, uint); | |
| static void brelease(XEvent *); | |
| static void bpress(XEvent *); | |
| static void bmotion(XEvent *); | |
| @@ -181,6 +92,7 @@ static void selnotify(XEvent *); | |
| static void selclear_(XEvent *); | |
| static void selrequest(XEvent *); | |
| static void setsel(char *, Time); | |
| +static int mouseaction(XEvent *, uint); | |
| static void mousesel(XEvent *, int); | |
| static void mousereport(XEvent *); | |
| static char *kmap(KeySym, uint); | |
| @@ -216,10 +128,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 { | |
| @@ -254,6 +167,7 @@ static char *opt_title = NULL; | |
| static uint buttons; /* bit field of pressed buttons */ | |
| + | |
| void | |
| clipcopy(const Arg *dummy) | |
| { | |
| @@ -279,17 +193,24 @@ clippaste(const Arg *dummy) | |
| xw.win, CurrentTime); | |
| } | |
| +void | |
| +numlock(const Arg *dummy) | |
| +{ | |
| + win.mode ^= MODE_NUMLOCK; | |
| +} | |
| + | |
| void | |
| selpaste(const Arg *dummy) | |
| { | |
| + | |
| XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, | |
| xw.win, CurrentTime); | |
| } | |
| void | |
| -numlock(const Arg *dummy) | |
| +ttysend(const Arg *arg) | |
| { | |
| - win.mode ^= MODE_NUMLOCK; | |
| + ttywrite(arg->s, strlen(arg->s), 1); | |
| } | |
| void | |
| @@ -298,14 +219,31 @@ zoom(const Arg *arg) | |
| Arg larg; | |
| larg.f = usedfontsize + arg->f; | |
| - zoomabs(&larg); | |
| + if (larg.f >= 1.0) | |
| + zoomabs(&larg); | |
| } | |
| void | |
| zoomabs(const Arg *arg) | |
| { | |
| + int i; | |
| + ImageList *im; | |
| + | |
| xunloadfonts(); | |
| xloadfonts(usedfont, arg->f); | |
| + | |
| + /* delete old pixmaps so that xfinishdraw() can create new scaled ones */ | |
| + for (im = term.images, i = 0; i < 2; i++, im = term.images_alt) { | |
| + for (; im; im = im->next) { | |
| + if (im->pixmap) | |
| + XFreePixmap(xw.dpy, (Drawable)im->pixmap); | |
| + if (im->clipmask) | |
| + XFreePixmap(xw.dpy, (Drawable)im->clipmask); | |
| + im->pixmap = NULL; | |
| + im->clipmask = NULL; | |
| + } | |
| + } | |
| + | |
| cresize(0, 0); | |
| redraw(); | |
| xhints(); | |
| @@ -322,12 +260,6 @@ zoomreset(const Arg *arg) | |
| } | |
| } | |
| -void | |
| -ttysend(const Arg *arg) | |
| -{ | |
| - ttywrite(arg->s, strlen(arg->s), 1); | |
| -} | |
| - | |
| int | |
| evcol(XEvent *e) | |
| { | |
| @@ -344,6 +276,40 @@ evrow(XEvent *e) | |
| return y / win.ch; | |
| } | |
| +uint | |
| +buttonmask(uint button) | |
| +{ | |
| + return button == Button1 ? Button1Mask | |
| + : button == Button2 ? Button2Mask | |
| + : button == Button3 ? Button3Mask | |
| + : button == Button4 ? Button4Mask | |
| + : button == Button5 ? Button5Mask | |
| + : 0; | |
| +} | |
| + | |
| +int | |
| +mouseaction(XEvent *e, uint release) | |
| +{ | |
| + MouseShortcut *ms; | |
| + int screen = tisaltscr() ? S_ALT : S_PRI; | |
| + | |
| + /* ignore Button<N>mask for Button<N> - it's set on release */ | |
| + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); | |
| + | |
| + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { | |
| + if (ms->release == release && | |
| + ms->button == e->xbutton.button && | |
| + (!ms->screen || (ms->screen == screen)) && | |
| + (match(ms->mod, state) || /* exact or forced */ | |
| + match(ms->mod, state & ~forcemousemod))) { | |
| + ms->func(&(ms->arg)); | |
| + return 1; | |
| + } | |
| + } | |
| + | |
| + return 0; | |
| +} | |
| + | |
| void | |
| mousesel(XEvent *e, int done) | |
| { | |
| @@ -378,6 +344,7 @@ mousereport(XEvent *e) | |
| /* MODE_MOUSEMOTION: no reporting if no button is pressed */ | |
| if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) | |
| return; | |
| + | |
| /* Set btn to lowest-numbered pressed button, or 12 if no | |
| * buttons are pressed. */ | |
| for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) | |
| @@ -433,38 +400,6 @@ mousereport(XEvent *e) | |
| ttywrite(buf, len, 0); | |
| } | |
| -uint | |
| -buttonmask(uint button) | |
| -{ | |
| - return button == Button1 ? Button1Mask | |
| - : button == Button2 ? Button2Mask | |
| - : button == Button3 ? Button3Mask | |
| - : button == Button4 ? Button4Mask | |
| - : button == Button5 ? Button5Mask | |
| - : 0; | |
| -} | |
| - | |
| -int | |
| -mouseaction(XEvent *e, uint release) | |
| -{ | |
| - MouseShortcut *ms; | |
| - | |
| - /* ignore Button<N>mask for Button<N> - it's set on release */ | |
| - uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); | |
| - | |
| - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { | |
| - if (ms->release == release && | |
| - ms->button == e->xbutton.button && | |
| - (match(ms->mod, state) || /* exact or forced */ | |
| - match(ms->mod, state & ~forcemousemod))) { | |
| - ms->func(&(ms->arg)); | |
| - return 1; | |
| - } | |
| - } | |
| - | |
| - return 0; | |
| -} | |
| - | |
| void | |
| bpress(XEvent *e) | |
| { | |
| @@ -500,6 +435,7 @@ bpress(XEvent *e) | |
| xsel.tclick1 = now; | |
| selstart(evcol(e), evrow(e), snap); | |
| + | |
| } | |
| } | |
| @@ -515,6 +451,7 @@ propnotify(XEvent *e) | |
| xpev->atom == clipboard)) { | |
| selnotify(e); | |
| } | |
| + | |
| } | |
| void | |
| @@ -545,7 +482,8 @@ selnotify(XEvent *e) | |
| return; | |
| } | |
| - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { | |
| + if (e->type == PropertyNotify && nitems == 0 && rem == 0) | |
| + { | |
| /* | |
| * If there is some PropertyNotify with no data, then | |
| * this is the signal of the selection owner that all | |
| @@ -686,6 +624,7 @@ setsel(char *str, Time t) | |
| XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); | |
| if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) | |
| selclear(); | |
| + | |
| } | |
| void | |
| @@ -709,13 +648,17 @@ brelease(XEvent *e) | |
| if (mouseaction(e, 1)) | |
| return; | |
| - if (btn == Button1) | |
| + | |
| + if (btn == Button1) { | |
| mousesel(e, 1); | |
| + } | |
| + | |
| } | |
| void | |
| bmotion(XEvent *e) | |
| { | |
| + | |
| if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { | |
| mousereport(e); | |
| return; | |
| @@ -736,7 +679,7 @@ cresize(int width, int height) | |
| col = (win.w - 2 * borderpx) / win.cw; | |
| row = (win.h - 2 * borderpx) / win.ch; | |
| - col = MAX(1, col); | |
| + col = MAX(2, col); | |
| row = MAX(1, row); | |
| tresize(col, row); | |
| @@ -752,7 +695,8 @@ xresize(int col, int row) | |
| XFreePixmap(xw.dpy, xw.buf); | |
| xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, | |
| - DefaultDepth(xw.dpy, xw.scr)); | |
| + DefaultDepth(xw.dpy, xw.scr) | |
| + ); | |
| XftDrawChange(xw.draw, xw.buf); | |
| xclear(0, 0, win.w, win.h); | |
| @@ -856,6 +800,12 @@ xclear(int x1, int y1, int x2, int y2) | |
| x1, y1, x2-x1, y2-y1); | |
| } | |
| +void | |
| +xclearwin(void) | |
| +{ | |
| + xclear(0, 0, win.w, win.h); | |
| +} | |
| + | |
| void | |
| xhints(void) | |
| { | |
| @@ -907,6 +857,60 @@ xgeommasktogravity(int mask) | |
| return SouthEastGravity; | |
| } | |
| +int | |
| +ximopen(Display *dpy) | |
| +{ | |
| + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; | |
| + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; | |
| + | |
| + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); | |
| + if (xw.ime.xim == NULL) | |
| + return 0; | |
| + | |
| + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) | |
| + fprintf(stderr, "XSetIMValues: " | |
| + "Could not set XNDestroyCallback.\n"); | |
| + | |
| + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, | |
| + NULL); | |
| + | |
| + if (xw.ime.xic == NULL) { | |
| + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, | |
| + XIMPreeditNothing | XIMStatusNothing, | |
| + XNClientWindow, xw.win, | |
| + XNDestroyCallback, &icdestroy, | |
| + NULL); | |
| + } | |
| + if (xw.ime.xic == NULL) | |
| + fprintf(stderr, "XCreateIC: Could not create input context.\n"); | |
| + | |
| + return 1; | |
| +} | |
| + | |
| +void | |
| +ximinstantiate(Display *dpy, XPointer client, XPointer call) | |
| +{ | |
| + if (ximopen(dpy)) | |
| + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
| + ximinstantiate, NULL); | |
| +} | |
| + | |
| +void | |
| +ximdestroy(XIM xim, XPointer client, XPointer call) | |
| +{ | |
| + xw.ime.xim = NULL; | |
| + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
| + ximinstantiate, NULL); | |
| + XFree(xw.ime.spotlist); | |
| +} | |
| + | |
| +int | |
| +xicdestroy(XIC xim, XPointer client, XPointer call) | |
| +{ | |
| + xw.ime.xic = NULL; | |
| + return 1; | |
| +} | |
| + | |
| int | |
| xloadfont(Font *f, FcPattern *pattern) | |
| { | |
| @@ -1062,6 +1066,7 @@ xunloadfont(Font *f) | |
| void | |
| xunloadfonts(void) | |
| { | |
| + | |
| /* Free the loaded fonts in the font cache. */ | |
| while (frclen > 0) | |
| XftFontClose(xw.dpy, frc[--frclen].font); | |
| @@ -1072,60 +1077,6 @@ xunloadfonts(void) | |
| xunloadfont(&dc.ibfont); | |
| } | |
| -int | |
| -ximopen(Display *dpy) | |
| -{ | |
| - XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; | |
| - XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; | |
| - | |
| - xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); | |
| - if (xw.ime.xim == NULL) | |
| - return 0; | |
| - | |
| - if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) | |
| - fprintf(stderr, "XSetIMValues: " | |
| - "Could not set XNDestroyCallback.\n"); | |
| - | |
| - xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, | |
| - NULL); | |
| - | |
| - if (xw.ime.xic == NULL) { | |
| - xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, | |
| - XIMPreeditNothing | XIMStatusNothing, | |
| - XNClientWindow, xw.win, | |
| - XNDestroyCallback, &icdestroy, | |
| - NULL); | |
| - } | |
| - if (xw.ime.xic == NULL) | |
| - fprintf(stderr, "XCreateIC: Could not create input context.\n"); | |
| - | |
| - return 1; | |
| -} | |
| - | |
| -void | |
| -ximinstantiate(Display *dpy, XPointer client, XPointer call) | |
| -{ | |
| - if (ximopen(dpy)) | |
| - XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
| - ximinstantiate, NULL); | |
| -} | |
| - | |
| -void | |
| -ximdestroy(XIM xim, XPointer client, XPointer call) | |
| -{ | |
| - xw.ime.xim = NULL; | |
| - XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
| - ximinstantiate, NULL); | |
| - XFree(xw.ime.spotlist); | |
| -} | |
| - | |
| -int | |
| -xicdestroy(XIC xim, XPointer client, XPointer call) | |
| -{ | |
| - xw.ime.xic = NULL; | |
| - return 1; | |
| -} | |
| - | |
| void | |
| xinit(int cols, int rows) | |
| { | |
| @@ -1138,6 +1089,7 @@ xinit(int cols, int rows) | |
| if (!(xw.dpy = XOpenDisplay(NULL))) | |
| die("can't open display\n"); | |
| xw.scr = XDefaultScreen(xw.dpy); | |
| + | |
| xw.vis = XDefaultVisual(xw.dpy, xw.scr); | |
| /* font */ | |
| @@ -1165,7 +1117,8 @@ xinit(int cols, int rows) | |
| xw.attrs.bit_gravity = NorthWestGravity; | |
| xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | |
| | ExposureMask | VisibilityChangeMask | StructureNotifyMask | |
| - | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | |
| + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask | |
| + ; | |
| xw.attrs.colormap = xw.cmap; | |
| root = XRootWindow(xw.dpy, xw.scr); | |
| @@ -1180,6 +1133,7 @@ xinit(int cols, int rows) | |
| memset(&gcvalues, 0, sizeof(gcvalues)); | |
| gcvalues.graphics_exposures = False; | |
| + | |
| dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, | |
| &gcvalues); | |
| xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, | |
| @@ -1196,7 +1150,7 @@ xinit(int cols, int rows) | |
| /* input methods */ | |
| if (!ximopen(xw.dpy)) { | |
| XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
| - ximinstantiate, NULL); | |
| + ximinstantiate, NULL); | |
| } | |
| /* white cursor, black outline */ | |
| @@ -1240,6 +1194,7 @@ xinit(int cols, int rows) | |
| xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); | |
| if (xsel.xtarget == None) | |
| xsel.xtarget = XA_STRING; | |
| + | |
| } | |
| int | |
| @@ -1249,7 +1204,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| ushort mode, prevmode = USHRT_MAX; | |
| Font *font = &dc.font; | |
| int frcflags = FRC_NORMAL; | |
| - float runewidth = win.cw; | |
| + float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f); | |
| Rune rune; | |
| FT_UInt glyphidx; | |
| FcResult fcres; | |
| @@ -1258,7 +1213,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| FcCharSet *fccharset; | |
| int i, f, numspecs = 0; | |
| - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
| + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) | |
| + { | |
| /* Fetch rune and mode for current glyph. */ | |
| rune = glyphs[i].u; | |
| mode = glyphs[i].mode; | |
| @@ -1314,8 +1270,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| /* Nothing was found. Use fontconfig to find matching font. */ | |
| if (f >= frclen) { | |
| if (!font->set) | |
| - font->set = FcFontSort(0, font->pattern, | |
| - 1, 0, &fcres); | |
| + font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); | |
| fcsets[0] = font->set; | |
| /* | |
| @@ -1329,16 +1284,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| fccharset = FcCharSetCreate(); | |
| FcCharSetAddChar(fccharset, rune); | |
| - FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
| - fccharset); | |
| + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | |
| FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
| - FcConfigSubstitute(0, fcpattern, | |
| - FcMatchPattern); | |
| + FcConfigSubstitute(0, fcpattern, FcMatchPattern); | |
| FcDefaultSubstitute(fcpattern); | |
| - fontpattern = FcFontSetMatch(0, fcsets, 1, | |
| - fcpattern, &fcres); | |
| + fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); | |
| /* Allocate memory for the new cache entry. */ | |
| if (frclen >= frccap) { | |
| @@ -1346,8 +1298,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| frc = xrealloc(frc, frccap * sizeof(Fontcache)); | |
| } | |
| - frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
| - fontpattern); | |
| + frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); | |
| if (!frc[frclen].font) | |
| die("XftFontOpenPattern failed seeking fallback font: %s\n", | |
| strerror(errno)); | |
| @@ -1375,11 +1326,11 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |
| } | |
| void | |
| -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) | |
| -{ | |
| +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y | |
| +) { | |
| int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
| - int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
| - width = charlen * win.cw; | |
| + int width = charlen * win.cw; | |
| + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch; | |
| Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
| XRenderColor colfg, colbg; | |
| XRectangle r; | |
| @@ -1482,6 +1433,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i | |
| xclear(winx, winy + win.ch, winx + width, win.h); | |
| /* Clean up the region we want to draw to. */ | |
| + | |
| XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); | |
| /* Set the clip region because Xft is sometimes dirty. */ | |
| @@ -1513,10 +1465,11 @@ void | |
| xdrawglyph(Glyph g, int x, int y) | |
| { | |
| int numspecs; | |
| - XftGlyphFontSpec spec; | |
| + XftGlyphFontSpec *specs = xw.specbuf; | |
| - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
| - xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
| + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
| + xdrawglyphfontspecs(specs, g, numspecs, x, y | |
| + ); | |
| } | |
| void | |
| @@ -1527,6 +1480,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
| /* remove the old cursor */ | |
| if (selected(ox, oy)) | |
| og.mode ^= ATTR_REVERSE; | |
| + | |
| xdrawglyph(og, ox, oy); | |
| if (IS_SET(MODE_HIDE)) | |
| @@ -1535,7 +1489,8 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
| /* | |
| * Select the right color for the right mode. | |
| */ | |
| - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; | |
| + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE | |
| + ; | |
| if (IS_SET(MODE_REVERSE)) { | |
| g.mode |= ATTR_REVERSE; | |
| @@ -1555,6 +1510,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
| g.fg = defaultbg; | |
| g.bg = defaultcs; | |
| } | |
| + | |
| drawcol = dc.col[g.bg]; | |
| } | |
| @@ -1564,13 +1520,13 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
| case 7: /* st extension */ | |
| g.u = 0x2603; /* snowman (U+2603) */ | |
| /* FALLTHROUGH */ | |
| - case 0: /* Blinking Block */ | |
| - case 1: /* Blinking Block (Default) */ | |
| - case 2: /* Steady Block */ | |
| + case 0: /* Blinking block */ | |
| + case 1: /* Blinking block (default) */ | |
| + case 2: /* Steady block */ | |
| xdrawglyph(g, cx, cy); | |
| break; | |
| - case 3: /* Blinking Underline */ | |
| - case 4: /* Steady Underline */ | |
| + case 3: /* Blinking underline */ | |
| + case 4: /* Steady underline */ | |
| XftDrawRect(xw.draw, &drawcol, | |
| borderpx + cx * win.cw, | |
| borderpx + (cy + 1) * win.ch - \ | |
| @@ -1624,7 +1580,7 @@ xseticontitle(char *p) | |
| p = opt_title; | |
| if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, | |
| - &prop) != Success) | |
| + &prop) != Success) | |
| return; | |
| XSetWMIconName(xw.dpy, xw.win, &prop); | |
| XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); | |
| @@ -1641,7 +1597,7 @@ xsettitle(char *p) | |
| p = opt_title; | |
| if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, | |
| - &prop) != Success) | |
| + &prop) != Success) | |
| return; | |
| XSetWMName(xw.dpy, xw.win, &prop); | |
| XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); | |
| @@ -1659,6 +1615,7 @@ xdrawline(Line line, int x1, int y1, int x2) | |
| { | |
| int i, x, ox, numspecs; | |
| Glyph base, new; | |
| + | |
| XftGlyphFontSpec *specs = xw.specbuf; | |
| numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
| @@ -1683,16 +1640,132 @@ xdrawline(Line line, int x1, int y1, int x2) | |
| } | |
| if (i > 0) | |
| xdrawglyphfontspecs(specs, base, i, ox, y1); | |
| + | |
| } | |
| void | |
| xfinishdraw(void) | |
| { | |
| - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, | |
| - win.h, 0, 0); | |
| - XSetForeground(xw.dpy, dc.gc, | |
| - dc.col[IS_SET(MODE_REVERSE)? | |
| - defaultfg : defaultbg].pixel); | |
| + ImageList *im, *next; | |
| + Imlib_Image origin, scaled; | |
| + XGCValues gcvalues; | |
| + GC gc = NULL; | |
| + int width, height; | |
| + int del, desty, mode, x1, x2, xend; | |
| + int bw = borderpx, bh = borderpx; | |
| + Line line; | |
| + | |
| + for (im = term.images; im; im = next) { | |
| + next = im->next; | |
| + | |
| + /* do not draw or process the image, if it is not visible */ | |
| + if (im->x >= term.col || im->y >= term.row || im->y < 0) | |
| + continue; | |
| + | |
| + /* scale the image */ | |
| + width = MAX(im->width * win.cw / im->cw, 1); | |
| + height = MAX(im->height * win.ch / im->ch, 1); | |
| + if (!im->pixmap) { | |
| + im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, width, height, | |
| + DefaultDepth(xw.dpy, xw.scr) | |
| + ); | |
| + if (!im->pixmap) | |
| + continue; | |
| + if (win.cw == im->cw && win.ch == im->ch) { | |
| + XImage ximage = { | |
| + .format = ZPixmap, | |
| + .data = (char *)im->pixels, | |
| + .width = im->width, | |
| + .height = im->height, | |
| + .xoffset = 0, | |
| + .byte_order = sixelbyteorder, | |
| + .bitmap_bit_order = MSBFirst, | |
| + .bits_per_pixel = 32, | |
| + .bytes_per_line = im->width * 4, | |
| + .bitmap_unit = 32, | |
| + .bitmap_pad = 32, | |
| + .depth = 24 | |
| + }; | |
| + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); | |
| + if (im->transparent) | |
| + im->clipmask = (void *)sixel_create_clipmask((char *)im->pixels, width, height); | |
| + } else { | |
| + origin = imlib_create_image_using_data(im->width, im->height, (DATA32 *)im->pixels); | |
| + if (!origin) | |
| + continue; | |
| + imlib_context_set_image(origin); | |
| + imlib_image_set_has_alpha(1); | |
| + imlib_context_set_anti_alias(im->transparent ? 0 : 1); /* anti-aliasing messes up the clip mask */ | |
| + scaled = imlib_create_cropped_scaled_image(0, 0, im->width, im->height, width, height); | |
| + imlib_free_image_and_decache(); | |
| + if (!scaled) | |
| + continue; | |
| + imlib_context_set_image(scaled); | |
| + imlib_image_set_has_alpha(1); | |
| + XImage ximage = { | |
| + .format = ZPixmap, | |
| + .data = (char *)imlib_image_get_data_for_reading_only(), | |
| + .width = width, | |
| + .height = height, | |
| + .xoffset = 0, | |
| + .byte_order = sixelbyteorder, | |
| + .bitmap_bit_order = MSBFirst, | |
| + .bits_per_pixel = 32, | |
| + .bytes_per_line = width * 4, | |
| + .bitmap_unit = 32, | |
| + .bitmap_pad = 32, | |
| + .depth = 24 | |
| + }; | |
| + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); | |
| + if (im->transparent) | |
| + im->clipmask = (void *)sixel_create_clipmask((char *)imlib_image_get_data_for_reading_only(), width, height); | |
| + imlib_free_image_and_decache(); | |
| + } | |
| + } | |
| + | |
| + /* create GC */ | |
| + if (!gc) { | |
| + memset(&gcvalues, 0, sizeof(gcvalues)); | |
| + gcvalues.graphics_exposures = False; | |
| + gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues); | |
| + } | |
| + | |
| + /* set the clip mask */ | |
| + desty = bh + im->y * win.ch; | |
| + if (im->clipmask) { | |
| + XSetClipMask(xw.dpy, gc, (Drawable)im->clipmask); | |
| + XSetClipOrigin(xw.dpy, gc, bw + im->x * win.cw, desty); | |
| + } | |
| + | |
| + /* draw only the parts of the image that are not erased */ | |
| + line = term.line[im->y] + im->x; | |
| + xend = MIN(im->x + im->cols, term.col); | |
| + for (del = 1, x1 = im->x; x1 < xend; x1 = x2) { | |
| + mode = line->mode & ATTR_SIXEL; | |
| + for (x2 = x1 + 1; x2 < xend; x2++) { | |
| + if (((++line)->mode & ATTR_SIXEL) != mode) | |
| + break; | |
| + } | |
| + if (mode) { | |
| + XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, | |
| + (x1 - im->x) * win.cw, 0, | |
| + MIN((x2 - x1) * win.cw, width - (x1 - im->x) * win.cw), height, | |
| + bw + x1 * win.cw, desty); | |
| + del = 0; | |
| + } | |
| + } | |
| + if (im->clipmask) | |
| + XSetClipMask(xw.dpy, gc, None); | |
| + | |
| + /* if all the parts are erased, we can delete the entire image */ | |
| + if (del && im->x + im->cols <= term.col) | |
| + delete_image(im); | |
| + } | |
| + if (gc) | |
| + XFreeGC(xw.dpy, gc); | |
| + | |
| + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); | |
| + XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); | |
| } | |
| void | |
| @@ -1844,7 +1917,7 @@ kpress(XEvent *ev) | |
| XKeyEvent *e = &ev->xkey; | |
| KeySym ksym = NoSymbol; | |
| char buf[64], *customkey; | |
| - int len; | |
| + int len, screen; | |
| Rune c; | |
| Status status; | |
| Shortcut *bp; | |
| @@ -1859,9 +1932,13 @@ kpress(XEvent *ev) | |
| } else { | |
| len = XLookupString(e, buf, sizeof buf, &ksym, NULL); | |
| } | |
| + | |
| + screen = tisaltscr() ? S_ALT : S_PRI; | |
| + | |
| /* 1. shortcuts */ | |
| for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { | |
| - if (ksym == bp->keysym && match(bp->mod, e->state)) { | |
| + if (ksym == bp->keysym && match(bp->mod, e->state) && | |
| + (!bp->screen || bp->screen == screen)) { | |
| bp->func(&(bp->arg)); | |
| return; | |
| } | |
| @@ -1914,6 +1991,7 @@ cmessage(XEvent *e) | |
| void | |
| resize(XEvent *e) | |
| { | |
| + | |
| if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) | |
| return; | |
| @@ -1992,7 +2070,8 @@ run(void) | |
| * maximum latency intervals during `cat huge.txt`, and perfect | |
| * sync with periodic updates from animations/key-repeats/etc. | |
| */ | |
| - if (FD_ISSET(ttyfd, &rfd) || xev) { | |
| + if (FD_ISSET(ttyfd, &rfd) || xev) | |
| + { | |
| if (!drawing) { | |
| trigger = now; | |
| drawing = 1; | |
| @@ -2005,7 +2084,8 @@ run(void) | |
| /* idle detected or maxlatency exhausted -> draw */ | |
| timeout = -1; | |
| - if (blinktimeout && tattrset(ATTR_BLINK)) { | |
| + if (blinktimeout && tattrset(ATTR_BLINK)) | |
| + { | |
| timeout = blinktimeout - TIMEDIFF(now, lastblink); | |
| if (timeout <= 0) { | |
| if (-timeout > blinktimeout) /* start visible */ | |
| @@ -2026,14 +2106,16 @@ run(void) | |
| void | |
| usage(void) | |
| { | |
| - die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" | |
| - " [-n name] [-o file]\n" | |
| - " [-T title] [-t title] [-w windowid]" | |
| - " [[-e] command [args ...]]\n" | |
| - " %s [-aiv] [-c class] [-f font] [-g geometry]" | |
| - " [-n name] [-o file]\n" | |
| - " [-T title] [-t title] [-w windowid] -l line" | |
| - " [stty_args ...]\n", argv0, argv0); | |
| + die("usage: %s [-aiv] [-c class]" | |
| + " [-f font] [-g geometry]" | |
| + " [-n name] [-o file]\n" | |
| + " [-T title] [-t title] [-w windowid]" | |
| + " [[-e] command [args ...]]\n" | |
| + " %s [-aiv] [-c class]" | |
| + " [-f font] [-g geometry]" | |
| + " [-n name] [-o file]\n" | |
| + " [-T title] [-t title] [-w windowid] -l line" | |
| + " [stty_args ...]\n", argv0, argv0); | |
| } | |
| int | |
| @@ -2096,6 +2178,7 @@ run: | |
| setlocale(LC_CTYPE, ""); | |
| XSetLocaleModifiers(""); | |
| + | |
| cols = MAX(cols, 1); | |
| rows = MAX(rows, 1); | |
| tnew(cols, rows); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment