2 * Test app for _NET_WM_ICON formatting.
3 * Copyright © 2022-2023 Nick Bowler
5 * Use a fake colour scheme to generate an icon of the chosen size (16x16,
6 * 24x24, 32x32 or 48x48) and display the pixels as characters.
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
44 static const char *progname = "ewmhicon";
45 static const char sopts[] = "c:VH";
46 static const struct option lopts[] = {
47 { "test-cmap", 0, NULL, LOPT_TEST_CMAP },
48 { "colourseq", 1, NULL, 'c' },
49 { "w32vga", 0, NULL, LOPT_W32VGA },
50 { "version", 0, NULL, 'V' },
51 { "help", 0, NULL, 'H' },
55 #define S8TO16(x) ((x) * 0xfffful / 0xff)
57 #define RGB8(r, g, b) { \
58 0xff000000 | (r << 16) | (g << 8) | b, \
59 S8TO16(r), S8TO16(g), S8TO16(b) }
61 #define COLOUR_SYS_XCOLOR(r, g, b) \
62 RGB8(0x ## r ## ul, 0x ## g ## ul, 0x ## b ## ul)
65 #define COLOUR_SYSTEM XCOLOR
67 #define COLOURTAB_(n) { n ## _PRIMARY, n ## _DARK, n ## _LIGHT }
68 #define COLOURTAB(n) COLOURTAB_(COLOUR ## n)
70 static const XColor colours[7][3] = {
72 { 0xffff0000, 0xffff },
73 { 0xff00ff00, 0, 0xffff },
74 { 0xff0000ff, 0, 0, 0xffff }
76 COLOURTAB(0), COLOURTAB(1), COLOURTAB(2),
77 COLOURTAB(3), COLOURTAB(4), COLOURTAB(5)
80 static void print_usage(FILE *f)
82 fprintf(f, "Usage: %s size\n", progname);
84 fprintf(f, "Try %s --help for more information.\n", progname);
87 static void print_help(void)
89 const struct option *opt;
94 for (opt = lopts; opt->name; opt++) {
95 if (help_print_optstring(opt, "ARG", 20))
100 printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
103 static void print_version(void)
105 printf("ewmhicon (%s) %s\n", PACKAGE, PACKAGE_VERSION);
106 printf("Copyright (C) 2023 Nick Bowler\n");
107 puts("License GPLv3+: GNU GPL version 3 or any later version");
108 puts("This is free software: you are free to change and redistribute it.");
109 puts("There is NO WARRANTY, to the extent permitted by law.");
113 #define COLOUR_SYS_HEX(r, g, b) 0x ## r ## g ## b ## ul
116 #define COLOUR_SYSTEM HEX
119 * Returns true iff this pixel is an "edge" -- that is, either there
120 * is no pixel to the right or below or those pixels are a different
123 static int is_edge(unsigned x, unsigned y, unsigned w, unsigned h,
124 unsigned long *p, unsigned long colour)
126 return y+1 >= h || x+1 >= w
127 || (p[1] & 0xffffff) != colour
128 || (p[w] & 0xffffff) != colour;
131 static void print_xpm_w32vga(unsigned long size, unsigned long *icon_buf)
133 unsigned w = size & 0xffff, h = (size >> 16) & 0xffff;
134 unsigned x, y, i, j, n;
136 const char colourchars[16] = "#rgybmc-+RGYBMC ";
137 unsigned long palette[16] = {
138 0x000000, 0x800000, 0x008000, 0x808000,
139 0x000080, 0x800080, 0x008080, 0xc0c0c0,
140 0x808080, 0xff0000, 0x00ff00, 0xffff00,
141 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
144 unsigned long prev_colour = 0;
145 unsigned used_colours, v;
147 size = (unsigned long) w * h;
148 for (i = 0; i < size; i++) {
149 unsigned long *p = &icon_buf[i];
154 switch (*p & 0xffffff) {
156 case COLOUR0_PRIMARY:
157 *p = y&1 ? ( x&1 ? 'r' : 'm' ) : ( x&1 ? 'y' : 'r' );
160 *p = (x^y)&1 ? '+' : 'r';
166 case COLOUR1_PRIMARY:
167 *p = y&1 ? 'y' : x&1 ? '+' : 'R';
170 *p = (x^y)&1 ? '+' : 'y';
173 *p = (x^y)&1 ? 'r' : 'y';
176 case COLOUR2_PRIMARY:
177 *p = (x^y)&1 ? '+' : 'Y';
180 *p = (x^y)&1 ? '-' : 'Y';
183 *p = (x^y)&1 ? '+' : 'Y';
184 if (is_edge(x, y, w, h, p, COLOUR2_DARK))
185 *p = (x^y)&1 ? '+' : 'y';
188 case COLOUR3_PRIMARY:
189 *p = y&1 ? 'g' : x&1 ? '+' : 'g';
192 *p = (x^y)&1 ? '+' : 'g';
196 if (is_edge(x, y, w, h, p, COLOUR3_DARK))
197 *p = (x^y)&1 ? '#' : 'g';
200 case COLOUR4_PRIMARY:
201 *p = (x^y)&1 ? 'b' : 'c';
205 if (is_edge(x, y, w, h, p, COLOUR4_LIGHT))
206 *p = (x^y)&1 ? 'b' : 'c';
210 if (is_edge(x, y, w, h, p, COLOUR4_DARK))
211 *p = (x^y)&1 ? '#' : 'b';
214 case COLOUR5_PRIMARY:
215 *p = (x^y)&1 ? '-' : ' ';
221 *p = (x^y)&1 ? '+' : ' ';
224 case 0xff0000: *p = 'R'; break;
225 case 0x00ff00: *p = 'G'; break;
226 case 0x0000ff: *p = 'B'; break;
229 c = strchr(colourchars, *p);
230 assert(c && c - colourchars < 16);
231 used_colours |= 1u << (c-colourchars);
234 for (n = 0, v = used_colours; v; n++)
238 puts("static char *icon[] = {");
239 printf("\"%u %u %u 1\",\n", w, h, n);
240 for (i = 0; i < sizeof colourchars; i++) {
241 if (!(used_colours & (1u << i)))
244 printf("\"%c c #%.6lx\",\n", colourchars[i], palette[i]);
247 for (y = 0; y < h; y++) {
249 for (x = 0; x < w; x++) {
250 putchar(icon_buf[y*h+x]);
252 printf("\"%.*s\n", y+1<h, ",");
259 print_xpm(const char *colourseq, unsigned long size, unsigned long *icon_buf)
261 const char colourchars[21] = ".%+"",Rr""-Oo""'Yy"":Gg"";Bb"" Ww";
262 unsigned w = size & 0xffff, h = (size >> 16) & 0xffff;
263 unsigned x, y, i, j, n;
266 for (i = 0; i < 7; i++)
267 n -= !strchr(colourseq, '0'+i);
270 puts("static char *icon[] = {");
271 printf("\"%u %u %u 1\",\n", w, h, 3*n);
272 for (i = 0; i < 7; i++) {
273 if (!strchr(colourseq, '0'+i))
276 for (j = 0; j < 3; j++) {
277 printf("\"%c c #%.6lx\",\n", colourchars[3*i+j],
278 colours[i][j].pixel & 0xffffff);
282 for (y = 0; y < h; y++) {
284 for (x = 0; x < w; x++) {
285 unsigned long val = icon_buf[y*h+x];
288 for (i = 0; i < sizeof colourchars; i++) {
289 if (colours[i/3][i%3].pixel == val) {
297 printf("\"%.*s\n", y+1<h, ",");
302 static int decode_colourseq(char *out, const char *arg)
306 for (i = 0; i < 9; i++) {
309 case 'R': case 'r': out[i] = '1'; break;
310 case 'O': case 'o': out[i] = '2'; break;
311 case 'Y': case 'y': out[i] = '3'; break;
312 case 'G': case 'g': out[i] = '4'; break;
313 case 'B': case 'b': out[i] = '5'; break;
314 case 'W': case 'w': out[i] = '6'; break;
315 case '1': case '2': case '3': case '4': case '5': case '6':
316 case '0': out[i] = arg[i]; break;
323 fprintf(stderr, "%s: error: invalid colour sequence '%s'\n",
331 /* Convert the user sequence into a list of colour index specifiers */
332 static unsigned long *expand_seq(unsigned long *out, const char *seq)
336 for (i = 0; i < 9; i++) {
337 out[i] = 0x30303 * (seq[i]-'0') + 0x20100;
343 static unsigned long decode_size(char *size_spec)
349 n = strspn(size_spec, "0123456789");
350 switch (size_spec[n]) {
353 case 0: case 'x': case 'X':
358 w = strtoul(size_spec, NULL, 10);
366 n = strspn(size_spec += n+1, "0123456789");
370 h = strtoul(size_spec, NULL, 10);
375 return (h << 16) | w;
377 fprintf(stderr, "%s: %s: %s\n", progname, size_spec, strerror(ERANGE));
380 fprintf(stderr, "%s: %s: %s\n", progname, size_spec,
381 "invalid size specification");
385 static unsigned long *find_icon(unsigned long size, unsigned long *ewmhicon)
387 unsigned long w = size & 0xffff, h = (size >> 16) & 0xffff;
393 for (i = 0; i < EWMH_ICON_NELEM-2;) {
394 unsigned long icon_w = ewmhicon[i];
395 unsigned long icon_h = ewmhicon[i+1];
397 if (w == icon_w && h == icon_h) {
402 assert(icon_w < ULONG_MAX / icon_h);
403 assert(i < ULONG_MAX - icon_w*icon_h);
404 i += 2 + icon_w*icon_h;
407 if (i < EWMH_ICON_NELEM)
410 fprintf(stderr, "%s: error: no %lux%lu icon found\n", progname, w, h);
414 static int test_ewmh_icon_prepare(void)
416 int ret = EXIT_SUCCESS;
419 XColor *template = (void *)colours;
420 XColor newmap[sizeof colours / sizeof colours[0][0]];
422 printf("1..%d\n", (int)XTRA_ARRAYSIZE(newmap));
423 for (i = 0; i < XTRA_ARRAYSIZE(newmap); i++) {
424 newmap[i] = template[i];
428 ewmh_icon_prepare_cmap(newmap, XTRA_ARRAYSIZE(newmap));
429 for (i = 0; i < XTRA_ARRAYSIZE(newmap); i++) {
430 XColor *c = &newmap[i];
432 if (c->pixel != template[i].pixel) {
437 printf("ok %u (%.4hx, %.4hx, %.4hx) -> %.8lx\n",
438 i+1, c->red, c->green, c->blue, c->pixel);
444 int main(int argc, char **argv)
446 unsigned long *ewmhicon, *icon, size, buf[9];
447 char colourseq[10] = "000000000";
448 int w32vga = 0, test_cmap = 0;
454 while ((opt = getopt_long(argc, argv, sopts, lopts, 0)) != -1) {
457 if (decode_colourseq(colourseq, optarg) != 0)
479 return test_ewmh_icon_prepare();
482 if (argc != optind+1) {
483 printf("%s: error: size not specified\n", progname);
488 size = decode_size(argv[optind]);
492 ewmhicon = ewmh_icon_generate(expand_seq(buf, colourseq), colours[0]);
494 fprintf(stderr, "%s: failed to allocate memory\n", progname);
498 icon = find_icon(size, ewmhicon);
503 print_xpm_w32vga(size, icon);
505 print_xpm(colourseq, size, icon);