- Fixed sloppy focus bug
[wmaker-crm.git] / util / wmsetbg.c
blob31592cb01806df74ac215f47056149b4a9e46fc5
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-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 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
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 w, h;
499 int iwidth, iheight;
500 RColor rcolor;
503 GETSTRORGOTO(val, tmp, 1, error);
505 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
506 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
509 if (!pixmap) {
510 image = loadImage(rc, tmp);
511 if (!image) {
512 goto error;
514 iwidth = image->width;
515 iheight = image->height;
518 GETSTRORGOTO(val, tmp, 2, error);
520 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
521 wwarning("could not parse color %s in texture %s\n", tmp, text);
522 RReleaseImage(image);
523 goto error;
525 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
526 rcolor.red = color.red >> 8;
527 rcolor.green = color.green >> 8;
528 rcolor.blue = color.blue >> 8;
529 RGetClosestXColor(rc, &rcolor, &color);
530 } else {
531 rcolor.red = 0;
532 rcolor.green = 0;
533 rcolor.blue = 0;
535 /* for images with a transparent color */
536 if (image->data[3]) {
537 RCombineImageWithColor(image, &rcolor);
540 switch (toupper(type[0])) {
541 case 'T':
542 texture->width = iwidth;
543 texture->height = iheight;
544 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
545 wwarning("could not convert texture:%s",
546 RMessageForError(RErrorCode));
547 RReleaseImage(image);
548 goto error;
550 if (image)
551 RReleaseImage(image);
553 texture->pixmap = pixmap;
554 texture->color = color;
555 break;
556 case 'S':
557 case 'M':
558 case 'C':
560 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
561 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
563 texture->pixmap = tpixmap;
564 texture->color = color;
565 texture->width = scrWidth;
566 texture->height = scrHeight;
568 #ifdef XINERAMA
569 if (xineInfo.count) {
570 int i;
571 for (i=0; i<xineInfo.count; ++i) {
572 applyImage(rc, texture, image, type[0],
573 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
574 xineInfo.screens[i].size.width, xineInfo.screens[i].size.height);
576 } else {
577 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
579 #else /* !XINERAMA */
580 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
581 #endif /* !XINERAMA */
582 RReleaseImage(image);
584 break;
586 } else if (strcasecmp(type, "thgradient")==0
587 || strcasecmp(type, "tvgradient")==0
588 || strcasecmp(type, "tdgradient")==0) {
589 XColor color;
590 RColor color1, color2;
591 RImage *image;
592 RImage *gradient;
593 RImage *tiled;
594 Pixmap pixmap;
595 int opaq;
596 char *file;
597 int gtype;
598 int twidth, theight;
600 GETSTRORGOTO(val, file, 1, error);
602 GETSTRORGOTO(val, tmp, 2, error);
604 opaq = atoi(tmp);
606 GETSTRORGOTO(val, tmp, 3, error);
608 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
609 wwarning("could not parse color %s in texture %s", tmp, text);
610 goto error;
613 color1.red = color.red >> 8;
614 color1.green = color.green >> 8;
615 color1.blue = color.blue >> 8;
617 GETSTRORGOTO(val, tmp, 4, error);
619 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
620 wwarning("could not parse color %s in texture %s", tmp, text);
621 goto error;
624 color2.red = color.red >> 8;
625 color2.green = color.green >> 8;
626 color2.blue = color.blue >> 8;
628 image = loadImage(rc, file);
629 if (!image) {
630 goto error;
633 switch (type[1]) {
634 case 'h':
635 case 'H':
636 gtype = RHorizontalGradient;
637 twidth = scrWidth;
638 theight = image->height > scrHeight ? scrHeight : image->height;
639 break;
640 case 'V':
641 case 'v':
642 gtype = RVerticalGradient;
643 twidth = image->width > scrWidth ? scrWidth : image->width;
644 theight = scrHeight;
645 break;
646 default:
647 gtype = RDiagonalGradient;
648 twidth = scrWidth;
649 theight = scrHeight;
650 break;
652 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
654 if (!gradient) {
655 wwarning("could not render texture:%s",
656 RMessageForError(RErrorCode));
657 RReleaseImage(gradient);
658 RReleaseImage(image);
659 goto error;
662 tiled = RMakeTiledImage(image, twidth, theight);
663 if (!tiled) {
664 wwarning("could not render texture:%s",
665 RMessageForError(RErrorCode));
666 RReleaseImage(gradient);
667 RReleaseImage(image);
668 goto error;
670 RReleaseImage(image);
672 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
673 RReleaseImage(gradient);
675 if (!RConvertImage(rc, tiled, &pixmap)) {
676 wwarning("could not convert texture:%s",
677 RMessageForError(RErrorCode));
678 RReleaseImage(tiled);
679 goto error;
681 texture->width = tiled->width;
682 texture->height = tiled->height;
684 RReleaseImage(tiled);
686 texture->pixmap = pixmap;
687 } else if (strcasecmp(type, "function")==0) {
688 #ifdef HAVE_DLFCN_H
689 void (*initFunc) (Display*, Colormap);
690 RImage* (*mainFunc) (int, char**, int, int, int);
691 Pixmap pixmap;
692 RImage *image = 0;
693 int success = 0;
694 char *lib, *func, **argv = 0;
695 void *handle = 0;
696 int i, argc;
698 if (count < 3)
699 goto function_cleanup;
701 /* get the library name */
702 GETSTRORGOTO(val, lib, 1, function_cleanup);
704 /* get the function name */
705 GETSTRORGOTO(val, func, 2, function_cleanup);
707 argc = count - 2;
708 argv = (char**)wmalloc(argc * sizeof(char*));
710 /* get the parameters */
711 argv[0] = func;
712 for (i=0; i<argc-1; i++) {
713 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
714 argv[i+1] = wstrdup(tmp);
717 handle = dlopen(lib, RTLD_LAZY);
718 if (!handle) {
719 wwarning("could not find library %s", lib);
720 goto function_cleanup;
723 initFunc = dlsym(handle, "initWindowMaker");
724 if (!initFunc) {
725 wwarning("could not initialize library %s", lib);
726 goto function_cleanup;
728 initFunc(dpy, DefaultColormap(dpy, scr));
730 mainFunc = dlsym(handle, func);
731 if (!mainFunc) {
732 wwarning("could not find function %s::%s", lib, func);
733 goto function_cleanup;
735 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
737 if (!RConvertImage(rc, image, &pixmap)) {
738 wwarning("could not convert texture:%s",
739 RMessageForError(RErrorCode));
740 goto function_cleanup;
742 texture->width = scrWidth;
743 texture->height = scrHeight;
744 texture->pixmap = pixmap;
745 success = 1;
747 function_cleanup:
748 if (argv) {
749 int i;
750 for (i=0; i<argc; i++) {
751 wfree(argv[i]);
754 if (handle) {
755 dlclose(handle);
757 if (image) {
758 RReleaseImage(image);
760 if (!success) {
761 goto error;
763 #else
764 wwarning("function textures not supported");
765 goto error;
766 #endif
767 } else {
768 wwarning("invalid texture type %s", text);
769 goto error;
772 texture->spec = wstrdup(text);
774 return texture;
776 error:
777 if (texture)
778 wfree(texture);
779 if (texarray)
780 WMReleasePropList(texarray);
782 return NULL;
786 void
787 freeTexture(BackgroundTexture *texture)
789 if (texture->solid) {
790 long pixel[1];
792 pixel[0] = texture->color.pixel;
793 /* dont free black/white pixels */
794 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
795 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
796 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
798 if (texture->pixmap) {
799 XFreePixmap(dpy, texture->pixmap);
801 wfree(texture->spec);
802 wfree(texture);
806 void
807 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
808 int workspace, char *texture)
810 BackgroundTexture *newTexture = NULL;
811 int i;
813 /* unset the texture */
814 if (!texture) {
815 if (textures[workspace]!=NULL) {
816 textures[workspace]->refcount--;
818 if (textures[workspace]->refcount == 0)
819 freeTexture(textures[workspace]);
821 textures[workspace] = NULL;
822 return;
825 if (textures[workspace]
826 && strcasecmp(textures[workspace]->spec, texture)==0) {
827 /* texture did not change */
828 return;
831 /* check if the same texture is already created */
832 for (i = 0; i < *maxTextures; i++) {
833 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
834 newTexture = textures[i];
835 break;
839 if (!newTexture) {
840 /* create the texture */
841 newTexture = parseTexture(rc, texture);
843 if (!newTexture)
844 return;
846 if (textures[workspace]!=NULL) {
848 textures[workspace]->refcount--;
850 if (textures[workspace]->refcount == 0)
851 freeTexture(textures[workspace]);
854 newTexture->refcount++;
855 textures[workspace] = newTexture;
857 if (*maxTextures < workspace)
858 *maxTextures = workspace;
863 Pixmap
864 duplicatePixmap(Pixmap pixmap, int width, int height)
866 Display *tmpDpy;
867 Pixmap copyP;
869 /* must open a new display or the RetainPermanent will
870 * leave stuff allocated in RContext unallocated after exit */
871 tmpDpy = XOpenDisplay(display);
872 if (!tmpDpy) {
873 wwarning("could not open display to update background image information");
875 return None;
876 } else {
877 XSync(dpy, False);
879 copyP = XCreatePixmap(tmpDpy, root, width, height,
880 DefaultDepth(tmpDpy, scr));
881 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
882 0, 0, width, height, 0, 0);
883 XSync(tmpDpy, False);
885 XSetCloseDownMode(tmpDpy, RetainPermanent);
886 XCloseDisplay(tmpDpy);
889 return copyP;
893 static int
894 dummyErrorHandler(Display *dpy, XErrorEvent *err)
896 return 0;
899 void
900 setPixmapProperty(Pixmap pixmap)
902 static Atom prop = 0;
903 Atom type;
904 int format;
905 unsigned long length, after;
906 unsigned char *data;
907 int mode;
909 if (!prop) {
910 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
913 XGrabServer(dpy);
915 /* Clear out the old pixmap */
916 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
917 &type, &format, &length, &after, &data);
919 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
920 XSetErrorHandler(dummyErrorHandler);
921 XKillClient(dpy, *((Pixmap *)data));
922 XSync(dpy, False);
923 XSetErrorHandler(NULL);
924 mode = PropModeReplace;
925 } else {
926 mode = PropModeAppend;
928 if (pixmap)
929 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
930 (unsigned char *) &pixmap, 1);
931 else
932 XDeleteProperty(dpy, root, prop);
935 XUngrabServer(dpy);
936 XFlush(dpy);
941 void
942 changeTexture(BackgroundTexture *texture)
944 if (!texture) {
945 return;
948 if (texture->solid) {
949 XSetWindowBackground(dpy, root, texture->color.pixel);
950 } else {
951 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
953 XClearWindow(dpy, root);
955 XSync(dpy, False);
958 Pixmap pixmap;
960 pixmap = duplicatePixmap(texture->pixmap, texture->width,
961 texture->height);
963 setPixmapProperty(pixmap);
969 readmsg(int fd, unsigned char *buffer, int size)
971 int count;
973 count = 0;
974 while (size>0) {
975 count = read(fd, buffer, size);
976 if (count < 0)
977 return -1;
978 size -= count;
979 buffer += count;
980 *buffer = 0;
983 return size;
988 * Message Format:
989 * sizeSntexture_spec - sets the texture for workspace n
990 * sizeCn - change background texture to the one for workspace n
991 * sizePpath - set the pixmap search path
993 * n is 4 bytes
994 * size = 4 bytes for length of the message data
996 void
997 helperLoop(RContext *rc)
999 BackgroundTexture *textures[WORKSPACE_COUNT];
1000 int maxTextures = 0;
1001 unsigned char buffer[2048], buf[8];
1002 int size;
1003 int errcount = 4;
1005 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
1008 while (1) {
1009 int workspace;
1011 /* get length of message */
1012 if (readmsg(0, buffer, 4) < 0) {
1013 wsyserror("error reading message from Window Maker");
1014 errcount--;
1015 if (errcount == 0) {
1016 wfatal("quitting");
1017 exit(1);
1019 continue;
1021 memcpy(buf, buffer, 4);
1022 buf[4] = 0;
1023 size = atoi(buf);
1025 /* get message */
1026 if (readmsg(0, buffer, size) < 0) {
1027 wsyserror("error reading message from Window Maker");
1028 errcount--;
1029 if (errcount == 0) {
1030 wfatal("quitting");
1031 exit(1);
1033 continue;
1035 #ifdef DEBUG
1036 printf("RECEIVED %s\n",buffer);
1037 #endif
1038 if (buffer[0]!='P' && buffer[0]!='K') {
1039 memcpy(buf, &buffer[1], 4);
1040 buf[4] = 0;
1041 workspace = atoi(buf);
1042 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
1043 wwarning("received message with invalid workspace number %i\n",
1044 workspace);
1045 continue;
1049 switch (buffer[0]) {
1050 case 'S':
1051 #ifdef DEBUG
1052 printf("set texture %s\n", &buffer[5]);
1053 #endif
1054 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1055 break;
1057 case 'C':
1058 #ifdef DEBUG
1059 printf("change texture %i\n", workspace);
1060 #endif
1061 if (!textures[workspace]) {
1062 changeTexture(textures[0]);
1063 } else {
1064 changeTexture(textures[workspace]);
1066 break;
1068 case 'P':
1069 #ifdef DEBUG
1070 printf("change pixmappath %s\n", &buffer[1]);
1071 #endif
1072 if (PixmapPath)
1073 wfree(PixmapPath);
1074 PixmapPath = wstrdup(&buffer[1]);
1075 break;
1077 case 'U':
1078 #ifdef DEBUG
1079 printf("unset workspace %i\n", workspace);
1080 #endif
1081 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1082 break;
1084 case 'K':
1085 #ifdef DEBUG
1086 printf("exit command\n");
1087 #endif
1088 exit(0);
1090 default:
1091 wwarning("unknown message received");
1092 break;
1098 void
1099 updateDomain(char *domain, char *key, char *texture)
1101 char *program = "wdwrite";
1103 /* here is a mem leak */
1104 system(wstrconcat("wdwrite ",
1105 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1106 : " SmoothWorkspaceBack NO")));
1108 execlp(program, program, domain, key, texture, NULL);
1109 wwarning("warning could not run \"%s\"", program);
1114 char*
1115 globalDefaultsPathForDomain(char *domain)
1117 char path[1024];
1119 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1121 return wstrdup(path);
1125 static WMPropList*
1126 getValueForKey(char *domain, char *keyName)
1128 char *path;
1129 WMPropList *key, *val, *d;
1131 key = WMCreatePLString(keyName);
1133 /* try to find PixmapPath in user defaults */
1134 path = wdefaultspathfordomain(domain);
1135 d = WMReadPropListFromFile(path);
1136 if (!d) {
1137 wwarning("could not open domain file %s", path);
1139 wfree(path);
1141 if (d && !WMIsPLDictionary(d)) {
1142 WMReleasePropList(d);
1143 d = NULL;
1145 if (d) {
1146 val = WMGetFromPLDictionary(d, key);
1147 } else {
1148 val = NULL;
1150 /* try to find PixmapPath in global defaults */
1151 if (!val) {
1152 path = globalDefaultsPathForDomain(domain);
1153 if (!path) {
1154 wwarning("could not locate file for domain %s", domain);
1155 d = NULL;
1156 } else {
1157 d = WMReadPropListFromFile(path);
1158 wfree(path);
1161 if (d && !WMIsPLDictionary(d)) {
1162 WMReleasePropList(d);
1163 d = NULL;
1165 if (d) {
1166 val = WMGetFromPLDictionary(d, key);
1168 } else {
1169 val = NULL;
1173 if (val)
1174 WMRetainPropList(val);
1176 WMReleasePropList(key);
1177 if (d)
1178 WMReleasePropList(d);
1180 return val;
1185 char*
1186 getPixmapPath(char *domain)
1188 WMPropList *val;
1189 char *ptr, *data;
1190 int len, i, count;
1192 val = getValueForKey(domain, "PixmapPath");
1194 if (!val || !WMIsPLArray(val)) {
1195 if (val)
1196 WMReleasePropList(val);
1197 return wstrdup("");
1200 count = WMGetPropListItemCount(val);
1201 len = 0;
1202 for (i=0; i<count; i++) {
1203 WMPropList *v;
1205 v = WMGetFromPLArray(val, i);
1206 if (!v || !WMIsPLString(v)) {
1207 continue;
1209 len += strlen(WMGetFromPLString(v))+1;
1212 ptr = data = wmalloc(len+1);
1213 *ptr = 0;
1215 for (i=0; i<count; i++) {
1216 WMPropList *v;
1218 v = WMGetFromPLArray(val, i);
1219 if (!v || !WMIsPLString(v)) {
1220 continue;
1222 strcpy(ptr, WMGetFromPLString(v));
1224 ptr += strlen(WMGetFromPLString(v));
1225 *ptr = ':';
1226 ptr++;
1228 if (i>0)
1229 ptr--; *(ptr--) = 0;
1231 WMReleasePropList(val);
1233 return data;
1237 char*
1238 getFullPixmapPath(char *file)
1240 char *tmp;
1242 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1243 int bsize = 512;
1244 char *path = wmalloc(bsize);
1246 while (!getcwd(path, bsize)) {
1247 bsize += bsize/2;
1248 path = wrealloc(path, bsize);
1251 tmp = wstrconcat(path, "/");
1252 wfree(path);
1253 path = wstrconcat(tmp, file);
1254 wfree(tmp);
1256 return path;
1259 /* the file is in the PixmapPath */
1260 wfree(tmp);
1262 return wstrdup(file);
1267 void
1268 wAbort()
1270 wfatal("aborting");
1271 exit(1);
1276 void
1277 print_help(char *ProgName)
1279 printf("Usage: %s [options] [image]\n", ProgName);
1280 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1281 puts("");
1282 #define P(m) puts(m)
1283 P(" -display display to use");
1284 P(" -d, --dither dither image");
1285 P(" -m, --match match colors");
1286 P(" -S, --smooth smooth scaled image");
1287 P(" -b, --back-color <color> background color");
1288 P(" -t, --tile tile image");
1289 P(" -e, --center center image");
1290 P(" -s, --scale scale image (default)");
1291 P(" -a, --maxscale scale image and keep aspect ratio");
1292 P(" -u, --update-wmaker update WindowMaker domain database");
1293 P(" -D, --update-domain <domain> update <domain> database");
1294 P(" -c, --colors <cpc> colors per channel to use");
1295 P(" -p, --parse <texture> proplist style texture specification");
1296 P(" -w, --workspace <workspace> update background for the specified workspace");
1297 P(" --version show version of wmsetbg and exit");
1298 P(" --help show this help and exit");
1299 #undef P
1304 void
1305 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1307 WMPropList *array, *val;
1308 char *value;
1309 int j;
1311 val = WMCreatePropListFromDescription(texture);
1312 if (!val) {
1313 wwarning("could not parse texture %s", texture);
1314 return;
1317 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1319 if (!array) {
1320 array = WMCreatePLArray(NULL, NULL);
1323 j = WMGetPropListItemCount(array);
1324 if (workspace >= j) {
1325 WMPropList *empty;
1327 empty = WMCreatePLArray(NULL, NULL);
1329 while (j++ < workspace-1) {
1330 WMAddToPLArray(array, empty);
1332 WMAddToPLArray(array, val);
1333 } else {
1334 WMDeleteFromPLArray(array, workspace);
1335 WMInsertInPLArray(array, workspace, val);
1338 value = WMGetPropListDescription(array, False);
1339 updateDomain(domain, "WorkspaceSpecificBack", value);
1344 main(int argc, char **argv)
1346 int i;
1347 int helperMode = 0;
1348 RContext *rc;
1349 RContextAttributes rattr;
1350 char *style = "spixmap";
1351 char *back_color = "gray20";
1352 char *image_name = NULL;
1353 char *domain = "WindowMaker";
1354 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1355 char *texture = NULL;
1356 int workspace = -1;
1358 signal(SIGINT, SIG_DFL);
1359 signal(SIGTERM, SIG_DFL);
1360 signal(SIGQUIT, SIG_DFL);
1361 signal(SIGSEGV, SIG_DFL);
1362 signal(SIGBUS, SIG_DFL);
1363 signal(SIGFPE, SIG_DFL);
1364 signal(SIGABRT, SIG_DFL);
1365 signal(SIGHUP, SIG_DFL);
1366 signal(SIGPIPE, SIG_DFL);
1367 signal(SIGCHLD, SIG_DFL);
1369 WMInitializeApplication("wmsetbg", &argc, argv);
1371 for (i=1; i<argc; i++) {
1372 if (strcmp(argv[i], "-helper")==0) {
1373 helperMode = 1;
1374 } else if (strcmp(argv[i], "-display")==0) {
1375 i++;
1376 if (i>=argc) {
1377 wfatal("too few arguments for %s\n", argv[i-1]);
1378 exit(1);
1380 display = argv[i];
1381 } else if (strcmp(argv[i], "-s")==0
1382 || strcmp(argv[i], "--scale")==0) {
1383 style = "spixmap";
1384 } else if (strcmp(argv[i], "-t")==0
1385 || strcmp(argv[i], "--tile")==0) {
1386 style = "tpixmap";
1387 } else if (strcmp(argv[i], "-e")==0
1388 || strcmp(argv[i], "--center")==0) {
1389 style = "cpixmap";
1390 } else if (strcmp(argv[i], "-a")==0
1391 || strcmp(argv[i], "--maxscale")==0) {
1392 style = "mpixmap";
1393 } else if (strcmp(argv[i], "-d")==0
1394 || strcmp(argv[i], "--dither")==0) {
1395 render_mode = RDitheredRendering;
1396 obey_user++;
1397 } else if (strcmp(argv[i], "-m")==0
1398 || strcmp(argv[i], "--match")==0) {
1399 render_mode = RBestMatchRendering;
1400 obey_user++;
1401 } else if (strcmp(argv[i], "-S")==0
1402 || strcmp(argv[i], "--smooth")==0) {
1403 smooth = True;
1404 } else if (strcmp(argv[i], "-u")==0
1405 || strcmp(argv[i], "--update-wmaker")==0) {
1406 update++;
1407 } else if (strcmp(argv[i], "-D")==0
1408 || strcmp(argv[i], "--update-domain")==0) {
1409 update++;
1410 i++;
1411 if (i>=argc) {
1412 wfatal("too few arguments for %s\n", argv[i-1]);
1413 exit(1);
1415 domain = wstrdup(argv[i]);
1416 } else if (strcmp(argv[i], "-c")==0
1417 || strcmp(argv[i], "--colors")==0) {
1418 i++;
1419 if (i>=argc) {
1420 wfatal("too few arguments for %s\n", argv[i-1]);
1421 exit(1);
1423 if (sscanf(argv[i], "%i", &cpc)!=1) {
1424 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1425 exit(1);
1427 } else if (strcmp(argv[i], "-b")==0
1428 || strcmp(argv[i], "--back-color")==0) {
1429 i++;
1430 if (i>=argc) {
1431 wfatal("too few arguments for %s\n", argv[i-1]);
1432 exit(1);
1434 back_color = argv[i];
1435 } else if (strcmp(argv[i], "-p")==0
1436 || strcmp(argv[i], "--parse")==0) {
1437 i++;
1438 if (i>=argc) {
1439 wfatal("too few arguments for %s\n", argv[i-1]);
1440 exit(1);
1442 texture = argv[i];
1443 } else if (strcmp(argv[i], "-w")==0
1444 || strcmp(argv[i], "--workspace")==0) {
1445 i++;
1446 if (i>=argc) {
1447 wfatal("too few arguments for %s\n", argv[i-1]);
1448 exit(1);
1450 if (sscanf(argv[i], "%i", &workspace)!=1) {
1451 wfatal("bad value for workspace number: \"%s\"",
1452 argv[i]);
1453 exit(1);
1455 } else if (strcmp(argv[i], "--version")==0) {
1457 printf(PROG_VERSION);
1458 exit(0);
1460 } else if (strcmp(argv[i], "--help")==0) {
1461 print_help(argv[0]);
1462 exit(0);
1463 } else if (argv[i][0] != '-') {
1464 image_name = argv[i];
1465 } else {
1466 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1467 printf("Try '%s --help' for more information\n", argv[0]);
1468 exit(1);
1471 if (!image_name && !texture && !helperMode) {
1472 printf("%s: you must specify a image file name or a texture\n",
1473 argv[0]);
1474 printf("Try '%s --help' for more information\n", argv[0]);
1475 exit(1);
1479 PixmapPath = getPixmapPath(domain);
1480 if (!smooth) {
1481 WMPropList *val;
1482 #if 0 /* some problem with Alpha... TODO: check if its right */
1483 val = WMGetFromPLDictionary(domain,
1484 WMCreatePLString("SmoothWorkspaceBack"));
1485 #else
1486 val = getValueForKey(domain, "SmoothWorkspaceBack");
1487 #endif
1489 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1490 smooth = True;
1493 dpy = XOpenDisplay(display);
1494 if (!dpy) {
1495 wfatal("could not open display");
1496 exit(1);
1498 #if 0
1499 XSynchronize(dpy, 1);
1500 #endif
1502 root = DefaultRootWindow(dpy);
1504 scr = DefaultScreen(dpy);
1506 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1507 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1508 scrX = scrY = 0;
1510 initXinerama();
1513 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1514 render_mode = RDitheredRendering;
1516 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1517 | RC_StandardColormap | RC_DefaultVisual;
1518 rattr.render_mode = render_mode;
1519 rattr.colors_per_channel = cpc;
1520 rattr.standard_colormap_mode = RCreateStdColormap;
1522 rc = RCreateContext(dpy, scr, &rattr);
1524 if (!rc) {
1525 rattr.standard_colormap_mode = RIgnoreStdColormap;
1526 rc = RCreateContext(dpy, scr, &rattr);
1529 if (!rc) {
1530 wfatal("could not initialize wrlib: %s",
1531 RMessageForError(RErrorCode));
1532 exit(1);
1535 if (helperMode) {
1536 /* lower priority, so that it wont use all the CPU */
1537 nice(15);
1539 helperLoop(rc);
1540 } else {
1541 BackgroundTexture *tex;
1542 char buffer[4098];
1544 if (!texture) {
1545 char *image_path = getFullPixmapPath(image_name);
1547 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1548 wfree(image_path);
1549 texture = (char*)buffer;
1552 if (update && workspace < 0) {
1553 updateDomain(domain, "WorkspaceBack", texture);
1556 tex = parseTexture(rc, texture);
1557 if (!tex)
1558 exit(1);
1560 if (workspace<0)
1561 changeTexture(tex);
1562 else {
1563 /* always update domain */
1564 changeTextureForWorkspace(domain, texture, workspace);
1568 return 0;