- Fixed all // comments
[wmaker-crm.git] / util / wmsetbg.c
blob7171672ea4accf0234d6d2aa8c67fa85121a4e36
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
4 * WindowMaker window manager
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 Dan Pascu
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
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xatom.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <ctype.h>
40 #include "../src/config.h"
42 #ifdef XINERAMA
43 # ifdef SOLARIS_XINERAMA /* sucks */
44 # include <X11/extensions/xinerama.h>
45 # else
46 # include <X11/extensions/Xinerama.h>
47 # endif
48 #endif
51 #ifdef HAVE_DLFCN_H
52 #include <dlfcn.h>
53 #endif
55 #include "../src/wconfig.h"
57 #include <WINGs/WINGs.h>
58 #include <wraster.h>
61 typedef struct {
62 WMRect *screens;
63 int count; /* screen count, 0 = inactive */
64 } WXineramaInfo;
67 #define PROG_VERSION "wmsetbg (Window Maker) 2.8"
70 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
73 Display *dpy;
74 char *display = "";
75 Window root;
76 int scr;
77 int scrWidth;
78 int scrHeight;
79 int scrX, scrY;
82 WXineramaInfo xineInfo;
84 Bool smooth = False;
87 Pixmap CurrentPixmap = None;
88 char *PixmapPath = NULL;
91 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
94 typedef struct BackgroundTexture {
95 int refcount;
97 int solid;
99 char *spec;
101 XColor color;
102 Pixmap pixmap; /* for all textures, including solid */
103 int width; /* size of the pixmap */
104 int height;
105 } BackgroundTexture;
109 void
110 initXinerama()
112 xineInfo.screens = NULL;
113 xineInfo.count = 0;
114 #ifdef XINERAMA
115 # ifdef SOLARIS_XINERAMA
116 if (XineramaGetState(dpy, scr)) {
117 XRectangle head[MAXFRAMEBUFFERS];
118 unsigned char hints[MAXFRAMEBUFFERS];
119 int i;
121 if (XineramaGetInfo(dpy, scr, head, hints,
122 &xineInfo.count)) {
124 xineInfo.screens = wmalloc(sizeof(WMRect)*(xineInfo.count+1));
126 for (i=0; i<xineInfo.count; i++) {
127 xineInfo.screens[i].pos.x = head[i].x;
128 xineInfo.screens[i].pos.y = head[i].y;
129 xineInfo.screens[i].size.width = head[i].width;
130 xineInfo.screens[i].size.height = head[i].height;
134 # else /* !SOLARIS_XINERAMA */
135 if (XineramaIsActive(dpy)) {
136 XineramaScreenInfo *xine_screens;
137 int i;
139 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
141 xineInfo.screens = wmalloc(sizeof(WMRect)*(xineInfo.count+1));
143 for (i=0; i<xineInfo.count; i++) {
144 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
145 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
146 xineInfo.screens[i].size.width = xine_screens[i].width;
147 xineInfo.screens[i].size.height = xine_screens[i].height;
149 XFree(xine_screens);
151 # endif /* !SOLARIS_XINERAMA */
152 #endif /* XINERAMA */
156 RImage*
157 loadImage(RContext *rc, char *file)
159 char *path;
160 RImage *image;
162 if (access(file, F_OK)!=0) {
163 path = wfindfile(PixmapPath, file);
164 if (!path) {
165 wwarning("%s:could not find image file used in texture", file);
166 return NULL;
168 } else {
169 path = wstrdup(file);
172 image = RLoadImage(rc, path, 0);
173 if (!image) {
174 wwarning("%s:could not load image file used in texture:%s", path,
175 RMessageForError(RErrorCode));
177 wfree(path);
179 return image;
183 static void
184 applyImage(RContext *rc, BackgroundTexture *texture, RImage *image, char type,
185 int x, int y, int width, int height)
187 int w, h;
188 Bool fimage = False;
190 switch (toupper(type)) {
191 case 'S':
192 case 'M':
193 if (toupper(type) == 'S') {
194 w = width;
195 h = height;
196 } else {
197 if (image->width*height > image->height*width) {
198 w = width;
199 h = (width*image->height) / image->width;
200 } else {
201 w = (height*image->width) / image->height;
202 h = height;
206 if (w != image->width || h != image->height) {
207 RImage * simage;
209 if (smooth) {
210 simage = RSmoothScaleImage(image, w, h);
211 } else {
212 simage = RScaleImage(image, w, h);
215 if (!simage) {
216 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
217 return;
219 fimage = True;
220 image = simage;
223 /* fall through */
224 case 'C':
226 Pixmap pixmap;
228 if (!RConvertImage(rc, image, &pixmap)) {
229 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
230 return;
233 if (image->width != width || image->height != height) {
234 int sx, sy, w, h;
236 if (image->height < height) {
237 h = image->height;
238 y += (height - h) / 2;
239 sy = 0;
240 } else {
241 sy = (image->height - height) / 2;
242 h = height;
244 if (image->width < width) {
245 w = image->width;
246 x += (width - w) / 2;
247 sx = 0;
248 } else {
249 sx = (image->width - width) / 2;
250 w = width;
253 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
254 } else
255 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height, x, y);
257 XFreePixmap(dpy, pixmap);
258 if (fimage) {
259 RReleaseImage( image);
262 break;
267 BackgroundTexture*
268 parseTexture(RContext *rc, char *text)
270 BackgroundTexture *texture = NULL;
271 WMPropList *texarray;
272 WMPropList *val;
273 int count;
274 char *tmp;
275 char *type;
277 #define GETSTRORGOTO(val, str, i, label) \
278 val = WMGetFromPLArray(texarray, i);\
279 if (!WMIsPLString(val)) {\
280 wwarning("could not parse texture %s", text);\
281 goto label;\
283 str = WMGetFromPLString(val)
285 texarray = WMCreatePropListFromDescription(text);
286 if (!texarray || !WMIsPLArray(texarray)
287 || (count = WMGetPropListItemCount(texarray)) < 2) {
289 wwarning("could not parse texture %s", text);
290 if (texarray)
291 WMReleasePropList(texarray);
292 return NULL;
295 texture = wmalloc(sizeof(BackgroundTexture));
296 memset(texture, 0, sizeof(BackgroundTexture));
298 GETSTRORGOTO(val, type, 0, error);
300 if (strcasecmp(type, "solid")==0) {
301 XColor color;
302 Pixmap pixmap;
304 texture->solid = 1;
306 GETSTRORGOTO(val, tmp, 1, error);
308 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
309 wwarning("could not parse color %s in texture %s", tmp, text);
310 goto error;
312 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
314 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
315 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
316 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
318 texture->pixmap = pixmap;
319 texture->color = color;
320 texture->width = 8;
321 texture->height = 8;
322 } else if (strcasecmp(type, "vgradient")==0
323 || strcasecmp(type, "dgradient")==0
324 || strcasecmp(type, "hgradient")==0) {
325 XColor color;
326 RColor color1, color2;
327 RImage *image;
328 Pixmap pixmap;
329 int gtype;
330 int iwidth, iheight;
332 GETSTRORGOTO(val, tmp, 1, error);
334 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
335 wwarning("could not parse color %s in texture %s", tmp, text);
336 goto error;
339 color1.red = color.red >> 8;
340 color1.green = color.green >> 8;
341 color1.blue = color.blue >> 8;
343 GETSTRORGOTO(val, tmp, 2, error);
345 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
346 wwarning("could not parse color %s in texture %s", tmp, text);
347 goto error;
350 color2.red = color.red >> 8;
351 color2.green = color.green >> 8;
352 color2.blue = color.blue >> 8;
354 switch (type[0]) {
355 case 'h':
356 case 'H':
357 gtype = RHorizontalGradient;
358 iwidth = scrWidth;
359 iheight = 32;
360 break;
361 case 'V':
362 case 'v':
363 gtype = RVerticalGradient;
364 iwidth = 32;
365 iheight = scrHeight;
366 break;
367 default:
368 gtype = RDiagonalGradient;
369 iwidth = scrWidth;
370 iheight = scrHeight;
371 break;
374 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
376 if (!image) {
377 wwarning("could not render gradient texture:%s",
378 RMessageForError(RErrorCode));
379 goto error;
382 if (!RConvertImage(rc, image, &pixmap)) {
383 wwarning("could not convert texture:%s",
384 RMessageForError(RErrorCode));
385 RReleaseImage(image);
386 goto error;
389 texture->width = image->width;
390 texture->height = image->height;
391 RReleaseImage(image);
393 texture->pixmap = pixmap;
394 } else if (strcasecmp(type, "mvgradient")==0
395 || strcasecmp(type, "mdgradient")==0
396 || strcasecmp(type, "mhgradient")==0) {
397 XColor color;
398 RColor **colors;
399 RImage *image;
400 Pixmap pixmap;
401 int i, j;
402 int gtype;
403 int iwidth, iheight;
405 colors = malloc(sizeof(RColor*)*(count-1));
406 if (!colors) {
407 wwarning("out of memory while parsing texture");
408 goto error;
410 memset(colors, 0, sizeof(RColor*)*(count-1));
412 for (i = 2; i < count; i++) {
413 val = WMGetFromPLArray(texarray, i);
414 if (!WMIsPLString(val)) {
415 wwarning("could not parse texture %s", text);
417 for (j = 0; colors[j]!=NULL; j++)
418 wfree(colors[j]);
419 wfree(colors);
420 goto error;
422 tmp = WMGetFromPLString(val);
424 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
425 wwarning("could not parse color %s in texture %s",
426 tmp, text);
428 for (j = 0; colors[j]!=NULL; j++)
429 wfree(colors[j]);
430 wfree(colors);
431 goto error;
433 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
434 wwarning("out of memory while parsing texture");
436 for (j = 0; colors[j]!=NULL; j++)
437 wfree(colors[j]);
438 wfree(colors);
439 goto error;
442 colors[i-2]->red = color.red >> 8;
443 colors[i-2]->green = color.green >> 8;
444 colors[i-2]->blue = color.blue >> 8;
447 switch (type[1]) {
448 case 'h':
449 case 'H':
450 gtype = RHorizontalGradient;
451 iwidth = scrWidth;
452 iheight = 32;
453 break;
454 case 'V':
455 case 'v':
456 gtype = RVerticalGradient;
457 iwidth = 32;
458 iheight = scrHeight;
459 break;
460 default:
461 gtype = RDiagonalGradient;
462 iwidth = scrWidth;
463 iheight = scrHeight;
464 break;
467 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
469 for (j = 0; colors[j]!=NULL; j++)
470 wfree(colors[j]);
471 wfree(colors);
473 if (!image) {
474 wwarning("could not render gradient texture:%s",
475 RMessageForError(RErrorCode));
476 goto error;
479 if (!RConvertImage(rc, image, &pixmap)) {
480 wwarning("could not convert texture:%s",
481 RMessageForError(RErrorCode));
482 RReleaseImage(image);
483 goto error;
486 texture->width = image->width;
487 texture->height = image->height;
488 RReleaseImage(image);
490 texture->pixmap = pixmap;
491 } else if (strcasecmp(type, "cpixmap")==0
492 || strcasecmp(type, "spixmap")==0
493 || strcasecmp(type, "mpixmap")==0
494 || strcasecmp(type, "tpixmap")==0) {
495 XColor color;
496 Pixmap pixmap = None;
497 RImage *image = NULL;
498 int iwidth, iheight;
499 RColor rcolor;
502 GETSTRORGOTO(val, tmp, 1, error);
504 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
505 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
508 if (!pixmap) {
509 image = loadImage(rc, tmp);
510 if (!image) {
511 goto error;
513 iwidth = image->width;
514 iheight = image->height;
517 GETSTRORGOTO(val, tmp, 2, error);
519 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
520 wwarning("could not parse color %s in texture %s\n", tmp, text);
521 RReleaseImage(image);
522 goto error;
524 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
525 rcolor.red = color.red >> 8;
526 rcolor.green = color.green >> 8;
527 rcolor.blue = color.blue >> 8;
528 RGetClosestXColor(rc, &rcolor, &color);
529 } else {
530 rcolor.red = 0;
531 rcolor.green = 0;
532 rcolor.blue = 0;
534 /* for images with a transparent color */
535 if (image->data[3]) {
536 RCombineImageWithColor(image, &rcolor);
539 switch (toupper(type[0])) {
540 case 'T':
541 texture->width = iwidth;
542 texture->height = iheight;
543 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
544 wwarning("could not convert texture:%s",
545 RMessageForError(RErrorCode));
546 RReleaseImage(image);
547 goto error;
549 if (image)
550 RReleaseImage(image);
552 texture->pixmap = pixmap;
553 texture->color = color;
554 break;
555 case 'S':
556 case 'M':
557 case 'C':
559 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
560 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
562 texture->pixmap = tpixmap;
563 texture->color = color;
564 texture->width = scrWidth;
565 texture->height = scrHeight;
567 #ifdef XINERAMA
568 if (xineInfo.count) {
569 int i;
570 for (i=0; i<xineInfo.count; ++i) {
571 applyImage(rc, texture, image, type[0],
572 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
573 xineInfo.screens[i].size.width, xineInfo.screens[i].size.height);
575 } else {
576 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
578 #else /* !XINERAMA */
579 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
580 #endif /* !XINERAMA */
581 RReleaseImage(image);
583 break;
585 } else if (strcasecmp(type, "thgradient")==0
586 || strcasecmp(type, "tvgradient")==0
587 || strcasecmp(type, "tdgradient")==0) {
588 XColor color;
589 RColor color1, color2;
590 RImage *image;
591 RImage *gradient;
592 RImage *tiled;
593 Pixmap pixmap;
594 int opaq;
595 char *file;
596 int gtype;
597 int twidth, theight;
599 GETSTRORGOTO(val, file, 1, error);
601 GETSTRORGOTO(val, tmp, 2, error);
603 opaq = atoi(tmp);
605 GETSTRORGOTO(val, tmp, 3, error);
607 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
608 wwarning("could not parse color %s in texture %s", tmp, text);
609 goto error;
612 color1.red = color.red >> 8;
613 color1.green = color.green >> 8;
614 color1.blue = color.blue >> 8;
616 GETSTRORGOTO(val, tmp, 4, error);
618 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
619 wwarning("could not parse color %s in texture %s", tmp, text);
620 goto error;
623 color2.red = color.red >> 8;
624 color2.green = color.green >> 8;
625 color2.blue = color.blue >> 8;
627 image = loadImage(rc, file);
628 if (!image) {
629 goto error;
632 switch (type[1]) {
633 case 'h':
634 case 'H':
635 gtype = RHorizontalGradient;
636 twidth = scrWidth;
637 theight = image->height > scrHeight ? scrHeight : image->height;
638 break;
639 case 'V':
640 case 'v':
641 gtype = RVerticalGradient;
642 twidth = image->width > scrWidth ? scrWidth : image->width;
643 theight = scrHeight;
644 break;
645 default:
646 gtype = RDiagonalGradient;
647 twidth = scrWidth;
648 theight = scrHeight;
649 break;
651 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
653 if (!gradient) {
654 wwarning("could not render texture:%s",
655 RMessageForError(RErrorCode));
656 RReleaseImage(gradient);
657 RReleaseImage(image);
658 goto error;
661 tiled = RMakeTiledImage(image, twidth, theight);
662 if (!tiled) {
663 wwarning("could not render texture:%s",
664 RMessageForError(RErrorCode));
665 RReleaseImage(gradient);
666 RReleaseImage(image);
667 goto error;
669 RReleaseImage(image);
671 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
672 RReleaseImage(gradient);
674 if (!RConvertImage(rc, tiled, &pixmap)) {
675 wwarning("could not convert texture:%s",
676 RMessageForError(RErrorCode));
677 RReleaseImage(tiled);
678 goto error;
680 texture->width = tiled->width;
681 texture->height = tiled->height;
683 RReleaseImage(tiled);
685 texture->pixmap = pixmap;
686 } else if (strcasecmp(type, "function")==0) {
687 #ifdef HAVE_DLFCN_H
688 void (*initFunc) (Display*, Colormap);
689 RImage* (*mainFunc) (int, char**, int, int, int);
690 Pixmap pixmap;
691 RImage *image = 0;
692 int success = 0;
693 char *lib, *func, **argv = 0;
694 void *handle = 0;
695 int i, argc;
697 if (count < 3)
698 goto function_cleanup;
700 /* get the library name */
701 GETSTRORGOTO(val, lib, 1, function_cleanup);
703 /* get the function name */
704 GETSTRORGOTO(val, func, 2, function_cleanup);
706 argc = count - 2;
707 argv = (char**)wmalloc(argc * sizeof(char*));
709 /* get the parameters */
710 argv[0] = func;
711 for (i=0; i<argc-1; i++) {
712 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
713 argv[i+1] = wstrdup(tmp);
716 handle = dlopen(lib, RTLD_LAZY);
717 if (!handle) {
718 wwarning("could not find library %s", lib);
719 goto function_cleanup;
722 initFunc = dlsym(handle, "initWindowMaker");
723 if (!initFunc) {
724 wwarning("could not initialize library %s", lib);
725 goto function_cleanup;
727 initFunc(dpy, DefaultColormap(dpy, scr));
729 mainFunc = dlsym(handle, func);
730 if (!mainFunc) {
731 wwarning("could not find function %s::%s", lib, func);
732 goto function_cleanup;
734 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
736 if (!RConvertImage(rc, image, &pixmap)) {
737 wwarning("could not convert texture:%s",
738 RMessageForError(RErrorCode));
739 goto function_cleanup;
741 texture->width = scrWidth;
742 texture->height = scrHeight;
743 texture->pixmap = pixmap;
744 success = 1;
746 function_cleanup:
747 if (argv) {
748 int i;
749 for (i=0; i<argc; i++) {
750 wfree(argv[i]);
753 if (handle) {
754 dlclose(handle);
756 if (image) {
757 RReleaseImage(image);
759 if (!success) {
760 goto error;
762 #else
763 wwarning("function textures not supported");
764 goto error;
765 #endif
766 } else {
767 wwarning("invalid texture type %s", text);
768 goto error;
771 texture->spec = wstrdup(text);
773 return texture;
775 error:
776 if (texture)
777 wfree(texture);
778 if (texarray)
779 WMReleasePropList(texarray);
781 return NULL;
785 void
786 freeTexture(BackgroundTexture *texture)
788 if (texture->solid) {
789 long pixel[1];
791 pixel[0] = texture->color.pixel;
792 /* dont free black/white pixels */
793 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
794 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
795 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
797 if (texture->pixmap) {
798 XFreePixmap(dpy, texture->pixmap);
800 wfree(texture->spec);
801 wfree(texture);
805 void
806 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
807 int workspace, char *texture)
809 BackgroundTexture *newTexture = NULL;
810 int i;
812 /* unset the texture */
813 if (!texture) {
814 if (textures[workspace]!=NULL) {
815 textures[workspace]->refcount--;
817 if (textures[workspace]->refcount == 0)
818 freeTexture(textures[workspace]);
820 textures[workspace] = NULL;
821 return;
824 if (textures[workspace]
825 && strcasecmp(textures[workspace]->spec, texture)==0) {
826 /* texture did not change */
827 return;
830 /* check if the same texture is already created */
831 for (i = 0; i < *maxTextures; i++) {
832 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
833 newTexture = textures[i];
834 break;
838 if (!newTexture) {
839 /* create the texture */
840 newTexture = parseTexture(rc, texture);
842 if (!newTexture)
843 return;
845 if (textures[workspace]!=NULL) {
847 textures[workspace]->refcount--;
849 if (textures[workspace]->refcount == 0)
850 freeTexture(textures[workspace]);
853 newTexture->refcount++;
854 textures[workspace] = newTexture;
856 if (*maxTextures < workspace)
857 *maxTextures = workspace;
862 Pixmap
863 duplicatePixmap(Pixmap pixmap, int width, int height)
865 Display *tmpDpy;
866 Pixmap copyP;
868 /* must open a new display or the RetainPermanent will
869 * leave stuff allocated in RContext unallocated after exit */
870 tmpDpy = XOpenDisplay(display);
871 if (!tmpDpy) {
872 wwarning("could not open display to update background image information");
874 return None;
875 } else {
876 XSync(dpy, False);
878 copyP = XCreatePixmap(tmpDpy, root, width, height,
879 DefaultDepth(tmpDpy, scr));
880 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
881 0, 0, width, height, 0, 0);
882 XSync(tmpDpy, False);
884 XSetCloseDownMode(tmpDpy, RetainPermanent);
885 XCloseDisplay(tmpDpy);
888 return copyP;
892 static int
893 dummyErrorHandler(Display *dpy, XErrorEvent *err)
895 return 0;
898 void
899 setPixmapProperty(Pixmap pixmap)
901 static Atom prop = 0;
902 Atom type;
903 int format;
904 unsigned long length, after;
905 unsigned char *data;
906 int mode;
908 if (!prop) {
909 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
912 XGrabServer(dpy);
914 /* Clear out the old pixmap */
915 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
916 &type, &format, &length, &after, &data);
918 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
919 XSetErrorHandler(dummyErrorHandler);
920 XKillClient(dpy, *((Pixmap *)data));
921 XSync(dpy, False);
922 XSetErrorHandler(NULL);
923 mode = PropModeReplace;
924 } else {
925 mode = PropModeAppend;
927 if (pixmap)
928 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
929 (unsigned char *) &pixmap, 1);
930 else
931 XDeleteProperty(dpy, root, prop);
934 XUngrabServer(dpy);
935 XFlush(dpy);
940 void
941 changeTexture(BackgroundTexture *texture)
943 if (!texture) {
944 return;
947 if (texture->solid) {
948 XSetWindowBackground(dpy, root, texture->color.pixel);
949 } else {
950 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
952 XClearWindow(dpy, root);
954 XSync(dpy, False);
957 Pixmap pixmap;
959 pixmap = duplicatePixmap(texture->pixmap, texture->width,
960 texture->height);
962 setPixmapProperty(pixmap);
968 readmsg(int fd, unsigned char *buffer, int size)
970 int count;
972 count = 0;
973 while (size>0) {
974 count = read(fd, buffer, size);
975 if (count < 0)
976 return -1;
977 size -= count;
978 buffer += count;
979 *buffer = 0;
982 return size;
987 * Message Format:
988 * sizeSntexture_spec - sets the texture for workspace n
989 * sizeCn - change background texture to the one for workspace n
990 * sizePpath - set the pixmap search path
992 * n is 4 bytes
993 * size = 4 bytes for length of the message data
995 void
996 helperLoop(RContext *rc)
998 BackgroundTexture *textures[WORKSPACE_COUNT];
999 int maxTextures = 0;
1000 unsigned char buffer[2048], buf[8];
1001 int size;
1002 int errcount = 4;
1004 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
1007 while (1) {
1008 int workspace;
1010 /* get length of message */
1011 if (readmsg(0, buffer, 4) < 0) {
1012 wsyserror("error reading message from Window Maker");
1013 errcount--;
1014 if (errcount == 0) {
1015 wfatal("quitting");
1016 exit(1);
1018 continue;
1020 memcpy(buf, buffer, 4);
1021 buf[4] = 0;
1022 size = atoi(buf);
1024 /* get message */
1025 if (readmsg(0, buffer, size) < 0) {
1026 wsyserror("error reading message from Window Maker");
1027 errcount--;
1028 if (errcount == 0) {
1029 wfatal("quitting");
1030 exit(1);
1032 continue;
1034 #ifdef DEBUG
1035 printf("RECEIVED %s\n",buffer);
1036 #endif
1037 if (buffer[0]!='P' && buffer[0]!='K') {
1038 memcpy(buf, &buffer[1], 4);
1039 buf[4] = 0;
1040 workspace = atoi(buf);
1041 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
1042 wwarning("received message with invalid workspace number %i\n",
1043 workspace);
1044 continue;
1048 switch (buffer[0]) {
1049 case 'S':
1050 #ifdef DEBUG
1051 printf("set texture %s\n", &buffer[5]);
1052 #endif
1053 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1054 break;
1056 case 'C':
1057 #ifdef DEBUG
1058 printf("change texture %i\n", workspace);
1059 #endif
1060 if (!textures[workspace]) {
1061 changeTexture(textures[0]);
1062 } else {
1063 changeTexture(textures[workspace]);
1065 break;
1067 case 'P':
1068 #ifdef DEBUG
1069 printf("change pixmappath %s\n", &buffer[1]);
1070 #endif
1071 if (PixmapPath)
1072 wfree(PixmapPath);
1073 PixmapPath = wstrdup(&buffer[1]);
1074 break;
1076 case 'U':
1077 #ifdef DEBUG
1078 printf("unset workspace %i\n", workspace);
1079 #endif
1080 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1081 break;
1083 case 'K':
1084 #ifdef DEBUG
1085 printf("exit command\n");
1086 #endif
1087 exit(0);
1089 default:
1090 wwarning("unknown message received");
1091 break;
1097 void
1098 updateDomain(char *domain, char *key, char *texture)
1100 char *program = "wdwrite";
1102 /* here is a mem leak */
1103 system(wstrconcat("wdwrite ",
1104 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1105 : " SmoothWorkspaceBack NO")));
1107 execlp(program, program, domain, key, texture, NULL);
1108 wwarning("warning could not run \"%s\"", program);
1113 char*
1114 globalDefaultsPathForDomain(char *domain)
1116 char path[1024];
1118 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1120 return wstrdup(path);
1124 static WMPropList*
1125 getValueForKey(char *domain, char *keyName)
1127 char *path;
1128 WMPropList *key, *val, *d;
1130 key = WMCreatePLString(keyName);
1132 /* try to find PixmapPath in user defaults */
1133 path = wdefaultspathfordomain(domain);
1134 d = WMReadPropListFromFile(path);
1135 if (!d) {
1136 wwarning("could not open domain file %s", path);
1138 wfree(path);
1140 if (d && !WMIsPLDictionary(d)) {
1141 WMReleasePropList(d);
1142 d = NULL;
1144 if (d) {
1145 val = WMGetFromPLDictionary(d, key);
1146 } else {
1147 val = NULL;
1149 /* try to find PixmapPath in global defaults */
1150 if (!val) {
1151 path = globalDefaultsPathForDomain(domain);
1152 if (!path) {
1153 wwarning("could not locate file for domain %s", domain);
1154 d = NULL;
1155 } else {
1156 d = WMReadPropListFromFile(path);
1157 wfree(path);
1160 if (d && !WMIsPLDictionary(d)) {
1161 WMReleasePropList(d);
1162 d = NULL;
1164 if (d) {
1165 val = WMGetFromPLDictionary(d, key);
1167 } else {
1168 val = NULL;
1172 if (val)
1173 WMRetainPropList(val);
1175 WMReleasePropList(key);
1176 if (d)
1177 WMReleasePropList(d);
1179 return val;
1184 char*
1185 getPixmapPath(char *domain)
1187 WMPropList *val;
1188 char *ptr, *data;
1189 int len, i, count;
1191 val = getValueForKey(domain, "PixmapPath");
1193 if (!val || !WMIsPLArray(val)) {
1194 if (val)
1195 WMReleasePropList(val);
1196 return wstrdup("");
1199 count = WMGetPropListItemCount(val);
1200 len = 0;
1201 for (i=0; i<count; i++) {
1202 WMPropList *v;
1204 v = WMGetFromPLArray(val, i);
1205 if (!v || !WMIsPLString(v)) {
1206 continue;
1208 len += strlen(WMGetFromPLString(v))+1;
1211 ptr = data = wmalloc(len+1);
1212 *ptr = 0;
1214 for (i=0; i<count; i++) {
1215 WMPropList *v;
1217 v = WMGetFromPLArray(val, i);
1218 if (!v || !WMIsPLString(v)) {
1219 continue;
1221 strcpy(ptr, WMGetFromPLString(v));
1223 ptr += strlen(WMGetFromPLString(v));
1224 *ptr = ':';
1225 ptr++;
1227 if (i>0)
1228 ptr--; *(ptr--) = 0;
1230 WMReleasePropList(val);
1232 return data;
1236 char*
1237 getFullPixmapPath(char *file)
1239 char *tmp;
1241 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1242 int bsize = 512;
1243 char *path = wmalloc(bsize);
1245 while (!getcwd(path, bsize)) {
1246 bsize += bsize/2;
1247 path = wrealloc(path, bsize);
1250 tmp = wstrconcat(path, "/");
1251 wfree(path);
1252 path = wstrconcat(tmp, file);
1253 wfree(tmp);
1255 return path;
1258 /* the file is in the PixmapPath */
1259 wfree(tmp);
1261 return wstrdup(file);
1266 void
1267 wAbort()
1269 wfatal("aborting");
1270 exit(1);
1275 void
1276 print_help(char *ProgName)
1278 printf("Usage: %s [options] [image]\n", ProgName);
1279 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1280 puts("");
1281 #define P(m) puts(m)
1282 P(" -display display to use");
1283 P(" -d, --dither dither image");
1284 P(" -m, --match match colors");
1285 P(" -S, --smooth smooth scaled image");
1286 P(" -b, --back-color <color> background color");
1287 P(" -t, --tile tile image");
1288 P(" -e, --center center image");
1289 P(" -s, --scale scale image (default)");
1290 P(" -a, --maxscale scale image and keep aspect ratio");
1291 P(" -u, --update-wmaker update WindowMaker domain database");
1292 P(" -D, --update-domain <domain> update <domain> database");
1293 P(" -c, --colors <cpc> colors per channel to use");
1294 P(" -p, --parse <texture> proplist style texture specification");
1295 P(" -w, --workspace <workspace> update background for the specified workspace");
1296 P(" --version show version of wmsetbg and exit");
1297 P(" --help show this help and exit");
1298 #undef P
1303 void
1304 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1306 WMPropList *array, *val;
1307 char *value;
1308 int j;
1310 val = WMCreatePropListFromDescription(texture);
1311 if (!val) {
1312 wwarning("could not parse texture %s", texture);
1313 return;
1316 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1318 if (!array) {
1319 array = WMCreatePLArray(NULL, NULL);
1322 j = WMGetPropListItemCount(array);
1323 if (workspace >= j) {
1324 WMPropList *empty;
1326 empty = WMCreatePLArray(NULL, NULL);
1328 while (j++ < workspace-1) {
1329 WMAddToPLArray(array, empty);
1331 WMAddToPLArray(array, val);
1332 } else {
1333 WMDeleteFromPLArray(array, workspace);
1334 WMInsertInPLArray(array, workspace, val);
1337 value = WMGetPropListDescription(array, False);
1338 updateDomain(domain, "WorkspaceSpecificBack", value);
1343 main(int argc, char **argv)
1345 int i;
1346 int helperMode = 0;
1347 RContext *rc;
1348 RContextAttributes rattr;
1349 char *style = "spixmap";
1350 char *back_color = "gray20";
1351 char *image_name = NULL;
1352 char *domain = "WindowMaker";
1353 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1354 char *texture = NULL;
1355 int workspace = -1;
1357 signal(SIGINT, SIG_DFL);
1358 signal(SIGTERM, SIG_DFL);
1359 signal(SIGQUIT, SIG_DFL);
1360 signal(SIGSEGV, SIG_DFL);
1361 signal(SIGBUS, SIG_DFL);
1362 signal(SIGFPE, SIG_DFL);
1363 signal(SIGABRT, SIG_DFL);
1364 signal(SIGHUP, SIG_DFL);
1365 signal(SIGPIPE, SIG_DFL);
1366 signal(SIGCHLD, SIG_DFL);
1368 WMInitializeApplication("wmsetbg", &argc, argv);
1370 for (i=1; i<argc; i++) {
1371 if (strcmp(argv[i], "-helper")==0) {
1372 helperMode = 1;
1373 } else if (strcmp(argv[i], "-display")==0) {
1374 i++;
1375 if (i>=argc) {
1376 wfatal("too few arguments for %s\n", argv[i-1]);
1377 exit(1);
1379 display = argv[i];
1380 } else if (strcmp(argv[i], "-s")==0
1381 || strcmp(argv[i], "--scale")==0) {
1382 style = "spixmap";
1383 } else if (strcmp(argv[i], "-t")==0
1384 || strcmp(argv[i], "--tile")==0) {
1385 style = "tpixmap";
1386 } else if (strcmp(argv[i], "-e")==0
1387 || strcmp(argv[i], "--center")==0) {
1388 style = "cpixmap";
1389 } else if (strcmp(argv[i], "-a")==0
1390 || strcmp(argv[i], "--maxscale")==0) {
1391 style = "mpixmap";
1392 } else if (strcmp(argv[i], "-d")==0
1393 || strcmp(argv[i], "--dither")==0) {
1394 render_mode = RDitheredRendering;
1395 obey_user++;
1396 } else if (strcmp(argv[i], "-m")==0
1397 || strcmp(argv[i], "--match")==0) {
1398 render_mode = RBestMatchRendering;
1399 obey_user++;
1400 } else if (strcmp(argv[i], "-S")==0
1401 || strcmp(argv[i], "--smooth")==0) {
1402 smooth = True;
1403 } else if (strcmp(argv[i], "-u")==0
1404 || strcmp(argv[i], "--update-wmaker")==0) {
1405 update++;
1406 } else if (strcmp(argv[i], "-D")==0
1407 || strcmp(argv[i], "--update-domain")==0) {
1408 update++;
1409 i++;
1410 if (i>=argc) {
1411 wfatal("too few arguments for %s\n", argv[i-1]);
1412 exit(1);
1414 domain = wstrdup(argv[i]);
1415 } else if (strcmp(argv[i], "-c")==0
1416 || strcmp(argv[i], "--colors")==0) {
1417 i++;
1418 if (i>=argc) {
1419 wfatal("too few arguments for %s\n", argv[i-1]);
1420 exit(1);
1422 if (sscanf(argv[i], "%i", &cpc)!=1) {
1423 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1424 exit(1);
1426 } else if (strcmp(argv[i], "-b")==0
1427 || strcmp(argv[i], "--back-color")==0) {
1428 i++;
1429 if (i>=argc) {
1430 wfatal("too few arguments for %s\n", argv[i-1]);
1431 exit(1);
1433 back_color = argv[i];
1434 } else if (strcmp(argv[i], "-p")==0
1435 || strcmp(argv[i], "--parse")==0) {
1436 i++;
1437 if (i>=argc) {
1438 wfatal("too few arguments for %s\n", argv[i-1]);
1439 exit(1);
1441 texture = argv[i];
1442 } else if (strcmp(argv[i], "-w")==0
1443 || strcmp(argv[i], "--workspace")==0) {
1444 i++;
1445 if (i>=argc) {
1446 wfatal("too few arguments for %s\n", argv[i-1]);
1447 exit(1);
1449 if (sscanf(argv[i], "%i", &workspace)!=1) {
1450 wfatal("bad value for workspace number: \"%s\"",
1451 argv[i]);
1452 exit(1);
1454 } else if (strcmp(argv[i], "--version")==0) {
1456 printf(PROG_VERSION);
1457 exit(0);
1459 } else if (strcmp(argv[i], "--help")==0) {
1460 print_help(argv[0]);
1461 exit(0);
1462 } else if (argv[i][0] != '-') {
1463 image_name = argv[i];
1464 } else {
1465 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1466 printf("Try '%s --help' for more information\n", argv[0]);
1467 exit(1);
1470 if (!image_name && !texture && !helperMode) {
1471 printf("%s: you must specify a image file name or a texture\n",
1472 argv[0]);
1473 printf("Try '%s --help' for more information\n", argv[0]);
1474 exit(1);
1478 PixmapPath = getPixmapPath(domain);
1479 if (!smooth) {
1480 WMPropList *val;
1481 #if 0 /* some problem with Alpha... TODO: check if its right */
1482 val = WMGetFromPLDictionary(domain,
1483 WMCreatePLString("SmoothWorkspaceBack"));
1484 #else
1485 val = getValueForKey(domain, "SmoothWorkspaceBack");
1486 #endif
1488 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1489 smooth = True;
1492 dpy = XOpenDisplay(display);
1493 if (!dpy) {
1494 wfatal("could not open display");
1495 exit(1);
1497 #if 0
1498 XSynchronize(dpy, 1);
1499 #endif
1501 root = DefaultRootWindow(dpy);
1503 scr = DefaultScreen(dpy);
1505 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1506 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1507 scrX = scrY = 0;
1509 initXinerama();
1512 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1513 render_mode = RDitheredRendering;
1515 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1516 | RC_StandardColormap | RC_DefaultVisual;
1517 rattr.render_mode = render_mode;
1518 rattr.colors_per_channel = cpc;
1519 rattr.standard_colormap_mode = RCreateStdColormap;
1521 rc = RCreateContext(dpy, scr, &rattr);
1523 if (!rc) {
1524 rattr.standard_colormap_mode = RIgnoreStdColormap;
1525 rc = RCreateContext(dpy, scr, &rattr);
1528 if (!rc) {
1529 wfatal("could not initialize wrlib: %s",
1530 RMessageForError(RErrorCode));
1531 exit(1);
1534 if (helperMode) {
1535 /* lower priority, so that it wont use all the CPU */
1536 nice(15);
1538 helperLoop(rc);
1539 } else {
1540 BackgroundTexture *tex;
1541 char buffer[4098];
1543 if (!texture) {
1544 char *image_path = getFullPixmapPath(image_name);
1546 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1547 wfree(image_path);
1548 texture = (char*)buffer;
1551 if (update && workspace < 0) {
1552 updateDomain(domain, "WorkspaceBack", texture);
1555 tex = parseTexture(rc, texture);
1556 if (!tex)
1557 exit(1);
1559 if (workspace<0)
1560 changeTexture(tex);
1561 else {
1562 /* always update domain */
1563 changeTextureForWorkspace(domain, texture, workspace);
1567 return 0;