From f0704cba12e437bbe72744281af71ec19951db46 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Tue, 29 Nov 2022 20:42:42 -0500 Subject: [PATCH] Make EWMH icon generation more abstract. Replace the low level EWMH icon drawing functions with new ones that are much more generic: instead of the specific icon format these new ones produce 1-byte-per-pixel images with user-controlled byte values. These new functions will hopefully be useful for other kinds of icon formats. In particular, to directly create colour-index bitmaps without working backwards from the ARGB values generated currently. Then for the actual EWMH icon support wrap it all up in a single function that allocates and creates all of the icon data in one shot, using the new generic functions under the hood. This simplifies the calling code somewhat. As a bonus, the overall code size seems to be reduced. --- Makefile.am | 6 +- src/ewmhicon.c | 157 +++++++++++++++++++----------------------------- src/ewmhicon.h | 36 +++++------ src/icon.c | 104 ++++++++++++++++++++++++++++++++ src/icon.h | 48 +++++++++++++++ src/motif.c | 1 + src/motif.h | 1 - src/x11.c | 53 +++++++---------- t/ewmhicon.c | 159 +++++++++++++++++++++++++++++++++---------------- 9 files changed, 364 insertions(+), 201 deletions(-) create mode 100644 src/icon.c create mode 100644 src/icon.h diff --git a/Makefile.am b/Makefile.am index c3efff6..c736530 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,13 +29,13 @@ endif EXTRA_DIST += doc/rrace-motif.1 -noinst_HEADERS = conf_post.h src/version.h +noinst_HEADERS = conf_post.h src/version.h src/icon.h src/ewmhicon.h rrace_curses_SOURCES = common/src/help.c src/game.c src/version.c rrace_curses_LDADD = $(libcursesmain_a_OBJECTS) libgnu.a $(CURSES_LIBS) rrace_motif_SOURCES = src/game.c src/x11.c src/game.h src/motif.h \ - src/colour.h src/ewmhicon.c src/ewmhicon.h \ + src/colour.h src/ewmhicon.c src/icon.c \ src/version.c src/xcounter.c src/xcounter.h rrace_motif_LDADD = $(libmotifmain_a_OBJECTS) $(libmotifui_a_OBJECTS) \ $(libglohelp_a_OBJECTS) libgnu.a $(MOTIF_LIBS) \ @@ -98,7 +98,7 @@ t_boardbit_SOURCES = t/boardbit.c t_boardbit_LDADD = libgnu.a $(t_boardbit_OBJECTS): $(gnulib_headers) -t_ewmhicon_SOURCES = t/ewmhicon.c src/ewmhicon.c common/src/help.c +t_ewmhicon_SOURCES = t/ewmhicon.c src/ewmhicon.c src/icon.c common/src/help.c t_ewmhicon_LDADD = libgnu.a $(MOTIF_LIBS) $(t_ewmhicon_OBJECTS): $(gnulib_headers) diff --git a/src/ewmhicon.c b/src/ewmhicon.c index 6bdf8e9..e0ab8a5 100644 --- a/src/ewmhicon.c +++ b/src/ewmhicon.c @@ -25,130 +25,95 @@ # include # include #endif + #include "ewmhicon.h" #include "colour.h" +#include "icon.h" -static unsigned long scale16to8(unsigned x) -{ - return x*0xfful / 0xffff; -} - -static unsigned long wm_pixel(const XColor *c) -{ - return 0xff000000 - | scale16to8(c->red) << 16 - | scale16to8(c->green) << 8 - | scale16to8(c->blue); -} - -static void format_row(unsigned long *row, const XColor *c, - unsigned s, unsigned y, unsigned w, unsigned h) -{ - unsigned x; - - for (x = 0; x < w; x++) { - row[x] = wm_pixel(&c[COLOUR_PRIMARY]); - if (x < s || y < s) - row[x] = wm_pixel(&c[COLOUR_LIGHT]); - if ((x+s >= w && x+y >= w) || (y+s >= h && x+y >= h)) - row[x] = wm_pixel(&c[COLOUR_DARK]); - } -} +enum { ICON_16x16, ICON_24x24, ICON_32x32, ICON_48x48, ICON_MAX }; /* - * The 16x16 icon is drawn with 1px shadow, 6x6 tiles, with 1 pixel cropped off - * all the edge tiles + * Allocate storage for the EWMH _NET_WM_ICON array. The sizes array is + * populated with pointers to the beginning of each icon's pixel data. For + * example, sizes[ICON_24x24] points to the first pixel of the 24x24 image. + * + * The returned value can then be passed to XChangeProperty to set the icon, + * (use EWMH_ICON_NELEM for the number of elements) and must be freed by the + * caller. */ -void ewmh_tile16(unsigned long *icon, const XColor *c, int tile_x, int tile_y) +static void *ewmh_icon_alloc(unsigned long **sizes) { - int out_x, out_y, out_w, y; - unsigned long row[6]; - - out_x = tile_x * 11 / 2; - out_y = tile_y * 11 / 2; - out_w = (5 + (tile_x == 1)) * sizeof row[0]; + unsigned long *buf; - for (y = 0+(tile_y == 0); y < 6-(tile_y == 2); y++) { - format_row(row, c, 1, y, 6, 6); - memcpy(&icon[16 * out_y++ + out_x], &row[tile_x == 0], out_w); - } -} + buf = calloc(sizeof *buf, EWMH_ICON_NELEM); + if (buf) { + sizes[ICON_16x16] = buf; + *sizes[ICON_16x16]++ = 16; + *sizes[ICON_16x16]++ = 16; -/* - * The 24x24 icon is drawn with 1px shadow and 8x8 tiles. - */ -void ewmh_tile24(unsigned long *icon, const XColor *c, int tile_x, int tile_y) -{ - int out_x, out_y, y; - unsigned long row[8]; + sizes[ICON_24x24] = sizes[ICON_16x16] + 16*16; + *sizes[ICON_24x24]++ = 24; + *sizes[ICON_24x24]++ = 24; - out_x = tile_x * 8; - out_y = tile_y * 8; + sizes[ICON_32x32] = sizes[ICON_24x24] + 24*24; + *sizes[ICON_32x32]++ = 32; + *sizes[ICON_32x32]++ = 32; - for (y = 0; y < 8; y++) { - format_row(row, c, 1, y, 8, 8); - memcpy(&icon[24 * out_y++ + out_x], row, sizeof row); + sizes[ICON_48x48] = sizes[ICON_32x32] + 32*32; + *sizes[ICON_48x48]++ = 48; + *sizes[ICON_48x48]++ = 48; } + + return buf; } -/* - * The 32x32 icon is drawn with 1px shadow with slightly uneven tiles on - * an 11-10-11 pixel grid. - */ -void ewmh_tile32(unsigned long *icon, const XColor *c, int tile_x, int tile_y) +static unsigned long scale16to8(unsigned x) { - int out_x, out_y, out_w, out_h, y; - unsigned long row[11]; - - out_x = 10*tile_x + (tile_x > 0); - out_y = 10*tile_y + (tile_y > 0); - out_w = 10 + (tile_x != 1); - out_h = 10 + (tile_y != 1); - - for (y = 0; y < out_h; y++) { - format_row(row, c, 1, y, out_w, out_h); - memcpy(&icon[32 * out_y++ + out_x], row, out_w * sizeof row[0]); - } + return x*0xfful / 0xffff; } -/* - * The 48x48 icon is drawn with 2px shadow and 16x16 tiles. - */ -void ewmh_tile48(unsigned long *icon, const XColor *c, int tile_x, int tile_y) +static unsigned long wm_pixel(const XColor *c) { - int out_x, out_y, y; - unsigned long row[16]; + return 0xff000000 + | scale16to8(c->red) << 16 + | scale16to8(c->green) << 8 + | scale16to8(c->blue); +} - out_x = tile_x * 16; - out_y = tile_y * 16; +static void do_remap(void *icon, unsigned size, const XColor *map) +{ + unsigned char *index = icon; + unsigned long *argb = icon; + unsigned i, count = size*size; - for (y = 0; y < 16; y++) { - format_row(row, c, 2, y, 16, 16); - memcpy(&icon[48 * out_y++ + out_x], row, sizeof row); + for (i = 0; i < count; i++) { + argb[count-i-1] = wm_pixel(map + index[count-i-1]); } } -void *ewmh_icon_alloc(unsigned long **sizes) +void *ewmh_icon_generate(const unsigned long *seq, const XColor *map) { - unsigned long *buf; + unsigned long *buf, *sizes[ICON_MAX]; + unsigned i, j; - buf = calloc(sizeof *buf, EWMH_ICON_NELEM); - if (buf) { - sizes[ICON_16x16] = buf; - *sizes[ICON_16x16]++ = 16; - *sizes[ICON_16x16]++ = 16; + buf = ewmh_icon_alloc(sizes); + if (!buf) + return NULL; - sizes[ICON_24x24] = sizes[ICON_16x16] + 16*16; - *sizes[ICON_24x24]++ = 24; - *sizes[ICON_24x24]++ = 24; + for (i = 0; i < 9; i++) { + unsigned x = i%3, y = i/3; - sizes[ICON_32x32] = sizes[ICON_24x24] + 24*24; - *sizes[ICON_32x32]++ = 32; - *sizes[ICON_32x32]++ = 32; + icon_tile16((void *)sizes[ICON_16x16], seq[i], x, y); + icon_tile24((void *)sizes[ICON_24x24], seq[i], x, y); + icon_tile32((void *)sizes[ICON_32x32], seq[i], x, y); + icon_tile48((void *)sizes[ICON_48x48], seq[i], x, y); + } - sizes[ICON_48x48] = sizes[ICON_32x32] + 32*32; - *sizes[ICON_48x48]++ = 48; - *sizes[ICON_48x48]++ = 48; + for (i = 0; i < ICON_MAX; i++) { + /* Produces the sequence 16, 24, 32, 48 */ + unsigned size = 16 + (1u<. */ -#ifndef EWMHICON_H_ -#define EWMHICON_H_ +#ifndef RRACE_EWMHICON_H_ +#define RRACE_EWMHICON_H_ /* * Define X_DISPLAY_MISSING to allow building without X11 (for test purposes) @@ -32,38 +32,34 @@ typedef struct { unsigned short red, green, blue; } XColor; -typedef struct stub_widget *Widget; - #endif /* - * Helpers for drawing window icons in various sizes. The tileXX functions - * draw one (out of 9) tiles at a particular size using a particular colour - * set. Call for each tile position to draw a complete icon. + * Size of generated EWMH icon data (nelements argument to XChangeProperty). */ -enum { ICON_16x16, ICON_24x24, ICON_32x32, ICON_48x48, ICON_MAX }; #define EWMH_ICON_NELEM (2+16*16 + 2+24*24 + 2+32*32 + 2+48*48) -void ewmh_tile16(unsigned long *icon, const XColor *c, int tile_x, int tile_y); -void ewmh_tile24(unsigned long *icon, const XColor *c, int tile_x, int tile_y); -void ewmh_tile32(unsigned long *icon, const XColor *c, int tile_x, int tile_y); -void ewmh_tile48(unsigned long *icon, const XColor *c, int tile_x, int tile_y); - /* - * Allocate storage for the EWMH _NET_WM_ICON array. The sizes array is - * populated with pointers to the beginning of each icon's pixel data. For - * example, sizes[ICON_24x24] points to the first pixel of the 24x24 image. + * Generate EWMH icon data based on a sequence of tile colours. Internally, + * this calls icon_tile16 and friends to draw each of the icon patterns. + * + * The sequence specifies the colour of all 9 icon tiles, beginning from the + * top left in row-major order. Each value specifies three offsets into + * the provided colour map, encoded in the same manner as icon_tile16 et al. * - * The returned value can then be passed to XChangeProperty to set the icon, - * (use EWMH_ICON_NELEM for the number of elements) and must be freed by the - * caller. + * Returns a newly-allocated buffer which is suitable for use directly with + * XChangeProperty, and should be freed by the caller. + * + * If allocation fails, a null pointer is returned. */ -void *ewmh_icon_alloc(unsigned long **sizes); +void *ewmh_icon_generate(const unsigned long *seq, const XColor *map); +#if !X_DISPLAY_MISSING /* * Check if the root window indicates support for EWMH icons. Returns 1 if * supported, or 0 otherwise. */ int ewmh_probe_wm_icon(Widget shell); +#endif #endif diff --git a/src/icon.c b/src/icon.c new file mode 100644 index 0000000..4970694 --- /dev/null +++ b/src/icon.c @@ -0,0 +1,104 @@ +/* + * Icon generation helpers for slide puzzle game + * Copyright © 2022 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "colour.h" +#include "icon.h" + +static void format_row(unsigned char *row, unsigned long c, + unsigned s, unsigned y, unsigned w, unsigned h) +{ + unsigned x; + + for (x = 0; x < w; x++) { + row[x] = (c >> 8*COLOUR_PRIMARY) & 0xff; + if (x < s || y < s) + row[x] = (c >> 8*COLOUR_LIGHT) & 0xff; + if ((x+s >= w && x+y >= w) || (y+s >= h && x+y >= h)) + row[x] = (c >> 8*COLOUR_DARK) & 0xff; + } +} + +/* + * The 16x16 icon is drawn with 1px shadow, 6x6 tiles, with 1 pixel cropped + * off all the edge tiles. + */ +void icon_tile16(unsigned char *icon, unsigned long c, int tile_x, int tile_y) +{ + int out_x, out_y, out_w, y; + unsigned char row[6]; + + out_x = tile_x * 11 / 2; + out_y = tile_y * 11 / 2; + out_w = (5 + (tile_x == 1)) * sizeof row[0]; + + for (y = 0+(tile_y == 0); y < 6-(tile_y == 2); y++) { + format_row(row, c, 1, y, 6, 6); + memcpy(&icon[16 * out_y++ + out_x], &row[tile_x == 0], out_w); + } +} + +/* + * The 24x24 icon is drawn with 1px shadow and 8x8 tiles. + */ +void icon_tile24(unsigned char *icon, unsigned long c, int tile_x, int tile_y) +{ + int out_x, out_y, y; + + out_x = tile_x * 8; + out_y = tile_y * 8; + + for (y = 0; y < 8; y++) { + format_row(&icon[24 * out_y++ + out_x], c, 1, y, 8, 8); + } +} + +/* + * The 32x32 icon is drawn with 1px shadow with slightly uneven tiles on + * an 11-10-11 pixel grid. + */ +void icon_tile32(unsigned char *icon, unsigned long c, int tile_x, int tile_y) +{ + int out_x, out_y, out_w, out_h, y; + + out_x = 10*tile_x + (tile_x > 0); + out_y = 10*tile_y + (tile_y > 0); + out_w = 10 + (tile_x != 1); + out_h = 10 + (tile_y != 1); + + for (y = 0; y < out_h; y++) { + format_row(&icon[32 * out_y++ + out_x], c, 1, y, out_w, out_h); + } +} + +/* + * The 48x48 icon is drawn with 2px shadow and 16x16 tiles. + */ +void icon_tile48(unsigned char *icon, unsigned long c, int tile_x, int tile_y) +{ + int out_x, out_y, y; + + out_x = tile_x * 16; + out_y = tile_y * 16; + + for (y = 0; y < 16; y++) { + format_row(&icon[48 * out_y++ + out_x], c, 2, y, 16, 16); + } +} diff --git a/src/icon.h b/src/icon.h new file mode 100644 index 0000000..20a8a8d --- /dev/null +++ b/src/icon.h @@ -0,0 +1,48 @@ +/* + * Icon generation helpers for slide puzzle game + * Copyright © 2022 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef RRACE_ICON_H_ +#define RRACE_ICON_H_ + +/* + * Generate one tile of the program icon at various sizes, which is a 3x3 grid + * of coloured tiles. + * + * These functions all operate as follows: + * + * - icon points to the start of the complete image buffer, in row-major + * order. Each byte represents one pixel of the icon, so for example the + * 16x16 icon requires a 256-byte buffer. + * + * - c controls the byte values that are written to the icon for this tile. + * The least significant 8 bits of c encode the primary colour, bits 8-15 + * encode the dark shade colour, and bits 16-23 encode the light shade + * colour. + * + * - tile_x and tile_y select which tile to render (each may be 0, 1 or 2). + * + * By calling the function 9 times with all combinations of tile_x/tile_y + * values, a complete icon is generated. + */ + +void icon_tile16(unsigned char *icon, unsigned long c, int tile_x, int tile_y); +void icon_tile24(unsigned char *icon, unsigned long c, int tile_x, int tile_y); +void icon_tile32(unsigned char *icon, unsigned long c, int tile_x, int tile_y); +void icon_tile48(unsigned char *icon, unsigned long c, int tile_x, int tile_y); + +#endif diff --git a/src/motif.c b/src/motif.c index ef06c52..c8261fc 100644 --- a/src/motif.c +++ b/src/motif.c @@ -26,6 +26,7 @@ #include "help.h" #include "motif.h" +#include "ewmhicon.h" #include "motifopt.h" #include "game.h" #include "version.h" diff --git a/src/motif.h b/src/motif.h index 28e1b76..c85e8a9 100644 --- a/src/motif.h +++ b/src/motif.h @@ -20,7 +20,6 @@ #define RRACE_MOTIF_H_ #include -#include "ewmhicon.h" #include "colour.h" #include "game.h" diff --git a/src/x11.c b/src/x11.c index 6954f06..0a34fee 100644 --- a/src/x11.c +++ b/src/x11.c @@ -26,7 +26,9 @@ #include #include #include + #include "motif.h" +#include "ewmhicon.h" /* Size of the traditional icon pixmap (multiple of 3) */ #define ICON_SIZE 48 @@ -277,36 +279,16 @@ void x11_redraw_icon(struct app_state *state, Widget shell) { Display *display = XtDisplay(shell); Dimension tilesz = ICON_SIZE/3; - int i, j, tile; - - XColor colours[(TILE_MAX-1)*COLOUR_MAX]; - unsigned long *icons[ICON_MAX]; - void *wm_icon = NULL; - - if (state->use_ewmh_icons && (wm_icon = ewmh_icon_alloc(icons))) { - Colormap cmap = DefaultColormapOfScreen(XtScreen(shell)); - - for (i = 0; i < TILE_MAX-1; i++) { - for (j = 0; j < COLOUR_MAX; j++) { - XColor *c = &colours[i*COLOUR_MAX+j]; - c->pixel = state->tile_colour[i][j]; - } - } - - XQueryColors(display, cmap, colours, i*j); - } + unsigned long ewmhseq[9]; + unsigned i; for (i = 0; i < 9; i++) { + int tile; + tile = redraw_goal_tile(state, display, state->icon_pixmap, - i%3, i/3, tilesz, tilesz); + i%3, i/3, tilesz, tilesz); - if (wm_icon) { - XColor *c = &colours[(tile-1)*COLOUR_MAX]; - ewmh_tile16(icons[ICON_16x16], c, i%3, i/3); - ewmh_tile24(icons[ICON_24x24], c, i%3, i/3); - ewmh_tile32(icons[ICON_32x32], c, i%3, i/3); - ewmh_tile48(icons[ICON_48x48], c, i%3, i/3); - } + ewmhseq[i] = COLOUR_MAX*0x10101 * (tile-1) + 0x20100; } /* @@ -316,13 +298,24 @@ void x11_redraw_icon(struct app_state *state, Widget shell) XtVaSetValues(shell, XtNiconPixmap, None, (char *)NULL); XtVaSetValues(shell, XtNiconPixmap, state->icon_pixmap, (char *)NULL); - if (wm_icon) { + if (state->use_ewmh_icons) { Atom net_wm_icon = XInternAtom(display, "_NET_WM_ICON", FALSE); + Colormap cmap = DefaultColormapOfScreen(XtScreen(shell)); + XColor colours[(TILE_MAX-1)*COLOUR_MAX]; + void *wm_icon; - XChangeProperty(display, XtWindow(shell), net_wm_icon, - XA_CARDINAL, 32, PropModeReplace, - wm_icon, EWMH_ICON_NELEM); + for (i = 0; i < XtNumber(colours); i++) { + uint_least32_t *p = state->tile_colour[0]; + colours[i].pixel = p[i]; + } + XQueryColors(display, cmap, colours, XtNumber(colours)); + wm_icon = ewmh_icon_generate(ewmhseq, colours); + if (wm_icon) { + XChangeProperty(display, XtWindow(shell), net_wm_icon, + XA_CARDINAL, 32, PropModeReplace, + wm_icon, EWMH_ICON_NELEM); + } free(wm_icon); } } diff --git a/t/ewmhicon.c b/t/ewmhicon.c index fd92e32..755cf5d 100644 --- a/t/ewmhicon.c +++ b/t/ewmhicon.c @@ -23,10 +23,13 @@ #include #include #include +#include +#include #include #include #include "ewmhicon.h" +#include "icon.h" #include "colour.h" #include "help.h" @@ -97,51 +100,12 @@ static void print_version(void) puts("There is NO WARRANTY, to the extent permitted by law."); } -static const char sizes[][6] = { - "16x16", "24x24", "32x32", "48x48" -}; - -static int to_size_enum(const char *arg) +static void +print_xpm(const char *colourseq, unsigned long size, unsigned long *icon_buf) { - char buf[8]; - unsigned i; - - if (!strchr(arg, 'x')) { - sprintf(buf, "%.3sx%.3s", arg, arg); - arg = buf; - } - - for (i = 0; i < sizeof sizes / sizeof sizes[0]; i++) { - if (!strcmp(arg, sizes[i])) - return i; - } - - return -1; -} - -static void gen_icon(int size, const char *colourseq) -{ - void (*tilefunc)(unsigned long *, const XColor *, int, int); - static unsigned long icon_buf[48*48]; const char colourchars[21] = ".%+"",Rr""-Oo""'Yy"":Gg"";Bb"" Ww"; - unsigned i, j, x, y, w, h, n; - - switch (size) { - case ICON_16x16: tilefunc = ewmh_tile16; w = h = 16; break; - case ICON_24x24: tilefunc = ewmh_tile24; w = h = 24; break; - case ICON_32x32: tilefunc = ewmh_tile32; w = h = 32; break; - case ICON_48x48: tilefunc = ewmh_tile48; w = h = 48; break; - default: assert(0); - } - - for (i = 0; i < 9; i++) { - const XColor *c; - - assert(colourseq[i] >= '0' && colourseq[i] <= '6'); - c = colours[colourseq[i]-'0']; - - tilefunc(icon_buf, c, i%3, i/3); - } + unsigned w = size & 0xffff, h = (size >> 16) & 0xffff; + unsigned x, y, i, j, n; n = 7; for (i = 0; i < 7; i++) @@ -180,7 +144,7 @@ static void gen_icon(int size, const char *colourseq) printf("};\n"); } -int decode_colourseq(char *out, const char *arg) +static int decode_colourseq(char *out, const char *arg) { int i; @@ -209,10 +173,94 @@ err: return 0; } +/* Convert the user sequence into a list of colour index specifiers */ +static unsigned long *expand_seq(unsigned long *out, const char *seq) +{ + int i; + + for (i = 0; i < 9; i++) { + out[i] = 0x30303 * (seq[i]-'0') + 0x20100; + } + + return out; +} + +static unsigned long decode_size(char *size_spec) +{ + unsigned long w, h; + char c = 0; + size_t n; + + n = strspn(size_spec, "0123456789"); + switch (size_spec[n]) { + default: + goto err_invalid; + case 0: case 'x': case 'X': + c = size_spec[n]; + size_spec[n] = 0; + } + + w = strtoul(size_spec, NULL, 10); + size_spec[n] = c; + if (w > 0xffff) + goto err_range; + + if (!c) { + h = w; + } else { + n = strspn(size_spec += n+1, "0123456789"); + if (size_spec[n]) + goto err_invalid; + + h = strtoul(size_spec, NULL, 10); + if (h > 0xffff) + goto err_range; + } + + return (h << 16) | w; +err_range: + fprintf(stderr, "%s: %s: %s\n", progname, size_spec, strerror(ERANGE)); + return 0; +err_invalid: + fprintf(stderr, "%s: %s: %s\n", progname, size_spec, + "invalid size specification"); + return 0; +} + +static unsigned long *find_icon(unsigned long size, unsigned long *ewmhicon) +{ + unsigned long w = size & 0xffff, h = (size >> 16) & 0xffff; + unsigned long i; + + if (!size) + return NULL; + + for (i = 0; i < EWMH_ICON_NELEM-2;) { + unsigned long icon_w = ewmhicon[i]; + unsigned long icon_h = ewmhicon[i+1]; + + if (w == icon_w && h == icon_h) { + i += 2; + break; + } + + assert(icon_w < ULONG_MAX / icon_h); + assert(i < ULONG_MAX - icon_w*icon_h); + i += 2 + icon_w*icon_h; + } + + if (i < EWMH_ICON_NELEM) + return &ewmhicon[i]; + + fprintf(stderr, "%s: error: no %lux%lu icon found\n", progname, w, h); + return NULL; +} + int main(int argc, char **argv) { - char colourseq[9] = "000000000"; - int opt, size; + unsigned long *ewmhicon, *icon, size, buf[9]; + char colourseq[10] = "000000000"; + int opt; if (argc > 0) progname = argv[0]; @@ -241,13 +289,22 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - size = to_size_enum(argv[optind]); - if (size < 0) { - printf("%s: error: invalid size %s\n", progname, argv[optind]); - print_usage(stderr); + size = decode_size(argv[optind]); + if (!size) + return EXIT_FAILURE; + + ewmhicon = ewmh_icon_generate(expand_seq(buf, colourseq), colours[0]); + if (!ewmhicon) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); return EXIT_FAILURE; } - gen_icon(size, colourseq); + icon = find_icon(size, ewmhicon); + if (!icon) + return EXIT_FAILURE; + + print_xpm(colourseq, size, icon); + + free(ewmhicon); return 0; } -- 2.43.2