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