dmenu-png-images-5.3.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       dmenu-png-images-5.3.diff (13922B)
       ---
            1 From 743d86e56e0c1eb4255a08fe338db03752cc99e7 Mon Sep 17 00:00:00 2001
            2 From: Max Schillinger <maxschillinger@web.de>
            3 Date: Fri, 1 Nov 2024 08:58:49 +0100
            4 Subject: [PATCH] Support PNG images using libspng
            5 
            6 ---
            7  config.mk |   2 +-
            8  dmenu.c   |  62 +++++++++++++++---
            9  drw.c     | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
           10  drw.h     |   5 ++
           11  util.c    |   6 ++
           12  util.h    |   1 +
           13  6 files changed, 254 insertions(+), 11 deletions(-)
           14 
           15 diff --git a/config.mk b/config.mk
           16 index 137f7c8..3217090 100644
           17 --- a/config.mk
           18 +++ b/config.mk
           19 @@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2
           20  
           21  # includes and libs
           22  INCS = -I$(X11INC) -I$(FREETYPEINC)
           23 -LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
           24 +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lspng
           25  
           26  # flags
           27  CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
           28 diff --git a/dmenu.c b/dmenu.c
           29 index 804da64..b0e4109 100644
           30 --- a/dmenu.c
           31 +++ b/dmenu.c
           32 @@ -38,11 +38,14 @@ static char *embed;
           33  static int bh, mw, mh;
           34  static int inputw = 0, promptw;
           35  static int lrpad; /* sum of left and right padding */
           36 +static int tbpad; /* sum of top and bottom padding for images */
           37  static size_t cursor;
           38  static struct item *items = NULL;
           39  static struct item *matches, *matchend;
           40  static struct item *prev, *curr, *next, *sel;
           41  static int mon = -1, screen;
           42 +static char *image_prefix = "PNG_IMAGE:";
           43 +static int image_size = -1; /* in pixels */
           44  
           45  static Atom clip, utf8;
           46  static Display *dpy;
           47 @@ -58,12 +61,26 @@ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
           48  static char *(*fstrstr)(const char *, const char *) = strstr;
           49  
           50  static unsigned int
           51 -textw_clamp(const char *str, unsigned int n)
           52 +textw_clamp(const char *str, unsigned int n, unsigned int maxw, unsigned int maxh)
           53  {
           54 -        unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
           55 +        unsigned int w;
           56 +        if (startswith(image_prefix, str) &&
           57 +                        (w = drw_getimagewidth_clamp(drw, str + strlen(image_prefix), maxw, maxh)))
           58 +                return MIN(w + lrpad, n);
           59 +        w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
           60          return MIN(w, n);
           61  }
           62  
           63 +static unsigned int
           64 +texth_clamp(const char *str, unsigned int n, unsigned int maxw, unsigned int maxh)
           65 +{
           66 +        unsigned int h;
           67 +        if (startswith(image_prefix, str) &&
           68 +                        (h = drw_getimageheight_clamp(drw, str + strlen(image_prefix), maxw, maxh)))
           69 +                return MIN(h + tbpad, n);
           70 +        return MIN(bh, n);
           71 +}
           72 +
           73  static void
           74  appenditem(struct item *item, struct item **list, struct item **last)
           75  {
           76 @@ -83,15 +100,19 @@ calcoffsets(void)
           77          int i, n;
           78  
           79          if (lines > 0)
           80 -                n = lines * bh;
           81 +                n = mh - bh;
           82          else
           83                  n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
           84          /* calculate which items will begin the next page and previous page */
           85          for (i = 0, next = curr; next; next = next->right)
           86 -                if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
           87 +                if ((i += (lines > 0)
           88 +                                        ? texth_clamp(next->text, n, mw - lrpad, image_size)
           89 +                                        : textw_clamp(next->text, n, image_size, bh)) > n)
           90                          break;
           91          for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
           92 -                if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
           93 +                if ((i += (lines > 0)
           94 +                                        ? texth_clamp(prev->left->text, n, mw - lrpad, image_size)
           95 +                                        : textw_clamp(prev->left->text, n, image_size, bh)) > n)
           96                          break;
           97  }
           98  
           99 @@ -139,7 +160,18 @@ drawitem(struct item *item, int x, int y, int w)
          100          else
          101                  drw_setscheme(drw, scheme[SchemeNorm]);
          102  
          103 -        return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
          104 +        int vertical = lines > 0;
          105 +        if (startswith(image_prefix, item->text)) {
          106 +                char *path = item->text + strlen(image_prefix);
          107 +                unsigned int image_width = vertical ? w - lrpad : image_size;
          108 +                unsigned int image_height = vertical ? image_size : bh;
          109 +                drw_image(drw, &x, &y, &image_width, &image_height,
          110 +                          lrpad, vertical ? tbpad : 0, path, vertical);
          111 +                if (image_width && image_height)
          112 +                        return vertical ? y : x;
          113 +        }
          114 +        int nextx = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
          115 +        return vertical ? y + bh : nextx;
          116  }
          117  
          118  static void
          119 @@ -169,8 +201,9 @@ drawmenu(void)
          120  
          121          if (lines > 0) {
          122                  /* draw vertical list */
          123 +                y = bh;
          124                  for (item = curr; item != next; item = item->right)
          125 -                        drawitem(item, x, y += bh, mw - x);
          126 +                        y = drawitem(item, x, y, mw - x);
          127          } else if (matches) {
          128                  /* draw horizontal list */
          129                  x += inputw;
          130 @@ -181,7 +214,7 @@ drawmenu(void)
          131                  }
          132                  x += w;
          133                  for (item = curr; item != next; item = item->right)
          134 -                        x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
          135 +                        x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"), image_size, bh));
          136                  if (next) {
          137                          w = TEXTW(">");
          138                          drw_setscheme(drw, scheme[SchemeNorm]);
          139 @@ -635,7 +668,10 @@ setup(void)
          140          /* calculate menu geometry */
          141          bh = drw->fonts->h + 2;
          142          lines = MAX(lines, 0);
          143 -        mh = (lines + 1) * bh;
          144 +        /* default values for image_size */
          145 +        if (image_size < 0)
          146 +                image_size = (lines > 0) ? 2 * bh : 8 * bh;
          147 +        mh = bh + ((lines > 0) ? MAX(lines * bh, image_size) : 0);
          148  #ifdef XINERAMA
          149          i = 0;
          150          if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
          151 @@ -715,7 +751,8 @@ static void
          152  usage(void)
          153  {
          154          die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
          155 -            "             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
          156 +            "             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n"
          157 +            "             [-ip image_prefix] [-is image_size]");
          158  }
          159  
          160  int
          161 @@ -757,6 +794,10 @@ main(int argc, char *argv[])
          162                          colors[SchemeSel][ColFg] = argv[++i];
          163                  else if (!strcmp(argv[i], "-w"))   /* embedding window id */
          164                          embed = argv[++i];
          165 +                else if (!strcmp(argv[i], "-ip"))  /* image prefix */
          166 +                        image_prefix = argv[++i];
          167 +                else if (!strcmp(argv[i], "-is"))  /* max. image preview size (height or width) */
          168 +                        image_size = atoi(argv[++i]);
          169                  else
          170                          usage();
          171  
          172 @@ -775,6 +816,7 @@ main(int argc, char *argv[])
          173          if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
          174                  die("no fonts could be loaded.");
          175          lrpad = drw->fonts->h;
          176 +        tbpad = lrpad / 2;
          177  
          178  #ifdef __OpenBSD__
          179          if (pledge("stdio rpath", NULL) == -1)
          180 diff --git a/drw.c b/drw.c
          181 index c41e6af..20c2125 100644
          182 --- a/drw.c
          183 +++ b/drw.c
          184 @@ -4,12 +4,24 @@
          185  #include <string.h>
          186  #include <X11/Xlib.h>
          187  #include <X11/Xft/Xft.h>
          188 +#include <spng.h>
          189  
          190  #include "drw.h"
          191  #include "util.h"
          192  
          193  #define UTF_INVALID 0xFFFD
          194  
          195 +struct image_item {
          196 +        const char *path;
          197 +        int width;
          198 +        int height;
          199 +        char *buf;
          200 +        Pixmap pixmap;
          201 +        struct image_item *next;
          202 +};
          203 +
          204 +static struct image_item *images = NULL;
          205 +
          206  static int
          207  utf8decode(const char *s_in, long *u, int *err)
          208  {
          209 @@ -382,6 +394,163 @@ no_match:
          210          return x + (render ? w : 0);
          211  }
          212  
          213 +static struct image_item *
          214 +load_image(Drw *drw, unsigned int maxw, unsigned int maxh, const char *path)
          215 +{
          216 +        FILE *png;
          217 +        spng_ctx *ctx = NULL;
          218 +        int ret = 0;
          219 +        struct spng_ihdr ihdr;
          220 +        struct spng_plte plte = {0};
          221 +        struct spng_row_info row_info = {0};
          222 +        char *spng_buf;
          223 +        int fmt = SPNG_FMT_RGBA8;
          224 +        int crop_width;
          225 +        int crop_height;
          226 +
          227 +        struct image_item *image = ecalloc(1, sizeof(struct image_item));
          228 +        image->path = path;
          229 +        image->next = images;
          230 +        images = image;
          231 +
          232 +        png = fopen(path, "rb");
          233 +        if (png == NULL) {
          234 +                fprintf(stderr, "error opening input file %s\n", path);
          235 +                return NULL;
          236 +        }
          237 +
          238 +        /* Create a context */
          239 +        ctx = spng_ctx_new(0);
          240 +        if (ctx == NULL) {
          241 +                fprintf(stderr, "%s: spng_ctx_new() failed\n", path);
          242 +                return NULL;
          243 +        }
          244 +
          245 +        /* Ignore and don't calculate chunk CRC's */
          246 +        spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE);
          247 +
          248 +        /* Set memory usage limits for storing standard and unknown chunks,
          249 +           this is important when reading untrusted files! */
          250 +        size_t limit = 1024 * 1024 * 64;
          251 +        spng_set_chunk_limits(ctx, limit, limit);
          252 +
          253 +        spng_set_png_file(ctx, png);
          254 +
          255 +        ret = spng_get_ihdr(ctx, &ihdr);
          256 +        if (ret) {
          257 +                fprintf(stderr, "%s: spng_get_ihdr() error: %s\n", path, spng_strerror(ret));
          258 +                return NULL;
          259 +        }
          260 +
          261 +        ret = spng_get_plte(ctx, &plte);
          262 +        if (ret && ret != SPNG_ECHUNKAVAIL) {
          263 +                fprintf(stderr, "%s: spng_get_plte() error: %s\n", path, spng_strerror(ret));
          264 +                return NULL;
          265 +        }
          266 +
          267 +        size_t image_size, bytes_per_row; /* size in bytes, not in pixels */
          268 +
          269 +        ret = spng_decoded_image_size(ctx, fmt, &image_size);
          270 +        if (ret)
          271 +                return NULL;
          272 +
          273 +        spng_buf = malloc(image_size);
          274 +        if (!spng_buf)
          275 +                return NULL;
          276 +
          277 +        ret = spng_decode_image(ctx, NULL, 0, fmt, SPNG_DECODE_PROGRESSIVE);
          278 +        if (ret) {
          279 +                fprintf(stderr, "%s: progressive spng_decode_image() error: %s\n",
          280 +                        path, spng_strerror(ret));
          281 +                return NULL;
          282 +        }
          283 +
          284 +        /* ihdr.height will always be non-zero if spng_get_ihdr() succeeds */
          285 +        bytes_per_row = image_size / ihdr.height;
          286 +        crop_width = MIN(ihdr.width, maxw);
          287 +        crop_height = MIN(ihdr.height, maxh);
          288 +
          289 +        do {
          290 +                ret = spng_get_row_info(ctx, &row_info);
          291 +                if (ret)
          292 +                        break;
          293 +                ret = spng_decode_row(ctx, spng_buf + row_info.row_num * bytes_per_row, bytes_per_row);
          294 +        } while (!ret && row_info.row_num < crop_height);
          295 +
          296 +        if (ret != SPNG_EOI && row_info.row_num < crop_height)
          297 +                fprintf(stderr, "%s: progressive decode error: %s\n", path, spng_strerror(ret));
          298 +
          299 +        image->buf = calloc(ihdr.width * crop_height * 4, sizeof(char));
          300 +        for (int i = 0; i < ihdr.width * crop_height; i++) {
          301 +                /* RGBA to BGRA */
          302 +                image->buf[i*4+2] = spng_buf[i*4+0];
          303 +                image->buf[i*4+1] = spng_buf[i*4+1];
          304 +                image->buf[i*4+0] = spng_buf[i*4+2];
          305 +                image->buf[i*4+3] = spng_buf[i*4+3];
          306 +        }
          307 +        image->width = crop_width;
          308 +        image->height = crop_height;
          309 +
          310 +        XImage *img = XCreateImage(drw->dpy, CopyFromParent, DefaultDepth(drw->dpy, drw->screen),
          311 +                                   ZPixmap, 0, image->buf, ihdr.width, crop_height, 32, 0);
          312 +        image->pixmap = XCreatePixmap(drw->dpy, drw->root, crop_width, crop_height, 24);
          313 +        XPutImage(drw->dpy, image->pixmap, drw->gc, img, 0, 0, 0, 0, crop_width, crop_height);
          314 +        spng_ctx_free(ctx);
          315 +        fclose(png);
          316 +        return image;
          317 +}
          318 +
          319 +void
          320 +drw_image(Drw *drw, int *x, int *y, unsigned int *w, unsigned int *h,
          321 +          unsigned int lrpad, unsigned int tbpad, const char *path, int vertical)
          322 +{
          323 +        /* *x and *y refer to box position including padding,
          324 +         * *w and *h are the maximum image width and height without padding */
          325 +        struct image_item *image = NULL;
          326 +        int render = *x || *y;
          327 +        int crop_width, crop_height;
          328 +
          329 +        // find path in images
          330 +        for (struct image_item *item = images; item != NULL; item = item->next) {
          331 +                if (!strcmp(item->path, path)) {
          332 +                        image = item;
          333 +                        if (!image->buf)
          334 +                                goto file_error;
          335 +                        break;
          336 +                }
          337 +        }
          338 +
          339 +        if (!image && !(image = load_image(drw, *w, *h, path)))
          340 +                goto file_error;
          341 +
          342 +        if (!render) {
          343 +                *w = image->width;
          344 +                *h = image->height;
          345 +                return;
          346 +        }
          347 +
          348 +        crop_width = MIN(image->width, *w);
          349 +        crop_height = MIN(image->height, *h);
          350 +        if (vertical)
          351 +                *h = crop_height;
          352 +        else
          353 +                *w = crop_width;
          354 +
          355 +        XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel);
          356 +        XFillRectangle(drw->dpy, drw->drawable, drw->gc, *x, *y, *w + lrpad, *h + tbpad);
          357 +        XCopyArea(drw->dpy, image->pixmap, drw->drawable, drw->gc, 0, 0,
          358 +                  crop_width, crop_height, *x + lrpad/2, *y + tbpad/2);
          359 +
          360 +        if (vertical)
          361 +                *y += *h + tbpad;
          362 +        else
          363 +                *x += *w + lrpad;
          364 +        return;
          365 +
          366 +file_error:
          367 +        *w = *h = 0;
          368 +}
          369 +
          370  void
          371  drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
          372  {
          373 @@ -424,6 +593,26 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
          374                  *h = font->h;
          375  }
          376  
          377 +unsigned int
          378 +drw_getimagewidth_clamp(Drw *drw, const char *path, unsigned int maxw, unsigned int maxh)
          379 +{
          380 +        int x = 0, y = 0;
          381 +        unsigned int w = maxw, h = maxh;
          382 +        if (drw && path && maxw && maxh)
          383 +                drw_image(drw, &x, &y, &w, &h, 0, 0, path, 0);
          384 +        return MIN(maxw, w);
          385 +}
          386 +
          387 +unsigned int
          388 +drw_getimageheight_clamp(Drw *drw, const char *path, unsigned int maxw, unsigned int maxh)
          389 +{
          390 +        int x = 0, y = 0;
          391 +        unsigned int w = maxw, h = maxh;
          392 +        if (drw && path && maxw && maxh)
          393 +                drw_image(drw, &x, &y, &w, &h, 0, 0, path, 1);
          394 +        return MIN(maxh, h);
          395 +}
          396 +
          397  Cur *
          398  drw_cur_create(Drw *drw, int shape)
          399  {
          400 diff --git a/drw.h b/drw.h
          401 index fd7631b..330722d 100644
          402 --- a/drw.h
          403 +++ b/drw.h
          404 @@ -38,6 +38,10 @@ unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
          405  unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
          406  void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
          407  
          408 +/* Image abstraction */
          409 +unsigned int drw_getimagewidth_clamp(Drw *drw, const char *path, unsigned int maxw, unsigned int maxh);
          410 +unsigned int drw_getimageheight_clamp(Drw *drw, const char *path, unsigned int maxw, unsigned int maxh);
          411 +
          412  /* Colorscheme abstraction */
          413  void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
          414  Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
          415 @@ -53,6 +57,7 @@ void drw_setscheme(Drw *drw, Clr *scm);
          416  /* Drawing functions */
          417  void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
          418  int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
          419 +void drw_image(Drw *drw, int *x, int *y, unsigned int *w, unsigned int *h, unsigned int lrpad, unsigned int tbpad, const char *path, int vertical);
          420  
          421  /* Map functions */
          422  void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
          423 diff --git a/util.c b/util.c
          424 index 8e26a51..975735b 100644
          425 --- a/util.c
          426 +++ b/util.c
          427 @@ -35,3 +35,9 @@ ecalloc(size_t nmemb, size_t size)
          428                  die("calloc:");
          429          return p;
          430  }
          431 +
          432 +int
          433 +startswith(const char* prefix, const char* str)
          434 +{
          435 +        return strncmp(prefix, str, strlen(prefix)) == 0;
          436 +}
          437 diff --git a/util.h b/util.h
          438 index c0a50d4..6db39c8 100644
          439 --- a/util.h
          440 +++ b/util.h
          441 @@ -7,3 +7,4 @@
          442  
          443  void die(const char *fmt, ...);
          444  void *ecalloc(size_t nmemb, size_t size);
          445 +int startswith(const char* prefix, const char* str);
          446 -- 
          447 2.47.0
          448