dmenu-png-images-5.3.diff - sites - public wiki contents of suckless.org | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
dmenu-png-images-5.3.diff (13922B) | |
--- | |
1 From 743d86e56e0c1eb4255a08fe338db03752cc99e7 Mon Sep 17 00:00:00 2001 | |
2 From: Max Schillinger <[email protected]> | |
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… | |
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 *, s… | |
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, unsigne… | |
53 { | |
54 - unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpa… | |
55 + unsigned int w; | |
56 + if (startswith(image_prefix, str) && | |
57 + (w = drw_getimagewidth_clamp(drw, str + strlen(… | |
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, unsigne… | |
65 +{ | |
66 + unsigned int h; | |
67 + if (startswith(image_prefix, str) && | |
68 + (h = drw_getimageheight_clamp(drw, str + strlen… | |
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 … | |
85 for (i = 0, next = curr; next; next = next->right) | |
86 - if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)… | |
87 + if ((i += (lines > 0) | |
88 + ? texth_clamp(next->text, n, mw… | |
89 + : textw_clamp(next->text, n, im… | |
90 break; | |
91 for (i = 0, prev = curr; prev && prev->left; prev = prev->left) | |
92 - if ((i += (lines > 0) ? bh : textw_clamp(prev->left->te… | |
93 + if ((i += (lines > 0) | |
94 + ? texth_clamp(prev->left->text,… | |
95 + : textw_clamp(prev->left->text,… | |
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… | |
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… | |
135 + x = drawitem(item, x, 0, textw_clamp(item->text… | |
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… | |
155 - " [-nb color] [-nf color] [-sb color] [-sf colo… | |
156 + " [-nb color] [-nf color] [-sb color] [-sf colo… | |
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 … | |
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 previe… | |
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 *… | |
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 chu… | |
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… | |
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… | |
264 + return NULL; | |
265 + } | |
266 + | |
267 + size_t image_size, bytes_per_row; /* size in bytes, not in pixe… | |
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_PROGRESS… | |
278 + if (ret) { | |
279 + fprintf(stderr, "%s: progressive spng_decode_image() er… | |
280 + path, spng_strerror(ret)); | |
281 + return NULL; | |
282 + } | |
283 + | |
284 + /* ihdr.height will always be non-zero if spng_get_ihdr() succe… | |
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 … | |
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", p… | |
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, DefaultDep… | |
311 + ZPixmap, 0, image->buf, ihdr.width, … | |
312 + image->pixmap = XCreatePixmap(drw->dpy, drw->root, crop_width, … | |
313 + XPutImage(drw->dpy, image->pixmap, drw->gc, img, 0, 0, 0, 0, cr… | |
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… | |
322 +{ | |
323 + /* *x and *y refer to box position including padding, | |
324 + * *w and *h are the maximum image width and height without pad… | |
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 = ite… | |
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 + l… | |
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 in… | |
372 { | |
373 @@ -424,6 +593,26 @@ drw_font_getexts(Fnt *font, const char *text, unsig… | |
374 *h = font->h; | |
375 } | |
376 | |
377 +unsigned int | |
378 +drw_getimagewidth_clamp(Drw *drw, const char *path, unsigned int maxw, … | |
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,… | |
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 cha… | |
405 unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, uns… | |
406 void drw_font_getexts(Fnt *font, const char *text, unsigned int len, un… | |
407 | |
408 +/* Image abstraction */ | |
409 +unsigned int drw_getimagewidth_clamp(Drw *drw, const char *path, unsign… | |
410 +unsigned int drw_getimageheight_clamp(Drw *drw, const char *path, unsig… | |
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, i… | |
418 int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, un… | |
419 +void drw_image(Drw *drw, int *x, int *y, unsigned int *w, unsigned int … | |
420 | |
421 /* Map functions */ | |
422 void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsign… | |
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 |