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/>.
36 static const char *progname = "ewmhicon";
37 static const char sopts[] = "c:VH";
38 static const struct option lopts[] = {
39 { "colourseq", 1, NULL, 'c' },
40 { "version", 0, NULL, 'V' },
41 { "help", 0, NULL, 'H' },
45 #define PASTE(a, b) a ## b
48 #define S8TO16(x) ((x) * 0xfffful / 0xff)
50 #define RGB8_(r, g, b) { \
51 0xff000000 | (r << 16) | (g << 8) | b, \
52 S8TO16(r), S8TO16(g), S8TO16(b) }
54 #define RGB8(r, g, b) RGB8_(0x ## r ## ul, 0x ## g ## ul, 0x ## b ## ul)
56 #define COLOURTAB(n) { \
57 INDIR(RGB8 PASTE(COLOUR ## n, _PRIMARY_RGB_)), \
58 INDIR(RGB8 PASTE(COLOUR ## n, _DARK_RGB_)), \
59 INDIR(RGB8 PASTE(COLOUR ## n, _LIGHT_RGB_)) }
61 static const XColor colours[7][3] = {
63 { 0xffff0000, 0xffff },
64 { 0xff00ff00, 0, 0xffff },
65 { 0xff0000ff, 0, 0, 0xffff }
67 COLOURTAB(0), COLOURTAB(1), COLOURTAB(2),
68 COLOURTAB(3), COLOURTAB(4), COLOURTAB(5)
71 static void print_usage(FILE *f)
73 fprintf(f, "Usage: %s size\n", progname);
75 fprintf(f, "Try %s --help for more information.\n", progname);
78 static void print_help(void)
80 const struct option *opt;
85 for (opt = lopts; opt->name; opt++) {
86 if (help_print_optstring(opt, "ARG", 20))
91 printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
94 static void print_version(void)
96 printf("ewmhicon (%s) %s\n", PACKAGE, PACKAGE_VERSION);
97 printf("Copyright (C) 2022 Nick Bowler\n");
98 puts("License GPLv3+: GNU GPL version 3 or any later version");
99 puts("This is free software: you are free to change and redistribute it.");
100 puts("There is NO WARRANTY, to the extent permitted by law.");
104 print_xpm(const char *colourseq, unsigned long size, unsigned long *icon_buf)
106 const char colourchars[21] = ".%+"",Rr""-Oo""'Yy"":Gg"";Bb"" Ww";
107 unsigned w = size & 0xffff, h = (size >> 16) & 0xffff;
108 unsigned x, y, i, j, n;
111 for (i = 0; i < 7; i++)
112 n -= !strchr(colourseq, '0'+i);
115 puts("static char *icon[] = {");
116 printf("\"%u %u %u 1\",\n", w, h, 3*n);
117 for (i = 0; i < 7; i++) {
118 if (!strchr(colourseq, '0'+i))
121 for (j = 0; j < 3; j++) {
122 printf("\"%c c #%.6lx\",\n", colourchars[3*i+j],
123 colours[i][j].pixel & 0xffffff);
127 for (y = 0; y < h; y++) {
129 for (x = 0; x < w; x++) {
130 unsigned long val = icon_buf[y*h+x];
133 for (i = 0; i < sizeof colourchars; i++) {
134 if (colours[i/3][i%3].pixel == val) {
142 printf("\"%.*s\n", y+1<h, ",");
147 static int decode_colourseq(char *out, const char *arg)
151 for (i = 0; i < 9; i++) {
154 case 'R': case 'r': out[i] = '1'; break;
155 case 'O': case 'o': out[i] = '2'; break;
156 case 'Y': case 'y': out[i] = '3'; break;
157 case 'G': case 'g': out[i] = '4'; break;
158 case 'B': case 'b': out[i] = '5'; break;
159 case 'W': case 'w': out[i] = '6'; break;
160 case '1': case '2': case '3': case '4': case '5': case '6':
161 case '0': out[i] = arg[i]; break;
168 fprintf(stderr, "%s: error: invalid colour sequence '%s'\n",
176 /* Convert the user sequence into a list of colour index specifiers */
177 static unsigned long *expand_seq(unsigned long *out, const char *seq)
181 for (i = 0; i < 9; i++) {
182 out[i] = 0x30303 * (seq[i]-'0') + 0x20100;
188 static unsigned long decode_size(char *size_spec)
194 n = strspn(size_spec, "0123456789");
195 switch (size_spec[n]) {
198 case 0: case 'x': case 'X':
203 w = strtoul(size_spec, NULL, 10);
211 n = strspn(size_spec += n+1, "0123456789");
215 h = strtoul(size_spec, NULL, 10);
220 return (h << 16) | w;
222 fprintf(stderr, "%s: %s: %s\n", progname, size_spec, strerror(ERANGE));
225 fprintf(stderr, "%s: %s: %s\n", progname, size_spec,
226 "invalid size specification");
230 static unsigned long *find_icon(unsigned long size, unsigned long *ewmhicon)
232 unsigned long w = size & 0xffff, h = (size >> 16) & 0xffff;
238 for (i = 0; i < EWMH_ICON_NELEM-2;) {
239 unsigned long icon_w = ewmhicon[i];
240 unsigned long icon_h = ewmhicon[i+1];
242 if (w == icon_w && h == icon_h) {
247 assert(icon_w < ULONG_MAX / icon_h);
248 assert(i < ULONG_MAX - icon_w*icon_h);
249 i += 2 + icon_w*icon_h;
252 if (i < EWMH_ICON_NELEM)
255 fprintf(stderr, "%s: error: no %lux%lu icon found\n", progname, w, h);
259 int main(int argc, char **argv)
261 unsigned long *ewmhicon, *icon, size, buf[9];
262 char colourseq[10] = "000000000";
268 while ((opt = getopt_long(argc, argv, sopts, lopts, 0)) != -1) {
271 if (decode_colourseq(colourseq, optarg) != 0)
286 if (argc != optind+1) {
287 printf("%s: error: size not specified\n", progname);
292 size = decode_size(argv[optind]);
296 ewmhicon = ewmh_icon_generate(expand_seq(buf, colourseq), colours[0]);
298 fprintf(stderr, "%s: failed to allocate memory\n", progname);
302 icon = find_icon(size, ewmhicon);
306 print_xpm(colourseq, size, icon);