X-Git-Url: http://git.draconx.ca/gitweb/rrace.git/blobdiff_plain/43a709ff25b9973a83327a06a6ecc94b09e7787b..d99493cb685f80aa13b3135d461fcde554fe121f:/t/ewmhicon.c diff --git a/t/ewmhicon.c b/t/ewmhicon.c index e088d41..d7c6aea 100644 --- a/t/ewmhicon.c +++ b/t/ewmhicon.c @@ -23,92 +23,448 @@ #include #include #include +#include +#include #include -#include -#include "motif.h" +#include + +#include "ewmhicon.h" +#include "icon.h" +#include "colour.h" +#include "help.h" + +enum { + SOPT_MAX = UCHAR_MAX, + LOPT_W32VGA, + LOPT_MAX +}; static const char *progname = "ewmhicon"; +static const char sopts[] = "c:VH"; +static const struct option lopts[] = { + { "colourseq", 1, NULL, 'c' }, + { "w32vga", 0, NULL, LOPT_W32VGA }, + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'H' }, + { 0 } +}; + +#define S8TO16(x) ((x) * 0xfffful / 0xff) + +#define RGB8(r, g, b) { \ + 0xff000000 | (r << 16) | (g << 8) | b, \ + S8TO16(r), S8TO16(g), S8TO16(b) } + +#define COLOUR_SYS_XCOLOR(r, g, b) \ + RGB8(0x ## r ## ul, 0x ## g ## ul, 0x ## b ## ul) + +#undef COLOUR_SYSTEM +#define COLOUR_SYSTEM XCOLOR + +#define COLOURTAB_(n) { n ## _PRIMARY, n ## _DARK, n ## _LIGHT } +#define COLOURTAB(n) COLOURTAB_(COLOUR ## n) + +static const XColor colours[7][3] = { + { + { 0xffff0000, 0xffff }, + { 0xff00ff00, 0, 0xffff }, + { 0xff0000ff, 0, 0, 0xffff } + }, + COLOURTAB(0), COLOURTAB(1), COLOURTAB(2), + COLOURTAB(3), COLOURTAB(4), COLOURTAB(5) +}; static void print_usage(FILE *f) { fprintf(f, "Usage: %s size\n", progname); + if (f != stdout) + fprintf(f, "Try %s --help for more information.\n", progname); } -static const char sizes[][6] = { - "16x16", "24x24", "32x32", "48x48" -}; - -int to_size_enum(const char *arg) +static void print_help(void) { - char buf[8]; - unsigned i; + const struct option *opt; - if (!strchr(arg, 'x')) { - sprintf(buf, "%.3sx%.3s", arg, arg); - arg = buf; + print_usage(stdout); + putchar('\n'); + puts("Options:"); + for (opt = lopts; opt->name; opt++) { + if (help_print_optstring(opt, "ARG", 20)) + putchar('\n'); } + putchar('\n'); - for (i = 0; i < sizeof sizes / sizeof sizes[0]; i++) { - if (!strcmp(arg, sizes[i])) - return i; - } + printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT); +} - return -1; +static void print_version(void) +{ + printf("ewmhicon (%s) %s\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright (C) 2022 Nick Bowler\n"); + puts("License GPLv3+: GNU GPL version 3 or any later version"); + puts("This is free software: you are free to change and redistribute it."); + puts("There is NO WARRANTY, to the extent permitted by law."); } -static unsigned long icon_buf[48*48]; -int main(int argc, char **argv) +#define COLOUR_SYS_HEX(r, g, b) 0x ## r ## g ## b ## ul + +#undef COLOUR_SYSTEM +#define COLOUR_SYSTEM HEX + +/* + * Returns true iff this pixel is an "edge" -- that is, either there + * is no pixel to the right or below or those pixels are a different + * colour. + */ +static int is_edge(unsigned x, unsigned y, unsigned w, unsigned h, + unsigned long *p, unsigned long colour) { - void (*tilefunc)(unsigned long *, const XColor *, int, int); - XColor c[COLOUR_MAX] = {0}; - int size, x, y, w, h; + return y+1 >= h || x+1 >= w + || (p[1] & 0xffffff) != colour + || (p[w] & 0xffffff) != colour; +} - if (argc > 0) - progname = argv[0]; +static void print_xpm_w32vga(unsigned long size, unsigned long *icon_buf) +{ + unsigned w = size & 0xffff, h = (size >> 16) & 0xffff; + unsigned x, y, i, j, n; - if (argc != 2) { - print_usage(stderr); - return EXIT_FAILURE; + const char colourchars[16] = "#rgybmc-+RGYBMC "; + unsigned long palette[16] = { + 0x000000, 0x800000, 0x008000, 0x808000, + 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff + }; + + unsigned long prev_colour = 0; + unsigned used_colours, v; + + size = (unsigned long) w * h; + for (i = 0; i < size; i++) { + unsigned long *p = &icon_buf[i]; + char *c; + + x = i%w, y=i/w; + + switch (*p & 0xffffff) { + /* red */ + case COLOUR0_PRIMARY: + *p = y&1 ? ( x&1 ? 'r' : 'm' ) : ( x&1 ? 'y' : 'r' ); + break; + case COLOUR0_LIGHT: + *p = (x^y)&1 ? '+' : 'r'; + break; + case COLOUR0_DARK: + *p = 'r'; + break; + /* orange */ + case COLOUR1_PRIMARY: + *p = y&1 ? 'y' : x&1 ? '+' : 'R'; + break; + case COLOUR1_LIGHT: + *p = (x^y)&1 ? '+' : 'y'; + break; + case COLOUR1_DARK: + *p = (x^y)&1 ? 'r' : 'y'; + break; + /* yellow */ + case COLOUR2_PRIMARY: + *p = (x^y)&1 ? '+' : 'Y'; + break; + case COLOUR2_LIGHT: + *p = (x^y)&1 ? '-' : 'Y'; + break; + case COLOUR2_DARK: + *p = (x^y)&1 ? '+' : 'Y'; + if (is_edge(x, y, w, h, p, COLOUR2_DARK)) + *p = (x^y)&1 ? '+' : 'y'; + break; + /* green */ + case COLOUR3_PRIMARY: + *p = y&1 ? 'g' : x&1 ? '+' : 'g'; + break; + case COLOUR3_LIGHT: + *p = (x^y)&1 ? '+' : 'g'; + break; + case COLOUR3_DARK: + *p = 'g'; + if (is_edge(x, y, w, h, p, COLOUR3_DARK)) + *p = (x^y)&1 ? '#' : 'g'; + break; + /* blue */ + case COLOUR4_PRIMARY: + *p = (x^y)&1 ? 'b' : 'c'; + break; + case COLOUR4_LIGHT: + *p = 'c'; + if (is_edge(x, y, w, h, p, COLOUR4_LIGHT)) + *p = (x^y)&1 ? 'b' : 'c'; + break; + case COLOUR4_DARK: + *p = 'b'; + if (is_edge(x, y, w, h, p, COLOUR4_DARK)) + *p = (x^y)&1 ? '#' : 'b'; + break; + /* white */ + case COLOUR5_PRIMARY: + *p = (x^y)&1 ? '-' : ' '; + break; + case COLOUR5_LIGHT: + *p = ' '; + break; + case COLOUR5_DARK: + *p = (x^y)&1 ? '+' : ' '; + break; + /* dummy */ + case 0xff0000: *p = 'R'; break; + case 0x00ff00: *p = 'G'; break; + case 0x0000ff: *p = 'B'; break; + } + + c = strchr(colourchars, *p); + assert(c && c - colourchars < 16); + used_colours |= 1u << (c-colourchars); } - size = to_size_enum(argv[1]); - if (size < 0) { - printf("%s: error: invalid size %s\n", progname, argv[1]); - return EXIT_FAILURE; + for (n = 0, v = used_colours; v; n++) + v &= v-1; + + puts("/* XPM */"); + puts("static char *icon[] = {"); + printf("\"%u %u %u 1\",\n", w, h, n); + for (i = 0; i < sizeof colourchars; i++) { + if (!(used_colours & (1u << i))) + continue; + + printf("\"%c c #%.6lx\",\n", colourchars[i], palette[i]); } - 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 (y = 0; y < h; y++) { + putchar('"'); + for (x = 0; x < w; x++) { + putchar(icon_buf[y*h+x]); + } + printf("\"%.*s\n", y+1> 16) & 0xffff; + unsigned x, y, i, j, n; + + n = 7; + for (i = 0; i < 7; i++) + n -= !strchr(colourseq, '0'+i); + + puts("/* XPM */"); + puts("static char *icon[] = {"); + printf("\"%u %u %u 1\",\n", w, h, 3*n); + for (i = 0; i < 7; i++) { + if (!strchr(colourseq, '0'+i)) + continue; - for (x = 0; x < 3; x++) { - for (y = 0; y < 3; y++) { - tilefunc(icon_buf, c, x, y); + for (j = 0; j < 3; j++) { + printf("\"%c c #%.6lx\",\n", colourchars[3*i+j], + colours[i][j].pixel & 0xffffff); } } for (y = 0; y < h; y++) { + putchar('"'); for (x = 0; x < w; x++) { unsigned long val = icon_buf[y*h+x]; - int c = ' '; + int c = '#'; - if (val == 0xffff0000) c = '.'; // primary - else if (val == 0xff00ff00) c = '%'; // dark - else if (val == 0xff0000ff) c = '+'; // light + for (i = 0; i < sizeof colourchars; i++) { + if (colours[i/3][i%3].pixel == val) { + c = colourchars[i]; + break; + } + } putchar(c); } - putchar('\n'); + printf("\"%.*s\n", y+1 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) +{ + unsigned long *ewmhicon, *icon, size, buf[9]; + char colourseq[10] = "000000000"; + int w32vga = 0; + int opt; + + if (argc > 0) + progname = argv[0]; + + while ((opt = getopt_long(argc, argv, sopts, lopts, 0)) != -1) { + switch (opt) { + case 'c': + if (decode_colourseq(colourseq, optarg) != 0) + return EXIT_FAILURE; + break; + case LOPT_W32VGA: + w32vga = 1; + break; + case 'H': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(); + return EXIT_SUCCESS; + default: + print_usage(stderr); + return EXIT_FAILURE; + } + } + + if (argc != optind+1) { + printf("%s: error: size not specified\n", progname); + print_usage(stderr); + return EXIT_FAILURE; + } + + 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; + } + + icon = find_icon(size, ewmhicon); + if (!icon) + return EXIT_FAILURE; + + if (w32vga) { + print_xpm_w32vga(size, icon); + } else { + print_xpm(colourseq, size, icon); } + free(ewmhicon); return 0; }