Reformat
[clav.git] / color-selection.c
blob21b65fddd89347494e115f9eece3ff246f7c0758
1 /*
2 * Copyright (c) 2016-2019, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
25 #include "macros.h"
28 * I am fully aware of how ugly this file is, which is why it's specially
29 * split out, so that it can be completely redone one day, when some
30 * kind of better solution exists.
32 static uint_fast8_t try_kdialog = 1;
33 static uint_fast8_t try_qarma = 1;
34 static uint_fast8_t try_yad = 1;
35 static uint_fast8_t try_zenity = 1;
36 static uint_fast8_t try_xdialog = 1;
37 static char *cmd_kdialog = "kdialog --getcolor 2>/dev/null";
38 static char *cmd_qarma = "qarma --color-selection 2>/dev/null";
39 static char *cmd_yad = "yad --color 2>/dev/null";
40 static char *cmd_zenity = "zenity --color-selection 2>/dev/null";
41 static char *cmd_xdialog = "Xdialog --stdout --colorsel '' 0 0 2>/dev/null";
43 /* Convert result of fgetc into [0,16), with special case for EOF */
44 static int
45 atoh(int c, uint_fast8_t *early_eof)
47 if (c == EOF) {
48 *early_eof = 1;
49 } else if ('0' <= c &&
50 c <= '9') {
51 return c - '0';
52 } else if ('a' <= c &&
53 c <= 'f') {
54 return c - 'a' + 10;
55 } else if ('A' <= c &&
56 c <= 'F') {
57 return c - 'A' + 10;
60 return 0;
63 /* Read in #rrggbb, returned by most of these programs */
64 static int
65 read_hex(FILE *f, uint32_t *out_argb, uint_fast8_t *out_affirmed)
67 int ret = 0;
68 int pret = 0;
69 uint32_t argb = 0;
70 uint_fast8_t early_eof = 0;
71 int c = '#';
72 int n = 0;
74 /* ignore leading '#', if there are any */
75 do {
76 c = fgetc(f);
77 n++;
78 } while (n < 128 &&
79 !('0' <= c &&
80 c <= '9') &&
81 !('a' <= c &&
82 c <= 'f') &&
83 !('A' <= c &&
84 c <= 'F'));
86 /* r */
87 argb |= atoh(c, &early_eof) << 20;
88 argb |= atoh(fgetc(f), &early_eof) << 16;
90 /* g */
91 argb |= atoh(fgetc(f), &early_eof) << 12;
92 argb |= atoh(fgetc(f), &early_eof) << 8;
94 /* b */
95 argb |= atoh(fgetc(f), &early_eof) << 4;
96 argb |= atoh(fgetc(f), &early_eof) << 0;
97 ret = 0;
99 /* Sometimes, zenity returns #rrrrggggbbbb */
100 if (early_eof) {
101 goto done;
104 if (fgetc(f) == EOF) {
105 goto done;
108 if (fgetc(f) == EOF) {
109 goto done;
112 /* We are certainly in #rrrrggggbbbb */
113 argb = (argb & 0xff0000) | ((argb & 0xff) << 8);
114 argb |= atoh(fgetc(f), &early_eof) << 4;
115 argb |= atoh(fgetc(f), &early_eof) << 0;
116 done:
117 pret = pclose(f);
119 if (!ret &&
120 WIFEXITED(pret) &&
121 WEXITSTATUS(pret) != 127) {
122 *out_argb = argb;
123 *out_affirmed = !early_eof;
124 } else {
125 ret = 1;
126 *out_argb = 0;
127 *out_affirmed = 0;
130 return ret;
133 /* Read in rrr ggg bbb, returned by xdialog */
134 static int
135 read_spaced(FILE *f, uint32_t *out_argb, uint_fast8_t *out_affirmed)
137 int ret = 0;
138 int pret = 0;
139 int c = 0;
140 int n = 0;
141 uint_fast8_t r = 0;
142 uint_fast8_t g = 0;
143 uint_fast8_t b = 0;
144 uint_fast8_t s = 0;
146 /* r */
147 s = 0;
149 while (1) {
150 n++;
151 c = fgetc(f);
153 if (c == EOF ||
154 n > 128) {
155 *out_affirmed = 0;
156 goto done;
157 } else if (s &&
158 (c == ' ' ||
159 c == ',')) {
160 break;
161 } else if ('0' <= c &&
162 c <= '9') {
163 r = 10 * r + (c - '0');
164 s = 1;
168 /* g */
169 s = 0;
171 while (1) {
172 n++;
173 c = fgetc(f);
175 if (c == EOF ||
176 n > 128) {
177 *out_affirmed = 0;
178 goto done;
179 } else if (s &&
180 (c == ' ' ||
181 c == ',')) {
182 break;
183 } else if ('0' <= c &&
184 c <= '9') {
185 g = 10 * g + (c - '0');
186 s = 1;
190 /* b */
191 s = 0;
193 while (1) {
194 n++;
195 c = fgetc(f);
197 if (c == EOF ||
198 n > 128) {
199 *out_affirmed = s;
200 break;
201 } else if (s &&
202 (c == ' ' ||
203 c == ',' ||
204 c == ')')) {
205 *out_affirmed = 1;
206 break;
207 } else if ('0' <= c &&
208 c <= '9') {
209 b = 10 * b + (c - '0');
210 s = 1;
214 ret = 0;
215 done:
216 pret = pclose(f);
218 if (!ret &&
219 WIFEXITED(pret) &&
220 WEXITSTATUS(pret) != 127) {
221 *out_argb = (r << 16) | (g << 8) | (b << 0);
222 } else {
223 ret = 1;
224 *out_argb = 0;
227 return ret;
230 /* read in EITHER rgb(rrr,ggg,bbb) or #rrggbb */
231 static int
232 read_zenity(FILE *f, uint32_t *out_argb, uint_fast8_t *out_affirmed)
234 int c = fgetc(f);
236 if (c == 'r') {
237 /* Probably zenity's rgb(rrr,ggg,bbb,aaa) thing */
238 fgetc(f);
239 fgetc(f);
240 fgetc(f);
242 return read_spaced(f, out_argb, out_affirmed);
243 } else if (c == '#') {
244 return read_hex(f, out_argb, out_affirmed);
247 /* This also covers the EOF case */
248 *out_affirmed = 0;
250 return 0;
254 choose_color(uint32_t *out_color, uint_fast8_t *out_affirmed)
256 uint32_t argb = 0;
257 uint_fast8_t affirmed = 0;
258 int ret = ENOTSUP;
259 FILE *f = 0;
261 fflush(0);
263 if (try_kdialog) {
264 if ((f = popen(cmd_kdialog, "r"))) {
265 if (!(ret = read_hex(f, &argb, &affirmed))) {
266 goto done;
270 try_kdialog = 0;
273 if (try_qarma) {
274 if ((f = popen(cmd_qarma, "r"))) {
275 if (!(ret = read_hex(f, &argb, &affirmed))) {
276 goto done;
280 try_qarma = 0;
283 if (try_yad) {
284 if ((f = popen(cmd_yad, "r"))) {
285 if (!(ret = read_hex(f, &argb, &affirmed))) {
286 goto done;
290 try_yad = 0;
293 if (try_zenity) {
294 if ((f = popen(cmd_zenity, "r"))) {
295 if (!(ret = read_zenity(f, &argb, &affirmed))) {
296 goto done;
300 try_zenity = 0;
303 if (try_xdialog) {
304 if ((f = popen(cmd_xdialog, "r"))) {
305 if (!(ret = read_spaced(f, &argb, &affirmed))) {
306 goto done;
310 try_xdialog = 0;
313 ret = ENOTSUP;
314 done:
316 if (!ret) {
317 *out_color = argb;
318 *out_affirmed = affirmed;
321 return ret;