many bug fixes, finished some delegate code, updated menu file bug from EXEC
[wmaker-crm.git] / util / wmsetbg.c
blob29dd4bf5b5b693a6ba436c2977d53b3e95568a53
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.3"
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;
69 Bool smooth = False;
72 Pixmap CurrentPixmap = None;
73 char *PixmapPath = NULL;
76 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
79 typedef struct BackgroundTexture {
80 int refcount;
82 int solid;
84 char *spec;
86 XColor color;
87 Pixmap pixmap; /* for all textures, including solid */
88 int width; /* size of the pixmap */
89 int height;
90 } BackgroundTexture;
94 RImage*
95 loadImage(RContext *rc, char *file)
97 char *path;
98 RImage *image;
100 if (access(file, F_OK)!=0) {
101 path = wfindfile(PixmapPath, file);
102 if (!path) {
103 wwarning("%s:could not find image file used in texture", file);
104 return NULL;
106 } else {
107 path = wstrdup(file);
110 image = RLoadImage(rc, path, 0);
111 if (!image) {
112 wwarning("%s:could not load image file used in texture:%s", path,
113 RMessageForError(RErrorCode));
115 free(path);
117 return image;
121 BackgroundTexture*
122 parseTexture(RContext *rc, char *text)
124 BackgroundTexture *texture = NULL;
125 proplist_t texarray;
126 proplist_t val;
127 int count;
128 char *tmp;
129 char *type;
131 #define GETSTRORGOTO(val, str, i, label) \
132 val = PLGetArrayElement(texarray, i);\
133 if (!PLIsString(val)) {\
134 wwarning("could not parse texture %s", text);\
135 goto label;\
137 str = PLGetString(val)
139 texarray = PLGetProplistWithDescription(text);
140 if (!texarray || !PLIsArray(texarray)
141 || (count = PLGetNumberOfElements(texarray)) < 2) {
143 wwarning("could not parse texture %s", text);
144 if (texarray)
145 PLRelease(texarray);
146 return NULL;
149 texture = wmalloc(sizeof(BackgroundTexture));
150 memset(texture, 0, sizeof(BackgroundTexture));
152 GETSTRORGOTO(val, type, 0, error);
154 if (strcasecmp(type, "solid")==0) {
155 XColor color;
156 Pixmap pixmap;
158 texture->solid = 1;
160 GETSTRORGOTO(val, tmp, 1, error);
162 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
163 wwarning("could not parse color %s in texture %s", tmp, text);
164 goto error;
166 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
168 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
169 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
170 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
172 texture->pixmap = pixmap;
173 texture->color = color;
174 texture->width = 8;
175 texture->height = 8;
176 } else if (strcasecmp(type, "vgradient")==0
177 || strcasecmp(type, "dgradient")==0
178 || strcasecmp(type, "hgradient")==0) {
179 XColor color;
180 RColor color1, color2;
181 RImage *image;
182 Pixmap pixmap;
183 int gtype;
184 int iwidth, iheight;
186 GETSTRORGOTO(val, tmp, 1, error);
188 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
189 wwarning("could not parse color %s in texture %s", tmp, text);
190 goto error;
193 color1.red = color.red >> 8;
194 color1.green = color.green >> 8;
195 color1.blue = color.blue >> 8;
197 GETSTRORGOTO(val, tmp, 2, error);
199 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
200 wwarning("could not parse color %s in texture %s", tmp, text);
201 goto error;
204 color2.red = color.red >> 8;
205 color2.green = color.green >> 8;
206 color2.blue = color.blue >> 8;
208 switch (type[0]) {
209 case 'h':
210 case 'H':
211 gtype = RHorizontalGradient;
212 iwidth = scrWidth;
213 iheight = 8;
214 break;
215 case 'V':
216 case 'v':
217 gtype = RVerticalGradient;
218 iwidth = 8;
219 iheight = scrHeight;
220 break;
221 default:
222 gtype = RDiagonalGradient;
223 iwidth = scrWidth;
224 iheight = scrHeight;
225 break;
228 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
230 if (!image) {
231 wwarning("could not render gradient texture:%s",
232 RMessageForError(RErrorCode));
233 goto error;
236 if (!RConvertImage(rc, image, &pixmap)) {
237 wwarning("could not convert texture:%s",
238 RMessageForError(RErrorCode));
239 RDestroyImage(image);
240 goto error;
243 texture->width = image->width;
244 texture->height = image->height;
245 RDestroyImage(image);
247 texture->pixmap = pixmap;
248 } else if (strcasecmp(type, "mvgradient")==0
249 || strcasecmp(type, "mdgradient")==0
250 || strcasecmp(type, "mhgradient")==0) {
251 XColor color;
252 RColor **colors;
253 RImage *image;
254 Pixmap pixmap;
255 int i, j;
256 int gtype;
257 int iwidth, iheight;
259 colors = malloc(sizeof(RColor*)*(count-1));
260 if (!colors) {
261 wwarning("out of memory while parsing texture");
262 goto error;
264 memset(colors, 0, sizeof(RColor*)*(count-1));
266 for (i = 2; i < count; i++) {
267 val = PLGetArrayElement(texarray, i);
268 if (!PLIsString(val)) {
269 wwarning("could not parse texture %s", text);
271 for (j = 0; colors[j]!=NULL; j++)
272 free(colors[j]);
273 free(colors);
274 goto error;
276 tmp = PLGetString(val);
278 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
279 wwarning("could not parse color %s in texture %s",
280 tmp, text);
282 for (j = 0; colors[j]!=NULL; j++)
283 free(colors[j]);
284 free(colors);
285 goto error;
287 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
288 wwarning("out of memory while parsing texture");
290 for (j = 0; colors[j]!=NULL; j++)
291 free(colors[j]);
292 free(colors);
293 goto error;
296 colors[i-2]->red = color.red >> 8;
297 colors[i-2]->green = color.green >> 8;
298 colors[i-2]->blue = color.blue >> 8;
301 switch (type[1]) {
302 case 'h':
303 case 'H':
304 gtype = RHorizontalGradient;
305 iwidth = scrWidth;
306 iheight = 8;
307 break;
308 case 'V':
309 case 'v':
310 gtype = RVerticalGradient;
311 iwidth = 8;
312 iheight = scrHeight;
313 break;
314 default:
315 gtype = RDiagonalGradient;
316 iwidth = scrWidth;
317 iheight = scrHeight;
318 break;
321 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
323 for (j = 0; colors[j]!=NULL; j++)
324 free(colors[j]);
325 free(colors);
327 if (!image) {
328 wwarning("could not render gradient texture:%s",
329 RMessageForError(RErrorCode));
330 goto error;
333 if (!RConvertImage(rc, image, &pixmap)) {
334 wwarning("could not convert texture:%s",
335 RMessageForError(RErrorCode));
336 RDestroyImage(image);
337 goto error;
340 texture->width = image->width;
341 texture->height = image->height;
342 RDestroyImage(image);
344 texture->pixmap = pixmap;
345 } else if (strcasecmp(type, "cpixmap")==0
346 || strcasecmp(type, "spixmap")==0
347 || strcasecmp(type, "mpixmap")==0
348 || strcasecmp(type, "tpixmap")==0) {
349 XColor color;
350 Pixmap pixmap = None;
351 RImage *image = NULL;
352 int w, h;
353 int iwidth, iheight;
355 GETSTRORGOTO(val, tmp, 1, error);
357 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
358 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
361 GETSTRORGOTO(val, tmp, 2, error);
363 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
364 wwarning("could not parse color %s in texture %s\n", tmp, text);
365 RDestroyImage(image);
366 goto error;
368 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
369 RColor rcolor;
371 rcolor.red = color.red >> 8;
372 rcolor.green = color.green >> 8;
373 rcolor.blue = color.blue >> 8;
374 RGetClosestXColor(rc, &rcolor, &color);
377 if (!pixmap) {
378 image = loadImage(rc, tmp);
379 if (!image) {
380 goto error;
382 iwidth = image->width;
383 iheight = image->height;
386 switch (toupper(type[0])) {
387 case 'T':
388 texture->width = iwidth;
389 texture->height = iheight;
390 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
391 wwarning("could not convert texture:%s",
392 RMessageForError(RErrorCode));
393 RDestroyImage(image);
394 goto error;
396 if (image)
397 RDestroyImage(image);
398 break;
399 case 'S':
400 case 'M':
401 if (toupper(type[0])=='S') {
402 w = scrWidth;
403 h = scrHeight;
404 } else {
405 if (iwidth*scrHeight > iheight*scrWidth) {
406 w = scrWidth;
407 h = (scrWidth*iheight)/iwidth;
408 } else {
409 h = scrHeight;
410 w = (scrHeight*iwidth)/iheight;
414 RImage *simage;
416 if (smooth)
417 simage = RSmoothScaleImage(image, w, h);
418 else
419 simage = RScaleImage(image, w, h);
420 if (!simage) {
421 wwarning("could not scale image:%s",
422 RMessageForError(RErrorCode));
423 RDestroyImage(image);
424 goto error;
426 RDestroyImage(image);
427 image = simage;
428 iwidth = image->width;
429 iheight = image->height;
431 /* fall through */
432 case 'C':
434 Pixmap cpixmap;
436 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
437 wwarning("could not convert texture:%s",
438 RMessageForError(RErrorCode));
439 RDestroyImage(image);
440 goto error;
443 if (iwidth != scrWidth || iheight != scrHeight) {
444 int x, y, sx, sy, w, h;
446 cpixmap = XCreatePixmap(dpy, root, scrWidth, scrHeight,
447 DefaultDepth(dpy, scr));
449 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
450 XFillRectangle(dpy, cpixmap, DefaultGC(dpy, scr),
451 0, 0, scrWidth, scrHeight);
453 if (iheight < scrHeight) {
454 h = iheight;
455 y = (scrHeight - h)/2;
456 sy = 0;
457 } else {
458 sy = (iheight - scrHeight)/2;
459 y = 0;
460 h = scrHeight;
462 if (iwidth < scrWidth) {
463 w = iwidth;
464 x = (scrWidth - w)/2;
465 sx = 0;
466 } else {
467 sx = (iwidth - scrWidth)/2;
468 x = 0;
469 w = scrWidth;
472 XCopyArea(dpy, pixmap, cpixmap, DefaultGC(dpy, scr),
473 sx, sy, w, h, x, y);
474 XFreePixmap(dpy, pixmap);
475 pixmap = cpixmap;
477 if (image)
478 RDestroyImage(image);
480 texture->width = scrWidth;
481 texture->height = scrHeight;
483 break;
486 texture->pixmap = pixmap;
487 texture->color = color;
488 } else if (strcasecmp(type, "thgradient")==0
489 || strcasecmp(type, "tvgradient")==0
490 || strcasecmp(type, "tdgradient")==0) {
491 XColor color;
492 RColor color1, color2;
493 RImage *image;
494 RImage *gradient;
495 RImage *tiled;
496 Pixmap pixmap;
497 int opaq;
498 char *file;
499 int gtype;
500 int twidth, theight;
502 GETSTRORGOTO(val, file, 1, error);
504 GETSTRORGOTO(val, tmp, 2, error);
506 opaq = atoi(tmp);
508 GETSTRORGOTO(val, tmp, 3, error);
510 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
511 wwarning("could not parse color %s in texture %s", tmp, text);
512 goto error;
515 color1.red = color.red >> 8;
516 color1.green = color.green >> 8;
517 color1.blue = color.blue >> 8;
519 GETSTRORGOTO(val, tmp, 4, error);
521 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
522 wwarning("could not parse color %s in texture %s", tmp, text);
523 goto error;
526 color2.red = color.red >> 8;
527 color2.green = color.green >> 8;
528 color2.blue = color.blue >> 8;
530 image = loadImage(rc, file);
531 if (!image) {
532 RDestroyImage(gradient);
533 goto error;
536 switch (type[1]) {
537 case 'h':
538 case 'H':
539 gtype = RHorizontalGradient;
540 twidth = scrWidth;
541 theight = image->height > scrHeight ? scrHeight : image->height;
542 break;
543 case 'V':
544 case 'v':
545 gtype = RVerticalGradient;
546 twidth = image->width > scrWidth ? scrWidth : image->width;
547 theight = scrHeight;
548 break;
549 default:
550 gtype = RDiagonalGradient;
551 twidth = scrWidth;
552 theight = scrHeight;
553 break;
555 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
557 if (!gradient) {
558 wwarning("could not render texture:%s",
559 RMessageForError(RErrorCode));
560 RDestroyImage(gradient);
561 goto error;
564 tiled = RMakeTiledImage(image, twidth, theight);
565 if (!tiled) {
566 wwarning("could not render texture:%s",
567 RMessageForError(RErrorCode));
568 RDestroyImage(gradient);
569 RDestroyImage(image);
570 goto error;
572 RDestroyImage(image);
574 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
575 RDestroyImage(gradient);
577 if (!RConvertImage(rc, tiled, &pixmap)) {
578 wwarning("could not convert texture:%s",
579 RMessageForError(RErrorCode));
580 RDestroyImage(image);
581 goto error;
583 texture->width = tiled->width;
584 texture->height = tiled->height;
586 RDestroyImage(tiled);
588 texture->pixmap = pixmap;
589 } else if (strcasecmp(type, "function")==0) {
590 #ifdef HAVE_DLFCN_H
591 void (*initFunc) (Display*, Colormap);
592 RImage* (*mainFunc) (int, char**, int, int, int);
593 Pixmap pixmap;
594 RImage *image = 0;
595 int success = 0;
596 char *lib, *func, **argv = 0;
597 void *handle = 0;
598 int i, argc;
600 if (count < 3)
601 goto function_cleanup;
603 /* get the library name */
604 GETSTRORGOTO(val, lib, 1, function_cleanup);
606 /* get the function name */
607 GETSTRORGOTO(val, func, 2, function_cleanup);
609 argc = count - 2;
610 argv = (char**)wmalloc(argc * sizeof(char*));
612 /* get the parameters */
613 argv[0] = func;
614 for (i=0; i<argc-1; i++) {
615 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
616 argv[i+1] = wstrdup(tmp);
619 handle = dlopen(lib, RTLD_LAZY);
620 if (!handle) {
621 wwarning("could not find library %s", lib);
622 goto function_cleanup;
625 initFunc = dlsym(handle, "initWindowMaker");
626 if (!initFunc) {
627 wwarning("could not initialize library %s", lib);
628 goto function_cleanup;
630 initFunc(dpy, DefaultColormap(dpy, scr));
632 mainFunc = dlsym(handle, func);
633 if (!mainFunc) {
634 wwarning("could not find function %s::%s", lib, func);
635 goto function_cleanup;
637 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
639 if (!RConvertImage(rc, image, &pixmap)) {
640 wwarning("could not convert texture:%s",
641 RMessageForError(RErrorCode));
642 goto function_cleanup;
644 texture->width = scrWidth;
645 texture->height = scrHeight;
646 texture->pixmap = pixmap;
647 success = 1;
649 function_cleanup:
650 if (argv) {
651 int i;
652 for (i=0; i<argc; i++) {
653 free(argv[i]);
656 if (handle) {
657 dlclose(handle);
659 if (image) {
660 RDestroyImage(image);
662 if (!success) {
663 goto error;
665 #else
666 wwarning("function textures not supported");
667 goto error;
668 #endif
669 } else {
670 wwarning("invalid texture type %s", text);
671 goto error;
674 texture->spec = wstrdup(text);
676 return texture;
678 error:
679 if (texture)
680 free(texture);
681 if (texarray)
682 PLRelease(texarray);
684 return NULL;
688 void
689 freeTexture(BackgroundTexture *texture)
691 if (texture->solid) {
692 long pixel[1];
694 pixel[0] = texture->color.pixel;
695 /* dont free black/white pixels */
696 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
697 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
698 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
700 if (texture->pixmap) {
701 XFreePixmap(dpy, texture->pixmap);
703 free(texture->spec);
704 free(texture);
708 void
709 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
710 int workspace, char *texture)
712 BackgroundTexture *newTexture = NULL;
713 int i;
715 /* unset the texture */
716 if (!texture) {
717 if (textures[workspace]!=NULL) {
718 textures[workspace]->refcount--;
720 if (textures[workspace]->refcount == 0)
721 freeTexture(textures[workspace]);
723 textures[workspace] = NULL;
724 return;
727 if (textures[workspace]
728 && strcasecmp(textures[workspace]->spec, texture)==0) {
729 /* texture did not change */
730 return;
733 /* check if the same texture is already created */
734 for (i = 0; i < *maxTextures; i++) {
735 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
736 newTexture = textures[i];
737 break;
741 if (!newTexture) {
742 /* create the texture */
743 newTexture = parseTexture(rc, texture);
745 if (!newTexture)
746 return;
748 if (textures[workspace]!=NULL) {
750 textures[workspace]->refcount--;
752 if (textures[workspace]->refcount == 0)
753 freeTexture(textures[workspace]);
756 newTexture->refcount++;
757 textures[workspace] = newTexture;
759 if (*maxTextures < workspace)
760 *maxTextures = workspace;
765 Pixmap
766 duplicatePixmap(Pixmap pixmap, int width, int height)
768 Display *tmpDpy;
769 Pixmap copyP;
771 /* must open a new display or the RetainPermanent will
772 * leave stuff allocated in RContext unallocated after exit */
773 tmpDpy = XOpenDisplay(display);
774 if (!tmpDpy) {
775 wwarning("could not open display to update background image information");
777 return None;
778 } else {
779 XSync(dpy, False);
781 copyP = XCreatePixmap(tmpDpy, root, width, height,
782 DefaultDepth(tmpDpy, scr));
783 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
784 0, 0, width, height, 0, 0);
785 XSync(tmpDpy, False);
787 XSetCloseDownMode(tmpDpy, RetainPermanent);
788 XCloseDisplay(tmpDpy);
791 return copyP;
795 static int
796 dummyErrorHandler(Display *dpy, XErrorEvent *err)
798 return 0;
801 void
802 setPixmapProperty(Pixmap pixmap)
804 static Atom prop = 0;
805 Atom type;
806 int format;
807 unsigned long length, after;
808 unsigned char *data;
809 int mode;
811 if (!prop) {
812 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
815 XGrabServer(dpy);
817 /* Clear out the old pixmap */
818 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
819 &type, &format, &length, &after, &data);
821 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
822 XSetErrorHandler(dummyErrorHandler);
823 XKillClient(dpy, *((Pixmap *)data));
824 XSync(dpy, False);
825 XSetErrorHandler(NULL);
826 mode = PropModeReplace;
827 } else {
828 mode = PropModeAppend;
830 if (pixmap)
831 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
832 (unsigned char *) &pixmap, 1);
833 else
834 XDeleteProperty(dpy, root, prop);
837 XUngrabServer(dpy);
838 XFlush(dpy);
843 void
844 changeTexture(BackgroundTexture *texture)
846 if (!texture)
847 return;
849 if (texture->solid) {
850 XSetWindowBackground(dpy, root, texture->color.pixel);
851 } else {
852 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
854 XClearWindow(dpy, root);
856 XSync(dpy, False);
859 Pixmap pixmap;
861 pixmap = duplicatePixmap(texture->pixmap, texture->width,
862 texture->height);
864 setPixmapProperty(pixmap);
870 readmsg(int fd, unsigned char *buffer, int size)
872 int count;
874 count = 0;
875 while (size>0) {
876 count = read(fd, buffer, size);
877 if (count < 0)
878 return -1;
879 size -= count;
880 buffer += count;
881 *buffer = 0;
884 return size;
889 * Message Format:
890 * sizeSntexture_spec - sets the texture for workspace n
891 * sizeCn - change background texture to the one for workspace n
892 * sizePpath - set the pixmap search path
894 * n is 4 bytes
895 * size = 4 bytes for length of the message data
897 void
898 helperLoop(RContext *rc)
900 BackgroundTexture *textures[WORKSPACE_COUNT];
901 int maxTextures = 0;
902 unsigned char buffer[2048], buf[8];
903 int size;
904 int errcount = 4;
906 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
909 while (1) {
910 int workspace;
912 /* get length of message */
913 if (readmsg(0, buffer, 4) < 0) {
914 wsyserror("error reading message from Window Maker");
915 errcount--;
916 if (errcount == 0) {
917 wfatal("quitting");
918 exit(1);
920 continue;
922 memcpy(buf, buffer, 4);
923 buf[4] = 0;
924 size = atoi(buf);
926 /* get message */
927 if (readmsg(0, buffer, size) < 0) {
928 wsyserror("error reading message from Window Maker");
929 errcount--;
930 if (errcount == 0) {
931 wfatal("quitting");
932 exit(1);
934 continue;
936 #ifdef DEBUG
937 printf("RECEIVED %s\n",buffer);
938 #endif
939 if (buffer[0]!='P' && buffer[0]!='K') {
940 memcpy(buf, &buffer[1], 4);
941 buf[4] = 0;
942 workspace = atoi(buf);
943 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
944 wwarning("received message with invalid workspace number %i\n",
945 workspace);
946 continue;
950 switch (buffer[0]) {
951 case 'S':
952 #ifdef DEBUG
953 printf("set texture %s\n", &buffer[5]);
954 #endif
955 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
956 break;
958 case 'C':
959 #ifdef DEBUG
960 printf("change texture %i\n", workspace);
961 #endif
962 if (!textures[workspace])
963 changeTexture(textures[0]);
964 else
965 changeTexture(textures[workspace]);
966 break;
968 case 'P':
969 #ifdef DEBUG
970 printf("change pixmappath %s\n", &buffer[1]);
971 #endif
972 if (PixmapPath)
973 free(PixmapPath);
974 PixmapPath = wstrdup(&buffer[1]);
975 break;
977 case 'U':
978 #ifdef DEBUG
979 printf("unset workspace %i\n", workspace);
980 #endif
981 setupTexture(rc, textures, &maxTextures, workspace, NULL);
982 break;
984 case 'K':
985 #ifdef DEBUG
986 printf("exit command\n");
987 #endif
988 exit(0);
990 default:
991 wwarning("unknown message received");
992 break;
998 void
999 updateDomain(char *domain, char *key, char *texture)
1001 char *program = "wdwrite";
1003 system(wstrappend("wdwrite ",
1004 wstrappend(domain, smooth ? " SmoothWorkspaceBack YES"
1005 : " SmoothWorkspaceBack NO")));
1007 execlp(program, program, domain, key, texture, NULL);
1008 wwarning("warning could not run \"%s\"", program);
1013 char*
1014 globalDefaultsPathForDomain(char *domain)
1016 char path[1024];
1018 sprintf(path, "%s/%s", SYSCONFDIR, domain);
1020 return wstrdup(path);
1024 proplist_t
1025 getValueForKey(char *domain, char *keyName)
1027 char *path;
1028 proplist_t key;
1029 proplist_t d;
1030 proplist_t val;
1032 key = PLMakeString(keyName);
1034 /* try to find PixmapPath in user defaults */
1035 path = wdefaultspathfordomain(domain);
1036 d = PLGetProplistWithPath(path);
1037 if (!d) {
1038 wwarning("could not open domain file %s", path);
1040 free(path);
1042 if (d && !PLIsDictionary(d)) {
1043 PLRelease(d);
1044 d = NULL;
1046 if (d) {
1047 val = PLGetDictionaryEntry(d, key);
1048 } else {
1049 val = NULL;
1051 /* try to find PixmapPath in global defaults */
1052 if (!val) {
1053 path = globalDefaultsPathForDomain(domain);
1054 if (!path) {
1055 wwarning("could not locate file for domain %s", domain);
1056 d = NULL;
1057 } else {
1058 d = PLGetProplistWithPath(path);
1059 free(path);
1062 if (d && !PLIsDictionary(d)) {
1063 PLRelease(d);
1064 d = NULL;
1066 if (d) {
1067 val = PLGetDictionaryEntry(d, key);
1069 } else {
1070 val = NULL;
1074 if (val)
1075 PLRetain(val);
1077 PLRelease(key);
1078 if (d)
1079 PLRelease(d);
1081 return val;
1086 char*
1087 getPixmapPath(char *domain)
1089 proplist_t val;
1090 char *ptr, *data;
1091 int len, i, count;
1093 val = getValueForKey(domain, "PixmapPath");
1095 if (!val || !PLIsArray(val)) {
1096 if (val)
1097 PLRelease(val);
1098 return wstrdup("");
1101 count = PLGetNumberOfElements(val);
1102 len = 0;
1103 for (i=0; i<count; i++) {
1104 proplist_t v;
1106 v = PLGetArrayElement(val, i);
1107 if (!v || !PLIsString(v)) {
1108 continue;
1110 len += strlen(PLGetString(v))+1;
1113 ptr = data = wmalloc(len+1);
1114 *ptr = 0;
1116 for (i=0; i<count; i++) {
1117 proplist_t v;
1119 v = PLGetArrayElement(val, i);
1120 if (!v || !PLIsString(v)) {
1121 continue;
1123 strcpy(ptr, PLGetString(v));
1125 ptr += strlen(PLGetString(v));
1126 *ptr = ':';
1127 ptr++;
1129 if (i>0)
1130 ptr--; *(ptr--) = 0;
1132 PLRelease(val);
1134 return data;
1138 void
1139 wAbort()
1141 wfatal("aborting");
1142 exit(1);
1147 void
1148 print_help(char *ProgName)
1150 printf("Usage: %s [options] [image]\n", ProgName);
1151 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1152 puts("");
1153 #define P(m) puts(m)
1154 P(" -display display to use");
1155 P(" -d, --dither dither image");
1156 P(" -m, --match match colors");
1157 P(" -S, --smooth smooth scaled image");
1158 P(" -b, --back-color <color> background color");
1159 P(" -t, --tile tile image");
1160 P(" -e, --center center image");
1161 P(" -s, --scale scale image (default)");
1162 P(" -a, --maxscale scale image and keep aspect ratio");
1163 P(" -u, --update-wmaker update WindowMaker domain database");
1164 P(" -D, --update-domain <domain> update <domain> database");
1165 P(" -c, --colors <cpc> colors per channel to use");
1166 P(" -p, --parse <texture> proplist style texture specification");
1167 P(" -w, --workspace <workspace> update background for the specified workspace");
1168 P(" --version show version of wmsetbg and exit");
1169 P(" --help show this help and exit");
1170 #undef P
1175 void
1176 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1178 proplist_t array;
1179 proplist_t val;
1180 char *value;
1181 int j;
1183 val = PLGetProplistWithDescription(texture);
1184 if (!val) {
1185 wwarning("could not parse texture %s", texture);
1186 return;
1189 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1191 if (!array) {
1192 array = PLMakeArrayFromElements(NULL, NULL);
1195 j = PLGetNumberOfElements(array);
1196 if (workspace >= j) {
1197 proplist_t empty;
1199 empty = PLMakeArrayFromElements(NULL, NULL);
1201 while (j++ < workspace-1) {
1202 PLAppendArrayElement(array, empty);
1204 PLAppendArrayElement(array, val);
1205 } else {
1206 PLRemoveArrayElement(array, workspace);
1207 PLInsertArrayElement(array, val, workspace);
1210 value = PLGetDescription(array);
1211 updateDomain(domain, "WorkspaceSpecificBack", value);
1216 main(int argc, char **argv)
1218 int i;
1219 int helperMode = 0;
1220 RContext *rc;
1221 RContextAttributes rattr;
1222 char *style = "spixmap";
1223 char *back_color = "gray20";
1224 char *image_name = NULL;
1225 char *domain = "WindowMaker";
1226 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1227 char *texture = NULL;
1228 int workspace = -1;
1230 signal(SIGINT, SIG_DFL);
1231 signal(SIGTERM, SIG_DFL);
1232 signal(SIGQUIT, SIG_DFL);
1233 signal(SIGSEGV, SIG_DFL);
1234 signal(SIGBUS, SIG_DFL);
1235 signal(SIGFPE, SIG_DFL);
1236 signal(SIGABRT, SIG_DFL);
1237 signal(SIGHUP, SIG_DFL);
1238 signal(SIGPIPE, SIG_DFL);
1239 signal(SIGCHLD, SIG_DFL);
1241 WMInitializeApplication("wmsetbg", &argc, argv);
1243 for (i=1; i<argc; i++) {
1244 if (strcmp(argv[i], "-helper")==0) {
1245 helperMode = 1;
1246 } else if (strcmp(argv[i], "-display")==0) {
1247 i++;
1248 if (i>=argc) {
1249 wfatal("too few arguments for %s\n", argv[i-1]);
1250 exit(1);
1252 display = argv[i];
1253 } else if (strcmp(argv[i], "-s")==0
1254 || strcmp(argv[i], "--scale")==0) {
1255 style = "spixmap";
1256 } else if (strcmp(argv[i], "-t")==0
1257 || strcmp(argv[i], "--tile")==0) {
1258 style = "tpixmap";
1259 } else if (strcmp(argv[i], "-e")==0
1260 || strcmp(argv[i], "--center")==0) {
1261 style = "cpixmap";
1262 } else if (strcmp(argv[i], "-a")==0
1263 || strcmp(argv[i], "--maxscale")==0) {
1264 style = "mpixmap";
1265 } else if (strcmp(argv[i], "-d")==0
1266 || strcmp(argv[i], "--dither")==0) {
1267 render_mode = RDitheredRendering;
1268 obey_user++;
1269 } else if (strcmp(argv[i], "-m")==0
1270 || strcmp(argv[i], "--match")==0) {
1271 render_mode = RBestMatchRendering;
1272 obey_user++;
1273 } else if (strcmp(argv[i], "-S")==0
1274 || strcmp(argv[i], "--smooth")==0) {
1275 smooth = True;
1276 } else if (strcmp(argv[i], "-u")==0
1277 || strcmp(argv[i], "--update-wmaker")==0) {
1278 update++;
1279 } else if (strcmp(argv[i], "-D")==0
1280 || strcmp(argv[i], "--update-domain")==0) {
1281 update++;
1282 i++;
1283 if (i>=argc) {
1284 wfatal("too few arguments for %s\n", argv[i-1]);
1285 exit(1);
1287 domain = wstrdup(argv[i]);
1288 } else if (strcmp(argv[i], "-c")==0
1289 || strcmp(argv[i], "--colors")==0) {
1290 i++;
1291 if (i>=argc) {
1292 wfatal("too few arguments for %s\n", argv[i-1]);
1293 exit(1);
1295 if (sscanf(argv[i], "%i", &cpc)!=1) {
1296 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1297 exit(1);
1299 } else if (strcmp(argv[i], "-b")==0
1300 || strcmp(argv[i], "--back-color")==0) {
1301 i++;
1302 if (i>=argc) {
1303 wfatal("too few arguments for %s\n", argv[i-1]);
1304 exit(1);
1306 back_color = argv[i];
1307 } else if (strcmp(argv[i], "-p")==0
1308 || strcmp(argv[i], "--parse")==0) {
1309 i++;
1310 if (i>=argc) {
1311 wfatal("too few arguments for %s\n", argv[i-1]);
1312 exit(1);
1314 texture = argv[i];
1315 } else if (strcmp(argv[i], "-w")==0
1316 || strcmp(argv[i], "--workspace")==0) {
1317 i++;
1318 if (i>=argc) {
1319 wfatal("too few arguments for %s\n", argv[i-1]);
1320 exit(1);
1322 if (sscanf(argv[i], "%i", &workspace)!=1) {
1323 wfatal("bad value for workspace number: \"%s\"",
1324 argv[i]);
1325 exit(1);
1327 } else if (strcmp(argv[i], "--version")==0) {
1329 printf(PROG_VERSION);
1330 exit(0);
1332 } else if (strcmp(argv[i], "--help")==0) {
1333 print_help(argv[0]);
1334 exit(0);
1335 } else if (argv[i][0] != '-') {
1336 image_name = argv[i];
1337 } else {
1338 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1339 printf("Try '%s --help' for more information\n", argv[0]);
1340 exit(1);
1343 if (!image_name && !texture && !helperMode) {
1344 printf("%s: you must specify a image file name or a texture\n",
1345 argv[0]);
1346 printf("Try '%s --help' for more information\n", argv[0]);
1347 exit(1);
1351 PixmapPath = getPixmapPath(domain);
1352 if (!smooth) {
1353 proplist_t val;
1355 val = PLGetDictionaryEntry(domain,
1356 PLMakeString("SmoothWorkspaceBack"));
1357 if (val && PLIsString(val) && strcasecmp(PLGetString(val), "YES")==0)
1358 smooth = True;
1361 dpy = XOpenDisplay(display);
1362 if (!dpy) {
1363 wfatal("could not open display");
1364 exit(1);
1366 #if 0
1367 XSynchronize(dpy, 1);
1368 #endif
1370 root = DefaultRootWindow(dpy);
1372 scr = DefaultScreen(dpy);
1374 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1375 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1377 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1378 render_mode = RDitheredRendering;
1380 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
1381 rattr.render_mode = render_mode;
1382 rattr.colors_per_channel = cpc;
1384 rc = RCreateContext(dpy, scr, &rattr);
1386 if (helperMode) {
1387 /* lower priority, so that it wont use all the CPU */
1388 nice(1000);
1390 helperLoop(rc);
1391 } else {
1392 BackgroundTexture *tex;
1393 char buffer[4098];
1395 if (!texture) {
1396 sprintf(buffer, "(%s, \"%s\", %s)", style, image_name, back_color);
1397 texture = (char*)buffer;
1400 if (update && workspace < 0) {
1401 updateDomain(domain, "WorkspaceBack", texture);
1404 tex = parseTexture(rc, texture);
1405 if (!tex)
1406 exit(1);
1408 if (workspace<0)
1409 changeTexture(tex);
1410 else {
1411 /* always update domain */
1412 changeTextureForWorkspace(domain, texture, workspace-1);
1416 return 0;