1 /* setstyle.c - loads style related options to wmaker
2  *
3  *  WindowMaker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #ifdef __GLIBC__
23 #define _GNU_SOURCE		/* getopt_long */
24 #endif
25 
26 #include "config.h"
27 
28 #include <sys/stat.h>
29 
30 #include <getopt.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <unistd.h>
37 #include <X11/Xlib.h>
38 
39 #ifdef HAVE_STDNORETURN
40 #include <stdnoreturn.h>
41 #endif
42 
43 #include <WINGs/WUtil.h>
44 
45 #include "../src/wconfig.h"
46 
47 #include "common.h"
48 
49 #define MAX_OPTIONS 128
50 
51 char *FontOptions[] = {
52 	"IconTitleFont",
53 	"ClipTitleFont",
54 	"LargeDisplayFont",
55 	"MenuTextFont",
56 	"MenuTitleFont",
57 	"WindowTitleFont",
58 	NULL
59 };
60 
61 char *CursorOptions[] = {
62 	"NormalCursor",
63 	"ArrowCursor",
64 	"MoveCursor",
65 	"ResizeCursor",
66 	"TopLeftResizeCursor",
67 	"TopRightResizeCursor",
68 	"BottomLeftResizeCursor",
69 	"BottomRightResizeCursor",
70 	"VerticalResizeCursor",
71 	"HorizontalResizeCursor",
72 	"WaitCursor",
73 	"QuestionCursor",
74 	"TextCursor",
75 	"SelectCursor",
76 	NULL
77 };
78 
79 static const char *prog_name;
80 int ignoreFonts = 0;
81 int ignoreCursors = 0;
82 
83 Display *dpy;
84 
85 
isCursorOption(const char * option)86 static Bool isCursorOption(const char *option)
87 {
88 	int i;
89 
90 	for (i = 0; CursorOptions[i] != NULL; i++) {
91 		if (strcasecmp(option, CursorOptions[i]) == 0) {
92 			return True;
93 		}
94 	}
95 
96 	return False;
97 }
98 
isFontOption(const char * option)99 static Bool isFontOption(const char *option)
100 {
101 	int i;
102 
103 	for (i = 0; FontOptions[i] != NULL; i++) {
104 		if (strcasecmp(option, FontOptions[i]) == 0) {
105 			return True;
106 		}
107 	}
108 
109 	return False;
110 }
111 
112 /*
113  * finds elements in `texture' that reference external files,
114  * prepends `prefix' to these files. `prefix' is a path component
115  * that qualifies the external references to be absolute, possibly
116  * pending further expansion
117  */
hackPathInTexture(WMPropList * texture,const char * prefix)118 static void hackPathInTexture(WMPropList * texture, const char *prefix)
119 {
120 	WMPropList *type;
121 	char *t;
122 
123 	/* get texture type */
124 	type = WMGetFromPLArray(texture, 0);
125 	t = WMGetFromPLString(type);
126 	if (t == NULL)
127 		return;
128 
129 	if (strcasecmp(t, "tpixmap") == 0 ||
130 	    strcasecmp(t, "spixmap") == 0 ||
131 	    strcasecmp(t, "mpixmap") == 0 ||
132 	    strcasecmp(t, "cpixmap") == 0 ||
133 	    strcasecmp(t, "fpixmap") == 0 ||
134 	    strcasecmp(t, "tvgradient") == 0 ||
135 	    strcasecmp(t, "thgradient") == 0 ||
136 	    strcasecmp(t, "tdgradient") == 0) {
137 		WMPropList *file;
138 		char buffer[4018];
139 
140 		/* get pixmap file path */
141 		file = WMGetFromPLArray(texture, 1);
142 		sprintf(buffer, "%s/%s", prefix, WMGetFromPLString(file));
143 		/* replace path with full path */
144 		WMDeleteFromPLArray(texture, 1);
145 		WMInsertInPLArray(texture, 1, WMCreatePLString(buffer));
146 
147 	} else if (strcasecmp(t, "bitmap") == 0) {
148 		WMPropList *file;
149 		char buffer[4018];
150 
151 		/* get bitmap file path */
152 		file = WMGetFromPLArray(texture, 1);
153 		sprintf(buffer, "%s/%s", prefix, WMGetFromPLString(file));
154 		/* replace path with full path */
155 		WMDeleteFromPLArray(texture, 1);
156 		WMInsertInPLArray(texture, 1, WMCreatePLString(buffer));
157 
158 		/* get mask file path */
159 		file = WMGetFromPLArray(texture, 2);
160 		sprintf(buffer, "%s/%s", prefix, WMGetFromPLString(file));
161 		/* replace path with full path */
162 		WMDeleteFromPLArray(texture, 2);
163 		WMInsertInPLArray(texture, 2, WMCreatePLString(buffer));
164 	}
165 }
166 
hackPaths(WMPropList * style,const char * prefix)167 static void hackPaths(WMPropList * style, const char *prefix)
168 {
169 	WMPropList *keys;
170 	WMPropList *key;
171 	WMPropList *value;
172 	int i;
173 
174 	keys = WMGetPLDictionaryKeys(style);
175 
176 	for (i = 0; i < WMGetPropListItemCount(keys); i++) {
177 		key = WMGetFromPLArray(keys, i);
178 
179 		value = WMGetFromPLDictionary(style, key);
180 		if (!value)
181 			continue;
182 
183 		if (strcasecmp(WMGetFromPLString(key), "WorkspaceSpecificBack") == 0) {
184 			if (WMIsPLArray(value)) {
185 				int j;
186 				WMPropList *texture;
187 
188 				for (j = 0; j < WMGetPropListItemCount(value); j++) {
189 					texture = WMGetFromPLArray(value, j);
190 
191 					if (texture && WMIsPLArray(texture)
192 					    && WMGetPropListItemCount(texture) > 2) {
193 
194 						hackPathInTexture(texture, prefix);
195 					}
196 				}
197 			}
198 		} else {
199 
200 			if (WMIsPLArray(value) && WMGetPropListItemCount(value) > 2) {
201 
202 				hackPathInTexture(value, prefix);
203 			}
204 		}
205 	}
206 
207 	WMReleasePropList(keys);
208 }
209 
getColor(WMPropList * texture)210 static WMPropList *getColor(WMPropList * texture)
211 {
212 	WMPropList *value, *type;
213 	char *str;
214 
215 	type = WMGetFromPLArray(texture, 0);
216 	if (!type)
217 		return NULL;
218 
219 	value = NULL;
220 
221 	str = WMGetFromPLString(type);
222 	if (strcasecmp(str, "solid") == 0) {
223 		value = WMGetFromPLArray(texture, 1);
224 	} else if (strcasecmp(str, "dgradient") == 0
225 		   || strcasecmp(str, "hgradient") == 0 || strcasecmp(str, "vgradient") == 0) {
226 		WMPropList *c1, *c2;
227 		int r1, g1, b1, r2, g2, b2;
228 		char buffer[32];
229 
230 		c1 = WMGetFromPLArray(texture, 1);
231 		c2 = WMGetFromPLArray(texture, 2);
232 		if (!dpy) {
233 			if (sscanf(WMGetFromPLString(c1), "#%2x%2x%2x", &r1, &g1, &b1) == 3
234 			    && sscanf(WMGetFromPLString(c2), "#%2x%2x%2x", &r2, &g2, &b2) == 3) {
235 				sprintf(buffer, "#%02x%02x%02x", (r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2);
236 				value = WMCreatePLString(buffer);
237 			} else {
238 				value = c1;
239 			}
240 		} else {
241 			XColor color1;
242 			XColor color2;
243 
244 			XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), WMGetFromPLString(c1), &color1);
245 			XParseColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), WMGetFromPLString(c2), &color2);
246 
247 			sprintf(buffer, "#%02x%02x%02x",
248 				(color1.red + color2.red) >> 9,
249 				(color1.green + color2.green) >> 9, (color1.blue + color2.blue) >> 9);
250 			value = WMCreatePLString(buffer);
251 		}
252 	} else if (strcasecmp(str, "mdgradient") == 0
253 		   || strcasecmp(str, "mhgradient") == 0 || strcasecmp(str, "mvgradient") == 0) {
254 
255 		value = WMGetFromPLArray(texture, 1);
256 
257 	} else if (strcasecmp(str, "tpixmap") == 0
258 		   || strcasecmp(str, "cpixmap") == 0 || strcasecmp(str, "spixmap") == 0) {
259 
260 		value = WMGetFromPLArray(texture, 2);
261 	}
262 
263 	return value;
264 }
265 
266 /*
267  * since some of the options introduce incompatibilities, we will need
268  * to do a kluge here or the themes ppl will get real annoying.
269  * So, treat for the absence of the following options:
270  * IconTitleColor
271  * IconTitleBack
272  */
hackStyle(WMPropList * style)273 static void hackStyle(WMPropList * style)
274 {
275 	WMPropList *keys, *tmp;
276 	int foundIconTitle = 0, foundResizebarBack = 0;
277 	int i;
278 
279 	keys = WMGetPLDictionaryKeys(style);
280 
281 	for (i = 0; i < WMGetPropListItemCount(keys); i++) {
282 		char *str;
283 
284 		tmp = WMGetFromPLArray(keys, i);
285 		str = WMGetFromPLString(tmp);
286 		if (str) {
287 			if (ignoreFonts && isFontOption(str)) {
288 				WMRemoveFromPLDictionary(style, tmp);
289 				continue;
290 			}
291 			if (ignoreCursors && isCursorOption(str)) {
292 				WMRemoveFromPLDictionary(style, tmp);
293 				continue;
294 			}
295 			if (isFontOption(str)) {
296 				WMPropList *value;
297 				char *newfont, *oldfont;
298 
299 				value = WMGetFromPLDictionary(style, tmp);
300 				if (value) {
301 					oldfont = WMGetFromPLString(value);
302 					newfont = convertFont(oldfont, False);
303 					if (newfont != oldfont) {
304 						value = WMCreatePLString(newfont);
305 						WMPutInPLDictionary(style, tmp, value);
306 						WMReleasePropList(value);
307 						wfree(newfont);
308 					}
309 				}
310 			}
311 			if (strcasecmp(str, "IconTitleColor") == 0 || strcasecmp(str, "IconTitleBack") == 0) {
312 				foundIconTitle = 1;
313 			} else if (strcasecmp(str, "ResizebarBack") == 0) {
314 				foundResizebarBack = 1;
315 			}
316 		}
317 	}
318 	WMReleasePropList(keys);
319 
320 	if (!foundIconTitle) {
321 		/* set the default values */
322 		tmp = WMGetFromPLDictionary(style, WMCreatePLString("FTitleColor"));
323 		if (tmp) {
324 			WMPutInPLDictionary(style, WMCreatePLString("IconTitleColor"), tmp);
325 		}
326 
327 		tmp = WMGetFromPLDictionary(style, WMCreatePLString("FTitleBack"));
328 		if (tmp) {
329 			WMPropList *value;
330 
331 			value = getColor(tmp);
332 
333 			if (value) {
334 				WMPutInPLDictionary(style, WMCreatePLString("IconTitleBack"), value);
335 			}
336 		}
337 	}
338 
339 	if (!foundResizebarBack) {
340 		/* set the default values */
341 		tmp = WMGetFromPLDictionary(style, WMCreatePLString("UTitleBack"));
342 		if (tmp) {
343 			WMPropList *value;
344 
345 			value = getColor(tmp);
346 
347 			if (value) {
348 				WMPropList *t;
349 
350 				t = WMCreatePLArray(WMCreatePLString("solid"), value, NULL);
351 				WMPutInPLDictionary(style, WMCreatePLString("ResizebarBack"), t);
352 			}
353 		}
354 	}
355 
356 	if (!WMGetFromPLDictionary(style, WMCreatePLString("MenuStyle"))) {
357 		WMPutInPLDictionary(style, WMCreatePLString("MenuStyle"), WMCreatePLString("normal"));
358 	}
359 }
360 
print_help(int print_usage,int exitval)361 static noreturn void print_help(int print_usage, int exitval)
362 {
363 	printf("Usage: %s [OPTIONS] FILE\n", prog_name);
364 	if (print_usage) {
365 		puts("Reads style/theme configuration from FILE and updates Window Maker.");
366 		puts("");
367 		puts("  --no-fonts          ignore font related options");
368 		puts("  --no-cursors        ignore cursor related options");
369 		puts("  --ignore <option>   ignore changes in the specified option");
370 		puts("  -h, --help          display this help and exit");
371 		puts("  -v, --version       output version information and exit");
372 	}
373 	exit(exitval);
374 }
375 
main(int argc,char ** argv)376 int main(int argc, char **argv)
377 {
378 	WMPropList *prop, *style;
379 	char *path;
380 	char *file = NULL;
381 	struct stat st;
382 	int i, ch, ignflag = 0;
383 	int ignoreCount = 0;
384 	char *ignoreList[MAX_OPTIONS];
385 	XEvent ev;
386 
387 	struct option longopts[] = {
388 		{ "version",	no_argument,		NULL,			'v' },
389 		{ "help",	no_argument,		NULL,			'h' },
390 		{ "no-fonts",	no_argument,		&ignoreFonts,		1 },
391 		{ "no-cursors",	no_argument,		&ignoreCursors,		1 },
392 		{ "ignore",	required_argument,	&ignflag,		1 },
393 		{ NULL,		0,			NULL,			0 }
394 	};
395 
396 	prog_name = argv[0];
397 	while ((ch = getopt_long(argc, argv, "hv", longopts, NULL)) != -1)
398 		switch(ch) {
399 			case 'v':
400 				printf("%s (Window Maker %s)\n", prog_name, VERSION);
401 				return 0;
402 				/* NOTREACHED */
403 			case 'h':
404 				print_help(1, 0);
405 				/* NOTREACHED */
406 			case 0:
407 				if (ignflag) {
408 					if (ignoreCount >= MAX_OPTIONS) {
409 						printf("Maximum %d `ignore' arguments\n", MAX_OPTIONS);
410 						return 1;
411 					}
412 					ignoreList[ignoreCount++] = optarg;
413 					ignflag = 0;
414 				};
415 				break;
416 			default:
417 				print_help(0, 1);
418 				/* NOTREACHED */
419 		}
420 
421 	argc -= optind;
422 	argv += optind;
423 
424 	if (argc != 1)
425 		print_help(0, 1);
426 
427 	file = argv[0];
428 
429 	WMPLSetCaseSensitive(False);
430 
431 	path = wdefaultspathfordomain("WindowMaker");
432 
433 	prop = WMReadPropListFromFile(path);
434 	if (!prop) {
435 		perror(path);
436 		printf("%s: could not load WindowMaker configuration file.\n", prog_name);
437 		return 1;
438 	}
439 
440 	if (stat(file, &st) < 0) {
441 		perror(file);
442 		return 1;
443 	}
444 	if (S_ISDIR(st.st_mode)) {		/* theme pack */
445 		char buf[PATH_MAX];
446 		char *homedir;
447 
448 		if (realpath(file, buf) == NULL) {
449 			perror(file);
450 			return 1;
451 		}
452 		strncat(buf, "/style", sizeof(buf) - strlen(buf) - 1);
453 
454 		if (stat(buf, &st) != 0 || !S_ISREG(st.st_mode)) {	/* maybe symlink too? */
455 			printf("%s: %s: style file not found or not a file\n", prog_name, buf);
456 			return 1;
457 		}
458 
459 		style = WMReadPropListFromFile(buf);
460 		if (!style) {
461 			perror(buf);
462 			printf("%s: could not load style file.\n", prog_name);
463 			return 1;
464 		}
465 
466 		buf[strlen(buf) - 6 /* strlen("/style") */] = '\0';
467 		homedir = wstrdup(wgethomedir());
468 		if (strlen(homedir) > 1	&&	/* this is insane, wgethomedir() returns `/' on error */
469 		    strncmp(homedir, buf, strlen(homedir)) == 0) {
470 			/* theme pack is under ${HOME}; exchange ${HOME} part
471 			 * for `~' so it gets portable references to the user home dir */
472 			*buf = '~';
473 			memmove(buf + 1, buf + strlen(homedir), strlen(buf) - strlen(homedir) + 1);
474 		}
475 		wfree(homedir);
476 
477 		hackPaths(style, buf);		/* this will prefix pixmaps in the style
478 						 * with absolute(ish) references */
479 
480 	} else {				/* normal style file */
481 
482 		style = WMReadPropListFromFile(file);
483 		if (!style) {
484 			perror(file);
485 			printf("%s:could not load style file.\n", prog_name);
486 			return 1;
487 		}
488 	}
489 
490 	if (!WMIsPLDictionary(style)) {
491 		printf("%s: '%s' is not a style file/theme\n", prog_name, file);
492 		return 1;
493 	}
494 
495 	hackStyle(style);
496 
497 	if (ignoreCount > 0) {
498 		for (i = 0; i < ignoreCount; i++) {
499 			WMRemoveFromPLDictionary(style, WMCreatePLString(ignoreList[i]));
500 		}
501 	}
502 
503 	WMMergePLDictionaries(prop, style, True);
504 
505 	WMWritePropListToFile(prop, path);
506 
507 	dpy = XOpenDisplay("");
508 	if (dpy) {
509 		memset(&ev, 0, sizeof(XEvent));
510 
511 		ev.xclient.type = ClientMessage;
512 		ev.xclient.message_type = XInternAtom(dpy, "_WINDOWMAKER_COMMAND", False);
513 		ev.xclient.window = DefaultRootWindow(dpy);
514 		ev.xclient.format = 8;
515 		strncpy(ev.xclient.data.b, "Reconfigure", sizeof(ev.xclient.data.b));
516 
517 		XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureRedirectMask, &ev);
518 		XFlush(dpy);
519 	}
520 
521 	return 0;
522 }
523