Created
December 8, 2024 12:29
-
-
Save CommandMaker/27ba97427424c06aff54d0a2a91a4ef1 to your computer and use it in GitHub Desktop.
Suckless ST 0.9.2 SIXEL patch
This file contains 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
--- a/config.def.h 2024-12-08 12:07:57.276000000 +0100 | |
+++ b/config.def.h 2024-12-08 12:09:05.244000000 +0100 | |
@@ -23,7 +23,10 @@ | |
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; | |
--- a/config.mk 2024-12-08 11:45:30.869000000 +0100 | |
+++ b/config.mk 2024-12-08 11:44:21.560000000 +0100 | |
@@ -12,11 +12,14 @@ | |
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 @@ | |
# 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 | |
--- a/Makefile 2024-12-07 22:14:30.919000000 +0100 | |
+++ b/Makefile 2024-12-07 22:37:23.124000000 +0100 | |
@@ -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 | |
--- /dev/null 2024-12-07 21:24:28.020000000 +0100 | |
+++ b/sixel.c 2024-12-07 22:30:35.271000000 +0100 | |
@@ -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; | |
+} | |
--- /dev/null 2024-12-07 21:24:28.020000000 +0100 | |
+++ b/sixel.h 2024-12-07 22:30:35.270000000 +0100 | |
@@ -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 | |
--- /dev/null 2024-12-07 21:24:28.020000000 +0100 | |
+++ b/sixel_hls.c 2024-12-07 22:30:35.271000000 +0100 | |
@@ -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); | |
+} | |
--- /dev/null 2024-12-07 21:24:28.020000000 +0100 | |
+++ b/sixel_hls.h 2024-12-07 22:30:35.271000000 +0100 | |
@@ -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); | |
--- a/st.c 2024-12-07 22:14:30.920000000 +0100 | |
+++ b/st.c 2024-12-07 22:30:35.483000000 +0100 | |
@@ -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,16 +90,10 @@ | |
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; | |
int snap; | |
@@ -109,27 +111,6 @@ | |
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 @@ | |
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 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 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 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 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 @@ | |
xstrdup(const char *s) | |
{ | |
char *p; | |
- | |
if ((p = strdup(s)) == NULL) | |
die("strdup: %s\n", strerror(errno)); | |
@@ -283,24 +265,27 @@ | |
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 @@ | |
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 @@ | |
/* 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 @@ | |
*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 @@ | |
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 @@ | |
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 @@ | |
* 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 @@ | |
{ | |
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 @@ | |
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 @@ | |
int stat; | |
pid_t p; | |
- if ((p = waitpid(pid, &stat, WNOHANG)) < 0) | |
- die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); | |
+ while ((p = waitpid(-1, &stat, WNOHANG)) > 0) { | |
+ if (p == pid) { | |
- if (pid != p) | |
- return; | |
- | |
- 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 @@ | |
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 @@ | |
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 @@ | |
#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 @@ | |
return 0; | |
} | |
+int | |
+tisaltscr(void) | |
+{ | |
+ return IS_SET(MODE_ALTSCREEN); | |
+} | |
+ | |
void | |
tsetdirt(int top, int bot) | |
{ | |
@@ -988,6 +992,13 @@ | |
} | |
void | |
+tsetsixelattr(Line line, int x1, int x2) | |
+{ | |
+ for (; x1 <= x2; x1++) | |
+ line[x1].mode |= ATTR_SIXEL; | |
+} | |
+ | |
+void | |
tfulldirt(void) | |
{ | |
tsetdirt(0, term.row-1); | |
@@ -1008,15 +1019,18 @@ | |
} | |
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 @@ | |
tmoveto(0, 0); | |
tcursor(CURSOR_SAVE); | |
tclearregion(0, 0, term.col-1, term.row-1); | |
+ tdeleteimages(); | |
tswapscreen(); | |
} | |
} | |
@@ -1047,9 +1062,12 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
term.dirty[y] = 1; | |
term.line[y][x] = *attr; | |
term.line[y][x].u = u; | |
+ | |
} | |
void | |
@@ -1292,10 +1341,22 @@ | |
} | |
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 | |
@@ -1469,7 +1530,8 @@ | |
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) { | |
@@ -1540,8 +1602,7 @@ | |
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(); | |
@@ -1564,6 +1625,12 @@ | |
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", | |
@@ -1599,8 +1666,11 @@ | |
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: | |
@@ -1698,19 +1768,30 @@ | |
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; | |
@@ -1719,19 +1800,43 @@ | |
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; | |
@@ -1779,9 +1884,9 @@ | |
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; | |
@@ -1800,6 +1905,27 @@ | |
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) */ | |
tcursor(CURSOR_LOAD); | |
break; | |
@@ -1860,8 +1986,8 @@ | |
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", | |
@@ -1881,6 +2007,11 @@ | |
{ 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(); | |
@@ -1914,6 +2045,8 @@ | |
} | |
} | |
return; | |
+ case 8: /* Clear Hyperlinks */ | |
+ return; | |
case 10: | |
case 11: | |
case 12: | |
@@ -1963,6 +2096,95 @@ | |
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; | |
@@ -1984,6 +2206,16 @@ | |
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') | |
@@ -2018,7 +2250,7 @@ | |
fprintf(stderr, "(%02x)", c); | |
} | |
} | |
- fprintf(stderr, "ESC\\\n"); | |
+ fprintf(stderr, (strescseq.term[0] == 0x1b) ? "ESC\\\n" : "BEL\n"); | |
} | |
void | |
@@ -2156,9 +2388,12 @@ | |
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 = '_'; | |
@@ -2170,7 +2405,6 @@ | |
c = ']'; | |
break; | |
} | |
- strreset(); | |
strescseq.type = c; | |
term.esc |= ESC_STR; | |
} | |
@@ -2197,6 +2431,7 @@ | |
case '\a': /* BEL */ | |
if (term.esc & ESC_STR_END) { | |
/* backwards compatibility to xterm */ | |
+ strescseq.term = STR_TERM_BEL; | |
strhandle(); | |
} else { | |
xbell(); | |
@@ -2272,6 +2507,38 @@ | |
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 | |
@@ -2290,6 +2557,7 @@ | |
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 */ | |
@@ -2349,8 +2617,10 @@ | |
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", | |
@@ -2369,7 +2639,8 @@ | |
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 { | |
@@ -2390,11 +2661,14 @@ | |
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 | |
@@ -2448,6 +2722,15 @@ | |
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) { | |
@@ -2466,6 +2749,7 @@ | |
*/ | |
return; | |
} | |
+ | |
if (selected(term.c.x, term.c.y)) | |
selclear(); | |
@@ -2518,7 +2802,11 @@ | |
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) | |
@@ -2545,11 +2833,13 @@ | |
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, | |
@@ -2557,23 +2847,19 @@ | |
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 */ | |
@@ -2593,25 +2879,27 @@ | |
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); | |
} | |
@@ -2621,7 +2909,22 @@ | |
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 | |
@@ -2644,6 +2947,7 @@ | |
} | |
} | |
+ | |
void | |
draw(void) | |
{ | |
@@ -2661,6 +2965,7 @@ | |
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; | |
--- a/st.h 2024-12-07 22:14:30.920000000 +0100 | |
+++ b/st.h 2024-12-07 22:30:35.271000000 +0100 | |
@@ -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 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 @@ | |
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 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 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 *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 defaultfg; | |
extern unsigned int defaultbg; | |
extern unsigned int defaultcs; | |
+ | |
+extern DC dc; | |
+extern XWindow xw; | |
+extern XSelection xsel; | |
+extern TermWindow win; | |
+extern Term term; | |
--- a/x.c 2024-12-07 22:42:04.400000000 +0100 | |
+++ b/x.c 2024-12-07 22:30:35.466000000 +0100 | |
@@ -20,30 +20,8 @@ | |
#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 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 @@ | |
#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 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 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 @@ | |
}; | |
/* 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 uint buttons; /* bit field of pressed buttons */ | |
+ | |
void | |
clipcopy(const Arg *dummy) | |
{ | |
@@ -280,16 +194,23 @@ | |
} | |
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 @@ | |
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 @@ | |
} | |
} | |
-void | |
-ttysend(const Arg *arg) | |
-{ | |
- ttywrite(arg->s, strlen(arg->s), 1); | |
-} | |
- | |
int | |
evcol(XEvent *e) | |
{ | |
@@ -344,6 +276,40 @@ | |
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 @@ | |
/* 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 @@ | |
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 @@ | |
xsel.tclick1 = now; | |
selstart(evcol(e), evrow(e), snap); | |
+ | |
} | |
} | |
@@ -515,6 +451,7 @@ | |
xpev->atom == clipboard)) { | |
selnotify(e); | |
} | |
+ | |
} | |
void | |
@@ -545,7 +482,8 @@ | |
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 @@ | |
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); | |
if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) | |
selclear(); | |
+ | |
} | |
void | |
@@ -709,13 +648,17 @@ | |
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 @@ | |
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 @@ | |
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); | |
@@ -857,6 +801,12 @@ | |
} | |
void | |
+xclearwin(void) | |
+{ | |
+ xclear(0, 0, win.w, win.h); | |
+} | |
+ | |
+void | |
xhints(void) | |
{ | |
XClassHint class = {opt_name ? opt_name : termname, | |
@@ -908,6 +858,60 @@ | |
} | |
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) | |
{ | |
FcPattern *configured; | |
@@ -1062,6 +1066,7 @@ | |
void | |
xunloadfonts(void) | |
{ | |
+ | |
/* Free the loaded fonts in the font cache. */ | |
while (frclen > 0) | |
XftFontClose(xw.dpy, frc[--frclen].font); | |
@@ -1072,60 +1077,6 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
/* input methods */ | |
if (!ximopen(xw.dpy)) { | |
XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | |
- ximinstantiate, NULL); | |
+ ximinstantiate, NULL); | |
} | |
/* white cursor, black outline */ | |
@@ -1240,6 +1194,7 @@ | |
xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); | |
if (xsel.xtarget == None) | |
xsel.xtarget = XA_STRING; | |
+ | |
} | |
int | |
@@ -1249,7 +1204,7 @@ | |
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 @@ | |
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 @@ | |
/* 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 @@ | |
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 @@ | |
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 @@ | |
} | |
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 @@ | |
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 @@ | |
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 @@ | |
/* 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 @@ | |
/* | |
* 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 @@ | |
g.fg = defaultbg; | |
g.bg = defaultcs; | |
} | |
+ | |
drawcol = dc.col[g.bg]; | |
} | |
@@ -1564,13 +1520,13 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
{ | |
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 @@ | |
} | |
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 @@ | |
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 @@ | |
} 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 @@ | |
void | |
resize(XEvent *e) | |
{ | |
+ | |
if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) | |
return; | |
@@ -1992,7 +2070,8 @@ | |
* 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 @@ | |
/* 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 @@ | |
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 @@ | |
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