Fixed some problems in 0.60.0
[wmaker-crm.git] / util / wmsetbg.c
blobf2ce9fd15efa0ebba742b73f7ea8c8f497339b86
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.h>
50 #include <wraster.h>
52 #include <proplist.h>
54 #define PROG_VERSION "wmsetbg (Window Maker) 2.3"
57 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
60 Display *dpy;
61 char *display = "";
62 Window root;
63 int scr;
64 int scrWidth;
65 int scrHeight;
68 Bool smooth = False;
71 Pixmap CurrentPixmap = None;
72 char *PixmapPath = NULL;
75 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
78 typedef struct BackgroundTexture {
79 int refcount;
81 int solid;
83 char *spec;
85 XColor color;
86 Pixmap pixmap; /* for all textures, including solid */
87 int width; /* size of the pixmap */
88 int height;
89 } BackgroundTexture;
93 RImage*
94 loadImage(RContext *rc, char *file)
96 char *path;
97 RImage *image;
99 if (access(file, F_OK)!=0) {
100 path = wfindfile(PixmapPath, file);
101 if (!path) {
102 wwarning("%s:could not find image file used in texture", file);
103 return NULL;
105 } else {
106 path = wstrdup(file);
109 image = RLoadImage(rc, path, 0);
110 if (!image) {
111 wwarning("%s:could not load image file used in texture:%s", path,
112 RMessageForError(RErrorCode));
114 free(path);
116 return image;
120 BackgroundTexture*
121 parseTexture(RContext *rc, char *text)
123 BackgroundTexture *texture = NULL;
124 proplist_t texarray;
125 proplist_t val;
126 int count;
127 char *tmp;
128 char *type;
130 #define GETSTRORGOTO(val, str, i, label) \
131 val = PLGetArrayElement(texarray, i);\
132 if (!PLIsString(val)) {\
133 wwarning("could not parse texture %s", text);\
134 goto label;\
136 str = PLGetString(val)
138 texarray = PLGetProplistWithDescription(text);
139 if (!texarray || !PLIsArray(texarray)
140 || (count = PLGetNumberOfElements(texarray)) < 2) {
142 wwarning("could not parse texture %s", text);
143 if (texarray)
144 PLRelease(texarray);
145 return NULL;
148 texture = wmalloc(sizeof(BackgroundTexture));
149 memset(texture, 0, sizeof(BackgroundTexture));
151 GETSTRORGOTO(val, type, 0, error);
153 if (strcasecmp(type, "solid")==0) {
154 XColor color;
155 Pixmap pixmap;
157 texture->solid = 1;
159 GETSTRORGOTO(val, tmp, 1, error);
161 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
162 wwarning("could not parse color %s in texture %s", tmp, text);
163 goto error;
165 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
167 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
168 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
169 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
171 texture->pixmap = pixmap;
172 texture->color = color;
173 texture->width = 8;
174 texture->height = 8;
175 } else if (strcasecmp(type, "vgradient")==0
176 || strcasecmp(type, "dgradient")==0
177 || strcasecmp(type, "hgradient")==0) {
178 XColor color;
179 RColor color1, color2;
180 RImage *image;
181 Pixmap pixmap;
182 int gtype;
183 int iwidth, iheight;
185 GETSTRORGOTO(val, tmp, 1, error);
187 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
188 wwarning("could not parse color %s in texture %s", tmp, text);
189 goto error;
192 color1.red = color.red >> 8;
193 color1.green = color.green >> 8;
194 color1.blue = color.blue >> 8;
196 GETSTRORGOTO(val, tmp, 2, error);
198 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
199 wwarning("could not parse color %s in texture %s", tmp, text);
200 goto error;
203 color2.red = color.red >> 8;
204 color2.green = color.green >> 8;
205 color2.blue = color.blue >> 8;
207 switch (type[0]) {
208 case 'h':
209 case 'H':
210 gtype = RHorizontalGradient;
211 iwidth = scrWidth;
212 iheight = 8;
213 break;
214 case 'V':
215 case 'v':
216 gtype = RVerticalGradient;
217 iwidth = 8;
218 iheight = scrHeight;
219 break;
220 default:
221 gtype = RDiagonalGradient;
222 iwidth = scrWidth;
223 iheight = scrHeight;
224 break;
227 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
229 if (!image) {
230 wwarning("could not render gradient texture:%s",
231 RMessageForError(RErrorCode));
232 goto error;
235 if (!RConvertImage(rc, image, &pixmap)) {
236 wwarning("could not convert texture:%s",
237 RMessageForError(RErrorCode));
238 RDestroyImage(image);
239 goto error;
242 texture->width = image->width;
243 texture->height = image->height;
244 RDestroyImage(image);
246 texture->pixmap = pixmap;
247 } else if (strcasecmp(type, "mvgradient")==0
248 || strcasecmp(type, "mdgradient")==0
249 || strcasecmp(type, "mhgradient")==0) {
250 XColor color;
251 RColor **colors;
252 RImage *image;
253 Pixmap pixmap;
254 int i, j;
255 int gtype;
256 int iwidth, iheight;
258 colors = malloc(sizeof(RColor*)*(count-1));
259 if (!colors) {
260 wwarning("out of memory while parsing texture");
261 goto error;
263 memset(colors, 0, sizeof(RColor*)*(count-1));
265 for (i = 2; i < count; i++) {
266 val = PLGetArrayElement(texarray, i);
267 if (!PLIsString(val)) {
268 wwarning("could not parse texture %s", text);
270 for (j = 0; colors[j]!=NULL; j++)
271 free(colors[j]);
272 free(colors);
273 goto error;
275 tmp = PLGetString(val);
277 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
278 wwarning("could not parse color %s in texture %s",
279 tmp, text);
281 for (j = 0; colors[j]!=NULL; j++)
282 free(colors[j]);
283 free(colors);
284 goto error;
286 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
287 wwarning("out of memory while parsing texture");
289 for (j = 0; colors[j]!=NULL; j++)
290 free(colors[j]);
291 free(colors);
292 goto error;
295 colors[i-2]->red = color.red >> 8;
296 colors[i-2]->green = color.green >> 8;
297 colors[i-2]->blue = color.blue >> 8;
300 switch (type[1]) {
301 case 'h':
302 case 'H':
303 gtype = RHorizontalGradient;
304 iwidth = scrWidth;
305 iheight = 8;
306 break;
307 case 'V':
308 case 'v':
309 gtype = RVerticalGradient;
310 iwidth = 8;
311 iheight = scrHeight;
312 break;
313 default:
314 gtype = RDiagonalGradient;
315 iwidth = scrWidth;
316 iheight = scrHeight;
317 break;
320 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
322 for (j = 0; colors[j]!=NULL; j++)
323 free(colors[j]);
324 free(colors);
326 if (!image) {
327 wwarning("could not render gradient texture:%s",
328 RMessageForError(RErrorCode));
329 goto error;
332 if (!RConvertImage(rc, image, &pixmap)) {
333 wwarning("could not convert texture:%s",
334 RMessageForError(RErrorCode));
335 RDestroyImage(image);
336 goto error;
339 texture->width = image->width;
340 texture->height = image->height;
341 RDestroyImage(image);
343 texture->pixmap = pixmap;
344 } else if (strcasecmp(type, "cpixmap")==0
345 || strcasecmp(type, "spixmap")==0
346 || strcasecmp(type, "mpixmap")==0
347 || strcasecmp(type, "tpixmap")==0) {
348 XColor color;
349 Pixmap pixmap = None;
350 RImage *image = NULL;
351 int w, h;
352 int iwidth, iheight;
354 GETSTRORGOTO(val, tmp, 1, error);
356 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
357 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
360 if (!pixmap) {
361 image = loadImage(rc, tmp);
362 if (!image) {
363 goto error;
365 iwidth = image->width;
366 iheight = image->height;
369 GETSTRORGOTO(val, tmp, 2, error);
371 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
372 wwarning("could not parse color %s in texture %s\n", tmp, text);
373 RDestroyImage(image);
374 goto error;
376 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
377 RColor rcolor;
379 rcolor.red = color.red >> 8;
380 rcolor.green = color.green >> 8;
381 rcolor.blue = color.blue >> 8;
382 RGetClosestXColor(rc, &rcolor, &color);
385 switch (toupper(type[0])) {
386 case 'T':
387 texture->width = iwidth;
388 texture->height = iheight;
389 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
390 wwarning("could not convert texture:%s",
391 RMessageForError(RErrorCode));
392 RDestroyImage(image);
393 goto error;
395 if (image)
396 RDestroyImage(image);
397 break;
398 case 'S':
399 case 'M':
400 if (toupper(type[0])=='S') {
401 w = scrWidth;
402 h = scrHeight;
403 } else {
404 if (iwidth*scrHeight > iheight*scrWidth) {
405 w = scrWidth;
406 h = (scrWidth*iheight)/iwidth;
407 } else {
408 h = scrHeight;
409 w = (scrHeight*iwidth)/iheight;
413 RImage *simage;
415 if (smooth)
416 simage = RSmoothScaleImage(image, w, h);
417 else
418 simage = RScaleImage(image, w, h);
419 if (!simage) {
420 wwarning("could not scale image:%s",
421 RMessageForError(RErrorCode));
422 RDestroyImage(image);
423 goto error;
425 RDestroyImage(image);
426 image = simage;
427 iwidth = image->width;
428 iheight = image->height;
430 /* fall through */
431 case 'C':
433 Pixmap cpixmap;
435 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
436 wwarning("could not convert texture:%s",
437 RMessageForError(RErrorCode));
438 RDestroyImage(image);
439 goto error;
442 if (iwidth != scrWidth || iheight != scrHeight) {
443 int x, y, sx, sy, w, h;
445 cpixmap = XCreatePixmap(dpy, root, scrWidth, scrHeight,
446 DefaultDepth(dpy, scr));
448 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
449 XFillRectangle(dpy, cpixmap, DefaultGC(dpy, scr),
450 0, 0, scrWidth, scrHeight);
452 if (iheight < scrHeight) {
453 h = iheight;
454 y = (scrHeight - h)/2;
455 sy = 0;
456 } else {
457 sy = (iheight - scrHeight)/2;
458 y = 0;
459 h = scrHeight;
461 if (iwidth < scrWidth) {
462 w = iwidth;
463 x = (scrWidth - w)/2;
464 sx = 0;
465 } else {
466 sx = (iwidth - scrWidth)/2;
467 x = 0;
468 w = scrWidth;
471 XCopyArea(dpy, pixmap, cpixmap, DefaultGC(dpy, scr),
472 sx, sy, w, h, x, y);
473 XFreePixmap(dpy, pixmap);
474 pixmap = cpixmap;
476 if (image)
477 RDestroyImage(image);
479 texture->width = scrWidth;
480 texture->height = scrHeight;
482 break;
485 texture->pixmap = pixmap;
486 texture->color = color;
487 } else if (strcasecmp(type, "thgradient")==0
488 || strcasecmp(type, "tvgradient")==0
489 || strcasecmp(type, "tdgradient")==0) {
490 XColor color;
491 RColor color1, color2;
492 RImage *image;
493 RImage *gradient;
494 RImage *tiled;
495 Pixmap pixmap;
496 int opaq;
497 char *file;
498 int gtype;
499 int twidth, theight;
501 GETSTRORGOTO(val, file, 1, error);
503 GETSTRORGOTO(val, tmp, 2, error);
505 opaq = atoi(tmp);
507 GETSTRORGOTO(val, tmp, 3, error);
509 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
510 wwarning("could not parse color %s in texture %s", tmp, text);
511 goto error;
514 color1.red = color.red >> 8;
515 color1.green = color.green >> 8;
516 color1.blue = color.blue >> 8;
518 GETSTRORGOTO(val, tmp, 4, error);
520 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
521 wwarning("could not parse color %s in texture %s", tmp, text);
522 goto error;
525 color2.red = color.red >> 8;
526 color2.green = color.green >> 8;
527 color2.blue = color.blue >> 8;
529 image = loadImage(rc, file);
530 if (!image) {
531 goto error;
534 switch (type[1]) {
535 case 'h':
536 case 'H':
537 gtype = RHorizontalGradient;
538 twidth = scrWidth;
539 theight = image->height > scrHeight ? scrHeight : image->height;
540 break;
541 case 'V':
542 case 'v':
543 gtype = RVerticalGradient;
544 twidth = image->width > scrWidth ? scrWidth : image->width;
545 theight = scrHeight;
546 break;
547 default:
548 gtype = RDiagonalGradient;
549 twidth = scrWidth;
550 theight = scrHeight;
551 break;
553 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
555 if (!gradient) {
556 wwarning("could not render texture:%s",
557 RMessageForError(RErrorCode));
558 RDestroyImage(gradient);
559 RDestroyImage(image);
560 goto error;
563 tiled = RMakeTiledImage(image, twidth, theight);
564 if (!tiled) {
565 wwarning("could not render texture:%s",
566 RMessageForError(RErrorCode));
567 RDestroyImage(gradient);
568 RDestroyImage(image);
569 goto error;
571 RDestroyImage(image);
573 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
574 RDestroyImage(gradient);
576 if (!RConvertImage(rc, tiled, &pixmap)) {
577 wwarning("could not convert texture:%s",
578 RMessageForError(RErrorCode));
579 RDestroyImage(tiled);
580 goto error;
582 texture->width = tiled->width;
583 texture->height = tiled->height;
585 RDestroyImage(tiled);
587 texture->pixmap = pixmap;
588 } else if (strcasecmp(type, "function")==0) {
589 #ifdef HAVE_DLFCN_H
590 void (*initFunc) (Display*, Colormap);
591 RImage* (*mainFunc) (int, char**, int, int, int);
592 Pixmap pixmap;
593 RImage *image = 0;
594 int success = 0;
595 char *lib, *func, **argv = 0;
596 void *handle = 0;
597 int i, argc;
599 if (count < 3)
600 goto function_cleanup;
602 /* get the library name */
603 GETSTRORGOTO(val, lib, 1, function_cleanup);
605 /* get the function name */
606 GETSTRORGOTO(val, func, 2, function_cleanup);
608 argc = count - 2;
609 argv = (char**)wmalloc(argc * sizeof(char*));
611 /* get the parameters */
612 argv[0] = func;
613 for (i=0; i<argc-1; i++) {
614 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
615 argv[i+1] = wstrdup(tmp);
618 handle = dlopen(lib, RTLD_LAZY);
619 if (!handle) {
620 wwarning("could not find library %s", lib);
621 goto function_cleanup;
624 initFunc = dlsym(handle, "initWindowMaker");
625 if (!initFunc) {
626 wwarning("could not initialize library %s", lib);
627 goto function_cleanup;
629 initFunc(dpy, DefaultColormap(dpy, scr));
631 mainFunc = dlsym(handle, func);
632 if (!mainFunc) {
633 wwarning("could not find function %s::%s", lib, func);
634 goto function_cleanup;
636 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
638 if (!RConvertImage(rc, image, &pixmap)) {
639 wwarning("could not convert texture:%s",
640 RMessageForError(RErrorCode));
641 goto function_cleanup;
643 texture->width = scrWidth;
644 texture->height = scrHeight;
645 texture->pixmap = pixmap;
646 success = 1;
648 function_cleanup:
649 if (argv) {
650 int i;
651 for (i=0; i<argc; i++) {
652 free(argv[i]);
655 if (handle) {
656 dlclose(handle);
658 if (image) {
659 RDestroyImage(image);
661 if (!success) {
662 goto error;
664 #else
665 wwarning("function textures not supported");
666 goto error;
667 #endif
668 } else {
669 wwarning("invalid texture type %s", text);
670 goto error;
673 texture->spec = wstrdup(text);
675 return texture;
677 error:
678 if (texture)
679 free(texture);
680 if (texarray)
681 PLRelease(texarray);
683 return NULL;
687 void
688 freeTexture(BackgroundTexture *texture)
690 if (texture->solid) {
691 long pixel[1];
693 pixel[0] = texture->color.pixel;
694 /* dont free black/white pixels */
695 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
696 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
697 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
699 if (texture->pixmap) {
700 XFreePixmap(dpy, texture->pixmap);
702 free(texture->spec);
703 free(texture);
707 void
708 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
709 int workspace, char *texture)
711 BackgroundTexture *newTexture = NULL;
712 int i;
714 /* unset the texture */
715 if (!texture) {
716 if (textures[workspace]!=NULL) {
717 textures[workspace]->refcount--;
719 if (textures[workspace]->refcount == 0)
720 freeTexture(textures[workspace]);
722 textures[workspace] = NULL;
723 return;
726 if (textures[workspace]
727 && strcasecmp(textures[workspace]->spec, texture)==0) {
728 /* texture did not change */
729 return;
732 /* check if the same texture is already created */
733 for (i = 0; i < *maxTextures; i++) {
734 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
735 newTexture = textures[i];
736 break;
740 if (!newTexture) {
741 /* create the texture */
742 newTexture = parseTexture(rc, texture);
744 if (!newTexture)
745 return;
747 if (textures[workspace]!=NULL) {
749 textures[workspace]->refcount--;
751 if (textures[workspace]->refcount == 0)
752 freeTexture(textures[workspace]);
755 newTexture->refcount++;
756 textures[workspace] = newTexture;
758 if (*maxTextures < workspace)
759 *maxTextures = workspace;
764 Pixmap
765 duplicatePixmap(Pixmap pixmap, int width, int height)
767 Display *tmpDpy;
768 Pixmap copyP;
770 /* must open a new display or the RetainPermanent will
771 * leave stuff allocated in RContext unallocated after exit */
772 tmpDpy = XOpenDisplay(display);
773 if (!tmpDpy) {
774 wwarning("could not open display to update background image information");
776 return None;
777 } else {
778 XSync(dpy, False);
780 copyP = XCreatePixmap(tmpDpy, root, width, height,
781 DefaultDepth(tmpDpy, scr));
782 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
783 0, 0, width, height, 0, 0);
784 XSync(tmpDpy, False);
786 XSetCloseDownMode(tmpDpy, RetainPermanent);
787 XCloseDisplay(tmpDpy);
790 return copyP;
794 static int
795 dummyErrorHandler(Display *dpy, XErrorEvent *err)
797 return 0;
800 void
801 setPixmapProperty(Pixmap pixmap)
803 static Atom prop = 0;
804 Atom type;
805 int format;
806 unsigned long length, after;
807 unsigned char *data;
808 int mode;
810 if (!prop) {
811 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
814 XGrabServer(dpy);
816 /* Clear out the old pixmap */
817 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
818 &type, &format, &length, &after, &data);
820 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
821 XSetErrorHandler(dummyErrorHandler);
822 XKillClient(dpy, *((Pixmap *)data));
823 XSync(dpy, False);
824 XSetErrorHandler(NULL);
825 mode = PropModeReplace;
826 } else {
827 mode = PropModeAppend;
829 if (pixmap)
830 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
831 (unsigned char *) &pixmap, 1);
832 else
833 XDeleteProperty(dpy, root, prop);
836 XUngrabServer(dpy);
837 XFlush(dpy);
842 void
843 changeTexture(BackgroundTexture *texture)
845 if (!texture)
846 return;
848 if (texture->solid) {
849 XSetWindowBackground(dpy, root, texture->color.pixel);
850 } else {
851 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
853 XClearWindow(dpy, root);
855 XSync(dpy, False);
858 Pixmap pixmap;
860 pixmap = duplicatePixmap(texture->pixmap, texture->width,
861 texture->height);
863 setPixmapProperty(pixmap);
869 readmsg(int fd, unsigned char *buffer, int size)
871 int count;
873 count = 0;
874 while (size>0) {
875 count = read(fd, buffer, size);
876 if (count < 0)
877 return -1;
878 size -= count;
879 buffer += count;
880 *buffer = 0;
883 return size;
888 * Message Format:
889 * sizeSntexture_spec - sets the texture for workspace n
890 * sizeCn - change background texture to the one for workspace n
891 * sizePpath - set the pixmap search path
893 * n is 4 bytes
894 * size = 4 bytes for length of the message data
896 void
897 helperLoop(RContext *rc)
899 BackgroundTexture *textures[WORKSPACE_COUNT];
900 int maxTextures = 0;
901 unsigned char buffer[2048], buf[8];
902 int size;
903 int errcount = 4;
905 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
908 while (1) {
909 int workspace;
911 /* get length of message */
912 if (readmsg(0, buffer, 4) < 0) {
913 wsyserror("error reading message from Window Maker");
914 errcount--;
915 if (errcount == 0) {
916 wfatal("quitting");
917 exit(1);
919 continue;
921 memcpy(buf, buffer, 4);
922 buf[4] = 0;
923 size = atoi(buf);
925 /* get message */
926 if (readmsg(0, buffer, size) < 0) {
927 wsyserror("error reading message from Window Maker");
928 errcount--;
929 if (errcount == 0) {
930 wfatal("quitting");
931 exit(1);
933 continue;
935 #ifdef DEBUG
936 printf("RECEIVED %s\n",buffer);
937 #endif
938 if (buffer[0]!='P' && buffer[0]!='K') {
939 memcpy(buf, &buffer[1], 4);
940 buf[4] = 0;
941 workspace = atoi(buf);
942 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
943 wwarning("received message with invalid workspace number %i\n",
944 workspace);
945 continue;
949 switch (buffer[0]) {
950 case 'S':
951 #ifdef DEBUG
952 printf("set texture %s\n", &buffer[5]);
953 #endif
954 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
955 break;
957 case 'C':
958 #ifdef DEBUG
959 printf("change texture %i\n", workspace);
960 #endif
961 if (!textures[workspace])
962 changeTexture(textures[0]);
963 else
964 changeTexture(textures[workspace]);
965 break;
967 case 'P':
968 #ifdef DEBUG
969 printf("change pixmappath %s\n", &buffer[1]);
970 #endif
971 if (PixmapPath)
972 free(PixmapPath);
973 PixmapPath = wstrdup(&buffer[1]);
974 break;
976 case 'U':
977 #ifdef DEBUG
978 printf("unset workspace %i\n", workspace);
979 #endif
980 setupTexture(rc, textures, &maxTextures, workspace, NULL);
981 break;
983 case 'K':
984 #ifdef DEBUG
985 printf("exit command\n");
986 #endif
987 exit(0);
989 default:
990 wwarning("unknown message received");
991 break;
997 void
998 updateDomain(char *domain, char *key, char *texture)
1000 char *program = "wdwrite";
1002 system(wstrappend("wdwrite ",
1003 wstrappend(domain, smooth ? " SmoothWorkspaceBack YES"
1004 : " SmoothWorkspaceBack NO")));
1006 execlp(program, program, domain, key, texture, NULL);
1007 wwarning("warning could not run \"%s\"", program);
1012 char*
1013 globalDefaultsPathForDomain(char *domain)
1015 char path[1024];
1017 sprintf(path, "%s/%s", SYSCONFDIR, domain);
1019 return wstrdup(path);
1023 proplist_t
1024 getValueForKey(char *domain, char *keyName)
1026 char *path;
1027 proplist_t key;
1028 proplist_t d;
1029 proplist_t val;
1031 key = PLMakeString(keyName);
1033 /* try to find PixmapPath in user defaults */
1034 path = wdefaultspathfordomain(domain);
1035 d = PLGetProplistWithPath(path);
1036 if (!d) {
1037 wwarning("could not open domain file %s", path);
1039 free(path);
1041 if (d && !PLIsDictionary(d)) {
1042 PLRelease(d);
1043 d = NULL;
1045 if (d) {
1046 val = PLGetDictionaryEntry(d, key);
1047 } else {
1048 val = NULL;
1050 /* try to find PixmapPath in global defaults */
1051 if (!val) {
1052 path = globalDefaultsPathForDomain(domain);
1053 if (!path) {
1054 wwarning("could not locate file for domain %s", domain);
1055 d = NULL;
1056 } else {
1057 d = PLGetProplistWithPath(path);
1058 free(path);
1061 if (d && !PLIsDictionary(d)) {
1062 PLRelease(d);
1063 d = NULL;
1065 if (d) {
1066 val = PLGetDictionaryEntry(d, key);
1068 } else {
1069 val = NULL;
1073 if (val)
1074 PLRetain(val);
1076 PLRelease(key);
1077 if (d)
1078 PLRelease(d);
1080 return val;
1085 char*
1086 getPixmapPath(char *domain)
1088 proplist_t val;
1089 char *ptr, *data;
1090 int len, i, count;
1092 val = getValueForKey(domain, "PixmapPath");
1094 if (!val || !PLIsArray(val)) {
1095 if (val)
1096 PLRelease(val);
1097 return wstrdup("");
1100 count = PLGetNumberOfElements(val);
1101 len = 0;
1102 for (i=0; i<count; i++) {
1103 proplist_t v;
1105 v = PLGetArrayElement(val, i);
1106 if (!v || !PLIsString(v)) {
1107 continue;
1109 len += strlen(PLGetString(v))+1;
1112 ptr = data = wmalloc(len+1);
1113 *ptr = 0;
1115 for (i=0; i<count; i++) {
1116 proplist_t v;
1118 v = PLGetArrayElement(val, i);
1119 if (!v || !PLIsString(v)) {
1120 continue;
1122 strcpy(ptr, PLGetString(v));
1124 ptr += strlen(PLGetString(v));
1125 *ptr = ':';
1126 ptr++;
1128 if (i>0)
1129 ptr--; *(ptr--) = 0;
1131 PLRelease(val);
1133 return data;
1137 void
1138 wAbort()
1140 wfatal("aborting");
1141 exit(1);
1146 void
1147 print_help(char *ProgName)
1149 printf("Usage: %s [options] [image]\n", ProgName);
1150 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1151 puts("");
1152 #define P(m) puts(m)
1153 P(" -display display to use");
1154 P(" -d, --dither dither image");
1155 P(" -m, --match match colors");
1156 P(" -S, --smooth smooth scaled image");
1157 P(" -b, --back-color <color> background color");
1158 P(" -t, --tile tile image");
1159 P(" -e, --center center image");
1160 P(" -s, --scale scale image (default)");
1161 P(" -a, --maxscale scale image and keep aspect ratio");
1162 P(" -u, --update-wmaker update WindowMaker domain database");
1163 P(" -D, --update-domain <domain> update <domain> database");
1164 P(" -c, --colors <cpc> colors per channel to use");
1165 P(" -p, --parse <texture> proplist style texture specification");
1166 P(" -w, --workspace <workspace> update background for the specified workspace");
1167 P(" --version show version of wmsetbg and exit");
1168 P(" --help show this help and exit");
1169 #undef P
1174 void
1175 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1177 proplist_t array;
1178 proplist_t val;
1179 char *value;
1180 int j;
1182 val = PLGetProplistWithDescription(texture);
1183 if (!val) {
1184 wwarning("could not parse texture %s", texture);
1185 return;
1188 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1190 if (!array) {
1191 array = PLMakeArrayFromElements(NULL, NULL);
1194 j = PLGetNumberOfElements(array);
1195 if (workspace >= j) {
1196 proplist_t empty;
1198 empty = PLMakeArrayFromElements(NULL, NULL);
1200 while (j++ < workspace-1) {
1201 PLAppendArrayElement(array, empty);
1203 PLAppendArrayElement(array, val);
1204 } else {
1205 PLRemoveArrayElement(array, workspace);
1206 PLInsertArrayElement(array, val, workspace);
1209 value = PLGetDescription(array);
1210 updateDomain(domain, "WorkspaceSpecificBack", value);
1215 main(int argc, char **argv)
1217 int i;
1218 int helperMode = 0;
1219 RContext *rc;
1220 RContextAttributes rattr;
1221 char *style = "spixmap";
1222 char *back_color = "gray20";
1223 char *image_name = NULL;
1224 char *domain = "WindowMaker";
1225 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1226 char *texture = NULL;
1227 int workspace = -1;
1229 signal(SIGINT, SIG_DFL);
1230 signal(SIGTERM, SIG_DFL);
1231 signal(SIGQUIT, SIG_DFL);
1232 signal(SIGSEGV, SIG_DFL);
1233 signal(SIGBUS, SIG_DFL);
1234 signal(SIGFPE, SIG_DFL);
1235 signal(SIGABRT, SIG_DFL);
1236 signal(SIGHUP, SIG_DFL);
1237 signal(SIGPIPE, SIG_DFL);
1238 signal(SIGCHLD, SIG_DFL);
1240 WMInitializeApplication("wmsetbg", &argc, argv);
1242 for (i=1; i<argc; i++) {
1243 if (strcmp(argv[i], "-helper")==0) {
1244 helperMode = 1;
1245 } else if (strcmp(argv[i], "-display")==0) {
1246 i++;
1247 if (i>=argc) {
1248 wfatal("too few arguments for %s\n", argv[i-1]);
1249 exit(1);
1251 display = argv[i];
1252 } else if (strcmp(argv[i], "-s")==0
1253 || strcmp(argv[i], "--scale")==0) {
1254 style = "spixmap";
1255 } else if (strcmp(argv[i], "-t")==0
1256 || strcmp(argv[i], "--tile")==0) {
1257 style = "tpixmap";
1258 } else if (strcmp(argv[i], "-e")==0
1259 || strcmp(argv[i], "--center")==0) {
1260 style = "cpixmap";
1261 } else if (strcmp(argv[i], "-a")==0
1262 || strcmp(argv[i], "--maxscale")==0) {
1263 style = "mpixmap";
1264 } else if (strcmp(argv[i], "-d")==0
1265 || strcmp(argv[i], "--dither")==0) {
1266 render_mode = RDitheredRendering;
1267 obey_user++;
1268 } else if (strcmp(argv[i], "-m")==0
1269 || strcmp(argv[i], "--match")==0) {
1270 render_mode = RBestMatchRendering;
1271 obey_user++;
1272 } else if (strcmp(argv[i], "-S")==0
1273 || strcmp(argv[i], "--smooth")==0) {
1274 smooth = True;
1275 } else if (strcmp(argv[i], "-u")==0
1276 || strcmp(argv[i], "--update-wmaker")==0) {
1277 update++;
1278 } else if (strcmp(argv[i], "-D")==0
1279 || strcmp(argv[i], "--update-domain")==0) {
1280 update++;
1281 i++;
1282 if (i>=argc) {
1283 wfatal("too few arguments for %s\n", argv[i-1]);
1284 exit(1);
1286 domain = wstrdup(argv[i]);
1287 } else if (strcmp(argv[i], "-c")==0
1288 || strcmp(argv[i], "--colors")==0) {
1289 i++;
1290 if (i>=argc) {
1291 wfatal("too few arguments for %s\n", argv[i-1]);
1292 exit(1);
1294 if (sscanf(argv[i], "%i", &cpc)!=1) {
1295 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1296 exit(1);
1298 } else if (strcmp(argv[i], "-b")==0
1299 || strcmp(argv[i], "--back-color")==0) {
1300 i++;
1301 if (i>=argc) {
1302 wfatal("too few arguments for %s\n", argv[i-1]);
1303 exit(1);
1305 back_color = argv[i];
1306 } else if (strcmp(argv[i], "-p")==0
1307 || strcmp(argv[i], "--parse")==0) {
1308 i++;
1309 if (i>=argc) {
1310 wfatal("too few arguments for %s\n", argv[i-1]);
1311 exit(1);
1313 texture = argv[i];
1314 } else if (strcmp(argv[i], "-w")==0
1315 || strcmp(argv[i], "--workspace")==0) {
1316 i++;
1317 if (i>=argc) {
1318 wfatal("too few arguments for %s\n", argv[i-1]);
1319 exit(1);
1321 if (sscanf(argv[i], "%i", &workspace)!=1) {
1322 wfatal("bad value for workspace number: \"%s\"",
1323 argv[i]);
1324 exit(1);
1326 } else if (strcmp(argv[i], "--version")==0) {
1328 printf(PROG_VERSION);
1329 exit(0);
1331 } else if (strcmp(argv[i], "--help")==0) {
1332 print_help(argv[0]);
1333 exit(0);
1334 } else if (argv[i][0] != '-') {
1335 image_name = argv[i];
1336 } else {
1337 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1338 printf("Try '%s --help' for more information\n", argv[0]);
1339 exit(1);
1342 if (!image_name && !texture && !helperMode) {
1343 printf("%s: you must specify a image file name or a texture\n",
1344 argv[0]);
1345 printf("Try '%s --help' for more information\n", argv[0]);
1346 exit(1);
1350 PixmapPath = getPixmapPath(domain);
1351 if (!smooth) {
1352 proplist_t val;
1354 val = PLGetDictionaryEntry(domain,
1355 PLMakeString("SmoothWorkspaceBack"));
1356 if (val && PLIsString(val) && strcasecmp(PLGetString(val), "YES")==0)
1357 smooth = True;
1360 dpy = XOpenDisplay(display);
1361 if (!dpy) {
1362 wfatal("could not open display");
1363 exit(1);
1365 #if 0
1366 XSynchronize(dpy, 1);
1367 #endif
1369 root = DefaultRootWindow(dpy);
1371 scr = DefaultScreen(dpy);
1373 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1374 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1376 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1377 render_mode = RDitheredRendering;
1379 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
1380 rattr.render_mode = render_mode;
1381 rattr.colors_per_channel = cpc;
1383 rc = RCreateContext(dpy, scr, &rattr);
1385 if (helperMode) {
1386 /* lower priority, so that it wont use all the CPU */
1387 nice(1000);
1389 helperLoop(rc);
1390 } else {
1391 BackgroundTexture *tex;
1392 char buffer[4098];
1394 if (!texture) {
1395 sprintf(buffer, "(%s, \"%s\", %s)", style, image_name, back_color);
1396 texture = (char*)buffer;
1399 if (update && workspace < 0) {
1400 updateDomain(domain, "WorkspaceBack", texture);
1403 tex = parseTexture(rc, texture);
1404 if (!tex)
1405 exit(1);
1407 if (workspace<0)
1408 changeTexture(tex);
1409 else {
1410 /* always update domain */
1411 changeTextureForWorkspace(domain, texture, workspace-1);
1415 return 0;