Compare commits

..

10 Commits

Author SHA1 Message Date
Bakkeby aa5957495d support colons in SGR character attributes
Patch by Mikhail Kot <to@myrrc.dev>
With some modifications to behave more like xterm (see note below).

Example:

	printf '\033[48;2;255:0:0mtest\n'

https://invisible-island.net/xterm/ctlseqs/ctlseqs.html

Some notes:

"CSI Pm m  Character Attributes (SGR).
[...]
o   xterm allows either colons (standard) or semicolons
(legacy) to separate the subparameters (but after the
first colon, colons must be used).
2024-05-02 09:28:20 +02:00
veltza 118e965d0c
sixel: add support for fully transparent bg (P2=1) (#132)
P2 selects how the terminal draws the background color.

P2                  Meaning
0 or 2 (default)    Pixel positions specified as 0 are set to the
                    current background color.
1                   Pixel positions specified as 0 remain at their
                    current color.

Both modes are now supported.

Ref. https://www.vt100.net/docs/vt3xx-gp/chapter14.html
2024-04-17 18:04:27 +02:00
Bakkeby dd8675943d Reset title when an empty title string is given
With this patch, st will reset its window title when an empty string is
given as the terminal title. For example:
	printf "\033]0;\007"

Some applications, like termdown, expect this functionality. xterm
implements it, but it seems that most other terminal emulators don't.
In any case, I don't see why there should ever be a case where the st
window doesn't have a title property.

Ref.
https://git.suckless.org/st/commit/497a75638291454875ba1ec8d484c7f3d6f41d66.html
2024-04-03 21:43:56 +02:00
veltza 9b463ac36d
sixel: prevent crashing when size is zero (#129)
Crashing happens when you zoom out and the width or height of an image
becomes zero.
2024-04-02 20:05:32 +02:00
Bakkeby fdae39e8b8 bump version to 0.9.1
ref.
https://git.suckless.org/st/commit/5ce971628106fb767ef91bf4386227423f5fdf98.html
2024-03-20 08:04:33 +01:00
Bakkeby dba3d178a4 config.def.h: improve latency for the default configuration
Ref.
https://git.suckless.org/st/commit/f20e169a20f3ee761f7e09714f1d4c10916cf4c6.html
2024-03-17 15:38:45 +01:00
Bakkeby 06bb70e2d1 externalpipe + reflow: compatibility fix correction ref. #125 2024-03-14 23:26:47 +01:00
Bakkeby f773016680 alpha: multiply each RGB value with alpha for a darker blend 2024-03-14 22:30:15 +01:00
Bakkeby 4997f1b1ae reflow: fix for scrollback buffer content getting lost following ctrl+l ref. #123 2024-03-14 16:58:22 +01:00
Bakkeby d318b3c03f externalpipe + reflow compatibility fix ref. #122 2024-03-14 10:01:37 +01:00
10 changed files with 114 additions and 50 deletions

View File

@ -1,4 +1,4 @@
Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this st 0.9 (95f22c5, 2024-03-04) project has a different take on st patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more.
Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this st 0.9.2 (5dbcca4, 2024-05-01) project has a different take on st patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more.
For example to include the `alpha` patch then you would only need to flip this setting from 0 to 1 in [patches.h](https://github.com/bakkeby/st-flexipatch/blob/master/patches.def.h):
```c

View File

@ -96,7 +96,7 @@ int allowwindowops = 0;
* near minlatency, but it waits longer for slow updates to avoid partial draw.
* low minlatency will tear/flicker more, as it can "detect" idle too early.
*/
static double minlatency = 8;
static double minlatency = 2;
static double maxlatency = 33;
#if SYNC_PATCH

View File

@ -1,5 +1,5 @@
# st version
VERSION = 0.9
VERSION = 0.9.2
# Customize below to fit your system

View File

@ -42,7 +42,11 @@ externalpipe(const Arg *arg)
newline = 0;
for (n = 0; n < term.row; n++) {
bp = term.line[n];
#if REFLOW_PATCH
lastpos = MIN(tlinelen(TLINE(n)) + 1, term.col) - 1;
#else
lastpos = MIN(tlinelen(n) + 1, term.col) - 1;
#endif // REFLOW_PATCH
if (lastpos < 0)
break;
end = &bp[lastpos + 1];

View File

@ -81,7 +81,7 @@ treflow(int col, int row)
int oce, nce, bot, scr;
int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
int cy = -1; /* proxy for new y coordinate of cursor */
int buflen, nlines, del;
int buflen, nlines;
Line *buf, bufline, line;
#if SIXEL_PATCH
ImageList *im, *next;
@ -219,21 +219,14 @@ treflow(int col, int row)
}
}
/* expand images into new text cells or
* delete images if there is text behind them */
/* expand images into new text cells */
for (im = term.images; im; im = next) {
next = im->next;
if (im->x < col) {
line = TLINE(im->y);
x2 = MIN(im->x + im->cols, col);
for (del = 0, x = im->x; x < x2; x++) {
if ((del = line[x].mode & ATTR_SET))
break;
line[x].u = ' ';
line[x].mode = ATTR_SIXEL;
}
if (del)
delete_image(im);
for (x = im->x; x < x2; x++)
line[x].mode |= ATTR_SIXEL;
}
}
#endif // SIXEL_PATCH

40
sixel.c
View File

@ -66,6 +66,8 @@ delete_image(ImageList *im)
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);
}
@ -217,6 +219,7 @@ sixel_image_deinit(sixel_image_t *image)
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)
@ -232,6 +235,7 @@ sixel_parser_init(sixel_state_t *st,
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;
@ -240,7 +244,7 @@ sixel_parser_init(sixel_state_t *st,
st->param = 0;
/* buffer initialization */
status = sixel_image_init(&st->image, 1, 1, fgcolor, bgcolor, use_private_register);
status = sixel_image_init(&st->image, 1, 1, fgcolor, transparent ? 0 : bgcolor, use_private_register);
return status;
}
@ -304,8 +308,10 @@ sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy,
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;
im->transparent = st->transparent;
}
if (!im || !im->pixels) {
for (im = *newimages; im; im = next) {
@ -652,3 +658,35 @@ 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;
}

View File

@ -39,6 +39,7 @@ typedef struct parser_context {
int attributed_pad;
int attributed_ph;
int attributed_pv;
int transparent;
int repeat_count;
int color_index;
int bgindex;
@ -52,10 +53,11 @@ typedef struct parser_context {
void scroll_images(int n);
void delete_image(ImageList *im);
int sixel_parser_init(sixel_state_t *st, sixel_color_t fgcolor, sixel_color_t bgcolor, unsigned char use_private_register, int cell_width, int cell_height);
int sixel_parser_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

50
st.c
View File

@ -1498,6 +1498,7 @@ csiparse(void)
{
char *p = csiescseq.buf, *np;
long int v;
int sep = ';'; /* colon or semi-colon, but not both */
csiescseq.narg = 0;
if (*p == '?') {
@ -1518,7 +1519,9 @@ csiparse(void)
#if UNDERCURL_PATCH
readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
#endif // UNDERCURL_PATCH
if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
if (sep == ';' && *p == ':')
sep = ':'; /* allow override to colon once */
if (*p != sep || csiescseq.narg == ESC_ARG_SIZ)
break;
p++;
}
@ -2227,10 +2230,8 @@ csihandle(void)
/* vte does this:
tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
/* alacritty does this: */
#if KEYBOARDSELECT_PATCH
for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--)
;
#endif // KEYBOARDSELECT_PATCH
#if SIXEL_PATCH
for (im = term.images; im; im = im->next)
n = MAX(im->y - term.scr, n);
@ -2716,14 +2717,18 @@ strhandle(void)
y1 = newimages->y;
x2 = x1 + newimages->cols;
y2 = y1 + numimages;
for (tail = NULL, im = term.images; im; im = next) {
next = im->next;
if (im->x >= x1 && im->x + im->cols <= x2 &&
im->y >= y1 && im->y <= y2) {
delete_image(im);
continue;
if (newimages->transparent) {
for (tail = term.images; tail && tail->next; tail = tail->next);
} else {
for (tail = NULL, im = term.images; im; im = next) {
next = im->next;
if (im->x >= x1 && im->x + im->cols <= x2 &&
im->y >= y1 && im->y <= y2) {
delete_image(im);
continue;
}
tail = im;
}
tail = im;
}
if (tail) {
tail->next = newimages;
@ -2749,8 +2754,7 @@ strhandle(void)
line = term.line[term.c.y];
}
for (x = im->x; x < x2; x++) {
line[x].u = ' ';
line[x].mode = ATTR_SIXEL;
line[x].mode |= ATTR_SIXEL;
}
term.dirty[MIN(im->y, term.row-1)] = 1;
if (!IS_SET(MODE_SIXEL_SDM) && i < numimages-1) {
@ -3099,7 +3103,7 @@ tcontrolcode(uchar ascii)
void
dcshandle(void)
{
int bgcolor;
int bgcolor, transparent;
unsigned char r, g, b, a = 255;
switch (csiescseq.mode[0]) {
@ -3121,6 +3125,7 @@ dcshandle(void)
break;
#endif // SYNC_PATCH
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;
@ -3131,7 +3136,7 @@ dcshandle(void)
a = dc.col[defaultbg].pixel >> 24 & 255;
}
bgcolor = a << 24 | r << 16 | g << 8 | b;
if (sixel_parser_init(&sixel_st, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0)
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;
@ -3634,10 +3639,8 @@ tresize(int col, int row)
line = term.line[im->y];
#endif // SCROLLBACK_PATCH
x2 = MIN(im->x + im->cols, term.col);
for (x = im->x; x < x2; x++) {
line[x].u = ' ';
line[x].mode = ATTR_SIXEL;
}
for (x = im->x; x < x2; x++)
line[x].mode |= ATTR_SIXEL;
}
tswapscreen();
}
@ -3694,12 +3697,10 @@ draw(void)
drawregion(0, 0, term.col, term.row);
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
if (!kbds_drawcursor()) {
#endif // KEYBOARDSELECT_PATCH
#if SCROLLBACK_PATCH
if (!kbds_drawcursor())
#elif REFLOW_PATCH || SCROLLBACK_PATCH
if (term.scr == 0)
#endif // SCROLLBACK_PATCH
#endif // SCROLLBACK_PATCH | REFLOW_PATCH | KEYBOARDSELECT_PATCH
#if LIGATURES_PATCH
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx],
@ -3708,9 +3709,6 @@ draw(void)
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
#endif // LIGATURES_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
}
#endif // KEYBOARDSELECT_PATCH
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();

2
st.h
View File

@ -77,6 +77,7 @@ typedef struct _ImageList {
struct _ImageList *next, *prev;
unsigned char *pixels;
void *pixmap;
void *clipmask;
int width;
int height;
int x;
@ -87,6 +88,7 @@ typedef struct _ImageList {
int cols;
int cw;
int ch;
int transparent;
} ImageList;
#endif // SIXEL_PATCH

43
x.c
View File

@ -325,7 +325,10 @@ zoomabs(const Arg *arg)
for (im = term.images; 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;
}
#endif // SIXEL_PATCH
@ -1018,6 +1021,9 @@ xloadcols(void)
dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
dc.col[defaultbg].pixel &= 0x00FFFFFF;
dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
dc.col[defaultbg].color.red *= alpha;
dc.col[defaultbg].color.green *= alpha;
dc.col[defaultbg].color.blue *= alpha;
#endif // ALPHA_PATCH
loaded = 1;
}
@ -1058,6 +1064,9 @@ xsetcolorname(int x, const char *name)
dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
dc.col[defaultbg].pixel &= 0x00FFFFFF;
dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
dc.col[defaultbg].color.red *= alpha;
dc.col[defaultbg].color.green *= alpha;
dc.col[defaultbg].color.blue *= alpha;
}
#endif // ALPHA_PATCH
return 0;
@ -2854,6 +2863,9 @@ xseticontitle(char *p)
XTextProperty prop;
DEFAULT(p, opt_title);
if (p[0] == '\0')
p = opt_title;
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
@ -2873,7 +2885,7 @@ xsettitle(char *p, int pop)
titlestack[tstki] = NULL;
tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE;
p = titlestack[tstki] ? titlestack[tstki] : opt_title;
} else if (p) {
} else if (p && p[0] != '\0') {
titlestack[tstki] = xstrdup(p);
} else {
titlestack[tstki] = NULL;
@ -2913,6 +2925,9 @@ xsettitle(char *p)
XTextProperty prop;
DEFAULT(p, opt_title);
if (p[0] == '\0')
p = opt_title;
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
@ -3126,7 +3141,7 @@ xfinishdraw(void)
XGCValues gcvalues;
GC gc;
int width, height;
int x, x2, del;
int x, x2, del, destx, desty;
Line line;
#endif // SIXEL_PATCH
@ -3139,8 +3154,8 @@ xfinishdraw(void)
continue;
/* scale the image */
width = im->width * win.cw / im->cw;
height = im->height * win.ch / im->ch;
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,
#if ALPHA_PATCH
@ -3149,6 +3164,8 @@ xfinishdraw(void)
DefaultDepth(xw.dpy, xw.scr)
#endif // ALPHA_PATCH
);
if (!im->pixmap)
continue;
if (win.cw == im->cw && win.ch == im->ch) {
XImage ximage = {
.format = ZPixmap,
@ -3169,12 +3186,15 @@ xfinishdraw(void)
#endif // ALPHA_PATCH
};
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)
@ -3200,6 +3220,8 @@ xfinishdraw(void)
#endif // ALPHA_PATCH
};
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();
}
}
@ -3228,12 +3250,17 @@ xfinishdraw(void)
gcvalues.graphics_exposures = False;
gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues);
#if ANYSIZE_PATCH
XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0,
width, height, win.hborderpx + im->x * win.cw, win.vborderpx + im->y * win.ch);
destx = win.hborderpx + im->x * win.cw;
desty = win.vborderpx + im->y * win.ch;
#else
XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0,
width, height, borderpx + im->x * win.cw, borderpx + im->y * win.ch);
destx = borderpx + im->x * win.cw;
desty = borderpx + im->y * win.ch;
#endif // ANYSIZE_PATCH
if (im->clipmask) {
XSetClipMask(xw.dpy, gc, (Drawable)im->clipmask);
XSetClipOrigin(xw.dpy, gc, destx, desty);
}
XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, width, height, destx, desty);
XFreeGC(xw.dpy, gc);
}
#endif // SIXEL_PATCH