2 * Test app for _NET_WM_ICON formatting.
3 * Copyright © 2022 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/>.
42 static const char *progname = "ewmhicon";
43 static const char sopts[] = "c:VH";
44 static const struct option lopts[] = {
45 { "colourseq", 1, NULL, 'c' },
46 { "w32vga", 0, NULL, LOPT_W32VGA },
47 { "version", 0, NULL, 'V' },
48 { "help", 0, NULL, 'H' },
52 #define S8TO16(x) ((x) * 0xfffful / 0xff)
54 #define RGB8(r, g, b) { \
55 0xff000000 | (r << 16) | (g << 8) | b, \
56 S8TO16(r), S8TO16(g), S8TO16(b) }
58 #define COLOUR_SYS_XCOLOR(r, g, b) \
59 RGB8(0x ## r ## ul, 0x ## g ## ul, 0x ## b ## ul)
62 #define COLOUR_SYSTEM XCOLOR
64 #define COLOURTAB_(n) { n ## _PRIMARY, n ## _DARK, n ## _LIGHT }
65 #define COLOURTAB(n) COLOURTAB_(COLOUR ## n)
67 static const XColor colours[7][3] = {
69 { 0xffff0000, 0xffff },
70 { 0xff00ff00, 0, 0xffff },
71 { 0xff0000ff, 0, 0, 0xffff }
73 COLOURTAB(0), COLOURTAB(1), COLOURTAB(2),
74 COLOURTAB(3), COLOURTAB(4), COLOURTAB(5)
77 static void print_usage(FILE *f)
79 fprintf(f, "Usage: %s size\n", progname);
81 fprintf(f, "Try %s --help for more information.\n", progname);
84 static void print_help(void)
86 const struct option *opt;
91 for (opt = lopts; opt->name; opt++) {
92 if (help_print_optstring(opt, "ARG", 20))
97 printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
100 static void print_version(void)
102 printf("ewmhicon (%s) %s\n", PACKAGE, PACKAGE_VERSION);
103 printf("Copyright (C) 2022 Nick Bowler\n");
104 puts("License GPLv3+: GNU GPL version 3 or any later version");
105 puts("This is free software: you are free to change and redistribute it.");
106 puts("There is NO WARRANTY, to the extent permitted by law.");
110 #define COLOUR_SYS_HEX(r, g, b) 0x ## r ## g ## b ## ul
113 #define COLOUR_SYSTEM HEX
116 * Returns true iff this pixel is an "edge" -- that is, either there
117 * is no pixel to the right or below or those pixels are a different
120 static int is_edge(unsigned x, unsigned y, unsigned w, unsigned h,
121 unsigned long *p, unsigned long colour)
123 return y+1 >= h || x+1 >= w
124 || (p[1] & 0xffffff) != colour
125 || (p[w] & 0xffffff) != colour;
128 static void print_xpm_w32vga(unsigned long size, unsigned long *icon_buf)
130 unsigned w = size & 0xffff, h = (size >> 16) & 0xffff;
131 unsigned x, y, i, j, n;
133 const char colourchars[16] = "#rgybmc-+RGYBMC ";
134 unsigned long palette[16] = {
135 0x000000, 0x800000, 0x008000, 0x808000,
136 0x000080, 0x800080, 0x008080, 0xc0c0c0,
137 0x808080, 0xff0000, 0x00ff00, 0xffff00,
138 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
141 unsigned long prev_colour = 0;
142 unsigned used_colours, v;
144 size = (unsigned long) w * h;
145 for (i = 0; i < size; i++) {
146 unsigned long *p = &icon_buf[i];
151 switch (*p & 0xffffff) {
153 case COLOUR0_PRIMARY:
154 *p = y&1 ? ( x&1 ? 'r' : 'm' ) : ( x&1 ? 'y' : 'r' );
157 *p = (x^y)&1 ? '+' : 'r';
163 case COLOUR1_PRIMARY:
164 *p = y&1 ? 'y' : x&1 ? '+' : 'R';
167 *p = (x^y)&1 ? '+' : 'y';
170 *p = (x^y)&1 ? 'r' : 'y';
173 case COLOUR2_PRIMARY:
174 *p = (x^y)&1 ? '+' : 'Y';
177 *p = (x^y)&1 ? '-' : 'Y';
180 *p = (x^y)&1 ? '+' : 'Y';
181 if (is_edge(x, y, w, h, p, COLOUR2_DARK))
182 *p = (x^y)&1 ? '+' : 'y';
185 case COLOUR3_PRIMARY:
186 *p = y&1 ? 'g' : x&1 ? '+' : 'g';
189 *p = (x^y)&1 ? '+' : 'g';
193 if (is_edge(x, y, w, h, p, COLOUR3_DARK))
194 *p = (x^y)&1 ? '#' : 'g';
197 case COLOUR4_PRIMARY:
198 *p = (x^y)&1 ? 'b' : 'c';
202 if (is_edge(x, y, w, h, p, COLOUR4_LIGHT))
203 *p = (x^y)&1 ? 'b' : 'c';
207 if (is_edge(x, y, w, h, p, COLOUR4_DARK))
208 *p = (x^y)&1 ? '#' : 'b';
211 case COLOUR5_PRIMARY:
212 *p = (x^y)&1 ? '-' : ' ';
218 *p = (x^y)&1 ? '+' : ' ';
221 case 0xff0000: *p = 'R'; break;
222 case 0x00ff00: *p = 'G'; break;
223 case 0x0000ff: *p = 'B'; break;
226 c = strchr(colourchars, *p);
227 assert(c && c - colourchars < 16);
228 used_colours |= 1u << (c-colourchars);
231 for (n = 0, v = used_colours; v; n++)
235 puts("static char *icon[] = {");
236 printf("\"%u %u %u 1\",\n", w, h, n);
237 for (i = 0; i < sizeof colourchars; i++) {
238 if (!(used_colours & (1u << i)))
241 printf("\"%c c #%.6lx\",\n", colourchars[i], palette[i]);
244 for (y = 0; y < h; y++) {
246 for (x = 0; x < w; x++) {
247 putchar(icon_buf[y*h+x]);
249 printf("\"%.*s\n", y+1<h, ",");
256 print_xpm(const char *colourseq, unsigned long size, unsigned long *icon_buf)
258 const char colourchars[21] = ".%+"",Rr""-Oo""'Yy"":Gg"";Bb"" Ww";
259 unsigned w = size & 0xffff, h = (size >> 16) & 0xffff;
260 unsigned x, y, i, j, n;
263 for (i = 0; i < 7; i++)
264 n -= !strchr(colourseq, '0'+i);
267 puts("static char *icon[] = {");
268 printf("\"%u %u %u 1\",\n", w, h, 3*n);
269 for (i = 0; i < 7; i++) {
270 if (!strchr(colourseq, '0'+i))
273 for (j = 0; j < 3; j++) {
274 printf("\"%c c #%.6lx\",\n", colourchars[3*i+j],
275 colours[i][j].pixel & 0xffffff);
279 for (y = 0; y < h; y++) {
281 for (x = 0; x < w; x++) {
282 unsigned long val = icon_buf[y*h+x];
285 for (i = 0; i < sizeof colourchars; i++) {
286 if (colours[i/3][i%3].pixel == val) {
294 printf("\"%.*s\n", y+1<h, ",");
299 static int decode_colourseq(char *out, const char *arg)
303 for (i = 0; i < 9; i++) {
306 case 'R': case 'r': out[i] = '1'; break;
307 case 'O': case 'o': out[i] = '2'; break;
308 case 'Y': case 'y': out[i] = '3'; break;
309 case 'G': case 'g': out[i] = '4'; break;
310 case 'B': case 'b': out[i] = '5'; break;
311 case 'W': case 'w': out[i] = '6'; break;
312 case '1': case '2': case '3': case '4': case '5': case '6':
313 case '0': out[i] = arg[i]; break;
320 fprintf(stderr, "%s: error: invalid colour sequence '%s'\n",
328 /* Convert the user sequence into a list of colour index specifiers */
329 static unsigned long *expand_seq(unsigned long *out, const char *seq)
333 for (i = 0; i < 9; i++) {
334 out[i] = 0x30303 * (seq[i]-'0') + 0x20100;
340 static unsigned long decode_size(char *size_spec)
346 n = strspn(size_spec, "0123456789");
347 switch (size_spec[n]) {
350 case 0: case 'x': case 'X':
355 w = strtoul(size_spec, NULL, 10);
363 n = strspn(size_spec += n+1, "0123456789");
367 h = strtoul(size_spec, NULL, 10);
372 return (h << 16) | w;
374 fprintf(stderr, "%s: %s: %s\n", progname, size_spec, strerror(ERANGE));
377 fprintf(stderr, "%s: %s: %s\n", progname, size_spec,
378 "invalid size specification");
382 static unsigned long *find_icon(unsigned long size, unsigned long *ewmhicon)
384 unsigned long w = size & 0xffff, h = (size >> 16) & 0xffff;
390 for (i = 0; i < EWMH_ICON_NELEM-2;) {
391 unsigned long icon_w = ewmhicon[i];
392 unsigned long icon_h = ewmhicon[i+1];
394 if (w == icon_w && h == icon_h) {
399 assert(icon_w < ULONG_MAX / icon_h);
400 assert(i < ULONG_MAX - icon_w*icon_h);
401 i += 2 + icon_w*icon_h;
404 if (i < EWMH_ICON_NELEM)
407 fprintf(stderr, "%s: error: no %lux%lu icon found\n", progname, w, h);
411 int main(int argc, char **argv)
413 unsigned long *ewmhicon, *icon, size, buf[9];
414 char colourseq[10] = "000000000";
421 while ((opt = getopt_long(argc, argv, sopts, lopts, 0)) != -1) {
424 if (decode_colourseq(colourseq, optarg) != 0)
442 if (argc != optind+1) {
443 printf("%s: error: size not specified\n", progname);
448 size = decode_size(argv[optind]);
452 ewmhicon = ewmh_icon_generate(expand_seq(buf, colourseq), colours[0]);
454 fprintf(stderr, "%s: failed to allocate memory\n", progname);
458 icon = find_icon(size, ewmhicon);
463 print_xpm_w32vga(size, icon);
465 print_xpm(colourseq, size, icon);