fixed bug in tpixmap after xinerama patch
[wmaker-crm.git] / util / wmsetbg.c
blobc999bb872b065f28949792cedafb7e08012f65ed
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 #include <X11/extensions/Xinerama.h>
44 #endif
46 #ifdef HAVE_DLFCN_H
47 #include <dlfcn.h>
48 #endif
50 #include "../src/wconfig.h"
52 #include <WINGs/WINGs.h>
53 #include <wraster.h>
56 #define PROG_VERSION "wmsetbg (Window Maker) 2.7"
59 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
62 Display *dpy;
63 char *display = "";
64 Window root;
65 int scr;
66 int scrWidth;
67 int scrHeight;
68 int scrX, scrY;
70 #ifdef XINERAMA
71 XineramaScreenInfo *xine_screens;
72 int xine_count;
73 #endif
75 Bool smooth = False;
78 Pixmap CurrentPixmap = None;
79 char *PixmapPath = NULL;
82 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
85 typedef struct BackgroundTexture {
86 int refcount;
88 int solid;
90 char *spec;
92 XColor color;
93 Pixmap pixmap; /* for all textures, including solid */
94 int width; /* size of the pixmap */
95 int height;
96 } BackgroundTexture;
100 RImage*
101 loadImage(RContext *rc, char *file)
103 char *path;
104 RImage *image;
106 if (access(file, F_OK)!=0) {
107 path = wfindfile(PixmapPath, file);
108 if (!path) {
109 wwarning("%s:could not find image file used in texture", file);
110 return NULL;
112 } else {
113 path = wstrdup(file);
116 image = RLoadImage(rc, path, 0);
117 if (!image) {
118 wwarning("%s:could not load image file used in texture:%s", path,
119 RMessageForError(RErrorCode));
121 wfree(path);
123 return image;
127 static void
128 applyImage(RContext *rc, BackgroundTexture *texture, RImage *image, char type,
129 int x, int y, int width, int height)
131 int w, h;
132 Bool fimage = False;
134 switch (toupper(type)) {
135 case 'S':
136 case 'M':
137 if (type == 'S') {
138 w = width;
139 h = height;
140 } else {
141 if (image->width*height > image->height*width) {
142 w = width;
143 h = (width*image->height) / image->width;
144 } else {
145 w = (height*image->width) / image->height;
146 h = height;
150 if (w != image->width || h != image->height) {
151 RImage * simage;
153 if (smooth) {
154 simage = RSmoothScaleImage(image, w, h);
155 } else {
156 simage = RScaleImage(image, w, h);
159 if (!simage) {
160 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
161 return;
163 fimage = True;
164 image = simage;
167 /* fall through */
168 case 'C':
170 Pixmap pixmap;
172 if (!RConvertImage(rc, image, &pixmap)) {
173 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
174 return;
177 if (image->width != width || image->height != height) {
178 int sx, sy, w, h;
180 if (image->height < height) {
181 h = image->height;
182 y += (height - h) / 2;
183 sy = 0;
184 } else {
185 sy = (image->height - height) / 2;
186 h = height;
188 if (image->width < width) {
189 w = image->width;
190 x += (width - w) / 2;
191 sx = 0;
192 } else {
193 sx = (image->width - width) / 2;
194 w = width;
197 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
198 } else
199 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height, x, y);
201 XFreePixmap(dpy, pixmap);
202 if (fimage) {
203 RReleaseImage( image);
206 break;
211 BackgroundTexture*
212 parseTexture(RContext *rc, char *text)
214 BackgroundTexture *texture = NULL;
215 WMPropList *texarray;
216 WMPropList *val;
217 int count;
218 char *tmp;
219 char *type;
221 #define GETSTRORGOTO(val, str, i, label) \
222 val = WMGetFromPLArray(texarray, i);\
223 if (!WMIsPLString(val)) {\
224 wwarning("could not parse texture %s", text);\
225 goto label;\
227 str = WMGetFromPLString(val)
229 texarray = WMCreatePropListFromDescription(text);
230 if (!texarray || !WMIsPLArray(texarray)
231 || (count = WMGetPropListItemCount(texarray)) < 2) {
233 wwarning("could not parse texture %s", text);
234 if (texarray)
235 WMReleasePropList(texarray);
236 return NULL;
239 texture = wmalloc(sizeof(BackgroundTexture));
240 memset(texture, 0, sizeof(BackgroundTexture));
242 GETSTRORGOTO(val, type, 0, error);
244 if (strcasecmp(type, "solid")==0) {
245 XColor color;
246 Pixmap pixmap;
248 texture->solid = 1;
250 GETSTRORGOTO(val, tmp, 1, error);
252 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
253 wwarning("could not parse color %s in texture %s", tmp, text);
254 goto error;
256 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
258 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
259 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
260 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
262 texture->pixmap = pixmap;
263 texture->color = color;
264 texture->width = 8;
265 texture->height = 8;
266 } else if (strcasecmp(type, "vgradient")==0
267 || strcasecmp(type, "dgradient")==0
268 || strcasecmp(type, "hgradient")==0) {
269 XColor color;
270 RColor color1, color2;
271 RImage *image;
272 Pixmap pixmap;
273 int gtype;
274 int iwidth, iheight;
276 GETSTRORGOTO(val, tmp, 1, error);
278 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
279 wwarning("could not parse color %s in texture %s", tmp, text);
280 goto error;
283 color1.red = color.red >> 8;
284 color1.green = color.green >> 8;
285 color1.blue = color.blue >> 8;
287 GETSTRORGOTO(val, tmp, 2, error);
289 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
290 wwarning("could not parse color %s in texture %s", tmp, text);
291 goto error;
294 color2.red = color.red >> 8;
295 color2.green = color.green >> 8;
296 color2.blue = color.blue >> 8;
298 switch (type[0]) {
299 case 'h':
300 case 'H':
301 gtype = RHorizontalGradient;
302 iwidth = scrWidth;
303 iheight = 32;
304 break;
305 case 'V':
306 case 'v':
307 gtype = RVerticalGradient;
308 iwidth = 32;
309 iheight = scrHeight;
310 break;
311 default:
312 gtype = RDiagonalGradient;
313 iwidth = scrWidth;
314 iheight = scrHeight;
315 break;
318 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
320 if (!image) {
321 wwarning("could not render gradient texture:%s",
322 RMessageForError(RErrorCode));
323 goto error;
326 if (!RConvertImage(rc, image, &pixmap)) {
327 wwarning("could not convert texture:%s",
328 RMessageForError(RErrorCode));
329 RReleaseImage(image);
330 goto error;
333 texture->width = image->width;
334 texture->height = image->height;
335 RReleaseImage(image);
337 texture->pixmap = pixmap;
338 } else if (strcasecmp(type, "mvgradient")==0
339 || strcasecmp(type, "mdgradient")==0
340 || strcasecmp(type, "mhgradient")==0) {
341 XColor color;
342 RColor **colors;
343 RImage *image;
344 Pixmap pixmap;
345 int i, j;
346 int gtype;
347 int iwidth, iheight;
349 colors = malloc(sizeof(RColor*)*(count-1));
350 if (!colors) {
351 wwarning("out of memory while parsing texture");
352 goto error;
354 memset(colors, 0, sizeof(RColor*)*(count-1));
356 for (i = 2; i < count; i++) {
357 val = WMGetFromPLArray(texarray, i);
358 if (!WMIsPLString(val)) {
359 wwarning("could not parse texture %s", text);
361 for (j = 0; colors[j]!=NULL; j++)
362 wfree(colors[j]);
363 wfree(colors);
364 goto error;
366 tmp = WMGetFromPLString(val);
368 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
369 wwarning("could not parse color %s in texture %s",
370 tmp, text);
372 for (j = 0; colors[j]!=NULL; j++)
373 wfree(colors[j]);
374 wfree(colors);
375 goto error;
377 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
378 wwarning("out of memory while parsing texture");
380 for (j = 0; colors[j]!=NULL; j++)
381 wfree(colors[j]);
382 wfree(colors);
383 goto error;
386 colors[i-2]->red = color.red >> 8;
387 colors[i-2]->green = color.green >> 8;
388 colors[i-2]->blue = color.blue >> 8;
391 switch (type[1]) {
392 case 'h':
393 case 'H':
394 gtype = RHorizontalGradient;
395 iwidth = scrWidth;
396 iheight = 32;
397 break;
398 case 'V':
399 case 'v':
400 gtype = RVerticalGradient;
401 iwidth = 32;
402 iheight = scrHeight;
403 break;
404 default:
405 gtype = RDiagonalGradient;
406 iwidth = scrWidth;
407 iheight = scrHeight;
408 break;
411 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
413 for (j = 0; colors[j]!=NULL; j++)
414 wfree(colors[j]);
415 wfree(colors);
417 if (!image) {
418 wwarning("could not render gradient texture:%s",
419 RMessageForError(RErrorCode));
420 goto error;
423 if (!RConvertImage(rc, image, &pixmap)) {
424 wwarning("could not convert texture:%s",
425 RMessageForError(RErrorCode));
426 RReleaseImage(image);
427 goto error;
430 texture->width = image->width;
431 texture->height = image->height;
432 RReleaseImage(image);
434 texture->pixmap = pixmap;
435 } else if (strcasecmp(type, "cpixmap")==0
436 || strcasecmp(type, "spixmap")==0
437 || strcasecmp(type, "mpixmap")==0
438 || strcasecmp(type, "tpixmap")==0) {
439 XColor color;
440 Pixmap pixmap = None;
441 RImage *image = NULL;
442 int w, h;
443 int iwidth, iheight;
444 RColor rcolor;
447 GETSTRORGOTO(val, tmp, 1, error);
449 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
450 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
453 if (!pixmap) {
454 image = loadImage(rc, tmp);
455 if (!image) {
456 goto error;
458 iwidth = image->width;
459 iheight = image->height;
462 GETSTRORGOTO(val, tmp, 2, error);
464 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
465 wwarning("could not parse color %s in texture %s\n", tmp, text);
466 RReleaseImage(image);
467 goto error;
469 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
470 rcolor.red = color.red >> 8;
471 rcolor.green = color.green >> 8;
472 rcolor.blue = color.blue >> 8;
473 RGetClosestXColor(rc, &rcolor, &color);
474 } else {
475 rcolor.red = 0;
476 rcolor.green = 0;
477 rcolor.blue = 0;
479 /* for images with a transparent color */
480 if (image->data[3]) {
481 RCombineImageWithColor(image, &rcolor);
484 switch (toupper(type[0])) {
485 case 'T':
486 texture->width = iwidth;
487 texture->height = iheight;
488 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
489 wwarning("could not convert texture:%s",
490 RMessageForError(RErrorCode));
491 RReleaseImage(image);
492 goto error;
494 if (image)
495 RReleaseImage(image);
497 texture->pixmap = pixmap;
498 texture->color = color;
499 break;
500 case 'S':
501 case 'M':
502 case 'C':
504 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
505 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
507 texture->pixmap = tpixmap;
508 texture->color = color;
509 texture->width = scrWidth;
510 texture->height = scrHeight;
512 #ifdef XINERAMA
513 if (xine_count) {
514 int i;
515 for (i=0; i<xine_count; ++i) {
516 applyImage(rc, texture, image, type[0],
517 xine_screens[i].x_org, xine_screens[i].y_org,
518 xine_screens[i].width, xine_screens[i].height);
520 } else {
521 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
523 #else /* !XINERAMA */
524 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
525 #endif /* !XINERAMA */
526 RReleaseImage(image);
528 break;
530 } else if (strcasecmp(type, "thgradient")==0
531 || strcasecmp(type, "tvgradient")==0
532 || strcasecmp(type, "tdgradient")==0) {
533 XColor color;
534 RColor color1, color2;
535 RImage *image;
536 RImage *gradient;
537 RImage *tiled;
538 Pixmap pixmap;
539 int opaq;
540 char *file;
541 int gtype;
542 int twidth, theight;
544 GETSTRORGOTO(val, file, 1, error);
546 GETSTRORGOTO(val, tmp, 2, error);
548 opaq = atoi(tmp);
550 GETSTRORGOTO(val, tmp, 3, error);
552 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
553 wwarning("could not parse color %s in texture %s", tmp, text);
554 goto error;
557 color1.red = color.red >> 8;
558 color1.green = color.green >> 8;
559 color1.blue = color.blue >> 8;
561 GETSTRORGOTO(val, tmp, 4, error);
563 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
564 wwarning("could not parse color %s in texture %s", tmp, text);
565 goto error;
568 color2.red = color.red >> 8;
569 color2.green = color.green >> 8;
570 color2.blue = color.blue >> 8;
572 image = loadImage(rc, file);
573 if (!image) {
574 goto error;
577 switch (type[1]) {
578 case 'h':
579 case 'H':
580 gtype = RHorizontalGradient;
581 twidth = scrWidth;
582 theight = image->height > scrHeight ? scrHeight : image->height;
583 break;
584 case 'V':
585 case 'v':
586 gtype = RVerticalGradient;
587 twidth = image->width > scrWidth ? scrWidth : image->width;
588 theight = scrHeight;
589 break;
590 default:
591 gtype = RDiagonalGradient;
592 twidth = scrWidth;
593 theight = scrHeight;
594 break;
596 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
598 if (!gradient) {
599 wwarning("could not render texture:%s",
600 RMessageForError(RErrorCode));
601 RReleaseImage(gradient);
602 RReleaseImage(image);
603 goto error;
606 tiled = RMakeTiledImage(image, twidth, theight);
607 if (!tiled) {
608 wwarning("could not render texture:%s",
609 RMessageForError(RErrorCode));
610 RReleaseImage(gradient);
611 RReleaseImage(image);
612 goto error;
614 RReleaseImage(image);
616 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
617 RReleaseImage(gradient);
619 if (!RConvertImage(rc, tiled, &pixmap)) {
620 wwarning("could not convert texture:%s",
621 RMessageForError(RErrorCode));
622 RReleaseImage(tiled);
623 goto error;
625 texture->width = tiled->width;
626 texture->height = tiled->height;
628 RReleaseImage(tiled);
630 texture->pixmap = pixmap;
631 } else if (strcasecmp(type, "function")==0) {
632 #ifdef HAVE_DLFCN_H
633 void (*initFunc) (Display*, Colormap);
634 RImage* (*mainFunc) (int, char**, int, int, int);
635 Pixmap pixmap;
636 RImage *image = 0;
637 int success = 0;
638 char *lib, *func, **argv = 0;
639 void *handle = 0;
640 int i, argc;
642 if (count < 3)
643 goto function_cleanup;
645 /* get the library name */
646 GETSTRORGOTO(val, lib, 1, function_cleanup);
648 /* get the function name */
649 GETSTRORGOTO(val, func, 2, function_cleanup);
651 argc = count - 2;
652 argv = (char**)wmalloc(argc * sizeof(char*));
654 /* get the parameters */
655 argv[0] = func;
656 for (i=0; i<argc-1; i++) {
657 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
658 argv[i+1] = wstrdup(tmp);
661 handle = dlopen(lib, RTLD_LAZY);
662 if (!handle) {
663 wwarning("could not find library %s", lib);
664 goto function_cleanup;
667 initFunc = dlsym(handle, "initWindowMaker");
668 if (!initFunc) {
669 wwarning("could not initialize library %s", lib);
670 goto function_cleanup;
672 initFunc(dpy, DefaultColormap(dpy, scr));
674 mainFunc = dlsym(handle, func);
675 if (!mainFunc) {
676 wwarning("could not find function %s::%s", lib, func);
677 goto function_cleanup;
679 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
681 if (!RConvertImage(rc, image, &pixmap)) {
682 wwarning("could not convert texture:%s",
683 RMessageForError(RErrorCode));
684 goto function_cleanup;
686 texture->width = scrWidth;
687 texture->height = scrHeight;
688 texture->pixmap = pixmap;
689 success = 1;
691 function_cleanup:
692 if (argv) {
693 int i;
694 for (i=0; i<argc; i++) {
695 wfree(argv[i]);
698 if (handle) {
699 dlclose(handle);
701 if (image) {
702 RReleaseImage(image);
704 if (!success) {
705 goto error;
707 #else
708 wwarning("function textures not supported");
709 goto error;
710 #endif
711 } else {
712 wwarning("invalid texture type %s", text);
713 goto error;
716 texture->spec = wstrdup(text);
718 return texture;
720 error:
721 if (texture)
722 wfree(texture);
723 if (texarray)
724 WMReleasePropList(texarray);
726 return NULL;
730 void
731 freeTexture(BackgroundTexture *texture)
733 if (texture->solid) {
734 long pixel[1];
736 pixel[0] = texture->color.pixel;
737 /* dont free black/white pixels */
738 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
739 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
740 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
742 if (texture->pixmap) {
743 XFreePixmap(dpy, texture->pixmap);
745 wfree(texture->spec);
746 wfree(texture);
750 void
751 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
752 int workspace, char *texture)
754 BackgroundTexture *newTexture = NULL;
755 int i;
757 /* unset the texture */
758 if (!texture) {
759 if (textures[workspace]!=NULL) {
760 textures[workspace]->refcount--;
762 if (textures[workspace]->refcount == 0)
763 freeTexture(textures[workspace]);
765 textures[workspace] = NULL;
766 return;
769 if (textures[workspace]
770 && strcasecmp(textures[workspace]->spec, texture)==0) {
771 /* texture did not change */
772 return;
775 /* check if the same texture is already created */
776 for (i = 0; i < *maxTextures; i++) {
777 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
778 newTexture = textures[i];
779 break;
783 if (!newTexture) {
784 /* create the texture */
785 newTexture = parseTexture(rc, texture);
787 if (!newTexture)
788 return;
790 if (textures[workspace]!=NULL) {
792 textures[workspace]->refcount--;
794 if (textures[workspace]->refcount == 0)
795 freeTexture(textures[workspace]);
798 newTexture->refcount++;
799 textures[workspace] = newTexture;
801 if (*maxTextures < workspace)
802 *maxTextures = workspace;
807 Pixmap
808 duplicatePixmap(Pixmap pixmap, int width, int height)
810 Display *tmpDpy;
811 Pixmap copyP;
813 /* must open a new display or the RetainPermanent will
814 * leave stuff allocated in RContext unallocated after exit */
815 tmpDpy = XOpenDisplay(display);
816 if (!tmpDpy) {
817 wwarning("could not open display to update background image information");
819 return None;
820 } else {
821 XSync(dpy, False);
823 copyP = XCreatePixmap(tmpDpy, root, width, height,
824 DefaultDepth(tmpDpy, scr));
825 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
826 0, 0, width, height, 0, 0);
827 XSync(tmpDpy, False);
829 XSetCloseDownMode(tmpDpy, RetainPermanent);
830 XCloseDisplay(tmpDpy);
833 return copyP;
837 static int
838 dummyErrorHandler(Display *dpy, XErrorEvent *err)
840 return 0;
843 void
844 setPixmapProperty(Pixmap pixmap)
846 static Atom prop = 0;
847 Atom type;
848 int format;
849 unsigned long length, after;
850 unsigned char *data;
851 int mode;
853 if (!prop) {
854 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
857 XGrabServer(dpy);
859 /* Clear out the old pixmap */
860 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
861 &type, &format, &length, &after, &data);
863 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
864 XSetErrorHandler(dummyErrorHandler);
865 XKillClient(dpy, *((Pixmap *)data));
866 XSync(dpy, False);
867 XSetErrorHandler(NULL);
868 mode = PropModeReplace;
869 } else {
870 mode = PropModeAppend;
872 if (pixmap)
873 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
874 (unsigned char *) &pixmap, 1);
875 else
876 XDeleteProperty(dpy, root, prop);
879 XUngrabServer(dpy);
880 XFlush(dpy);
885 void
886 changeTexture(BackgroundTexture *texture)
888 if (!texture) {
889 return;
892 if (texture->solid) {
893 XSetWindowBackground(dpy, root, texture->color.pixel);
894 } else {
895 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
897 XClearWindow(dpy, root);
899 XSync(dpy, False);
902 Pixmap pixmap;
904 pixmap = duplicatePixmap(texture->pixmap, texture->width,
905 texture->height);
907 setPixmapProperty(pixmap);
913 readmsg(int fd, unsigned char *buffer, int size)
915 int count;
917 count = 0;
918 while (size>0) {
919 count = read(fd, buffer, size);
920 if (count < 0)
921 return -1;
922 size -= count;
923 buffer += count;
924 *buffer = 0;
927 return size;
932 * Message Format:
933 * sizeSntexture_spec - sets the texture for workspace n
934 * sizeCn - change background texture to the one for workspace n
935 * sizePpath - set the pixmap search path
937 * n is 4 bytes
938 * size = 4 bytes for length of the message data
940 void
941 helperLoop(RContext *rc)
943 BackgroundTexture *textures[WORKSPACE_COUNT];
944 int maxTextures = 0;
945 unsigned char buffer[2048], buf[8];
946 int size;
947 int errcount = 4;
949 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
952 while (1) {
953 int workspace;
955 /* get length of message */
956 if (readmsg(0, buffer, 4) < 0) {
957 wsyserror("error reading message from Window Maker");
958 errcount--;
959 if (errcount == 0) {
960 wfatal("quitting");
961 exit(1);
963 continue;
965 memcpy(buf, buffer, 4);
966 buf[4] = 0;
967 size = atoi(buf);
969 /* get message */
970 if (readmsg(0, buffer, size) < 0) {
971 wsyserror("error reading message from Window Maker");
972 errcount--;
973 if (errcount == 0) {
974 wfatal("quitting");
975 exit(1);
977 continue;
979 #ifdef DEBUG
980 printf("RECEIVED %s\n",buffer);
981 #endif
982 if (buffer[0]!='P' && buffer[0]!='K') {
983 memcpy(buf, &buffer[1], 4);
984 buf[4] = 0;
985 workspace = atoi(buf);
986 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
987 wwarning("received message with invalid workspace number %i\n",
988 workspace);
989 continue;
993 switch (buffer[0]) {
994 case 'S':
995 #ifdef DEBUG
996 printf("set texture %s\n", &buffer[5]);
997 #endif
998 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
999 break;
1001 case 'C':
1002 #ifdef DEBUG
1003 printf("change texture %i\n", workspace);
1004 #endif
1005 if (!textures[workspace]) {
1006 changeTexture(textures[0]);
1007 } else {
1008 changeTexture(textures[workspace]);
1010 break;
1012 case 'P':
1013 #ifdef DEBUG
1014 printf("change pixmappath %s\n", &buffer[1]);
1015 #endif
1016 if (PixmapPath)
1017 wfree(PixmapPath);
1018 PixmapPath = wstrdup(&buffer[1]);
1019 break;
1021 case 'U':
1022 #ifdef DEBUG
1023 printf("unset workspace %i\n", workspace);
1024 #endif
1025 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1026 break;
1028 case 'K':
1029 #ifdef DEBUG
1030 printf("exit command\n");
1031 #endif
1032 exit(0);
1034 default:
1035 wwarning("unknown message received");
1036 break;
1042 void
1043 updateDomain(char *domain, char *key, char *texture)
1045 char *program = "wdwrite";
1047 /* here is a mem leak */
1048 system(wstrconcat("wdwrite ",
1049 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1050 : " SmoothWorkspaceBack NO")));
1052 execlp(program, program, domain, key, texture, NULL);
1053 wwarning("warning could not run \"%s\"", program);
1058 char*
1059 globalDefaultsPathForDomain(char *domain)
1061 char path[1024];
1063 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1065 return wstrdup(path);
1069 static WMPropList*
1070 getValueForKey(char *domain, char *keyName)
1072 char *path;
1073 WMPropList *key, *val, *d;
1075 key = WMCreatePLString(keyName);
1077 /* try to find PixmapPath in user defaults */
1078 path = wdefaultspathfordomain(domain);
1079 d = WMReadPropListFromFile(path);
1080 if (!d) {
1081 wwarning("could not open domain file %s", path);
1083 wfree(path);
1085 if (d && !WMIsPLDictionary(d)) {
1086 WMReleasePropList(d);
1087 d = NULL;
1089 if (d) {
1090 val = WMGetFromPLDictionary(d, key);
1091 } else {
1092 val = NULL;
1094 /* try to find PixmapPath in global defaults */
1095 if (!val) {
1096 path = globalDefaultsPathForDomain(domain);
1097 if (!path) {
1098 wwarning("could not locate file for domain %s", domain);
1099 d = NULL;
1100 } else {
1101 d = WMReadPropListFromFile(path);
1102 wfree(path);
1105 if (d && !WMIsPLDictionary(d)) {
1106 WMReleasePropList(d);
1107 d = NULL;
1109 if (d) {
1110 val = WMGetFromPLDictionary(d, key);
1112 } else {
1113 val = NULL;
1117 if (val)
1118 WMRetainPropList(val);
1120 WMReleasePropList(key);
1121 if (d)
1122 WMReleasePropList(d);
1124 return val;
1129 char*
1130 getPixmapPath(char *domain)
1132 WMPropList *val;
1133 char *ptr, *data;
1134 int len, i, count;
1136 val = getValueForKey(domain, "PixmapPath");
1138 if (!val || !WMIsPLArray(val)) {
1139 if (val)
1140 WMReleasePropList(val);
1141 return wstrdup("");
1144 count = WMGetPropListItemCount(val);
1145 len = 0;
1146 for (i=0; i<count; i++) {
1147 WMPropList *v;
1149 v = WMGetFromPLArray(val, i);
1150 if (!v || !WMIsPLString(v)) {
1151 continue;
1153 len += strlen(WMGetFromPLString(v))+1;
1156 ptr = data = wmalloc(len+1);
1157 *ptr = 0;
1159 for (i=0; i<count; i++) {
1160 WMPropList *v;
1162 v = WMGetFromPLArray(val, i);
1163 if (!v || !WMIsPLString(v)) {
1164 continue;
1166 strcpy(ptr, WMGetFromPLString(v));
1168 ptr += strlen(WMGetFromPLString(v));
1169 *ptr = ':';
1170 ptr++;
1172 if (i>0)
1173 ptr--; *(ptr--) = 0;
1175 WMReleasePropList(val);
1177 return data;
1181 char*
1182 getFullPixmapPath(char *file)
1184 char *tmp;
1186 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1187 int bsize = 512;
1188 char *path = wmalloc(bsize);
1190 while (!getcwd(path, bsize)) {
1191 bsize += bsize/2;
1192 path = wrealloc(path, bsize);
1195 tmp = wstrconcat(path, "/");
1196 wfree(path);
1197 path = wstrconcat(tmp, file);
1198 wfree(tmp);
1200 return path;
1203 /* the file is in the PixmapPath */
1204 wfree(tmp);
1206 return wstrdup(file);
1211 void
1212 wAbort()
1214 wfatal("aborting");
1215 exit(1);
1220 void
1221 print_help(char *ProgName)
1223 printf("Usage: %s [options] [image]\n", ProgName);
1224 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1225 puts("");
1226 #define P(m) puts(m)
1227 P(" -display display to use");
1228 P(" -d, --dither dither image");
1229 P(" -m, --match match colors");
1230 P(" -S, --smooth smooth scaled image");
1231 P(" -b, --back-color <color> background color");
1232 P(" -t, --tile tile image");
1233 P(" -e, --center center image");
1234 P(" -s, --scale scale image (default)");
1235 P(" -a, --maxscale scale image and keep aspect ratio");
1236 P(" -u, --update-wmaker update WindowMaker domain database");
1237 P(" -D, --update-domain <domain> update <domain> database");
1238 P(" -c, --colors <cpc> colors per channel to use");
1239 P(" -p, --parse <texture> proplist style texture specification");
1240 P(" -w, --workspace <workspace> update background for the specified workspace");
1241 P(" --version show version of wmsetbg and exit");
1242 P(" --help show this help and exit");
1243 #undef P
1248 void
1249 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1251 WMPropList *array, *val;
1252 char *value;
1253 int j;
1255 val = WMCreatePropListFromDescription(texture);
1256 if (!val) {
1257 wwarning("could not parse texture %s", texture);
1258 return;
1261 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1263 if (!array) {
1264 array = WMCreatePLArray(NULL, NULL);
1267 j = WMGetPropListItemCount(array);
1268 if (workspace >= j) {
1269 WMPropList *empty;
1271 empty = WMCreatePLArray(NULL, NULL);
1273 while (j++ < workspace-1) {
1274 WMAddToPLArray(array, empty);
1276 WMAddToPLArray(array, val);
1277 } else {
1278 WMDeleteFromPLArray(array, workspace);
1279 WMInsertInPLArray(array, workspace, val);
1282 value = WMGetPropListDescription(array, False);
1283 updateDomain(domain, "WorkspaceSpecificBack", value);
1288 main(int argc, char **argv)
1290 int i;
1291 int helperMode = 0;
1292 RContext *rc;
1293 RContextAttributes rattr;
1294 char *style = "spixmap";
1295 char *back_color = "gray20";
1296 char *image_name = NULL;
1297 char *domain = "WindowMaker";
1298 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1299 char *texture = NULL;
1300 int workspace = -1;
1302 signal(SIGINT, SIG_DFL);
1303 signal(SIGTERM, SIG_DFL);
1304 signal(SIGQUIT, SIG_DFL);
1305 signal(SIGSEGV, SIG_DFL);
1306 signal(SIGBUS, SIG_DFL);
1307 signal(SIGFPE, SIG_DFL);
1308 signal(SIGABRT, SIG_DFL);
1309 signal(SIGHUP, SIG_DFL);
1310 signal(SIGPIPE, SIG_DFL);
1311 signal(SIGCHLD, SIG_DFL);
1313 WMInitializeApplication("wmsetbg", &argc, argv);
1315 for (i=1; i<argc; i++) {
1316 if (strcmp(argv[i], "-helper")==0) {
1317 helperMode = 1;
1318 } else if (strcmp(argv[i], "-display")==0) {
1319 i++;
1320 if (i>=argc) {
1321 wfatal("too few arguments for %s\n", argv[i-1]);
1322 exit(1);
1324 display = argv[i];
1325 } else if (strcmp(argv[i], "-s")==0
1326 || strcmp(argv[i], "--scale")==0) {
1327 style = "spixmap";
1328 } else if (strcmp(argv[i], "-t")==0
1329 || strcmp(argv[i], "--tile")==0) {
1330 style = "tpixmap";
1331 } else if (strcmp(argv[i], "-e")==0
1332 || strcmp(argv[i], "--center")==0) {
1333 style = "cpixmap";
1334 } else if (strcmp(argv[i], "-a")==0
1335 || strcmp(argv[i], "--maxscale")==0) {
1336 style = "mpixmap";
1337 } else if (strcmp(argv[i], "-d")==0
1338 || strcmp(argv[i], "--dither")==0) {
1339 render_mode = RDitheredRendering;
1340 obey_user++;
1341 } else if (strcmp(argv[i], "-m")==0
1342 || strcmp(argv[i], "--match")==0) {
1343 render_mode = RBestMatchRendering;
1344 obey_user++;
1345 } else if (strcmp(argv[i], "-S")==0
1346 || strcmp(argv[i], "--smooth")==0) {
1347 smooth = True;
1348 } else if (strcmp(argv[i], "-u")==0
1349 || strcmp(argv[i], "--update-wmaker")==0) {
1350 update++;
1351 } else if (strcmp(argv[i], "-D")==0
1352 || strcmp(argv[i], "--update-domain")==0) {
1353 update++;
1354 i++;
1355 if (i>=argc) {
1356 wfatal("too few arguments for %s\n", argv[i-1]);
1357 exit(1);
1359 domain = wstrdup(argv[i]);
1360 } else if (strcmp(argv[i], "-c")==0
1361 || strcmp(argv[i], "--colors")==0) {
1362 i++;
1363 if (i>=argc) {
1364 wfatal("too few arguments for %s\n", argv[i-1]);
1365 exit(1);
1367 if (sscanf(argv[i], "%i", &cpc)!=1) {
1368 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1369 exit(1);
1371 } else if (strcmp(argv[i], "-b")==0
1372 || strcmp(argv[i], "--back-color")==0) {
1373 i++;
1374 if (i>=argc) {
1375 wfatal("too few arguments for %s\n", argv[i-1]);
1376 exit(1);
1378 back_color = argv[i];
1379 } else if (strcmp(argv[i], "-p")==0
1380 || strcmp(argv[i], "--parse")==0) {
1381 i++;
1382 if (i>=argc) {
1383 wfatal("too few arguments for %s\n", argv[i-1]);
1384 exit(1);
1386 texture = argv[i];
1387 } else if (strcmp(argv[i], "-w")==0
1388 || strcmp(argv[i], "--workspace")==0) {
1389 i++;
1390 if (i>=argc) {
1391 wfatal("too few arguments for %s\n", argv[i-1]);
1392 exit(1);
1394 if (sscanf(argv[i], "%i", &workspace)!=1) {
1395 wfatal("bad value for workspace number: \"%s\"",
1396 argv[i]);
1397 exit(1);
1399 } else if (strcmp(argv[i], "--version")==0) {
1401 printf(PROG_VERSION);
1402 exit(0);
1404 } else if (strcmp(argv[i], "--help")==0) {
1405 print_help(argv[0]);
1406 exit(0);
1407 } else if (argv[i][0] != '-') {
1408 image_name = argv[i];
1409 } else {
1410 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1411 printf("Try '%s --help' for more information\n", argv[0]);
1412 exit(1);
1415 if (!image_name && !texture && !helperMode) {
1416 printf("%s: you must specify a image file name or a texture\n",
1417 argv[0]);
1418 printf("Try '%s --help' for more information\n", argv[0]);
1419 exit(1);
1423 PixmapPath = getPixmapPath(domain);
1424 if (!smooth) {
1425 WMPropList *val;
1426 #if 0 /* some problem with Alpha... TODO: check if its right */
1427 val = WMGetFromPLDictionary(domain,
1428 WMCreatePLString("SmoothWorkspaceBack"));
1429 #else
1430 val = getValueForKey(domain, "SmoothWorkspaceBack");
1431 #endif
1433 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1434 smooth = True;
1437 dpy = XOpenDisplay(display);
1438 if (!dpy) {
1439 wfatal("could not open display");
1440 exit(1);
1442 #if 1
1443 XSynchronize(dpy, 1);
1444 #endif
1446 root = DefaultRootWindow(dpy);
1448 scr = DefaultScreen(dpy);
1450 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1451 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1452 scrX = scrY = 0;
1454 #ifdef XINERAMA
1455 xine_screens = XineramaQueryScreens(dpy, &xine_count);
1456 #endif
1459 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1460 render_mode = RDitheredRendering;
1462 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1463 | RC_StandardColormap | RC_DefaultVisual;
1464 rattr.render_mode = render_mode;
1465 rattr.colors_per_channel = cpc;
1466 rattr.standard_colormap_mode = RCreateStdColormap;
1468 rc = RCreateContext(dpy, scr, &rattr);
1470 if (!rc) {
1471 rattr.standard_colormap_mode = RIgnoreStdColormap;
1472 rc = RCreateContext(dpy, scr, &rattr);
1475 if (!rc) {
1476 wfatal("could not initialize wrlib: %s",
1477 RMessageForError(RErrorCode));
1478 exit(1);
1481 if (helperMode) {
1482 /* lower priority, so that it wont use all the CPU */
1483 nice(15);
1485 helperLoop(rc);
1486 } else {
1487 BackgroundTexture *tex;
1488 char buffer[4098];
1490 if (!texture) {
1491 char *image_path = getFullPixmapPath(image_name);
1493 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1494 wfree(image_path);
1495 texture = (char*)buffer;
1498 if (update && workspace < 0) {
1499 updateDomain(domain, "WorkspaceBack", texture);
1502 tex = parseTexture(rc, texture);
1503 if (!tex)
1504 exit(1);
1506 if (workspace<0)
1507 changeTexture(tex);
1508 else {
1509 /* always update domain */
1510 changeTextureForWorkspace(domain, texture, workspace);
1514 return 0;