solaris xinerama fixes, wmsetbg updated for xinerama
[wmaker-crm.git] / util / wmsetbg.c
blob37efe03e2e06e0dba1097d8956eb2a827f002441
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 XineramaInfo *xine_screens;
137 WXineramaInfo *info = &scr->xine_info;
138 int i;
140 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
142 xineInfo.screens = wmalloc(sizeof(WMRect)*(xineInfo.count+1));
144 for (i=0; i<xineInfo.count; i++) {
145 xineInfo.screens[i].pos.x = xineInfo.screens[i].x_org;
146 xineInfo.screens[i].pos.y = xineInfo.screens[i].y_org;
147 xineInfo.screens[i].size.width = xineInfo.screens[i].width;
148 xineInfo.screens[i].size.height = xineInfo.screens[i].height;
150 XFree(xine_screens);
152 # endif /* !SOLARIS_XINERAMA */
153 #endif /* XINERAMA */
157 RImage*
158 loadImage(RContext *rc, char *file)
160 char *path;
161 RImage *image;
163 if (access(file, F_OK)!=0) {
164 path = wfindfile(PixmapPath, file);
165 if (!path) {
166 wwarning("%s:could not find image file used in texture", file);
167 return NULL;
169 } else {
170 path = wstrdup(file);
173 image = RLoadImage(rc, path, 0);
174 if (!image) {
175 wwarning("%s:could not load image file used in texture:%s", path,
176 RMessageForError(RErrorCode));
178 wfree(path);
180 return image;
184 static void
185 applyImage(RContext *rc, BackgroundTexture *texture, RImage *image, char type,
186 int x, int y, int width, int height)
188 int w, h;
189 Bool fimage = False;
191 switch (toupper(type)) {
192 case 'S':
193 case 'M':
194 if (toupper(type) == 'S') {
195 w = width;
196 h = height;
197 } else {
198 if (image->width*height > image->height*width) {
199 w = width;
200 h = (width*image->height) / image->width;
201 } else {
202 w = (height*image->width) / image->height;
203 h = height;
207 if (w != image->width || h != image->height) {
208 RImage * simage;
210 if (smooth) {
211 simage = RSmoothScaleImage(image, w, h);
212 } else {
213 simage = RScaleImage(image, w, h);
216 if (!simage) {
217 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
218 return;
220 fimage = True;
221 image = simage;
224 /* fall through */
225 case 'C':
227 Pixmap pixmap;
229 if (!RConvertImage(rc, image, &pixmap)) {
230 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
231 return;
234 if (image->width != width || image->height != height) {
235 int sx, sy, w, h;
237 if (image->height < height) {
238 h = image->height;
239 y += (height - h) / 2;
240 sy = 0;
241 } else {
242 sy = (image->height - height) / 2;
243 h = height;
245 if (image->width < width) {
246 w = image->width;
247 x += (width - w) / 2;
248 sx = 0;
249 } else {
250 sx = (image->width - width) / 2;
251 w = width;
254 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
255 } else
256 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height, x, y);
258 XFreePixmap(dpy, pixmap);
259 if (fimage) {
260 RReleaseImage( image);
263 break;
268 BackgroundTexture*
269 parseTexture(RContext *rc, char *text)
271 BackgroundTexture *texture = NULL;
272 WMPropList *texarray;
273 WMPropList *val;
274 int count;
275 char *tmp;
276 char *type;
278 #define GETSTRORGOTO(val, str, i, label) \
279 val = WMGetFromPLArray(texarray, i);\
280 if (!WMIsPLString(val)) {\
281 wwarning("could not parse texture %s", text);\
282 goto label;\
284 str = WMGetFromPLString(val)
286 texarray = WMCreatePropListFromDescription(text);
287 if (!texarray || !WMIsPLArray(texarray)
288 || (count = WMGetPropListItemCount(texarray)) < 2) {
290 wwarning("could not parse texture %s", text);
291 if (texarray)
292 WMReleasePropList(texarray);
293 return NULL;
296 texture = wmalloc(sizeof(BackgroundTexture));
297 memset(texture, 0, sizeof(BackgroundTexture));
299 GETSTRORGOTO(val, type, 0, error);
301 if (strcasecmp(type, "solid")==0) {
302 XColor color;
303 Pixmap pixmap;
305 texture->solid = 1;
307 GETSTRORGOTO(val, tmp, 1, error);
309 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
310 wwarning("could not parse color %s in texture %s", tmp, text);
311 goto error;
313 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
315 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
316 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
317 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
319 texture->pixmap = pixmap;
320 texture->color = color;
321 texture->width = 8;
322 texture->height = 8;
323 } else if (strcasecmp(type, "vgradient")==0
324 || strcasecmp(type, "dgradient")==0
325 || strcasecmp(type, "hgradient")==0) {
326 XColor color;
327 RColor color1, color2;
328 RImage *image;
329 Pixmap pixmap;
330 int gtype;
331 int iwidth, iheight;
333 GETSTRORGOTO(val, tmp, 1, error);
335 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
336 wwarning("could not parse color %s in texture %s", tmp, text);
337 goto error;
340 color1.red = color.red >> 8;
341 color1.green = color.green >> 8;
342 color1.blue = color.blue >> 8;
344 GETSTRORGOTO(val, tmp, 2, error);
346 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
347 wwarning("could not parse color %s in texture %s", tmp, text);
348 goto error;
351 color2.red = color.red >> 8;
352 color2.green = color.green >> 8;
353 color2.blue = color.blue >> 8;
355 switch (type[0]) {
356 case 'h':
357 case 'H':
358 gtype = RHorizontalGradient;
359 iwidth = scrWidth;
360 iheight = 32;
361 break;
362 case 'V':
363 case 'v':
364 gtype = RVerticalGradient;
365 iwidth = 32;
366 iheight = scrHeight;
367 break;
368 default:
369 gtype = RDiagonalGradient;
370 iwidth = scrWidth;
371 iheight = scrHeight;
372 break;
375 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
377 if (!image) {
378 wwarning("could not render gradient texture:%s",
379 RMessageForError(RErrorCode));
380 goto error;
383 if (!RConvertImage(rc, image, &pixmap)) {
384 wwarning("could not convert texture:%s",
385 RMessageForError(RErrorCode));
386 RReleaseImage(image);
387 goto error;
390 texture->width = image->width;
391 texture->height = image->height;
392 RReleaseImage(image);
394 texture->pixmap = pixmap;
395 } else if (strcasecmp(type, "mvgradient")==0
396 || strcasecmp(type, "mdgradient")==0
397 || strcasecmp(type, "mhgradient")==0) {
398 XColor color;
399 RColor **colors;
400 RImage *image;
401 Pixmap pixmap;
402 int i, j;
403 int gtype;
404 int iwidth, iheight;
406 colors = malloc(sizeof(RColor*)*(count-1));
407 if (!colors) {
408 wwarning("out of memory while parsing texture");
409 goto error;
411 memset(colors, 0, sizeof(RColor*)*(count-1));
413 for (i = 2; i < count; i++) {
414 val = WMGetFromPLArray(texarray, i);
415 if (!WMIsPLString(val)) {
416 wwarning("could not parse texture %s", text);
418 for (j = 0; colors[j]!=NULL; j++)
419 wfree(colors[j]);
420 wfree(colors);
421 goto error;
423 tmp = WMGetFromPLString(val);
425 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
426 wwarning("could not parse color %s in texture %s",
427 tmp, text);
429 for (j = 0; colors[j]!=NULL; j++)
430 wfree(colors[j]);
431 wfree(colors);
432 goto error;
434 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
435 wwarning("out of memory while parsing texture");
437 for (j = 0; colors[j]!=NULL; j++)
438 wfree(colors[j]);
439 wfree(colors);
440 goto error;
443 colors[i-2]->red = color.red >> 8;
444 colors[i-2]->green = color.green >> 8;
445 colors[i-2]->blue = color.blue >> 8;
448 switch (type[1]) {
449 case 'h':
450 case 'H':
451 gtype = RHorizontalGradient;
452 iwidth = scrWidth;
453 iheight = 32;
454 break;
455 case 'V':
456 case 'v':
457 gtype = RVerticalGradient;
458 iwidth = 32;
459 iheight = scrHeight;
460 break;
461 default:
462 gtype = RDiagonalGradient;
463 iwidth = scrWidth;
464 iheight = scrHeight;
465 break;
468 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
470 for (j = 0; colors[j]!=NULL; j++)
471 wfree(colors[j]);
472 wfree(colors);
474 if (!image) {
475 wwarning("could not render gradient texture:%s",
476 RMessageForError(RErrorCode));
477 goto error;
480 if (!RConvertImage(rc, image, &pixmap)) {
481 wwarning("could not convert texture:%s",
482 RMessageForError(RErrorCode));
483 RReleaseImage(image);
484 goto error;
487 texture->width = image->width;
488 texture->height = image->height;
489 RReleaseImage(image);
491 texture->pixmap = pixmap;
492 } else if (strcasecmp(type, "cpixmap")==0
493 || strcasecmp(type, "spixmap")==0
494 || strcasecmp(type, "mpixmap")==0
495 || strcasecmp(type, "tpixmap")==0) {
496 XColor color;
497 Pixmap pixmap = None;
498 RImage *image = NULL;
499 int w, h;
500 int iwidth, iheight;
501 RColor rcolor;
504 GETSTRORGOTO(val, tmp, 1, error);
506 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
507 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
510 if (!pixmap) {
511 image = loadImage(rc, tmp);
512 if (!image) {
513 goto error;
515 iwidth = image->width;
516 iheight = image->height;
519 GETSTRORGOTO(val, tmp, 2, error);
521 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
522 wwarning("could not parse color %s in texture %s\n", tmp, text);
523 RReleaseImage(image);
524 goto error;
526 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
527 rcolor.red = color.red >> 8;
528 rcolor.green = color.green >> 8;
529 rcolor.blue = color.blue >> 8;
530 RGetClosestXColor(rc, &rcolor, &color);
531 } else {
532 rcolor.red = 0;
533 rcolor.green = 0;
534 rcolor.blue = 0;
536 /* for images with a transparent color */
537 if (image->data[3]) {
538 RCombineImageWithColor(image, &rcolor);
541 switch (toupper(type[0])) {
542 case 'T':
543 texture->width = iwidth;
544 texture->height = iheight;
545 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
546 wwarning("could not convert texture:%s",
547 RMessageForError(RErrorCode));
548 RReleaseImage(image);
549 goto error;
551 if (image)
552 RReleaseImage(image);
554 texture->pixmap = pixmap;
555 texture->color = color;
556 break;
557 case 'S':
558 case 'M':
559 case 'C':
561 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
562 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
564 texture->pixmap = tpixmap;
565 texture->color = color;
566 texture->width = scrWidth;
567 texture->height = scrHeight;
569 #ifdef XINERAMA
570 if (xineInfo.count) {
571 int i;
572 for (i=0; i<xineInfo.count; ++i) {
573 applyImage(rc, texture, image, type[0],
574 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
575 xineInfo.screens[i].size.width, xineInfo.screens[i].size.height);
577 } else {
578 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
580 #else /* !XINERAMA */
581 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
582 #endif /* !XINERAMA */
583 RReleaseImage(image);
585 break;
587 } else if (strcasecmp(type, "thgradient")==0
588 || strcasecmp(type, "tvgradient")==0
589 || strcasecmp(type, "tdgradient")==0) {
590 XColor color;
591 RColor color1, color2;
592 RImage *image;
593 RImage *gradient;
594 RImage *tiled;
595 Pixmap pixmap;
596 int opaq;
597 char *file;
598 int gtype;
599 int twidth, theight;
601 GETSTRORGOTO(val, file, 1, error);
603 GETSTRORGOTO(val, tmp, 2, error);
605 opaq = atoi(tmp);
607 GETSTRORGOTO(val, tmp, 3, error);
609 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
610 wwarning("could not parse color %s in texture %s", tmp, text);
611 goto error;
614 color1.red = color.red >> 8;
615 color1.green = color.green >> 8;
616 color1.blue = color.blue >> 8;
618 GETSTRORGOTO(val, tmp, 4, error);
620 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
621 wwarning("could not parse color %s in texture %s", tmp, text);
622 goto error;
625 color2.red = color.red >> 8;
626 color2.green = color.green >> 8;
627 color2.blue = color.blue >> 8;
629 image = loadImage(rc, file);
630 if (!image) {
631 goto error;
634 switch (type[1]) {
635 case 'h':
636 case 'H':
637 gtype = RHorizontalGradient;
638 twidth = scrWidth;
639 theight = image->height > scrHeight ? scrHeight : image->height;
640 break;
641 case 'V':
642 case 'v':
643 gtype = RVerticalGradient;
644 twidth = image->width > scrWidth ? scrWidth : image->width;
645 theight = scrHeight;
646 break;
647 default:
648 gtype = RDiagonalGradient;
649 twidth = scrWidth;
650 theight = scrHeight;
651 break;
653 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
655 if (!gradient) {
656 wwarning("could not render texture:%s",
657 RMessageForError(RErrorCode));
658 RReleaseImage(gradient);
659 RReleaseImage(image);
660 goto error;
663 tiled = RMakeTiledImage(image, twidth, theight);
664 if (!tiled) {
665 wwarning("could not render texture:%s",
666 RMessageForError(RErrorCode));
667 RReleaseImage(gradient);
668 RReleaseImage(image);
669 goto error;
671 RReleaseImage(image);
673 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
674 RReleaseImage(gradient);
676 if (!RConvertImage(rc, tiled, &pixmap)) {
677 wwarning("could not convert texture:%s",
678 RMessageForError(RErrorCode));
679 RReleaseImage(tiled);
680 goto error;
682 texture->width = tiled->width;
683 texture->height = tiled->height;
685 RReleaseImage(tiled);
687 texture->pixmap = pixmap;
688 } else if (strcasecmp(type, "function")==0) {
689 #ifdef HAVE_DLFCN_H
690 void (*initFunc) (Display*, Colormap);
691 RImage* (*mainFunc) (int, char**, int, int, int);
692 Pixmap pixmap;
693 RImage *image = 0;
694 int success = 0;
695 char *lib, *func, **argv = 0;
696 void *handle = 0;
697 int i, argc;
699 if (count < 3)
700 goto function_cleanup;
702 /* get the library name */
703 GETSTRORGOTO(val, lib, 1, function_cleanup);
705 /* get the function name */
706 GETSTRORGOTO(val, func, 2, function_cleanup);
708 argc = count - 2;
709 argv = (char**)wmalloc(argc * sizeof(char*));
711 /* get the parameters */
712 argv[0] = func;
713 for (i=0; i<argc-1; i++) {
714 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
715 argv[i+1] = wstrdup(tmp);
718 handle = dlopen(lib, RTLD_LAZY);
719 if (!handle) {
720 wwarning("could not find library %s", lib);
721 goto function_cleanup;
724 initFunc = dlsym(handle, "initWindowMaker");
725 if (!initFunc) {
726 wwarning("could not initialize library %s", lib);
727 goto function_cleanup;
729 initFunc(dpy, DefaultColormap(dpy, scr));
731 mainFunc = dlsym(handle, func);
732 if (!mainFunc) {
733 wwarning("could not find function %s::%s", lib, func);
734 goto function_cleanup;
736 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
738 if (!RConvertImage(rc, image, &pixmap)) {
739 wwarning("could not convert texture:%s",
740 RMessageForError(RErrorCode));
741 goto function_cleanup;
743 texture->width = scrWidth;
744 texture->height = scrHeight;
745 texture->pixmap = pixmap;
746 success = 1;
748 function_cleanup:
749 if (argv) {
750 int i;
751 for (i=0; i<argc; i++) {
752 wfree(argv[i]);
755 if (handle) {
756 dlclose(handle);
758 if (image) {
759 RReleaseImage(image);
761 if (!success) {
762 goto error;
764 #else
765 wwarning("function textures not supported");
766 goto error;
767 #endif
768 } else {
769 wwarning("invalid texture type %s", text);
770 goto error;
773 texture->spec = wstrdup(text);
775 return texture;
777 error:
778 if (texture)
779 wfree(texture);
780 if (texarray)
781 WMReleasePropList(texarray);
783 return NULL;
787 void
788 freeTexture(BackgroundTexture *texture)
790 if (texture->solid) {
791 long pixel[1];
793 pixel[0] = texture->color.pixel;
794 /* dont free black/white pixels */
795 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
796 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
797 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
799 if (texture->pixmap) {
800 XFreePixmap(dpy, texture->pixmap);
802 wfree(texture->spec);
803 wfree(texture);
807 void
808 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
809 int workspace, char *texture)
811 BackgroundTexture *newTexture = NULL;
812 int i;
814 /* unset the texture */
815 if (!texture) {
816 if (textures[workspace]!=NULL) {
817 textures[workspace]->refcount--;
819 if (textures[workspace]->refcount == 0)
820 freeTexture(textures[workspace]);
822 textures[workspace] = NULL;
823 return;
826 if (textures[workspace]
827 && strcasecmp(textures[workspace]->spec, texture)==0) {
828 /* texture did not change */
829 return;
832 /* check if the same texture is already created */
833 for (i = 0; i < *maxTextures; i++) {
834 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
835 newTexture = textures[i];
836 break;
840 if (!newTexture) {
841 /* create the texture */
842 newTexture = parseTexture(rc, texture);
844 if (!newTexture)
845 return;
847 if (textures[workspace]!=NULL) {
849 textures[workspace]->refcount--;
851 if (textures[workspace]->refcount == 0)
852 freeTexture(textures[workspace]);
855 newTexture->refcount++;
856 textures[workspace] = newTexture;
858 if (*maxTextures < workspace)
859 *maxTextures = workspace;
864 Pixmap
865 duplicatePixmap(Pixmap pixmap, int width, int height)
867 Display *tmpDpy;
868 Pixmap copyP;
870 /* must open a new display or the RetainPermanent will
871 * leave stuff allocated in RContext unallocated after exit */
872 tmpDpy = XOpenDisplay(display);
873 if (!tmpDpy) {
874 wwarning("could not open display to update background image information");
876 return None;
877 } else {
878 XSync(dpy, False);
880 copyP = XCreatePixmap(tmpDpy, root, width, height,
881 DefaultDepth(tmpDpy, scr));
882 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
883 0, 0, width, height, 0, 0);
884 XSync(tmpDpy, False);
886 XSetCloseDownMode(tmpDpy, RetainPermanent);
887 XCloseDisplay(tmpDpy);
890 return copyP;
894 static int
895 dummyErrorHandler(Display *dpy, XErrorEvent *err)
897 return 0;
900 void
901 setPixmapProperty(Pixmap pixmap)
903 static Atom prop = 0;
904 Atom type;
905 int format;
906 unsigned long length, after;
907 unsigned char *data;
908 int mode;
910 if (!prop) {
911 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
914 XGrabServer(dpy);
916 /* Clear out the old pixmap */
917 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
918 &type, &format, &length, &after, &data);
920 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
921 XSetErrorHandler(dummyErrorHandler);
922 XKillClient(dpy, *((Pixmap *)data));
923 XSync(dpy, False);
924 XSetErrorHandler(NULL);
925 mode = PropModeReplace;
926 } else {
927 mode = PropModeAppend;
929 if (pixmap)
930 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
931 (unsigned char *) &pixmap, 1);
932 else
933 XDeleteProperty(dpy, root, prop);
936 XUngrabServer(dpy);
937 XFlush(dpy);
942 void
943 changeTexture(BackgroundTexture *texture)
945 if (!texture) {
946 return;
949 if (texture->solid) {
950 XSetWindowBackground(dpy, root, texture->color.pixel);
951 } else {
952 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
954 XClearWindow(dpy, root);
956 XSync(dpy, False);
959 Pixmap pixmap;
961 pixmap = duplicatePixmap(texture->pixmap, texture->width,
962 texture->height);
964 setPixmapProperty(pixmap);
970 readmsg(int fd, unsigned char *buffer, int size)
972 int count;
974 count = 0;
975 while (size>0) {
976 count = read(fd, buffer, size);
977 if (count < 0)
978 return -1;
979 size -= count;
980 buffer += count;
981 *buffer = 0;
984 return size;
989 * Message Format:
990 * sizeSntexture_spec - sets the texture for workspace n
991 * sizeCn - change background texture to the one for workspace n
992 * sizePpath - set the pixmap search path
994 * n is 4 bytes
995 * size = 4 bytes for length of the message data
997 void
998 helperLoop(RContext *rc)
1000 BackgroundTexture *textures[WORKSPACE_COUNT];
1001 int maxTextures = 0;
1002 unsigned char buffer[2048], buf[8];
1003 int size;
1004 int errcount = 4;
1006 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
1009 while (1) {
1010 int workspace;
1012 /* get length of message */
1013 if (readmsg(0, buffer, 4) < 0) {
1014 wsyserror("error reading message from Window Maker");
1015 errcount--;
1016 if (errcount == 0) {
1017 wfatal("quitting");
1018 exit(1);
1020 continue;
1022 memcpy(buf, buffer, 4);
1023 buf[4] = 0;
1024 size = atoi(buf);
1026 /* get message */
1027 if (readmsg(0, buffer, size) < 0) {
1028 wsyserror("error reading message from Window Maker");
1029 errcount--;
1030 if (errcount == 0) {
1031 wfatal("quitting");
1032 exit(1);
1034 continue;
1036 #ifdef DEBUG
1037 printf("RECEIVED %s\n",buffer);
1038 #endif
1039 if (buffer[0]!='P' && buffer[0]!='K') {
1040 memcpy(buf, &buffer[1], 4);
1041 buf[4] = 0;
1042 workspace = atoi(buf);
1043 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
1044 wwarning("received message with invalid workspace number %i\n",
1045 workspace);
1046 continue;
1050 switch (buffer[0]) {
1051 case 'S':
1052 #ifdef DEBUG
1053 printf("set texture %s\n", &buffer[5]);
1054 #endif
1055 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1056 break;
1058 case 'C':
1059 #ifdef DEBUG
1060 printf("change texture %i\n", workspace);
1061 #endif
1062 if (!textures[workspace]) {
1063 changeTexture(textures[0]);
1064 } else {
1065 changeTexture(textures[workspace]);
1067 break;
1069 case 'P':
1070 #ifdef DEBUG
1071 printf("change pixmappath %s\n", &buffer[1]);
1072 #endif
1073 if (PixmapPath)
1074 wfree(PixmapPath);
1075 PixmapPath = wstrdup(&buffer[1]);
1076 break;
1078 case 'U':
1079 #ifdef DEBUG
1080 printf("unset workspace %i\n", workspace);
1081 #endif
1082 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1083 break;
1085 case 'K':
1086 #ifdef DEBUG
1087 printf("exit command\n");
1088 #endif
1089 exit(0);
1091 default:
1092 wwarning("unknown message received");
1093 break;
1099 void
1100 updateDomain(char *domain, char *key, char *texture)
1102 char *program = "wdwrite";
1104 /* here is a mem leak */
1105 system(wstrconcat("wdwrite ",
1106 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1107 : " SmoothWorkspaceBack NO")));
1109 execlp(program, program, domain, key, texture, NULL);
1110 wwarning("warning could not run \"%s\"", program);
1115 char*
1116 globalDefaultsPathForDomain(char *domain)
1118 char path[1024];
1120 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1122 return wstrdup(path);
1126 static WMPropList*
1127 getValueForKey(char *domain, char *keyName)
1129 char *path;
1130 WMPropList *key, *val, *d;
1132 key = WMCreatePLString(keyName);
1134 /* try to find PixmapPath in user defaults */
1135 path = wdefaultspathfordomain(domain);
1136 d = WMReadPropListFromFile(path);
1137 if (!d) {
1138 wwarning("could not open domain file %s", path);
1140 wfree(path);
1142 if (d && !WMIsPLDictionary(d)) {
1143 WMReleasePropList(d);
1144 d = NULL;
1146 if (d) {
1147 val = WMGetFromPLDictionary(d, key);
1148 } else {
1149 val = NULL;
1151 /* try to find PixmapPath in global defaults */
1152 if (!val) {
1153 path = globalDefaultsPathForDomain(domain);
1154 if (!path) {
1155 wwarning("could not locate file for domain %s", domain);
1156 d = NULL;
1157 } else {
1158 d = WMReadPropListFromFile(path);
1159 wfree(path);
1162 if (d && !WMIsPLDictionary(d)) {
1163 WMReleasePropList(d);
1164 d = NULL;
1166 if (d) {
1167 val = WMGetFromPLDictionary(d, key);
1169 } else {
1170 val = NULL;
1174 if (val)
1175 WMRetainPropList(val);
1177 WMReleasePropList(key);
1178 if (d)
1179 WMReleasePropList(d);
1181 return val;
1186 char*
1187 getPixmapPath(char *domain)
1189 WMPropList *val;
1190 char *ptr, *data;
1191 int len, i, count;
1193 val = getValueForKey(domain, "PixmapPath");
1195 if (!val || !WMIsPLArray(val)) {
1196 if (val)
1197 WMReleasePropList(val);
1198 return wstrdup("");
1201 count = WMGetPropListItemCount(val);
1202 len = 0;
1203 for (i=0; i<count; i++) {
1204 WMPropList *v;
1206 v = WMGetFromPLArray(val, i);
1207 if (!v || !WMIsPLString(v)) {
1208 continue;
1210 len += strlen(WMGetFromPLString(v))+1;
1213 ptr = data = wmalloc(len+1);
1214 *ptr = 0;
1216 for (i=0; i<count; i++) {
1217 WMPropList *v;
1219 v = WMGetFromPLArray(val, i);
1220 if (!v || !WMIsPLString(v)) {
1221 continue;
1223 strcpy(ptr, WMGetFromPLString(v));
1225 ptr += strlen(WMGetFromPLString(v));
1226 *ptr = ':';
1227 ptr++;
1229 if (i>0)
1230 ptr--; *(ptr--) = 0;
1232 WMReleasePropList(val);
1234 return data;
1238 char*
1239 getFullPixmapPath(char *file)
1241 char *tmp;
1243 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1244 int bsize = 512;
1245 char *path = wmalloc(bsize);
1247 while (!getcwd(path, bsize)) {
1248 bsize += bsize/2;
1249 path = wrealloc(path, bsize);
1252 tmp = wstrconcat(path, "/");
1253 wfree(path);
1254 path = wstrconcat(tmp, file);
1255 wfree(tmp);
1257 return path;
1260 /* the file is in the PixmapPath */
1261 wfree(tmp);
1263 return wstrdup(file);
1268 void
1269 wAbort()
1271 wfatal("aborting");
1272 exit(1);
1277 void
1278 print_help(char *ProgName)
1280 printf("Usage: %s [options] [image]\n", ProgName);
1281 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1282 puts("");
1283 #define P(m) puts(m)
1284 P(" -display display to use");
1285 P(" -d, --dither dither image");
1286 P(" -m, --match match colors");
1287 P(" -S, --smooth smooth scaled image");
1288 P(" -b, --back-color <color> background color");
1289 P(" -t, --tile tile image");
1290 P(" -e, --center center image");
1291 P(" -s, --scale scale image (default)");
1292 P(" -a, --maxscale scale image and keep aspect ratio");
1293 P(" -u, --update-wmaker update WindowMaker domain database");
1294 P(" -D, --update-domain <domain> update <domain> database");
1295 P(" -c, --colors <cpc> colors per channel to use");
1296 P(" -p, --parse <texture> proplist style texture specification");
1297 P(" -w, --workspace <workspace> update background for the specified workspace");
1298 P(" --version show version of wmsetbg and exit");
1299 P(" --help show this help and exit");
1300 #undef P
1305 void
1306 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1308 WMPropList *array, *val;
1309 char *value;
1310 int j;
1312 val = WMCreatePropListFromDescription(texture);
1313 if (!val) {
1314 wwarning("could not parse texture %s", texture);
1315 return;
1318 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1320 if (!array) {
1321 array = WMCreatePLArray(NULL, NULL);
1324 j = WMGetPropListItemCount(array);
1325 if (workspace >= j) {
1326 WMPropList *empty;
1328 empty = WMCreatePLArray(NULL, NULL);
1330 while (j++ < workspace-1) {
1331 WMAddToPLArray(array, empty);
1333 WMAddToPLArray(array, val);
1334 } else {
1335 WMDeleteFromPLArray(array, workspace);
1336 WMInsertInPLArray(array, workspace, val);
1339 value = WMGetPropListDescription(array, False);
1340 updateDomain(domain, "WorkspaceSpecificBack", value);
1345 main(int argc, char **argv)
1347 int i;
1348 int helperMode = 0;
1349 RContext *rc;
1350 RContextAttributes rattr;
1351 char *style = "spixmap";
1352 char *back_color = "gray20";
1353 char *image_name = NULL;
1354 char *domain = "WindowMaker";
1355 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1356 char *texture = NULL;
1357 int workspace = -1;
1359 signal(SIGINT, SIG_DFL);
1360 signal(SIGTERM, SIG_DFL);
1361 signal(SIGQUIT, SIG_DFL);
1362 signal(SIGSEGV, SIG_DFL);
1363 signal(SIGBUS, SIG_DFL);
1364 signal(SIGFPE, SIG_DFL);
1365 signal(SIGABRT, SIG_DFL);
1366 signal(SIGHUP, SIG_DFL);
1367 signal(SIGPIPE, SIG_DFL);
1368 signal(SIGCHLD, SIG_DFL);
1370 WMInitializeApplication("wmsetbg", &argc, argv);
1372 for (i=1; i<argc; i++) {
1373 if (strcmp(argv[i], "-helper")==0) {
1374 helperMode = 1;
1375 } else if (strcmp(argv[i], "-display")==0) {
1376 i++;
1377 if (i>=argc) {
1378 wfatal("too few arguments for %s\n", argv[i-1]);
1379 exit(1);
1381 display = argv[i];
1382 } else if (strcmp(argv[i], "-s")==0
1383 || strcmp(argv[i], "--scale")==0) {
1384 style = "spixmap";
1385 } else if (strcmp(argv[i], "-t")==0
1386 || strcmp(argv[i], "--tile")==0) {
1387 style = "tpixmap";
1388 } else if (strcmp(argv[i], "-e")==0
1389 || strcmp(argv[i], "--center")==0) {
1390 style = "cpixmap";
1391 } else if (strcmp(argv[i], "-a")==0
1392 || strcmp(argv[i], "--maxscale")==0) {
1393 style = "mpixmap";
1394 } else if (strcmp(argv[i], "-d")==0
1395 || strcmp(argv[i], "--dither")==0) {
1396 render_mode = RDitheredRendering;
1397 obey_user++;
1398 } else if (strcmp(argv[i], "-m")==0
1399 || strcmp(argv[i], "--match")==0) {
1400 render_mode = RBestMatchRendering;
1401 obey_user++;
1402 } else if (strcmp(argv[i], "-S")==0
1403 || strcmp(argv[i], "--smooth")==0) {
1404 smooth = True;
1405 } else if (strcmp(argv[i], "-u")==0
1406 || strcmp(argv[i], "--update-wmaker")==0) {
1407 update++;
1408 } else if (strcmp(argv[i], "-D")==0
1409 || strcmp(argv[i], "--update-domain")==0) {
1410 update++;
1411 i++;
1412 if (i>=argc) {
1413 wfatal("too few arguments for %s\n", argv[i-1]);
1414 exit(1);
1416 domain = wstrdup(argv[i]);
1417 } else if (strcmp(argv[i], "-c")==0
1418 || strcmp(argv[i], "--colors")==0) {
1419 i++;
1420 if (i>=argc) {
1421 wfatal("too few arguments for %s\n", argv[i-1]);
1422 exit(1);
1424 if (sscanf(argv[i], "%i", &cpc)!=1) {
1425 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1426 exit(1);
1428 } else if (strcmp(argv[i], "-b")==0
1429 || strcmp(argv[i], "--back-color")==0) {
1430 i++;
1431 if (i>=argc) {
1432 wfatal("too few arguments for %s\n", argv[i-1]);
1433 exit(1);
1435 back_color = argv[i];
1436 } else if (strcmp(argv[i], "-p")==0
1437 || strcmp(argv[i], "--parse")==0) {
1438 i++;
1439 if (i>=argc) {
1440 wfatal("too few arguments for %s\n", argv[i-1]);
1441 exit(1);
1443 texture = argv[i];
1444 } else if (strcmp(argv[i], "-w")==0
1445 || strcmp(argv[i], "--workspace")==0) {
1446 i++;
1447 if (i>=argc) {
1448 wfatal("too few arguments for %s\n", argv[i-1]);
1449 exit(1);
1451 if (sscanf(argv[i], "%i", &workspace)!=1) {
1452 wfatal("bad value for workspace number: \"%s\"",
1453 argv[i]);
1454 exit(1);
1456 } else if (strcmp(argv[i], "--version")==0) {
1458 printf(PROG_VERSION);
1459 exit(0);
1461 } else if (strcmp(argv[i], "--help")==0) {
1462 print_help(argv[0]);
1463 exit(0);
1464 } else if (argv[i][0] != '-') {
1465 image_name = argv[i];
1466 } else {
1467 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1468 printf("Try '%s --help' for more information\n", argv[0]);
1469 exit(1);
1472 if (!image_name && !texture && !helperMode) {
1473 printf("%s: you must specify a image file name or a texture\n",
1474 argv[0]);
1475 printf("Try '%s --help' for more information\n", argv[0]);
1476 exit(1);
1480 PixmapPath = getPixmapPath(domain);
1481 if (!smooth) {
1482 WMPropList *val;
1483 #if 0 /* some problem with Alpha... TODO: check if its right */
1484 val = WMGetFromPLDictionary(domain,
1485 WMCreatePLString("SmoothWorkspaceBack"));
1486 #else
1487 val = getValueForKey(domain, "SmoothWorkspaceBack");
1488 #endif
1490 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1491 smooth = True;
1494 dpy = XOpenDisplay(display);
1495 if (!dpy) {
1496 wfatal("could not open display");
1497 exit(1);
1499 #if 0
1500 XSynchronize(dpy, 1);
1501 #endif
1503 root = DefaultRootWindow(dpy);
1505 scr = DefaultScreen(dpy);
1507 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1508 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1509 scrX = scrY = 0;
1511 initXinerama();
1514 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1515 render_mode = RDitheredRendering;
1517 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1518 | RC_StandardColormap | RC_DefaultVisual;
1519 rattr.render_mode = render_mode;
1520 rattr.colors_per_channel = cpc;
1521 rattr.standard_colormap_mode = RCreateStdColormap;
1523 rc = RCreateContext(dpy, scr, &rattr);
1525 if (!rc) {
1526 rattr.standard_colormap_mode = RIgnoreStdColormap;
1527 rc = RCreateContext(dpy, scr, &rattr);
1530 if (!rc) {
1531 wfatal("could not initialize wrlib: %s",
1532 RMessageForError(RErrorCode));
1533 exit(1);
1536 if (helperMode) {
1537 /* lower priority, so that it wont use all the CPU */
1538 nice(15);
1540 helperLoop(rc);
1541 } else {
1542 BackgroundTexture *tex;
1543 char buffer[4098];
1545 if (!texture) {
1546 char *image_path = getFullPixmapPath(image_name);
1548 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1549 wfree(image_path);
1550 texture = (char*)buffer;
1553 if (update && workspace < 0) {
1554 updateDomain(domain, "WorkspaceBack", texture);
1557 tex = parseTexture(rc, texture);
1558 if (!tex)
1559 exit(1);
1561 if (workspace<0)
1562 changeTexture(tex);
1563 else {
1564 /* always update domain */
1565 changeTextureForWorkspace(domain, texture, workspace);
1569 return 0;