- made deiconification not automatically focus window in sloppy focus
[wmaker-crm.git] / util / wmsetbg.c
blob95ea989937f7c318ddf452ba0ddaffc6cb9bd029
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
4 * WindowMaker window manager
5 *
6 * Copyright (c) 1998, 1999 Alfredo K. Kojima
7 * Copyright (c) 1998 Dan Pascu
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22 * USA.
26 * TODO: rewrite, too dirty
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <sys/types.h>
39 #include <ctype.h>
41 #include "../src/config.h"
43 #ifdef HAVE_DLFCN_H
44 #include <dlfcn.h>
45 #endif
47 #include "../src/wconfig.h"
49 #include "../WINGs/WINGs.h"
50 #include "../WINGs/WUtil.h"
51 #include "../wrlib/wraster.h"
53 #include <proplist.h>
55 #define PROG_VERSION "wmsetbg (Window Maker) 2.1"
58 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
61 Display *dpy;
62 char *display = "";
63 Window root;
64 int scr;
65 int scrWidth;
66 int scrHeight;
68 Pixmap CurrentPixmap = None;
69 char *PixmapPath = NULL;
72 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
75 typedef struct BackgroundTexture {
76 int refcount;
78 int solid;
80 char *spec;
82 XColor color;
83 Pixmap pixmap; /* for all textures, including solid */
84 int width; /* size of the pixmap */
85 int height;
86 } BackgroundTexture;
90 RImage*
91 loadImage(RContext *rc, char *file)
93 char *path;
94 RImage *image;
96 if (access(file, F_OK)!=0) {
97 path = wfindfile(PixmapPath, file);
98 if (!path) {
99 wwarning("%s:could not find image file used in texture", file);
100 return NULL;
102 } else {
103 path = wstrdup(file);
106 image = RLoadImage(rc, path, 0);
107 if (!image) {
108 wwarning("%s:could not load image file used in texture:%s", path,
109 RMessageForError(RErrorCode));
111 free(path);
113 return image;
117 BackgroundTexture*
118 parseTexture(RContext *rc, char *text)
120 BackgroundTexture *texture = NULL;
121 proplist_t texarray;
122 proplist_t val;
123 int count;
124 char *tmp;
125 char *type;
127 #define GETSTRORGOTO(val, str, i, label) \
128 val = PLGetArrayElement(texarray, i);\
129 if (!PLIsString(val)) {\
130 wwarning("could not parse texture %s", text);\
131 goto label;\
133 str = PLGetString(val)
135 texarray = PLGetProplistWithDescription(text);
136 if (!texarray || !PLIsArray(texarray)
137 || (count = PLGetNumberOfElements(texarray)) < 2) {
139 wwarning("could not parse texture %s", text);
140 if (texarray)
141 PLRelease(texarray);
142 return NULL;
145 texture = wmalloc(sizeof(BackgroundTexture));
146 memset(texture, 0, sizeof(BackgroundTexture));
148 GETSTRORGOTO(val, type, 0, error);
150 if (strcasecmp(type, "solid")==0) {
151 XColor color;
152 Pixmap pixmap;
154 texture->solid = 1;
156 GETSTRORGOTO(val, tmp, 1, error);
158 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
159 wwarning("could not parse color %s in texture %s", tmp, text);
160 goto error;
162 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
164 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
165 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
166 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
168 texture->pixmap = pixmap;
169 texture->color = color;
170 texture->width = 8;
171 texture->height = 8;
172 } else if (strcasecmp(type, "vgradient")==0
173 || strcasecmp(type, "dgradient")==0
174 || strcasecmp(type, "hgradient")==0) {
175 XColor color;
176 RColor color1, color2;
177 RImage *image;
178 Pixmap pixmap;
179 int gtype;
180 int iwidth, iheight;
182 GETSTRORGOTO(val, tmp, 1, error);
184 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
185 wwarning("could not parse color %s in texture %s", tmp, text);
186 goto error;
189 color1.red = color.red >> 8;
190 color1.green = color.green >> 8;
191 color1.blue = color.blue >> 8;
193 GETSTRORGOTO(val, tmp, 2, error);
195 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
196 wwarning("could not parse color %s in texture %s", tmp, text);
197 goto error;
200 color2.red = color.red >> 8;
201 color2.green = color.green >> 8;
202 color2.blue = color.blue >> 8;
204 switch (type[0]) {
205 case 'h':
206 case 'H':
207 gtype = RHorizontalGradient;
208 iwidth = scrWidth;
209 iheight = 8;
210 break;
211 case 'V':
212 case 'v':
213 gtype = RVerticalGradient;
214 iwidth = 8;
215 iheight = scrHeight;
216 break;
217 default:
218 gtype = RDiagonalGradient;
219 iwidth = scrWidth;
220 iheight = scrHeight;
221 break;
224 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
226 if (!image) {
227 wwarning("could not render gradient texture:%s",
228 RMessageForError(RErrorCode));
229 goto error;
232 if (!RConvertImage(rc, image, &pixmap)) {
233 wwarning("could not convert texture:%s",
234 RMessageForError(RErrorCode));
235 RDestroyImage(image);
236 goto error;
239 texture->width = image->width;
240 texture->height = image->height;
241 RDestroyImage(image);
243 texture->pixmap = pixmap;
244 } else if (strcasecmp(type, "mvgradient")==0
245 || strcasecmp(type, "mdgradient")==0
246 || strcasecmp(type, "mhgradient")==0) {
247 XColor color;
248 RColor **colors;
249 RImage *image;
250 Pixmap pixmap;
251 int i, j;
252 int gtype;
253 int iwidth, iheight;
255 colors = malloc(sizeof(RColor*)*(count-1));
256 if (!colors) {
257 wwarning("out of memory while parsing texture");
258 goto error;
260 memset(colors, 0, sizeof(RColor*)*(count-1));
262 for (i = 2; i < count; i++) {
263 val = PLGetArrayElement(texarray, i);
264 if (!PLIsString(val)) {
265 wwarning("could not parse texture %s", text);
267 for (j = 0; colors[j]!=NULL; j++)
268 free(colors[j]);
269 free(colors);
270 goto error;
272 tmp = PLGetString(val);
274 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
275 wwarning("could not parse color %s in texture %s",
276 tmp, text);
278 for (j = 0; colors[j]!=NULL; j++)
279 free(colors[j]);
280 free(colors);
281 goto error;
283 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
284 wwarning("out of memory while parsing texture");
286 for (j = 0; colors[j]!=NULL; j++)
287 free(colors[j]);
288 free(colors);
289 goto error;
292 colors[i-2]->red = color.red >> 8;
293 colors[i-2]->green = color.green >> 8;
294 colors[i-2]->blue = color.blue >> 8;
297 switch (type[1]) {
298 case 'h':
299 case 'H':
300 gtype = RHorizontalGradient;
301 iwidth = scrWidth;
302 iheight = 8;
303 break;
304 case 'V':
305 case 'v':
306 gtype = RVerticalGradient;
307 iwidth = 8;
308 iheight = scrHeight;
309 break;
310 default:
311 gtype = RDiagonalGradient;
312 iwidth = scrWidth;
313 iheight = scrHeight;
314 break;
317 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
319 for (j = 0; colors[j]!=NULL; j++)
320 free(colors[j]);
321 free(colors);
323 if (!image) {
324 wwarning("could not render gradient texture:%s",
325 RMessageForError(RErrorCode));
326 goto error;
329 if (!RConvertImage(rc, image, &pixmap)) {
330 wwarning("could not convert texture:%s",
331 RMessageForError(RErrorCode));
332 RDestroyImage(image);
333 goto error;
336 texture->width = image->width;
337 texture->height = image->height;
338 RDestroyImage(image);
340 texture->pixmap = pixmap;
341 } else if (strcasecmp(type, "cpixmap")==0
342 || strcasecmp(type, "spixmap")==0
343 || strcasecmp(type, "mpixmap")==0
344 || strcasecmp(type, "tpixmap")==0) {
345 XColor color;
346 Pixmap pixmap = None;
347 RImage *image = NULL;
348 int w, h;
349 int iwidth, iheight;
351 GETSTRORGOTO(val, tmp, 1, error);
353 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
354 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
357 if (!pixmap) {
358 image = loadImage(rc, tmp);
359 if (!image) {
360 goto error;
362 iwidth = image->width;
363 iheight = image->height;
366 GETSTRORGOTO(val, tmp, 2, error);
368 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
369 wwarning("could not parse color %s in texture %s\n", tmp, text);
370 RDestroyImage(image);
371 goto error;
373 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
374 RColor rcolor;
376 rcolor.red = color.red >> 8;
377 rcolor.green = color.green >> 8;
378 rcolor.blue = color.blue >> 8;
379 RGetClosestXColor(rc, &rcolor, &color);
381 switch (toupper(type[0])) {
382 case 'T':
383 texture->width = iwidth;
384 texture->height = iheight;
385 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
386 wwarning("could not convert texture:%s",
387 RMessageForError(RErrorCode));
388 RDestroyImage(image);
389 goto error;
391 if (image)
392 RDestroyImage(image);
393 break;
394 case 'S':
395 case 'M':
396 if (toupper(type[0])=='S') {
397 w = scrWidth;
398 h = scrHeight;
399 } else {
400 if (iwidth*scrHeight > iheight*scrWidth) {
401 w = scrWidth;
402 h = (scrWidth*iheight)/iwidth;
403 } else {
404 h = scrHeight;
405 w = (scrHeight*iwidth)/iheight;
409 RImage *simage;
411 simage = RScaleImage(image, w, h);
412 if (!simage) {
413 wwarning("could not scale image:%s",
414 RMessageForError(RErrorCode));
415 RDestroyImage(image);
416 goto error;
418 RDestroyImage(image);
419 image = simage;
420 iwidth = image->width;
421 iheight = image->height;
423 /* fall through */
424 case 'C':
426 Pixmap cpixmap;
428 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
429 wwarning("could not convert texture:%s",
430 RMessageForError(RErrorCode));
431 RDestroyImage(image);
432 goto error;
435 if (iwidth != scrWidth || iheight != scrHeight) {
436 int x, y, sx, sy, w, h;
438 cpixmap = XCreatePixmap(dpy, root, scrWidth, scrHeight,
439 DefaultDepth(dpy, scr));
441 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
442 XFillRectangle(dpy, cpixmap, DefaultGC(dpy, scr),
443 0, 0, scrWidth, scrHeight);
445 if (iheight < scrHeight) {
446 h = iheight;
447 y = (scrHeight - h)/2;
448 sy = 0;
449 } else {
450 sy = (iheight - scrHeight)/2;
451 y = 0;
452 h = scrHeight;
454 if (iwidth < scrWidth) {
455 w = iwidth;
456 x = (scrWidth - w)/2;
457 sx = 0;
458 } else {
459 sx = (iwidth - scrWidth)/2;
460 x = 0;
461 w = scrWidth;
464 XCopyArea(dpy, pixmap, cpixmap, DefaultGC(dpy, scr),
465 sx, sy, w, h, x, y);
466 XFreePixmap(dpy, pixmap);
467 pixmap = cpixmap;
469 if (image)
470 RDestroyImage(image);
472 texture->width = scrWidth;
473 texture->height = scrHeight;
475 break;
478 texture->pixmap = pixmap;
479 texture->color = color;
480 } else if (strcasecmp(type, "thgradient")==0
481 || strcasecmp(type, "tvgradient")==0
482 || strcasecmp(type, "tdgradient")==0) {
483 XColor color;
484 RColor color1, color2;
485 RImage *image;
486 RImage *gradient;
487 RImage *tiled;
488 Pixmap pixmap;
489 int opaq;
490 char *file;
491 int gtype;
492 int twidth, theight;
494 GETSTRORGOTO(val, file, 1, error);
496 GETSTRORGOTO(val, tmp, 2, error);
498 opaq = atoi(tmp);
500 GETSTRORGOTO(val, tmp, 3, error);
502 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
503 wwarning("could not parse color %s in texture %s", tmp, text);
504 goto error;
507 color1.red = color.red >> 8;
508 color1.green = color.green >> 8;
509 color1.blue = color.blue >> 8;
511 GETSTRORGOTO(val, tmp, 4, error);
513 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
514 wwarning("could not parse color %s in texture %s", tmp, text);
515 goto error;
518 color2.red = color.red >> 8;
519 color2.green = color.green >> 8;
520 color2.blue = color.blue >> 8;
522 image = loadImage(rc, file);
523 if (!image) {
524 RDestroyImage(gradient);
525 goto error;
528 switch (type[1]) {
529 case 'h':
530 case 'H':
531 gtype = RHorizontalGradient;
532 twidth = scrWidth;
533 theight = image->height > scrHeight ? scrHeight : image->height;
534 break;
535 case 'V':
536 case 'v':
537 gtype = RVerticalGradient;
538 twidth = image->width > scrWidth ? scrWidth : image->width;
539 theight = scrHeight;
540 break;
541 default:
542 gtype = RDiagonalGradient;
543 twidth = scrWidth;
544 theight = scrHeight;
545 break;
547 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
549 if (!gradient) {
550 wwarning("could not render texture:%s",
551 RMessageForError(RErrorCode));
552 RDestroyImage(gradient);
553 goto error;
556 tiled = RMakeTiledImage(image, twidth, theight);
557 if (!tiled) {
558 wwarning("could not render texture:%s",
559 RMessageForError(RErrorCode));
560 RDestroyImage(gradient);
561 RDestroyImage(image);
562 goto error;
564 RDestroyImage(image);
566 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
567 RDestroyImage(gradient);
569 if (!RConvertImage(rc, tiled, &pixmap)) {
570 wwarning("could not convert texture:%s",
571 RMessageForError(RErrorCode));
572 RDestroyImage(image);
573 goto error;
575 texture->width = tiled->width;
576 texture->height = tiled->height;
578 RDestroyImage(tiled);
580 texture->pixmap = pixmap;
581 } else if (strcasecmp(type, "function")==0) {
582 #ifdef HAVE_DLFCN_H
583 void (*initFunc) (Display*, Colormap);
584 RImage* (*mainFunc) (int, char**, int, int, int);
585 Pixmap pixmap;
586 RImage *image = 0;
587 int success = 0;
588 char *lib, *func, **argv = 0;
589 void *handle = 0;
590 int i, argc;
592 if (count < 3)
593 goto function_cleanup;
595 /* get the library name */
596 GETSTRORGOTO(val, lib, 1, function_cleanup);
598 /* get the function name */
599 GETSTRORGOTO(val, func, 2, function_cleanup);
601 argc = count - 2;
602 argv = (char**)wmalloc(argc * sizeof(char*));
604 /* get the parameters */
605 argv[0] = func;
606 for (i=0; i<argc-1; i++) {
607 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
608 argv[i+1] = wstrdup(tmp);
611 handle = dlopen(lib, RTLD_LAZY);
612 if (!handle) {
613 wwarning("could not find library %s", lib);
614 goto function_cleanup;
617 initFunc = dlsym(handle, "initWindowMaker");
618 if (!initFunc) {
619 wwarning("could not initialize library %s", lib);
620 goto function_cleanup;
622 initFunc(dpy, DefaultColormap(dpy, scr));
624 mainFunc = dlsym(handle, func);
625 if (!mainFunc) {
626 wwarning("could not find function %s::%s", lib, func);
627 goto function_cleanup;
629 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
631 if (!RConvertImage(rc, image, &pixmap)) {
632 wwarning("could not convert texture:%s",
633 RMessageForError(RErrorCode));
634 goto function_cleanup;
636 texture->width = scrWidth;
637 texture->height = scrHeight;
638 texture->pixmap = pixmap;
639 success = 1;
641 function_cleanup:
642 if (argv) {
643 int i;
644 for (i=0; i<argc; i++) {
645 free(argv[i]);
648 if (handle) {
649 dlclose(handle);
651 if (image) {
652 RDestroyImage(image);
654 if (!success) {
655 goto error;
657 #else
658 wwarning("function textures not supported");
659 goto error;
660 #endif
661 } else {
662 wwarning("invalid texture type %s", text);
663 goto error;
666 texture->spec = wstrdup(text);
668 return texture;
670 error:
671 if (texture)
672 free(texture);
673 if (texarray)
674 PLRelease(texarray);
676 return NULL;
680 void
681 freeTexture(BackgroundTexture *texture)
683 if (texture->solid) {
684 long pixel[1];
686 pixel[0] = texture->color.pixel;
687 /* dont free black/white pixels */
688 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
689 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
690 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
692 if (texture->pixmap) {
693 XFreePixmap(dpy, texture->pixmap);
695 free(texture->spec);
696 free(texture);
700 void
701 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
702 int workspace, char *texture)
704 BackgroundTexture *newTexture = NULL;
705 int i;
707 /* unset the texture */
708 if (!texture) {
709 if (textures[workspace]!=NULL) {
710 textures[workspace]->refcount--;
712 if (textures[workspace]->refcount == 0)
713 freeTexture(textures[workspace]);
715 textures[workspace] = NULL;
716 return;
719 if (textures[workspace]
720 && strcasecmp(textures[workspace]->spec, texture)==0) {
721 /* texture did not change */
722 return;
725 /* check if the same texture is already created */
726 for (i = 0; i < *maxTextures; i++) {
727 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
728 newTexture = textures[i];
729 break;
733 if (!newTexture) {
734 /* create the texture */
735 newTexture = parseTexture(rc, texture);
737 if (!newTexture)
738 return;
740 if (textures[workspace]!=NULL) {
742 textures[workspace]->refcount--;
744 if (textures[workspace]->refcount == 0)
745 freeTexture(textures[workspace]);
748 newTexture->refcount++;
749 textures[workspace] = newTexture;
751 if (*maxTextures < workspace)
752 *maxTextures = workspace;
757 Pixmap
758 duplicatePixmap(Pixmap pixmap, int width, int height)
760 Display *tmpDpy;
761 Pixmap copyP;
763 /* must open a new display or the RetainPermanent will
764 * leave stuff allocated in RContext unallocated after exit */
765 tmpDpy = XOpenDisplay(display);
766 if (!tmpDpy) {
767 wwarning("could not open display to update background image information");
769 return None;
770 } else {
771 XSync(dpy, False);
773 copyP = XCreatePixmap(tmpDpy, root, width, height,
774 DefaultDepth(tmpDpy, scr));
775 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
776 0, 0, width, height, 0, 0);
777 XSync(tmpDpy, False);
779 XSetCloseDownMode(tmpDpy, RetainPermanent);
780 XCloseDisplay(tmpDpy);
783 return copyP;
787 static int
788 dummyErrorHandler(Display *dpy, XErrorEvent *err)
790 return 0;
793 void
794 setPixmapProperty(Pixmap pixmap)
796 static Atom prop = 0;
797 Atom type;
798 int format;
799 unsigned long length, after;
800 unsigned char *data;
801 int mode;
803 if (!prop) {
804 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
807 XGrabServer(dpy);
809 /* Clear out the old pixmap */
810 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
811 &type, &format, &length, &after, &data);
813 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
814 XSetErrorHandler(dummyErrorHandler);
815 XKillClient(dpy, *((Pixmap *)data));
816 XSync(dpy, False);
817 XSetErrorHandler(NULL);
818 mode = PropModeReplace;
819 } else {
820 mode = PropModeAppend;
822 if (pixmap)
823 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
824 (unsigned char *) &pixmap, 1);
825 else
826 XDeleteProperty(dpy, root, prop);
829 XUngrabServer(dpy);
830 XFlush(dpy);
835 void
836 changeTexture(BackgroundTexture *texture)
838 if (!texture)
839 return;
841 if (texture->solid) {
842 XSetWindowBackground(dpy, root, texture->color.pixel);
843 } else {
844 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
846 XClearWindow(dpy, root);
848 XSync(dpy, False);
851 Pixmap pixmap;
853 pixmap = duplicatePixmap(texture->pixmap, texture->width,
854 texture->height);
856 setPixmapProperty(pixmap);
862 readmsg(int fd, unsigned char *buffer, int size)
864 int count;
866 count = 0;
867 while (size>0) {
868 count = read(fd, buffer, size);
869 if (count < 0)
870 return -1;
871 size -= count;
872 buffer += count;
873 *buffer = 0;
876 return size;
881 * Message Format:
882 * sizeSntexture_spec - sets the texture for workspace n
883 * sizeCn - change background texture to the one for workspace n
884 * sizePpath - set the pixmap search path
886 * n is 4 bytes
887 * size = 4 bytes for length of the message data
889 void
890 helperLoop(RContext *rc)
892 BackgroundTexture *textures[WORKSPACE_COUNT];
893 int maxTextures = 0;
894 unsigned char buffer[2048], buf[8];
895 int size;
896 int errcount = 4;
898 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
901 while (1) {
902 int workspace;
904 /* get length of message */
905 if (readmsg(0, buffer, 4) < 0) {
906 wsyserror("error reading message from Window Maker");
907 errcount--;
908 if (errcount == 0) {
909 wfatal("quitting");
910 exit(1);
912 continue;
914 memcpy(buf, buffer, 4);
915 buf[4] = 0;
916 size = atoi(buf);
918 /* get message */
919 if (readmsg(0, buffer, size) < 0) {
920 wsyserror("error reading message from Window Maker");
921 errcount--;
922 if (errcount == 0) {
923 wfatal("quitting");
924 exit(1);
926 continue;
928 #ifdef DEBUG
929 printf("RECEIVED %s\n",buffer);
930 #endif
931 if (buffer[0]!='P' && buffer[0]!='K') {
932 memcpy(buf, &buffer[1], 4);
933 buf[4] = 0;
934 workspace = atoi(buf);
935 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
936 wwarning("received message with invalid workspace number %i\n",
937 workspace);
938 continue;
942 switch (buffer[0]) {
943 case 'S':
944 #ifdef DEBUG
945 printf("set texture %s\n", &buffer[5]);
946 #endif
947 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
948 break;
950 case 'C':
951 #ifdef DEBUG
952 printf("change texture %i\n", workspace);
953 #endif
954 if (!textures[workspace])
955 changeTexture(textures[0]);
956 else
957 changeTexture(textures[workspace]);
958 break;
960 case 'P':
961 #ifdef DEBUG
962 printf("change pixmappath %s\n", &buffer[1]);
963 #endif
964 if (PixmapPath)
965 free(PixmapPath);
966 PixmapPath = wstrdup(&buffer[1]);
967 break;
969 case 'U':
970 #ifdef DEBUG
971 printf("unset workspace %i\n", workspace);
972 #endif
973 setupTexture(rc, textures, &maxTextures, workspace, NULL);
974 break;
976 case 'K':
977 #ifdef DEBUG
978 printf("exit command\n");
979 #endif
980 exit(0);
982 default:
983 wwarning("unknown message received");
984 break;
990 void
991 updateDomain(char *domain, char *key, char *texture)
993 char *program = "wdwrite";
995 execlp(program, program, domain, key, texture, NULL);
996 wwarning("warning could not run \"%s\"", program);
1001 char*
1002 globalDefaultsPathForDomain(char *domain)
1004 char path[1024];
1006 sprintf(path, "%s/%s", SYSCONFDIR, domain);
1008 return wstrdup(path);
1012 proplist_t
1013 getValueForKey(char *domain, char *keyName)
1015 char *path;
1016 proplist_t key;
1017 proplist_t d;
1018 proplist_t val;
1020 key = PLMakeString(keyName);
1022 /* try to find PixmapPath in user defaults */
1023 path = wdefaultspathfordomain(domain);
1024 d = PLGetProplistWithPath(path);
1025 if (!d) {
1026 wwarning("could not open domain file %s", path);
1028 free(path);
1030 if (d && !PLIsDictionary(d)) {
1031 PLRelease(d);
1032 d = NULL;
1034 if (d) {
1035 val = PLGetDictionaryEntry(d, key);
1036 } else {
1037 val = NULL;
1039 /* try to find PixmapPath in global defaults */
1040 if (!val) {
1041 path = globalDefaultsPathForDomain(domain);
1042 if (!path) {
1043 wwarning("could not locate file for domain %s", domain);
1044 d = NULL;
1045 } else {
1046 d = PLGetProplistWithPath(path);
1047 free(path);
1050 if (d && !PLIsDictionary(d)) {
1051 PLRelease(d);
1052 d = NULL;
1054 if (d) {
1055 val = PLGetDictionaryEntry(d, key);
1057 } else {
1058 val = NULL;
1062 if (val)
1063 PLRetain(val);
1065 PLRelease(key);
1066 if (d)
1067 PLRelease(d);
1069 return val;
1074 char*
1075 getPixmapPath(char *domain)
1077 proplist_t val;
1078 char *ptr, *data;
1079 int len, i, count;
1081 val = getValueForKey(domain, "PixmapPath");
1083 if (!val || !PLIsArray(val)) {
1084 if (val)
1085 PLRelease(val);
1086 return wstrdup("");
1089 count = PLGetNumberOfElements(val);
1090 len = 0;
1091 for (i=0; i<count; i++) {
1092 proplist_t v;
1094 v = PLGetArrayElement(val, i);
1095 if (!v || !PLIsString(v)) {
1096 continue;
1098 len += strlen(PLGetString(v))+1;
1101 ptr = data = wmalloc(len+1);
1102 *ptr = 0;
1104 for (i=0; i<count; i++) {
1105 proplist_t v;
1107 v = PLGetArrayElement(val, i);
1108 if (!v || !PLIsString(v)) {
1109 continue;
1111 strcpy(ptr, PLGetString(v));
1113 ptr += strlen(PLGetString(v));
1114 *ptr = ':';
1115 ptr++;
1117 if (i>0)
1118 ptr--; *(ptr--) = 0;
1120 PLRelease(val);
1122 return data;
1126 void
1127 wAbort()
1129 wfatal("aborting");
1130 exit(1);
1135 void
1136 print_help(char *ProgName)
1138 printf("Usage: %s [options] [image]\n", ProgName);
1139 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1140 puts("");
1141 #define P(m) puts(m)
1142 P(" -display display to use");
1143 P(" -d, --dither dither image");
1144 P(" -m, --match match colors");
1145 P(" -b, --back-color <color> background color");
1146 P(" -t, --tile tile image");
1147 P(" -e, --center center image");
1148 P(" -s, --scale scale image (default)");
1149 P(" -a, --maxscale scale image and keep aspect ratio");
1150 P(" -u, --update-wmaker update WindowMaker domain database");
1151 P(" -D, --update-domain <domain> update <domain> database");
1152 P(" -c, --colors <cpc> colors per channel to use");
1153 P(" -p, --parse <texture> proplist style texture specification");
1154 P(" -w, --workspace <workspace> update background for the specified workspace");
1155 P(" --version show version of wmsetbg and exit");
1156 P(" --help show this help and exit");
1157 #undef P
1162 void
1163 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1165 proplist_t array;
1166 proplist_t val;
1167 char *value;
1168 int j;
1170 val = PLGetProplistWithDescription(texture);
1171 if (!val) {
1172 wwarning("could not parse texture %s", texture);
1173 return;
1176 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1178 if (!array) {
1179 array = PLMakeArrayFromElements(NULL, NULL);
1182 j = PLGetNumberOfElements(array);
1183 if (workspace >= j) {
1184 proplist_t empty;
1186 empty = PLMakeArrayFromElements(NULL, NULL);
1188 while (j++ < workspace-1) {
1189 PLAppendArrayElement(array, empty);
1191 PLAppendArrayElement(array, val);
1192 } else {
1193 PLRemoveArrayElement(array, workspace);
1194 PLInsertArrayElement(array, val, workspace);
1197 value = PLGetDescription(array);
1198 updateDomain(domain, "WorkspaceSpecificBack", value);
1203 main(int argc, char **argv)
1205 int i;
1206 int helperMode = 0;
1207 RContext *rc;
1208 RContextAttributes rattr;
1209 char *style = "spixmap";
1210 char *back_color = "gray20";
1211 char *image_name = NULL;
1212 char *domain = "WindowMaker";
1213 int update=0, cpc=4, render_mode=RM_DITHER, obey_user=0;
1214 char *texture = NULL;
1215 int workspace = -1;
1217 signal(SIGINT, SIG_DFL);
1218 signal(SIGTERM, SIG_DFL);
1219 signal(SIGQUIT, SIG_DFL);
1220 signal(SIGSEGV, SIG_DFL);
1221 signal(SIGBUS, SIG_DFL);
1222 signal(SIGFPE, SIG_DFL);
1223 signal(SIGABRT, SIG_DFL);
1224 signal(SIGHUP, SIG_DFL);
1225 signal(SIGPIPE, SIG_DFL);
1226 signal(SIGCHLD, SIG_DFL);
1228 WMInitializeApplication("wmsetbg", &argc, argv);
1230 for (i=1; i<argc; i++) {
1231 if (strcmp(argv[i], "-helper")==0) {
1232 helperMode = 1;
1233 } else if (strcmp(argv[i], "-display")==0) {
1234 i++;
1235 if (i>=argc) {
1236 wfatal("too few arguments for %s\n", argv[i-1]);
1237 exit(1);
1239 display = argv[i];
1240 } else if (strcmp(argv[i], "-s")==0
1241 || strcmp(argv[i], "--scale")==0) {
1242 style = "spixmap";
1243 } else if (strcmp(argv[i], "-t")==0
1244 || strcmp(argv[i], "--tile")==0) {
1245 style = "tpixmap";
1246 } else if (strcmp(argv[i], "-e")==0
1247 || strcmp(argv[i], "--center")==0) {
1248 style = "cpixmap";
1249 } else if (strcmp(argv[i], "-a")==0
1250 || strcmp(argv[i], "--maxscale")==0) {
1251 style = "mpixmap";
1252 } else if (strcmp(argv[i], "-d")==0
1253 || strcmp(argv[i], "--dither")==0) {
1254 render_mode = RM_DITHER;
1255 obey_user++;
1256 } else if (strcmp(argv[i], "-m")==0
1257 || strcmp(argv[i], "--match")==0) {
1258 render_mode = RM_MATCH;
1259 obey_user++;
1260 } else if (strcmp(argv[i], "-u")==0
1261 || strcmp(argv[i], "--update-wmaker")==0) {
1262 update++;
1263 } else if (strcmp(argv[i], "-D")==0
1264 || strcmp(argv[i], "--update-domain")==0) {
1265 update++;
1266 i++;
1267 if (i>=argc) {
1268 wfatal("too few arguments for %s\n", argv[i-1]);
1269 exit(1);
1271 domain = wstrdup(argv[i]);
1272 } else if (strcmp(argv[i], "-c")==0
1273 || strcmp(argv[i], "--colors")==0) {
1274 i++;
1275 if (i>=argc) {
1276 wfatal("too few arguments for %s\n", argv[i-1]);
1277 exit(1);
1279 if (sscanf(argv[i], "%i", &cpc)!=1) {
1280 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1281 exit(1);
1283 } else if (strcmp(argv[i], "-b")==0
1284 || strcmp(argv[i], "--back-color")==0) {
1285 i++;
1286 if (i>=argc) {
1287 wfatal("too few arguments for %s\n", argv[i-1]);
1288 exit(1);
1290 back_color = argv[i];
1291 } else if (strcmp(argv[i], "-p")==0
1292 || strcmp(argv[i], "--parse")==0) {
1293 i++;
1294 if (i>=argc) {
1295 wfatal("too few arguments for %s\n", argv[i-1]);
1296 exit(1);
1298 texture = argv[i];
1299 } else if (strcmp(argv[i], "-w")==0
1300 || strcmp(argv[i], "--workspace")==0) {
1301 i++;
1302 if (i>=argc) {
1303 wfatal("too few arguments for %s\n", argv[i-1]);
1304 exit(1);
1306 if (sscanf(argv[i], "%i", &workspace)!=1) {
1307 wfatal("bad value for workspace number: \"%s\"",
1308 argv[i]);
1309 exit(1);
1311 } else if (strcmp(argv[i], "--version")==0) {
1313 printf(PROG_VERSION);
1314 exit(0);
1316 } else if (strcmp(argv[i], "--help")==0) {
1317 print_help(argv[0]);
1318 exit(0);
1319 } else if (argv[i][0] != '-') {
1320 image_name = argv[i];
1321 } else {
1322 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1323 printf("Try '%s --help' for more information\n", argv[0]);
1324 exit(1);
1327 if (!image_name && !texture && !helperMode) {
1328 printf("%s: you must specify a image file name or a texture\n",
1329 argv[0]);
1330 printf("Try '%s --help' for more information\n", argv[0]);
1331 exit(1);
1335 PixmapPath = getPixmapPath(domain);
1337 dpy = XOpenDisplay(display);
1338 if (!dpy) {
1339 wfatal("could not open display");
1340 exit(1);
1342 #if 0
1343 XSynchronize(dpy, 1);
1344 #endif
1346 root = DefaultRootWindow(dpy);
1348 scr = DefaultScreen(dpy);
1350 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1351 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1353 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1354 render_mode = RM_DITHER;
1356 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
1357 rattr.render_mode = render_mode;
1358 rattr.colors_per_channel = cpc;
1360 rc = RCreateContext(dpy, scr, &rattr);
1362 if (helperMode) {
1363 /* lower priority, so that it wont use all the CPU */
1364 nice(1000);
1366 helperLoop(rc);
1367 } else {
1368 BackgroundTexture *tex;
1369 char buffer[4098];
1371 if (!texture) {
1372 sprintf(buffer, "(%s, \"%s\", %s)", style, image_name, back_color);
1373 texture = (char*)buffer;
1376 if (update && workspace < 0) {
1377 updateDomain(domain, "WorkspaceBack", texture);
1380 tex = parseTexture(rc, texture);
1381 if (!tex)
1382 exit(1);
1384 if (workspace<0)
1385 changeTexture(tex);
1386 else {
1387 /* always update domain */
1388 changeTextureForWorkspace(domain, texture, workspace-1);
1392 return 0;