]> git.draconx.ca Git - rrace.git/blob - t/ewmhicon.c
Make tile colours configurable via X resources.
[rrace.git] / t / ewmhicon.c
1 /*
2  * Test app for _NET_WM_ICON formatting.
3  * Copyright © 2022 Nick Bowler
4  *
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.
7  *
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.
12  *
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.
17  *
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/>.
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <getopt.h>
28
29 #include "ewmhicon.h"
30 #include "colour.h"
31 #include "help.h"
32
33 static const char *progname = "ewmhicon";
34 static const char sopts[] = "c:VH";
35 static const struct option lopts[] = {
36         { "colourseq", 1, NULL, 'c' },
37         { "version",   0, NULL, 'V' },
38         { "help",      0, NULL, 'H' },
39         { 0 }
40 };
41
42 #define PASTE(a, b) a ## b
43 #define INDIR(a) a
44
45 #define S8TO16(x) ((x) * 0xfffful / 0xff)
46
47 #define RGB8_(r, g, b) { \
48         0xff000000 | (r << 16) | (g << 8) | b, \
49         S8TO16(r), S8TO16(g), S8TO16(b) }
50
51 #define RGB8(r, g, b) RGB8_(0x ## r ## ul, 0x ## g ## ul, 0x ## b ## ul)
52
53 #define COLOURTAB(n) { \
54         INDIR(RGB8 PASTE(COLOUR ## n, _PRIMARY_RGB_)), \
55         INDIR(RGB8 PASTE(COLOUR ## n, _DARK_RGB_)), \
56         INDIR(RGB8 PASTE(COLOUR ## n, _LIGHT_RGB_)) }
57
58 static const XColor colours[7][3] = {
59         {
60                 { 0xffff0000, 0xffff },
61                 { 0xff00ff00, 0, 0xffff },
62                 { 0xff0000ff, 0, 0, 0xffff }
63         },
64         COLOURTAB(0), COLOURTAB(1), COLOURTAB(2),
65         COLOURTAB(3), COLOURTAB(4), COLOURTAB(5)
66 };
67
68 static void print_usage(FILE *f)
69 {
70         fprintf(f, "Usage: %s size\n", progname);
71         if (f != stdout)
72                 fprintf(f, "Try %s --help for more information.\n", progname);
73 }
74
75 static void print_help(void)
76 {
77         const struct option *opt;
78
79         print_usage(stdout);
80         putchar('\n');
81         puts("Options:");
82         for (opt = lopts; opt->name; opt++) {
83                 if (help_print_optstring(opt, "ARG", 20))
84                         putchar('\n');
85         }
86         putchar('\n');
87
88         printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
89 }
90
91 static void print_version(void)
92 {
93         printf("ewmhicon (%s) %s\n", PACKAGE, PACKAGE_VERSION);
94         printf("Copyright (C) 2022 Nick Bowler\n");
95         puts("License GPLv3+: GNU GPL version 3 or any later version");
96         puts("This is free software: you are free to change and redistribute it.");
97         puts("There is NO WARRANTY, to the extent permitted by law.");
98 }
99
100 static const char sizes[][6] = {
101         "16x16", "24x24", "32x32", "48x48"
102 };
103
104 static int to_size_enum(const char *arg)
105 {
106         char buf[8];
107         unsigned i;
108
109         if (!strchr(arg, 'x')) {
110                 sprintf(buf, "%.3sx%.3s", arg, arg);
111                 arg = buf;
112         }
113
114         for (i = 0; i < sizeof sizes / sizeof sizes[0]; i++) {
115                 if (!strcmp(arg, sizes[i]))
116                         return i;
117         }
118
119         return -1;
120 }
121
122 static void gen_icon(int size, const char *colourseq)
123 {
124         void (*tilefunc)(unsigned long *, const XColor *, int, int);
125         static unsigned long icon_buf[48*48];
126         const char colourchars[21] = ".%+"",Rr""-Oo""'Yy"":Gg"";Bb"" Ww";
127         unsigned i, j, x, y, w, h, n;
128
129         switch (size) {
130         case ICON_16x16: tilefunc = ewmh_tile16; w = h = 16; break;
131         case ICON_24x24: tilefunc = ewmh_tile24; w = h = 24; break;
132         case ICON_32x32: tilefunc = ewmh_tile32; w = h = 32; break;
133         case ICON_48x48: tilefunc = ewmh_tile48; w = h = 48; break;
134         default: assert(0);
135         }
136
137         for (i = 0; i < 9; i++) {
138                 const XColor *c;
139
140                 assert(colourseq[i] >= '0' && colourseq[i] <= '6');
141                 c = colours[colourseq[i]-'0'];
142
143                 tilefunc(icon_buf, c, i%3, i/3);
144         }
145
146         n = 7;
147         for (i = 0; i < 7; i++)
148                 n -= !strchr(colourseq, '0'+i);
149
150         puts("/* XPM */");
151         puts("static char *icon[] = {");
152         printf("\"%u %u %u 1\",\n", w, h, 3*n);
153         for (i = 0; i < 7; i++) {
154                 if (!strchr(colourseq, '0'+i))
155                         continue;
156
157                 for (j = 0; j < 3; j++) {
158                         printf("\"%c c #%.6lx\",\n", colourchars[3*i+j],
159                                        colours[i][j].pixel & 0xffffff);
160                 }
161         }
162
163         for (y = 0; y < h; y++) {
164                 putchar('"');
165                 for (x = 0; x < w; x++) {
166                         unsigned long val = icon_buf[y*h+x];
167                         int c = '#';
168
169                         for (i = 0; i < sizeof colourchars; i++) {
170                                 if (colours[i/3][i%3].pixel == val) {
171                                         c = colourchars[i];
172                                         break;
173                                 }
174                         }
175
176                         putchar(c);
177                 }
178                 printf("\"%.*s\n", y+1<h, ",");
179         }
180         printf("};\n");
181 }
182
183 int decode_colourseq(char *out, const char *arg)
184 {
185         int i;
186
187         for (i = 0; i < 9; i++) {
188                 switch (arg[i]) {
189                 case 0: return 0;
190                 case 'R': case 'r': out[i] = '1'; break;
191                 case 'O': case 'o': out[i] = '2'; break;
192                 case 'Y': case 'y': out[i] = '3'; break;
193                 case 'G': case 'g': out[i] = '4'; break;
194                 case 'B': case 'b': out[i] = '5'; break;
195                 case 'W': case 'w': out[i] = '6'; break;
196                 case '1': case '2': case '3': case '4': case '5': case '6':
197                         case '0': out[i] = arg[i]; break;
198                 default: goto err;
199                 }
200         }
201
202         if (arg[i]) {
203 err:
204                 fprintf(stderr, "%s: error: invalid colour sequence '%s'\n",
205                                 progname, arg);
206                 return -1;
207         }
208
209         return 0;
210 }
211
212 int main(int argc, char **argv)
213 {
214         char colourseq[9] = "000000000";
215         int opt, size;
216
217         if (argc > 0)
218                 progname = argv[0];
219
220         while ((opt = getopt_long(argc, argv, sopts, lopts, 0)) != -1) {
221                 switch (opt) {
222                 case 'c':
223                         if (decode_colourseq(colourseq, optarg) != 0)
224                                 return EXIT_FAILURE;
225                         break;
226                 case 'H':
227                         print_help();
228                         return EXIT_SUCCESS;
229                 case 'V':
230                         print_version();
231                         return EXIT_SUCCESS;
232                 default:
233                         print_usage(stderr);
234                         return EXIT_FAILURE;
235                 }
236         }
237
238         if (argc != optind+1) {
239                 printf("%s: error: size not specified\n", progname);
240                 print_usage(stderr);
241                 return EXIT_FAILURE;
242         }
243
244         size = to_size_enum(argv[optind]);
245         if (size < 0) {
246                 printf("%s: error: invalid size %s\n", progname, argv[optind]);
247                 print_usage(stderr);
248                 return EXIT_FAILURE;
249         }
250
251         gen_icon(size, colourseq);
252         return 0;
253 }