diff --git a/README.md b/README.md index 4155e2b..0b7549a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: +2022-03-10 - Added the background image patch + 2022-02-24 - Upgraded to st 0.8.5 e823e23, 2022-02-17 - removing osc_10_11_12_2 patch as no longer relevant 2021-08-18 - Added the CSI 22 & 23 patch @@ -95,6 +97,10 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - a patch that aims to prevent black bars being drawn on the edges of st terminals using the anysize patch + - [background-image](https://st.suckless.org/patches/background_image/) + - draws a background image in farbfeld format in place of the defaultbg color allowing for + pseudo transparency + - [blinking-cursor](https://st.suckless.org/patches/blinking_cursor/) - allows the use of a blinking cursor diff --git a/config.def.h b/config.def.h index 3835b61..5b0f055 100644 --- a/config.def.h +++ b/config.def.h @@ -14,6 +14,16 @@ static char *font2[] = { }; #endif // FONT2_PATCH +#if BACKGROUND_IMAGE_PATCH +/* + * background image + * expects farbfeld format + * pseudo transparency fixes coordinates to the screen origin + */ +static const char *bgfile = "/path/to/image.ff"; +static const int pseudotransparency = 0; +#endif // BACKGROUND_IMAGE_PATCH + #if RELATIVEBORDER_PATCH /* borderperc: percentage of cell width to use as a border * 0 = no border, 100 = border width is same as cell width */ diff --git a/patch/background_image_x.c b/patch/background_image_x.c new file mode 100644 index 0000000..06cc681 --- /dev/null +++ b/patch/background_image_x.c @@ -0,0 +1,104 @@ +void +updatexy() +{ + Window child; + XTranslateCoordinates(xw.dpy, xw.win, DefaultRootWindow(xw.dpy), 0, 0, &win.x, &win.y, &child); +} + +/* + * load farbfeld file to XImage + */ +XImage* +loadff(const char *filename) +{ + uint32_t i, hdr[4], w, h, size; + uint64_t *data; + FILE *f = fopen(filename, "rb"); + + if (f == NULL) { + fprintf(stderr, "could not load background image.\n"); + return NULL; + } + + if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) + if (ferror(f)) { + fprintf(stderr, "fread:"); + return NULL; + } + else { + fprintf(stderr, "fread: Unexpected end of file\n"); + return NULL; + } + + if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) { + fprintf(stderr, "Invalid magic value"); + return NULL; + } + + w = ntohl(hdr[2]); + h = ntohl(hdr[3]); + size = w * h; + data = malloc(size * sizeof(uint64_t)); + + if (fread(data, sizeof(uint64_t), size, f) != size) + if (ferror(f)) { + fprintf(stderr, "fread:"); + return NULL; + } + else { + fprintf(stderr, "fread: Unexpected end of file"); + return NULL; + } + + fclose(f); + + for (i = 0; i < size; i++) + data[i] = (data[i] & 0x00000000000000FF) << 16 | + (data[i] & 0x0000000000FF0000) >> 8 | + (data[i] & 0x000000FF00000000) >> 32 | + (data[i] & 0x00FF000000000000) >> 24; + + #if ALPHA_PATCH + XImage *xi = XCreateImage(xw.dpy, xw.vis, xw.depth, ZPixmap, 0, + (char *)data, w, h, 32, w * 8); + #else + XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr), + DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0, + (char *)data, w, h, 32, w * 8); + #endif // ALPHA_PATCH + xi->bits_per_pixel = 64; + return xi; +} + +/* + * initialize background image + */ +void +bginit() +{ + XGCValues gcvalues; + Drawable bgimg; + XImage *bgxi = loadff(bgfile); + + memset(&gcvalues, 0, sizeof(gcvalues)); + xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues); + if (!bgxi) + return; + #if ALPHA_PATCH + bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height, + xw.depth); + #else + bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height, + DefaultDepth(xw.dpy, xw.scr)); + #endif // ALPHA_PATCH + XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width, + bgxi->height); + XDestroyImage(bgxi); + XSetTile(xw.dpy, xw.bggc, bgimg); + XSetFillStyle(xw.dpy, xw.bggc, FillTiled); + if (pseudotransparency) { + updatexy(); + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); + } +} \ No newline at end of file diff --git a/patch/background_image_x.h b/patch/background_image_x.h new file mode 100644 index 0000000..ea67d7f --- /dev/null +++ b/patch/background_image_x.h @@ -0,0 +1,5 @@ +#include + +static void updatexy(void); +static XImage *loadff(const char *); +static void bginit(); \ No newline at end of file diff --git a/patch/x_include.c b/patch/x_include.c index 9f722ed..19d2a30 100644 --- a/patch/x_include.c +++ b/patch/x_include.c @@ -1,4 +1,7 @@ /* Patches */ +#if BACKGROUND_IMAGE_PATCH +#include "background_image_x.c" +#endif #if BOXDRAW_PATCH #include "boxdraw.c" #endif diff --git a/patch/x_include.h b/patch/x_include.h index b380d24..ea1ebc1 100644 --- a/patch/x_include.h +++ b/patch/x_include.h @@ -1,4 +1,7 @@ /* Patches */ +#if BACKGROUND_IMAGE_PATCH +#include "background_image_x.h" +#endif #if BOXDRAW_PATCH #include "boxdraw.h" #endif diff --git a/patches.def.h b/patches.def.h index d880242..be7e2fa 100644 --- a/patches.def.h +++ b/patches.def.h @@ -48,6 +48,12 @@ */ #define ANYSIZE_SIMPLE_PATCH 0 +/* Draws a background image in farbfeld format in place of the defaultbg color allowing for pseudo + * transparency. + * https://st.suckless.org/patches/background_image/ + */ +#define BACKGROUND_IMAGE_PATCH 0 + /* This patch allows the use of a blinking cursor. * Only cursor styles 0, 1, 3, 5, and 7 blink. Set cursorstyle accordingly. * Cursor styles are defined here: diff --git a/st.h b/st.h index 3badccf..5fc5797 100644 --- a/st.h +++ b/st.h @@ -181,6 +181,9 @@ typedef union { typedef struct { int tw, th; /* tty width and height */ int w, h; /* window width and height */ + #if BACKGROUND_IMAGE_PATCH + int x, y; /* window location */ + #endif // BACKGROUND_IMAGE_PATCH #if ANYSIZE_PATCH int hborderpx, vborderpx; #endif // ANYSIZE_PATCH @@ -210,6 +213,9 @@ typedef struct { XVaNestedList spotlist; } ime; Draw draw; + #if BACKGROUND_IMAGE_PATCH + GC bggc; /* Graphics Context for background */ + #endif // BACKGROUND_IMAGE_PATCH Visual *vis; XSetWindowAttributes attrs; #if HIDECURSOR_PATCH diff --git a/x.c b/x.c index de52422..5a8b945 100644 --- a/x.c +++ b/x.c @@ -548,6 +548,14 @@ propnotify(XEvent *e) xpev->atom == clipboard)) { selnotify(e); } + + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency && + !strncmp(XGetAtomName(xw.dpy, e->xproperty.atom), "_NET_WM_STATE", 13)) { + updatexy(); + redraw(); + } + #endif // BACKGROUND_IMAGE_PATCH } void @@ -578,7 +586,12 @@ selnotify(XEvent *e) return; } - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + #if BACKGROUND_IMAGE_PATCH + if (e->type == PropertyNotify && nitems == 0 && rem == 0 && !pseudotransparency) + #else + if (e->type == PropertyNotify && nitems == 0 && rem == 0) + #endif // BACKGROUND_IMAGE_PATCH + { /* * If there is some PropertyNotify with no data, then * this is the signal of the selection owner that all @@ -596,9 +609,15 @@ selnotify(XEvent *e) * when the selection owner does send us the next * chunk of data. */ + #if BACKGROUND_IMAGE_PATCH + if (!pseudotransparency) { + #endif // BACKGROUND_IMAGE_PATCH MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); + #if BACKGROUND_IMAGE_PATCH + } + #endif // BACKGROUND_IMAGE_PATCH /* * Deleting the property is the transfer start signal. @@ -997,7 +1016,11 @@ xsetcolorname(int x, const char *name) void xclear(int x1, int y1, int x2, int y2) { - #if INVERT_PATCH + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency) + XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y); + XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1); + #elif INVERT_PATCH Color c; c = dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg]; if (invertcolors) { @@ -1911,6 +1934,11 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i #endif // ANYSIZE_PATCH /* Clean up the region we want to draw to. */ + #if BACKGROUND_IMAGE_PATCH + if (bg == &dc.col[defaultbg]) + xclear(winx, winy, winx + width, winy + win.ch); + else + #endif // BACKGROUND_IMAGE_PATCH XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); #if WIDE_GLYPHS_PATCH } @@ -3094,6 +3122,15 @@ resize(XEvent *e) XWindowChanges wc; #endif // ST_EMBEDDER_PATCH + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency) { + if (e->xconfigure.width == win.w && + e->xconfigure.height == win.h && + e->xconfigure.x == win.x && e->xconfigure.y == win.y) + return; + updatexy(); + } else + #endif // BACKGROUND_IMAGE_PATCH if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) return; @@ -3379,6 +3416,9 @@ run: #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH tnew(cols, rows); xinit(cols, rows); + #if BACKGROUND_IMAGE_PATCH + bginit(); + #endif // BACKGROUND_IMAGE_PATCH xsetenv(); selinit(); #if WORKINGDIR_PATCH