- Fixed crashing bug in menu.c
[wmaker-crm.git] / util / wmsetbg.c
blob62b9b2702ebbcbb6af3ac59c082e7e36804fc6f0
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);
496 break;
497 case 'S':
498 case 'M':
499 #if 0
500 if (toupper(type[0])=='S') {
501 w = scrWidth;
502 h = scrHeight;
503 } else {
504 if (iwidth*scrHeight > iheight*scrWidth) {
505 w = scrWidth;
506 h = (scrWidth*iheight)/iwidth;
507 } else {
508 h = scrHeight;
509 w = (scrHeight*iwidth)/iheight;
512 if (w != image->width || h != image->height) {
513 RImage *simage;
515 if (smooth)
516 simage = RSmoothScaleImage(image, w, h);
517 else
518 simage = RScaleImage(image, w, h);
519 if (!simage) {
520 wwarning("could not scale image:%s",
521 RMessageForError(RErrorCode));
522 RReleaseImage(image);
523 goto error;
525 RReleaseImage(image);
526 image = simage;
528 iwidth = image->width;
529 iheight = image->height;
531 /* fall through */
532 case 'C':
534 Pixmap cpixmap;
536 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
537 wwarning("could not convert texture:%s",
538 RMessageForError(RErrorCode));
539 RReleaseImage(image);
540 goto error;
543 if (iwidth != scrWidth || iheight != scrHeight) {
544 int x, y, sx, sy, w, h;
546 cpixmap = XCreatePixmap(dpy, root, scrWidth, scrHeight,
547 DefaultDepth(dpy, scr));
549 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
550 XFillRectangle(dpy, cpixmap, DefaultGC(dpy, scr),
551 0, 0, scrWidth, scrHeight);
553 if (iheight < scrHeight) {
554 h = iheight;
555 y = (scrHeight - h)/2;
556 sy = 0;
557 } else {
558 sy = (iheight - scrHeight)/2;
559 y = 0;
560 h = scrHeight;
562 if (iwidth < scrWidth) {
563 w = iwidth;
564 x = (scrWidth - w)/2;
565 sx = 0;
566 } else {
567 sx = (iwidth - scrWidth)/2;
568 x = 0;
569 w = scrWidth;
572 XCopyArea(dpy, pixmap, cpixmap, DefaultGC(dpy, scr),
573 sx, sy, w, h, x, y);
574 XFreePixmap(dpy, pixmap);
575 pixmap = cpixmap;
577 if (image)
578 RReleaseImage(image);
580 texture->width = scrWidth;
581 texture->height = scrHeight;
583 break;
584 #else
585 case 'C':
587 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
588 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
590 texture->pixmap = tpixmap;
591 texture->color = color;
592 texture->width = scrWidth;
593 texture->height = scrHeight;
595 #ifdef XINERAMA
596 if (xine_count) {
597 int i;
598 for (i=0; i<xine_count; ++i) {
599 applyImage(rc, texture, image, type[0],
600 xine_screens[i].x_org, xine_screens[i].y_org,
601 xine_screens[i].width, xine_screens[i].height);
603 } else {
604 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
606 #else
607 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
608 #endif
609 RReleaseImage(image);
611 break;
612 #endif
615 #if 0
616 texture->pixmap = pixmap;
617 texture->color = color;
618 #endif
619 } else if (strcasecmp(type, "thgradient")==0
620 || strcasecmp(type, "tvgradient")==0
621 || strcasecmp(type, "tdgradient")==0) {
622 XColor color;
623 RColor color1, color2;
624 RImage *image;
625 RImage *gradient;
626 RImage *tiled;
627 Pixmap pixmap;
628 int opaq;
629 char *file;
630 int gtype;
631 int twidth, theight;
633 GETSTRORGOTO(val, file, 1, error);
635 GETSTRORGOTO(val, tmp, 2, error);
637 opaq = atoi(tmp);
639 GETSTRORGOTO(val, tmp, 3, error);
641 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
642 wwarning("could not parse color %s in texture %s", tmp, text);
643 goto error;
646 color1.red = color.red >> 8;
647 color1.green = color.green >> 8;
648 color1.blue = color.blue >> 8;
650 GETSTRORGOTO(val, tmp, 4, error);
652 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
653 wwarning("could not parse color %s in texture %s", tmp, text);
654 goto error;
657 color2.red = color.red >> 8;
658 color2.green = color.green >> 8;
659 color2.blue = color.blue >> 8;
661 image = loadImage(rc, file);
662 if (!image) {
663 goto error;
666 switch (type[1]) {
667 case 'h':
668 case 'H':
669 gtype = RHorizontalGradient;
670 twidth = scrWidth;
671 theight = image->height > scrHeight ? scrHeight : image->height;
672 break;
673 case 'V':
674 case 'v':
675 gtype = RVerticalGradient;
676 twidth = image->width > scrWidth ? scrWidth : image->width;
677 theight = scrHeight;
678 break;
679 default:
680 gtype = RDiagonalGradient;
681 twidth = scrWidth;
682 theight = scrHeight;
683 break;
685 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
687 if (!gradient) {
688 wwarning("could not render texture:%s",
689 RMessageForError(RErrorCode));
690 RReleaseImage(gradient);
691 RReleaseImage(image);
692 goto error;
695 tiled = RMakeTiledImage(image, twidth, theight);
696 if (!tiled) {
697 wwarning("could not render texture:%s",
698 RMessageForError(RErrorCode));
699 RReleaseImage(gradient);
700 RReleaseImage(image);
701 goto error;
703 RReleaseImage(image);
705 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
706 RReleaseImage(gradient);
708 if (!RConvertImage(rc, tiled, &pixmap)) {
709 wwarning("could not convert texture:%s",
710 RMessageForError(RErrorCode));
711 RReleaseImage(tiled);
712 goto error;
714 texture->width = tiled->width;
715 texture->height = tiled->height;
717 RReleaseImage(tiled);
719 texture->pixmap = pixmap;
720 } else if (strcasecmp(type, "function")==0) {
721 #ifdef HAVE_DLFCN_H
722 void (*initFunc) (Display*, Colormap);
723 RImage* (*mainFunc) (int, char**, int, int, int);
724 Pixmap pixmap;
725 RImage *image = 0;
726 int success = 0;
727 char *lib, *func, **argv = 0;
728 void *handle = 0;
729 int i, argc;
731 if (count < 3)
732 goto function_cleanup;
734 /* get the library name */
735 GETSTRORGOTO(val, lib, 1, function_cleanup);
737 /* get the function name */
738 GETSTRORGOTO(val, func, 2, function_cleanup);
740 argc = count - 2;
741 argv = (char**)wmalloc(argc * sizeof(char*));
743 /* get the parameters */
744 argv[0] = func;
745 for (i=0; i<argc-1; i++) {
746 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
747 argv[i+1] = wstrdup(tmp);
750 handle = dlopen(lib, RTLD_LAZY);
751 if (!handle) {
752 wwarning("could not find library %s", lib);
753 goto function_cleanup;
756 initFunc = dlsym(handle, "initWindowMaker");
757 if (!initFunc) {
758 wwarning("could not initialize library %s", lib);
759 goto function_cleanup;
761 initFunc(dpy, DefaultColormap(dpy, scr));
763 mainFunc = dlsym(handle, func);
764 if (!mainFunc) {
765 wwarning("could not find function %s::%s", lib, func);
766 goto function_cleanup;
768 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
770 if (!RConvertImage(rc, image, &pixmap)) {
771 wwarning("could not convert texture:%s",
772 RMessageForError(RErrorCode));
773 goto function_cleanup;
775 texture->width = scrWidth;
776 texture->height = scrHeight;
777 texture->pixmap = pixmap;
778 success = 1;
780 function_cleanup:
781 if (argv) {
782 int i;
783 for (i=0; i<argc; i++) {
784 wfree(argv[i]);
787 if (handle) {
788 dlclose(handle);
790 if (image) {
791 RReleaseImage(image);
793 if (!success) {
794 goto error;
796 #else
797 wwarning("function textures not supported");
798 goto error;
799 #endif
800 } else {
801 wwarning("invalid texture type %s", text);
802 goto error;
805 texture->spec = wstrdup(text);
807 return texture;
809 error:
810 if (texture)
811 wfree(texture);
812 if (texarray)
813 WMReleasePropList(texarray);
815 return NULL;
819 void
820 freeTexture(BackgroundTexture *texture)
822 if (texture->solid) {
823 long pixel[1];
825 pixel[0] = texture->color.pixel;
826 /* dont free black/white pixels */
827 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
828 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
829 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
831 if (texture->pixmap) {
832 XFreePixmap(dpy, texture->pixmap);
834 wfree(texture->spec);
835 wfree(texture);
839 void
840 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
841 int workspace, char *texture)
843 BackgroundTexture *newTexture = NULL;
844 int i;
846 /* unset the texture */
847 if (!texture) {
848 if (textures[workspace]!=NULL) {
849 textures[workspace]->refcount--;
851 if (textures[workspace]->refcount == 0)
852 freeTexture(textures[workspace]);
854 textures[workspace] = NULL;
855 return;
858 if (textures[workspace]
859 && strcasecmp(textures[workspace]->spec, texture)==0) {
860 /* texture did not change */
861 return;
864 /* check if the same texture is already created */
865 for (i = 0; i < *maxTextures; i++) {
866 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
867 newTexture = textures[i];
868 break;
872 if (!newTexture) {
873 /* create the texture */
874 newTexture = parseTexture(rc, texture);
876 if (!newTexture)
877 return;
879 if (textures[workspace]!=NULL) {
881 textures[workspace]->refcount--;
883 if (textures[workspace]->refcount == 0)
884 freeTexture(textures[workspace]);
887 newTexture->refcount++;
888 textures[workspace] = newTexture;
890 if (*maxTextures < workspace)
891 *maxTextures = workspace;
896 Pixmap
897 duplicatePixmap(Pixmap pixmap, int width, int height)
899 Display *tmpDpy;
900 Pixmap copyP;
902 /* must open a new display or the RetainPermanent will
903 * leave stuff allocated in RContext unallocated after exit */
904 tmpDpy = XOpenDisplay(display);
905 if (!tmpDpy) {
906 wwarning("could not open display to update background image information");
908 return None;
909 } else {
910 XSync(dpy, False);
912 copyP = XCreatePixmap(tmpDpy, root, width, height,
913 DefaultDepth(tmpDpy, scr));
914 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
915 0, 0, width, height, 0, 0);
916 XSync(tmpDpy, False);
918 XSetCloseDownMode(tmpDpy, RetainPermanent);
919 XCloseDisplay(tmpDpy);
922 return copyP;
926 static int
927 dummyErrorHandler(Display *dpy, XErrorEvent *err)
929 return 0;
932 void
933 setPixmapProperty(Pixmap pixmap)
935 static Atom prop = 0;
936 Atom type;
937 int format;
938 unsigned long length, after;
939 unsigned char *data;
940 int mode;
942 if (!prop) {
943 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
946 XGrabServer(dpy);
948 /* Clear out the old pixmap */
949 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
950 &type, &format, &length, &after, &data);
952 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
953 XSetErrorHandler(dummyErrorHandler);
954 XKillClient(dpy, *((Pixmap *)data));
955 XSync(dpy, False);
956 XSetErrorHandler(NULL);
957 mode = PropModeReplace;
958 } else {
959 mode = PropModeAppend;
961 if (pixmap)
962 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
963 (unsigned char *) &pixmap, 1);
964 else
965 XDeleteProperty(dpy, root, prop);
968 XUngrabServer(dpy);
969 XFlush(dpy);
974 void
975 changeTexture(BackgroundTexture *texture)
977 if (!texture) {
978 return;
981 if (texture->solid) {
982 XSetWindowBackground(dpy, root, texture->color.pixel);
983 } else {
984 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
986 XClearWindow(dpy, root);
988 XSync(dpy, False);
991 Pixmap pixmap;
993 pixmap = duplicatePixmap(texture->pixmap, texture->width,
994 texture->height);
996 setPixmapProperty(pixmap);
1002 readmsg(int fd, unsigned char *buffer, int size)
1004 int count;
1006 count = 0;
1007 while (size>0) {
1008 count = read(fd, buffer, size);
1009 if (count < 0)
1010 return -1;
1011 size -= count;
1012 buffer += count;
1013 *buffer = 0;
1016 return size;
1021 * Message Format:
1022 * sizeSntexture_spec - sets the texture for workspace n
1023 * sizeCn - change background texture to the one for workspace n
1024 * sizePpath - set the pixmap search path
1026 * n is 4 bytes
1027 * size = 4 bytes for length of the message data
1029 void
1030 helperLoop(RContext *rc)
1032 BackgroundTexture *textures[WORKSPACE_COUNT];
1033 int maxTextures = 0;
1034 unsigned char buffer[2048], buf[8];
1035 int size;
1036 int errcount = 4;
1038 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
1041 while (1) {
1042 int workspace;
1044 /* get length of message */
1045 if (readmsg(0, buffer, 4) < 0) {
1046 wsyserror("error reading message from Window Maker");
1047 errcount--;
1048 if (errcount == 0) {
1049 wfatal("quitting");
1050 exit(1);
1052 continue;
1054 memcpy(buf, buffer, 4);
1055 buf[4] = 0;
1056 size = atoi(buf);
1058 /* get message */
1059 if (readmsg(0, buffer, size) < 0) {
1060 wsyserror("error reading message from Window Maker");
1061 errcount--;
1062 if (errcount == 0) {
1063 wfatal("quitting");
1064 exit(1);
1066 continue;
1068 #ifdef DEBUG
1069 printf("RECEIVED %s\n",buffer);
1070 #endif
1071 if (buffer[0]!='P' && buffer[0]!='K') {
1072 memcpy(buf, &buffer[1], 4);
1073 buf[4] = 0;
1074 workspace = atoi(buf);
1075 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
1076 wwarning("received message with invalid workspace number %i\n",
1077 workspace);
1078 continue;
1082 switch (buffer[0]) {
1083 case 'S':
1084 #ifdef DEBUG
1085 printf("set texture %s\n", &buffer[5]);
1086 #endif
1087 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1088 break;
1090 case 'C':
1091 #ifdef DEBUG
1092 printf("change texture %i\n", workspace);
1093 #endif
1094 if (!textures[workspace]) {
1095 changeTexture(textures[0]);
1096 } else {
1097 changeTexture(textures[workspace]);
1099 break;
1101 case 'P':
1102 #ifdef DEBUG
1103 printf("change pixmappath %s\n", &buffer[1]);
1104 #endif
1105 if (PixmapPath)
1106 wfree(PixmapPath);
1107 PixmapPath = wstrdup(&buffer[1]);
1108 break;
1110 case 'U':
1111 #ifdef DEBUG
1112 printf("unset workspace %i\n", workspace);
1113 #endif
1114 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1115 break;
1117 case 'K':
1118 #ifdef DEBUG
1119 printf("exit command\n");
1120 #endif
1121 exit(0);
1123 default:
1124 wwarning("unknown message received");
1125 break;
1131 void
1132 updateDomain(char *domain, char *key, char *texture)
1134 char *program = "wdwrite";
1136 /* here is a mem leak */
1137 system(wstrconcat("wdwrite ",
1138 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1139 : " SmoothWorkspaceBack NO")));
1141 execlp(program, program, domain, key, texture, NULL);
1142 wwarning("warning could not run \"%s\"", program);
1147 char*
1148 globalDefaultsPathForDomain(char *domain)
1150 char path[1024];
1152 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1154 return wstrdup(path);
1158 static WMPropList*
1159 getValueForKey(char *domain, char *keyName)
1161 char *path;
1162 WMPropList *key, *val, *d;
1164 key = WMCreatePLString(keyName);
1166 /* try to find PixmapPath in user defaults */
1167 path = wdefaultspathfordomain(domain);
1168 d = WMReadPropListFromFile(path);
1169 if (!d) {
1170 wwarning("could not open domain file %s", path);
1172 wfree(path);
1174 if (d && !WMIsPLDictionary(d)) {
1175 WMReleasePropList(d);
1176 d = NULL;
1178 if (d) {
1179 val = WMGetFromPLDictionary(d, key);
1180 } else {
1181 val = NULL;
1183 /* try to find PixmapPath in global defaults */
1184 if (!val) {
1185 path = globalDefaultsPathForDomain(domain);
1186 if (!path) {
1187 wwarning("could not locate file for domain %s", domain);
1188 d = NULL;
1189 } else {
1190 d = WMReadPropListFromFile(path);
1191 wfree(path);
1194 if (d && !WMIsPLDictionary(d)) {
1195 WMReleasePropList(d);
1196 d = NULL;
1198 if (d) {
1199 val = WMGetFromPLDictionary(d, key);
1201 } else {
1202 val = NULL;
1206 if (val)
1207 WMRetainPropList(val);
1209 WMReleasePropList(key);
1210 if (d)
1211 WMReleasePropList(d);
1213 return val;
1218 char*
1219 getPixmapPath(char *domain)
1221 WMPropList *val;
1222 char *ptr, *data;
1223 int len, i, count;
1225 val = getValueForKey(domain, "PixmapPath");
1227 if (!val || !WMIsPLArray(val)) {
1228 if (val)
1229 WMReleasePropList(val);
1230 return wstrdup("");
1233 count = WMGetPropListItemCount(val);
1234 len = 0;
1235 for (i=0; i<count; i++) {
1236 WMPropList *v;
1238 v = WMGetFromPLArray(val, i);
1239 if (!v || !WMIsPLString(v)) {
1240 continue;
1242 len += strlen(WMGetFromPLString(v))+1;
1245 ptr = data = wmalloc(len+1);
1246 *ptr = 0;
1248 for (i=0; i<count; i++) {
1249 WMPropList *v;
1251 v = WMGetFromPLArray(val, i);
1252 if (!v || !WMIsPLString(v)) {
1253 continue;
1255 strcpy(ptr, WMGetFromPLString(v));
1257 ptr += strlen(WMGetFromPLString(v));
1258 *ptr = ':';
1259 ptr++;
1261 if (i>0)
1262 ptr--; *(ptr--) = 0;
1264 WMReleasePropList(val);
1266 return data;
1270 char*
1271 getFullPixmapPath(char *file)
1273 char *tmp;
1275 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1276 int bsize = 512;
1277 char *path = wmalloc(bsize);
1279 while (!getcwd(path, bsize)) {
1280 bsize += bsize/2;
1281 path = wrealloc(path, bsize);
1284 tmp = wstrconcat(path, "/");
1285 wfree(path);
1286 path = wstrconcat(tmp, file);
1287 wfree(tmp);
1289 return path;
1292 /* the file is in the PixmapPath */
1293 wfree(tmp);
1295 return wstrdup(file);
1300 void
1301 wAbort()
1303 wfatal("aborting");
1304 exit(1);
1309 void
1310 print_help(char *ProgName)
1312 printf("Usage: %s [options] [image]\n", ProgName);
1313 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1314 puts("");
1315 #define P(m) puts(m)
1316 P(" -display display to use");
1317 P(" -d, --dither dither image");
1318 P(" -m, --match match colors");
1319 P(" -S, --smooth smooth scaled image");
1320 P(" -b, --back-color <color> background color");
1321 P(" -t, --tile tile image");
1322 P(" -e, --center center image");
1323 P(" -s, --scale scale image (default)");
1324 P(" -a, --maxscale scale image and keep aspect ratio");
1325 P(" -u, --update-wmaker update WindowMaker domain database");
1326 P(" -D, --update-domain <domain> update <domain> database");
1327 P(" -c, --colors <cpc> colors per channel to use");
1328 P(" -p, --parse <texture> proplist style texture specification");
1329 P(" -w, --workspace <workspace> update background for the specified workspace");
1330 P(" --version show version of wmsetbg and exit");
1331 P(" --help show this help and exit");
1332 #undef P
1337 void
1338 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1340 WMPropList *array, *val;
1341 char *value;
1342 int j;
1344 val = WMCreatePropListFromDescription(texture);
1345 if (!val) {
1346 wwarning("could not parse texture %s", texture);
1347 return;
1350 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1352 if (!array) {
1353 array = WMCreatePLArray(NULL, NULL);
1356 j = WMGetPropListItemCount(array);
1357 if (workspace >= j) {
1358 WMPropList *empty;
1360 empty = WMCreatePLArray(NULL, NULL);
1362 while (j++ < workspace-1) {
1363 WMAddToPLArray(array, empty);
1365 WMAddToPLArray(array, val);
1366 } else {
1367 WMDeleteFromPLArray(array, workspace);
1368 WMInsertInPLArray(array, workspace, val);
1371 value = WMGetPropListDescription(array, False);
1372 updateDomain(domain, "WorkspaceSpecificBack", value);
1377 main(int argc, char **argv)
1379 int i;
1380 int helperMode = 0;
1381 RContext *rc;
1382 RContextAttributes rattr;
1383 char *style = "spixmap";
1384 char *back_color = "gray20";
1385 char *image_name = NULL;
1386 char *domain = "WindowMaker";
1387 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1388 char *texture = NULL;
1389 int workspace = -1;
1391 signal(SIGINT, SIG_DFL);
1392 signal(SIGTERM, SIG_DFL);
1393 signal(SIGQUIT, SIG_DFL);
1394 signal(SIGSEGV, SIG_DFL);
1395 signal(SIGBUS, SIG_DFL);
1396 signal(SIGFPE, SIG_DFL);
1397 signal(SIGABRT, SIG_DFL);
1398 signal(SIGHUP, SIG_DFL);
1399 signal(SIGPIPE, SIG_DFL);
1400 signal(SIGCHLD, SIG_DFL);
1402 WMInitializeApplication("wmsetbg", &argc, argv);
1404 for (i=1; i<argc; i++) {
1405 if (strcmp(argv[i], "-helper")==0) {
1406 helperMode = 1;
1407 } else if (strcmp(argv[i], "-display")==0) {
1408 i++;
1409 if (i>=argc) {
1410 wfatal("too few arguments for %s\n", argv[i-1]);
1411 exit(1);
1413 display = argv[i];
1414 } else if (strcmp(argv[i], "-s")==0
1415 || strcmp(argv[i], "--scale")==0) {
1416 style = "spixmap";
1417 } else if (strcmp(argv[i], "-t")==0
1418 || strcmp(argv[i], "--tile")==0) {
1419 style = "tpixmap";
1420 } else if (strcmp(argv[i], "-e")==0
1421 || strcmp(argv[i], "--center")==0) {
1422 style = "cpixmap";
1423 } else if (strcmp(argv[i], "-a")==0
1424 || strcmp(argv[i], "--maxscale")==0) {
1425 style = "mpixmap";
1426 } else if (strcmp(argv[i], "-d")==0
1427 || strcmp(argv[i], "--dither")==0) {
1428 render_mode = RDitheredRendering;
1429 obey_user++;
1430 } else if (strcmp(argv[i], "-m")==0
1431 || strcmp(argv[i], "--match")==0) {
1432 render_mode = RBestMatchRendering;
1433 obey_user++;
1434 } else if (strcmp(argv[i], "-S")==0
1435 || strcmp(argv[i], "--smooth")==0) {
1436 smooth = True;
1437 } else if (strcmp(argv[i], "-u")==0
1438 || strcmp(argv[i], "--update-wmaker")==0) {
1439 update++;
1440 } else if (strcmp(argv[i], "-D")==0
1441 || strcmp(argv[i], "--update-domain")==0) {
1442 update++;
1443 i++;
1444 if (i>=argc) {
1445 wfatal("too few arguments for %s\n", argv[i-1]);
1446 exit(1);
1448 domain = wstrdup(argv[i]);
1449 } else if (strcmp(argv[i], "-c")==0
1450 || strcmp(argv[i], "--colors")==0) {
1451 i++;
1452 if (i>=argc) {
1453 wfatal("too few arguments for %s\n", argv[i-1]);
1454 exit(1);
1456 if (sscanf(argv[i], "%i", &cpc)!=1) {
1457 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1458 exit(1);
1460 } else if (strcmp(argv[i], "-b")==0
1461 || strcmp(argv[i], "--back-color")==0) {
1462 i++;
1463 if (i>=argc) {
1464 wfatal("too few arguments for %s\n", argv[i-1]);
1465 exit(1);
1467 back_color = argv[i];
1468 } else if (strcmp(argv[i], "-p")==0
1469 || strcmp(argv[i], "--parse")==0) {
1470 i++;
1471 if (i>=argc) {
1472 wfatal("too few arguments for %s\n", argv[i-1]);
1473 exit(1);
1475 texture = argv[i];
1476 } else if (strcmp(argv[i], "-w")==0
1477 || strcmp(argv[i], "--workspace")==0) {
1478 i++;
1479 if (i>=argc) {
1480 wfatal("too few arguments for %s\n", argv[i-1]);
1481 exit(1);
1483 if (sscanf(argv[i], "%i", &workspace)!=1) {
1484 wfatal("bad value for workspace number: \"%s\"",
1485 argv[i]);
1486 exit(1);
1488 } else if (strcmp(argv[i], "--version")==0) {
1490 printf(PROG_VERSION);
1491 exit(0);
1493 } else if (strcmp(argv[i], "--help")==0) {
1494 print_help(argv[0]);
1495 exit(0);
1496 } else if (argv[i][0] != '-') {
1497 image_name = argv[i];
1498 } else {
1499 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1500 printf("Try '%s --help' for more information\n", argv[0]);
1501 exit(1);
1504 if (!image_name && !texture && !helperMode) {
1505 printf("%s: you must specify a image file name or a texture\n",
1506 argv[0]);
1507 printf("Try '%s --help' for more information\n", argv[0]);
1508 exit(1);
1512 PixmapPath = getPixmapPath(domain);
1513 if (!smooth) {
1514 WMPropList *val;
1515 #if 0 /* some problem with Alpha... TODO: check if its right */
1516 val = WMGetFromPLDictionary(domain,
1517 WMCreatePLString("SmoothWorkspaceBack"));
1518 #else
1519 val = getValueForKey(domain, "SmoothWorkspaceBack");
1520 #endif
1522 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1523 smooth = True;
1526 dpy = XOpenDisplay(display);
1527 if (!dpy) {
1528 wfatal("could not open display");
1529 exit(1);
1531 #if 0
1532 XSynchronize(dpy, 1);
1533 #endif
1535 root = DefaultRootWindow(dpy);
1537 scr = DefaultScreen(dpy);
1539 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1540 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1541 scrX = scrY = 0;
1543 #ifdef XINERAMA
1544 xine_screens = XineramaQueryScreens(dpy, &xine_count);
1545 #endif
1548 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1549 render_mode = RDitheredRendering;
1551 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1552 | RC_StandardColormap | RC_DefaultVisual;
1553 rattr.render_mode = render_mode;
1554 rattr.colors_per_channel = cpc;
1555 rattr.standard_colormap_mode = RCreateStdColormap;
1557 rc = RCreateContext(dpy, scr, &rattr);
1559 if (!rc) {
1560 rattr.standard_colormap_mode = RIgnoreStdColormap;
1561 rc = RCreateContext(dpy, scr, &rattr);
1564 if (!rc) {
1565 wfatal("could not initialize wrlib: %s",
1566 RMessageForError(RErrorCode));
1567 exit(1);
1570 if (helperMode) {
1571 /* lower priority, so that it wont use all the CPU */
1572 nice(15);
1574 helperLoop(rc);
1575 } else {
1576 BackgroundTexture *tex;
1577 char buffer[4098];
1579 if (!texture) {
1580 char *image_path = getFullPixmapPath(image_name);
1582 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1583 wfree(image_path);
1584 texture = (char*)buffer;
1587 if (update && workspace < 0) {
1588 updateDomain(domain, "WorkspaceBack", texture);
1591 tex = parseTexture(rc, texture);
1592 if (!tex)
1593 exit(1);
1595 if (workspace<0)
1596 changeTexture(tex);
1597 else {
1598 /* always update domain */
1599 changeTextureForWorkspace(domain, texture, workspace);
1603 return 0;