1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Alfonso Sabato Siciliano
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32
33 #include <bsddialog.h>
34 #include <bsddialog_theme.h>
35
36 #include "util_theme.h"
37
38 static struct bsddialog_theme t;
39 static char title[1024];
40
41 enum typeprop {
42 BOOL,
43 CHAR,
44 INT,
45 UINT,
46 COLOR
47 };
48
49 struct property {
50 const char* name;
51 enum typeprop type;
52 void *value;
53 };
54
55 #define NPROPERTY 38
56 static struct property p[NPROPERTY] = {
57 { "theme.screen.color", COLOR, &t.screen.color },
58
59 { "theme.shadow.color", COLOR, &t.shadow.color },
60 { "theme.shadow.y", UINT, &t.shadow.y },
61 { "theme.shadow.x", UINT, &t.shadow.x },
62
63 { "theme.dialog.color", COLOR, &t.dialog.color },
64 { "theme.dialog.delimtitle", BOOL, &t.dialog.delimtitle },
65 { "theme.dialog.titlecolor", COLOR, &t.dialog.titlecolor },
66 { "theme.dialog.lineraisecolor", COLOR, &t.dialog.lineraisecolor },
67 { "theme.dialog.linelowercolor", COLOR, &t.dialog.linelowercolor },
68 { "theme.dialog.bottomtitlecolor", COLOR, &t.dialog.bottomtitlecolor },
69 { "theme.dialog.arrowcolor", COLOR, &t.dialog.arrowcolor },
70
71 { "theme.menu.f_selectorcolor", COLOR, &t.menu.f_selectorcolor},
72 { "theme.menu.selectorcolor", COLOR, &t.menu.selectorcolor},
73 { "theme.menu.f_namecolor", COLOR, &t.menu.f_namecolor},
74 { "theme.menu.namecolor", COLOR, &t.menu.namecolor},
75 { "theme.menu.f_desccolor", COLOR, &t.menu.f_desccolor},
76 { "theme.menu.desccolor", COLOR, &t.menu.desccolor},
77 { "theme.menu.namesepcolor", COLOR, &t.menu.namesepcolor},
78 { "theme.menu.descsepcolor", COLOR, &t.menu.descsepcolor},
79 { "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor},
80 { "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor},
81 { "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor},
82
83 { "theme.form.f_fieldcolor", COLOR, &t.form.f_fieldcolor},
84 { "theme.form.fieldcolor", COLOR, &t.form.fieldcolor},
85 { "theme.form.readonlycolor", COLOR, &t.form.readonlycolor},
86 { "theme.form.bottomdesccolor", COLOR, &t.form.bottomdesccolor},
87
88 { "theme.bar.f_color", COLOR, &t.bar.f_color},
89 { "theme.bar.color", COLOR, &t.bar.color},
90
91 { "theme.button.minmargin", UINT, &t.button.minmargin},
92 { "theme.button.maxmargin", UINT, &t.button.maxmargin},
93 { "theme.button.leftdelim", CHAR, &t.button.leftdelim},
94 { "theme.button.rightdelim", CHAR, &t.button.rightdelim},
95 { "theme.button.delimcolor", COLOR, &t.button.delimcolor},
96 { "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor},
97 { "theme.button.color", COLOR, &t.button.color},
98 { "theme.button.f_color", COLOR, &t.button.f_color},
99 { "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor},
100 { "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor}
101 };
102
103 static const char *color[8] = {
104 "black",
105 "red",
106 "green",
107 "yellow",
108 "blue",
109 "magenta",
110 "cyan",
111 "white"
112 };
113
114 #define EXIT_FMTERROR(fmt, ...) do { \
115 bsddialog_end(); \
116 printf("Error: "); \
117 printf(fmt, __VA_ARGS__); \
118 printf(".\n"); \
119 exit (255); \
120 } while (0)
121
savetheme(const char * file,const char * version)122 void savetheme(const char *file, const char *version)
123 {
124 int i;
125 unsigned int flags;
126 enum bsddialog_color bg, fg;
127 time_t clock;
128 FILE *fp;
129
130 if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
131 EXIT_FMTERROR("cannot save theme: %s", bsddialog_geterror());
132
133 if(time(&clock) < 0)
134 EXIT_FMTERROR("%s", "cannot save profile getting current time");
135
136 if ((fp = fopen(file, "w")) == NULL)
137 EXIT_FMTERROR("cannot open %s to save profile", file);
138
139 fprintf(fp, "### bsddialog theme - %s", ctime(&clock));
140 fputs("# Refer to bsddialog(3) manual for theme.* properties\n", fp);
141 fprintf(fp, "version %s\n", version);
142
143 for (i = 0; i < NPROPERTY; i++) {
144 switch (p[i].type) {
145 case CHAR:
146 fprintf(fp, "%s %c\n", p[i].name, *((char*)p[i].value));
147 break;
148 case INT:
149 fprintf(fp, "%s %d\n", p[i].name, *((int*)p[i].value));
150 break;
151 case UINT:
152 fprintf(fp, "%s %u\n", p[i].name,
153 *((unsigned int*)p[i].value));
154 break;
155 case BOOL:
156 fprintf(fp, "%s %s\n", p[i].name,
157 *((bool*)p[i].value) ? "true" : "false");
158 break;
159 case COLOR:
160 bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg,
161 &flags);
162 fprintf(fp, "%s %s %s%s%s%s\n",
163 p[i].name, color[fg], color[bg],
164 flags & BSDDIALOG_BOLD ? " bold" : "",
165 flags & BSDDIALOG_REVERSE ? " reverse" : "",
166 flags & BSDDIALOG_UNDERLINE ? " underline" : "");
167 break;
168 }
169 }
170
171 fclose(fp);
172 }
173
setdeftheme(enum bsddialog_default_theme theme)174 void setdeftheme(enum bsddialog_default_theme theme)
175 {
176 if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK)
177 EXIT_FMTERROR("%s", bsddialog_geterror());
178 }
179
loadtheme(const char * file)180 void loadtheme(const char *file)
181 {
182 bool boolvalue;
183 char charvalue, *value;
184 char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ];
185 int i, j, intvalue, flags;
186 unsigned int uintvalue;
187 enum bsddialog_color bg, fg;
188 FILE *fp;
189
190 if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
191 EXIT_FMTERROR("Cannot get current theme: %s",
192 bsddialog_geterror());
193
194 if((fp = fopen(file, "r")) == NULL)
195 EXIT_FMTERROR("Cannot open theme \"%s\"", file);
196
197 #define EXIT_ERROR(name, error) do { \
198 fclose(fp); \
199 EXIT_FMTERROR("%s for \"%s\"", error, name); \
200 } while (0)
201
202 while(fgets(line, BUFSIZ, fp) != NULL) {
203 if(line[0] == '#' || line[0] == '\n')
204 continue; /* superfluous, only for efficiency */
205 sscanf(line, "%s", name);
206 for (i = 0; i < NPROPERTY; i++) {
207 if (strcmp(name, p[i].name) == 0) {
208 value = &line[strlen(name)];
209 break;
210 }
211 }
212 if (i >= NPROPERTY) {
213 if (strcmp(name, "version") == 0)
214 continue;
215 EXIT_ERROR(name, "Unknown theme property name");
216 }
217 switch (p[i].type) {
218 case CHAR:
219 while (value[0] == ' ' || value[0] == '\n' ||
220 value[0] == '\0')
221 value++;
222 if (sscanf(value, "%c", &charvalue) != 1)
223 EXIT_ERROR(p[i].name, "Cannot get a char");
224 *((int*)p[i].value) = charvalue;
225 break;
226 case INT:
227 if (sscanf(value, "%d", &intvalue) != 1)
228 EXIT_ERROR(p[i].name, "Cannot get a int");
229 *((int*)p[i].value) = intvalue;
230 break;
231 case UINT:
232 if (sscanf(value, "%u", &uintvalue) != 1)
233 EXIT_ERROR(p[i].name, "Cannot get a uint");
234 *((unsigned int*)p[i].value) = uintvalue;
235 break;
236 case BOOL:
237 boolvalue = (strstr(value, "true") != NULL) ?
238 true :false;
239 *((bool*)p[i].value) = boolvalue;
240 break;
241 case COLOR:
242 if (sscanf(value, "%s %s", c1, c2) != 2)
243 EXIT_ERROR(p[i].name, "Cannot get 2 colors");
244 /* Foreground */
245 for (j = 0; j < 8 ; j++)
246 if ((strstr(c1, color[j])) != NULL)
247 break;
248 if ((fg = j) > 7)
249 EXIT_ERROR(p[i].name, "Bad foreground");
250 /* Background */
251 for (j = 0; j < 8 ; j++)
252 if ((value = strstr(c2, color[j])) != NULL)
253 break;
254 if ((bg = j) > 7)
255 EXIT_ERROR(p[i].name, "Bad background");
256 /* Flags */
257 flags = 0;
258 if (strstr(value, "bold") != NULL)
259 flags |= BSDDIALOG_BOLD;
260 if (strstr(value, "reverse") != NULL)
261 flags |= BSDDIALOG_REVERSE;
262 if (strstr(value, "underline") != NULL)
263 flags |= BSDDIALOG_UNDERLINE;
264 *((int*)p[i].value) = bsddialog_color(fg, bg, flags);
265 break;
266 }
267 }
268
269 fclose(fp);
270
271 bsddialog_set_theme(&t);
272 }
273
bikeshed(struct bsddialog_conf * conf)274 void bikeshed(struct bsddialog_conf *conf)
275 {
276 int margin, i;
277 int colors[8] = {0, 0, 0, 0 ,0 ,0 , 0, 0};
278 enum bsddialog_color col[6];
279 time_t clock;
280
281 time(&clock);
282 srand(clock);
283
284 /* theme */
285 if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
286 EXIT_FMTERROR("%s", bsddialog_geterror());
287
288 for (i = 0; i < 6; i++) {
289 do {
290 col[i] = rand() % 8;
291 } while (colors[col[i]] == 1);
292 colors[col[i]] = 1;
293 }
294
295 t.screen.color = bsddialog_color(col[4], col[3], 0);
296
297 t.shadow.color = bsddialog_color(col[0], col[0], 0);
298 t.shadow.y = 1,
299 t.shadow.x = 2,
300
301 t.dialog.delimtitle = (rand() % 2 == 0) ? true : false;
302 t.dialog.titlecolor = bsddialog_color(col[3], col[5], 0);
303 t.dialog.lineraisecolor = bsddialog_color(col[0], col[5], 0);
304 t.dialog.linelowercolor = bsddialog_color(col[0], col[5], 0);
305 t.dialog.color = bsddialog_color(col[0], col[5], 0);
306 t.dialog.bottomtitlecolor = bsddialog_color(col[0], col[5], 0);
307 t.dialog.arrowcolor = bsddialog_color(col[3], col[5], 0);
308
309 t.menu.f_selectorcolor = bsddialog_color(col[5], col[3], 0);
310 t.menu.selectorcolor = bsddialog_color(col[0], col[5], 0);
311 t.menu.f_desccolor = bsddialog_color(col[5], col[3], 0);
312 t.menu.desccolor = bsddialog_color(col[0], col[5], 0);
313 t.menu.f_namecolor = bsddialog_color(col[5], col[3], 0);
314 t.menu.namecolor = bsddialog_color(col[3], col[5], 0);
315 t.menu.namesepcolor = bsddialog_color(col[1], col[5], 0);
316 t.menu.descsepcolor = bsddialog_color(col[1], col[5], 0);
317 t.menu.f_shortcutcolor = bsddialog_color(col[1], col[3], 0);
318 t.menu.shortcutcolor = bsddialog_color(col[1], col[5], 0);
319 t.menu.bottomdesccolor = bsddialog_color(col[4], col[3], 0);
320
321 t.form.f_fieldcolor = bsddialog_color(col[5], col[3], 0);
322 t.form.fieldcolor = bsddialog_color(col[5], col[4], 0);
323 t.form.readonlycolor = bsddialog_color(col[4], col[5], 0);
324 t.form.bottomdesccolor = bsddialog_color(col[4], col[3], 0);
325
326 t.bar.f_color = bsddialog_color(col[5], col[3], 0);
327 t.bar.color = bsddialog_color(col[3], col[5], 0);
328
329 t.button.minmargin = 1,
330 t.button.maxmargin = 5,
331 t.button.leftdelim = '[',
332 t.button.rightdelim = ']',
333 t.button.f_delimcolor = bsddialog_color(col[5], col[3], 0);
334 t.button.delimcolor = bsddialog_color(col[0], col[5], 0);
335 t.button.f_color = bsddialog_color(col[2], col[3], 0);
336 t.button.color = bsddialog_color(col[0], col[5], 0);
337 t.button.f_shortcutcolor = bsddialog_color(col[5], col[3], 0);
338 t.button.shortcutcolor = bsddialog_color(col[1], col[5], 0);
339
340 if (bsddialog_set_theme(&t))
341 EXIT_FMTERROR("%s", bsddialog_geterror());
342
343 /* conf */
344 conf->button.always_active = (rand() % 2 == 0) ? true : false;
345 if (conf->title != NULL) {
346 memset(title, 0, 1024);
347 margin = rand() % 5;
348 memset(title, ' ', margin);
349 strcpy(title + margin, conf->title);
350 memset(title + strlen(title), ' ', margin);
351 conf->title = title;
352 }
353 }
354