- Fixed sloppy focus bug (Pawel S. Veselov <pv76716@druid.SFBay.Sun.COM>)
[wmaker-crm.git] / util / wmsetbg.c
blob7e0ad594da6a4550a45a2b264b6dc87a59765435
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-2002 Alfredo K. Kojima
7 * Copyright (c) 1998-2002 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
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <sys/types.h>
39 #include <ctype.h>
41 #include "../src/config.h"
43 #ifdef XINERAMA
44 #include <X11/extensions/Xinerama.h>
45 #endif
47 #ifdef HAVE_DLFCN_H
48 #include <dlfcn.h>
49 #endif
51 #include "../src/wconfig.h"
53 #include <WINGs/WINGs.h>
54 #include <wraster.h>
57 #define PROG_VERSION "wmsetbg (Window Maker) 2.7"
60 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
63 Display *dpy;
64 char *display = "";
65 Window root;
66 int scr;
67 int scrWidth;
68 int scrHeight;
69 int scrX, scrY;
71 #ifdef XINERAMA
72 XineramaScreenInfo *xine_screens;
73 int xine_count;
74 #endif
76 Bool smooth = False;
79 Pixmap CurrentPixmap = None;
80 char *PixmapPath = NULL;
83 extern Pixmap LoadJPEG(RContext *rc, char *file_name, int *width, int *height);
86 typedef struct BackgroundTexture {
87 int refcount;
89 int solid;
91 char *spec;
93 XColor color;
94 Pixmap pixmap; /* for all textures, including solid */
95 int width; /* size of the pixmap */
96 int height;
97 } BackgroundTexture;
101 RImage*
102 loadImage(RContext *rc, char *file)
104 char *path;
105 RImage *image;
107 if (access(file, F_OK)!=0) {
108 path = wfindfile(PixmapPath, file);
109 if (!path) {
110 wwarning("%s:could not find image file used in texture", file);
111 return NULL;
113 } else {
114 path = wstrdup(file);
117 image = RLoadImage(rc, path, 0);
118 if (!image) {
119 wwarning("%s:could not load image file used in texture:%s", path,
120 RMessageForError(RErrorCode));
122 wfree(path);
124 return image;
128 void applyImage( RContext * rc, BackgroundTexture *texture, RImage *image, char type, int x, int y, int width, int height) {
130 int w, h;
131 Bool fimage = False;
133 switch( toupper(type)) {
134 case 'S':
135 case 'M':
136 if ( type == 'S') {
137 w = width;
138 h = height;
139 } else {
140 if ( image->width*height > image->height*width) {
141 w = width;
142 h = (width*image->height) / image->width;
143 } else {
144 w = (height*image->width) / image->height;
145 h = height;
149 if ( w != image->width || h != image->height) {
150 RImage * simage;
152 if ( smooth) {
153 simage = RSmoothScaleImage( image, w, h);
154 } else {
155 simage = RScaleImage( image, w, h);
158 if ( !simage) {
159 wwarning( "could not scale image:%s", RMessageForError(RErrorCode));
160 return;
162 fimage = True;
163 image = simage;
166 /* fall through */
167 case 'C':
169 Pixmap pixmap;
171 if ( !RConvertImage(rc, image, &pixmap)) {
172 wwarning( "could not convert texture:%s", RMessageForError(RErrorCode));
173 return;
176 if ( image->width != width || image->height != height) {
177 int sx, sy, w, h;
179 if ( image->height < height) {
180 h = image->height;
181 y += (height - h) / 2;
182 sy = 0;
183 } else {
184 sy = (image->height - height) / 2;
185 h = height;
187 if ( image->width < width) {
188 w = image->width;
189 x += (width - w) / 2;
190 sx = 0;
191 } else {
192 sx = (image->width - width) / 2;
193 w = width;
196 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
197 } else
198 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height, x, y);
200 XFreePixmap(dpy, pixmap);
201 if ( fimage) RReleaseImage( image);
203 break;
208 BackgroundTexture*
209 parseTexture(RContext *rc, char *text)
211 BackgroundTexture *texture = NULL;
212 WMPropList *texarray;
213 WMPropList *val;
214 int count;
215 char *tmp;
216 char *type;
218 #define GETSTRORGOTO(val, str, i, label) \
219 val = WMGetFromPLArray(texarray, i);\
220 if (!WMIsPLString(val)) {\
221 wwarning("could not parse texture %s", text);\
222 goto label;\
224 str = WMGetFromPLString(val)
226 texarray = WMCreatePropListFromDescription(text);
227 if (!texarray || !WMIsPLArray(texarray)
228 || (count = WMGetPropListItemCount(texarray)) < 2) {
230 wwarning("could not parse texture %s", text);
231 if (texarray)
232 WMReleasePropList(texarray);
233 return NULL;
236 texture = wmalloc(sizeof(BackgroundTexture));
237 memset(texture, 0, sizeof(BackgroundTexture));
239 GETSTRORGOTO(val, type, 0, error);
241 if (strcasecmp(type, "solid")==0) {
242 XColor color;
243 Pixmap pixmap;
245 texture->solid = 1;
247 GETSTRORGOTO(val, tmp, 1, error);
249 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
250 wwarning("could not parse color %s in texture %s", tmp, text);
251 goto error;
253 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
255 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
256 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
257 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
259 texture->pixmap = pixmap;
260 texture->color = color;
261 texture->width = 8;
262 texture->height = 8;
263 } else if (strcasecmp(type, "vgradient")==0
264 || strcasecmp(type, "dgradient")==0
265 || strcasecmp(type, "hgradient")==0) {
266 XColor color;
267 RColor color1, color2;
268 RImage *image;
269 Pixmap pixmap;
270 int gtype;
271 int iwidth, iheight;
273 GETSTRORGOTO(val, tmp, 1, error);
275 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
276 wwarning("could not parse color %s in texture %s", tmp, text);
277 goto error;
280 color1.red = color.red >> 8;
281 color1.green = color.green >> 8;
282 color1.blue = color.blue >> 8;
284 GETSTRORGOTO(val, tmp, 2, error);
286 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
287 wwarning("could not parse color %s in texture %s", tmp, text);
288 goto error;
291 color2.red = color.red >> 8;
292 color2.green = color.green >> 8;
293 color2.blue = color.blue >> 8;
295 switch (type[0]) {
296 case 'h':
297 case 'H':
298 gtype = RHorizontalGradient;
299 iwidth = scrWidth;
300 iheight = 32;
301 break;
302 case 'V':
303 case 'v':
304 gtype = RVerticalGradient;
305 iwidth = 32;
306 iheight = scrHeight;
307 break;
308 default:
309 gtype = RDiagonalGradient;
310 iwidth = scrWidth;
311 iheight = scrHeight;
312 break;
315 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
317 if (!image) {
318 wwarning("could not render gradient texture:%s",
319 RMessageForError(RErrorCode));
320 goto error;
323 if (!RConvertImage(rc, image, &pixmap)) {
324 wwarning("could not convert texture:%s",
325 RMessageForError(RErrorCode));
326 RReleaseImage(image);
327 goto error;
330 texture->width = image->width;
331 texture->height = image->height;
332 RReleaseImage(image);
334 texture->pixmap = pixmap;
335 } else if (strcasecmp(type, "mvgradient")==0
336 || strcasecmp(type, "mdgradient")==0
337 || strcasecmp(type, "mhgradient")==0) {
338 XColor color;
339 RColor **colors;
340 RImage *image;
341 Pixmap pixmap;
342 int i, j;
343 int gtype;
344 int iwidth, iheight;
346 colors = malloc(sizeof(RColor*)*(count-1));
347 if (!colors) {
348 wwarning("out of memory while parsing texture");
349 goto error;
351 memset(colors, 0, sizeof(RColor*)*(count-1));
353 for (i = 2; i < count; i++) {
354 val = WMGetFromPLArray(texarray, i);
355 if (!WMIsPLString(val)) {
356 wwarning("could not parse texture %s", text);
358 for (j = 0; colors[j]!=NULL; j++)
359 wfree(colors[j]);
360 wfree(colors);
361 goto error;
363 tmp = WMGetFromPLString(val);
365 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
366 wwarning("could not parse color %s in texture %s",
367 tmp, text);
369 for (j = 0; colors[j]!=NULL; j++)
370 wfree(colors[j]);
371 wfree(colors);
372 goto error;
374 if (!(colors[i-2] = malloc(sizeof(RColor)))) {
375 wwarning("out of memory while parsing texture");
377 for (j = 0; colors[j]!=NULL; j++)
378 wfree(colors[j]);
379 wfree(colors);
380 goto error;
383 colors[i-2]->red = color.red >> 8;
384 colors[i-2]->green = color.green >> 8;
385 colors[i-2]->blue = color.blue >> 8;
388 switch (type[1]) {
389 case 'h':
390 case 'H':
391 gtype = RHorizontalGradient;
392 iwidth = scrWidth;
393 iheight = 32;
394 break;
395 case 'V':
396 case 'v':
397 gtype = RVerticalGradient;
398 iwidth = 32;
399 iheight = scrHeight;
400 break;
401 default:
402 gtype = RDiagonalGradient;
403 iwidth = scrWidth;
404 iheight = scrHeight;
405 break;
408 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
410 for (j = 0; colors[j]!=NULL; j++)
411 wfree(colors[j]);
412 wfree(colors);
414 if (!image) {
415 wwarning("could not render gradient texture:%s",
416 RMessageForError(RErrorCode));
417 goto error;
420 if (!RConvertImage(rc, image, &pixmap)) {
421 wwarning("could not convert texture:%s",
422 RMessageForError(RErrorCode));
423 RReleaseImage(image);
424 goto error;
427 texture->width = image->width;
428 texture->height = image->height;
429 RReleaseImage(image);
431 texture->pixmap = pixmap;
432 } else if (strcasecmp(type, "cpixmap")==0
433 || strcasecmp(type, "spixmap")==0
434 || strcasecmp(type, "mpixmap")==0
435 || strcasecmp(type, "tpixmap")==0) {
436 XColor color;
437 Pixmap pixmap = None;
438 RImage *image = NULL;
439 int w, h;
440 int iwidth, iheight;
441 RColor rcolor;
444 GETSTRORGOTO(val, tmp, 1, error);
446 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
447 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
450 if (!pixmap) {
451 image = loadImage(rc, tmp);
452 if (!image) {
453 goto error;
455 iwidth = image->width;
456 iheight = image->height;
459 GETSTRORGOTO(val, tmp, 2, error);
461 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
462 wwarning("could not parse color %s in texture %s\n", tmp, text);
463 RReleaseImage(image);
464 goto error;
466 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
467 rcolor.red = color.red >> 8;
468 rcolor.green = color.green >> 8;
469 rcolor.blue = color.blue >> 8;
470 RGetClosestXColor(rc, &rcolor, &color);
471 } else {
472 rcolor.red = 0;
473 rcolor.green = 0;
474 rcolor.blue = 0;
476 /* for images with a transparent color */
477 if (image->data[3]) {
478 RCombineImageWithColor(image, &rcolor);
481 switch (toupper(type[0])) {
482 case 'T':
483 texture->width = iwidth;
484 texture->height = iheight;
485 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
486 wwarning("could not convert texture:%s",
487 RMessageForError(RErrorCode));
488 RReleaseImage(image);
489 goto error;
491 if (image)
492 RReleaseImage(image);
493 break;
494 case 'S':
495 case 'M':
496 #if 0
497 if (toupper(type[0])=='S') {
498 w = scrWidth;
499 h = scrHeight;
500 } else {
501 if (iwidth*scrHeight > iheight*scrWidth) {
502 w = scrWidth;
503 h = (scrWidth*iheight)/iwidth;
504 } else {
505 h = scrHeight;
506 w = (scrHeight*iwidth)/iheight;
509 if (w != image->width || h != image->height) {
510 RImage *simage;
512 if (smooth)
513 simage = RSmoothScaleImage(image, w, h);
514 else
515 simage = RScaleImage(image, w, h);
516 if (!simage) {
517 wwarning("could not scale image:%s",
518 RMessageForError(RErrorCode));
519 RReleaseImage(image);
520 goto error;
522 RReleaseImage(image);
523 image = simage;
525 iwidth = image->width;
526 iheight = image->height;
528 /* fall through */
529 case 'C':
531 Pixmap cpixmap;
533 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
534 wwarning("could not convert texture:%s",
535 RMessageForError(RErrorCode));
536 RReleaseImage(image);
537 goto error;
540 if (iwidth != scrWidth || iheight != scrHeight) {
541 int x, y, sx, sy, w, h;
543 cpixmap = XCreatePixmap(dpy, root, scrWidth, scrHeight,
544 DefaultDepth(dpy, scr));
546 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
547 XFillRectangle(dpy, cpixmap, DefaultGC(dpy, scr),
548 0, 0, scrWidth, scrHeight);
550 if (iheight < scrHeight) {
551 h = iheight;
552 y = (scrHeight - h)/2;
553 sy = 0;
554 } else {
555 sy = (iheight - scrHeight)/2;
556 y = 0;
557 h = scrHeight;
559 if (iwidth < scrWidth) {
560 w = iwidth;
561 x = (scrWidth - w)/2;
562 sx = 0;
563 } else {
564 sx = (iwidth - scrWidth)/2;
565 x = 0;
566 w = scrWidth;
569 XCopyArea(dpy, pixmap, cpixmap, DefaultGC(dpy, scr),
570 sx, sy, w, h, x, y);
571 XFreePixmap(dpy, pixmap);
572 pixmap = cpixmap;
574 if (image)
575 RReleaseImage(image);
577 texture->width = scrWidth;
578 texture->height = scrHeight;
580 break;
581 #else
582 case 'C':
584 Pixmap tpixmap = XCreatePixmap( dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
585 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
587 texture->pixmap = tpixmap;
588 texture->color = color;
589 texture->width = scrWidth;
590 texture->height = scrHeight;
592 if ( xine_count) {
593 int i;
594 for ( i=0; i<xine_count; ++i) {
595 applyImage( rc, texture, image, type[0],
596 xine_screens[i].x_org, xine_screens[i].y_org,
597 xine_screens[i].width, xine_screens[i].height);
599 } else {
600 applyImage( rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
602 RReleaseImage( image);
604 break;
605 #endif
608 #if 0
609 texture->pixmap = pixmap;
610 texture->color = color;
611 #endif
612 } else if (strcasecmp(type, "thgradient")==0
613 || strcasecmp(type, "tvgradient")==0
614 || strcasecmp(type, "tdgradient")==0) {
615 XColor color;
616 RColor color1, color2;
617 RImage *image;
618 RImage *gradient;
619 RImage *tiled;
620 Pixmap pixmap;
621 int opaq;
622 char *file;
623 int gtype;
624 int twidth, theight;
626 GETSTRORGOTO(val, file, 1, error);
628 GETSTRORGOTO(val, tmp, 2, error);
630 opaq = atoi(tmp);
632 GETSTRORGOTO(val, tmp, 3, error);
634 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
635 wwarning("could not parse color %s in texture %s", tmp, text);
636 goto error;
639 color1.red = color.red >> 8;
640 color1.green = color.green >> 8;
641 color1.blue = color.blue >> 8;
643 GETSTRORGOTO(val, tmp, 4, error);
645 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
646 wwarning("could not parse color %s in texture %s", tmp, text);
647 goto error;
650 color2.red = color.red >> 8;
651 color2.green = color.green >> 8;
652 color2.blue = color.blue >> 8;
654 image = loadImage(rc, file);
655 if (!image) {
656 goto error;
659 switch (type[1]) {
660 case 'h':
661 case 'H':
662 gtype = RHorizontalGradient;
663 twidth = scrWidth;
664 theight = image->height > scrHeight ? scrHeight : image->height;
665 break;
666 case 'V':
667 case 'v':
668 gtype = RVerticalGradient;
669 twidth = image->width > scrWidth ? scrWidth : image->width;
670 theight = scrHeight;
671 break;
672 default:
673 gtype = RDiagonalGradient;
674 twidth = scrWidth;
675 theight = scrHeight;
676 break;
678 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
680 if (!gradient) {
681 wwarning("could not render texture:%s",
682 RMessageForError(RErrorCode));
683 RReleaseImage(gradient);
684 RReleaseImage(image);
685 goto error;
688 tiled = RMakeTiledImage(image, twidth, theight);
689 if (!tiled) {
690 wwarning("could not render texture:%s",
691 RMessageForError(RErrorCode));
692 RReleaseImage(gradient);
693 RReleaseImage(image);
694 goto error;
696 RReleaseImage(image);
698 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
699 RReleaseImage(gradient);
701 if (!RConvertImage(rc, tiled, &pixmap)) {
702 wwarning("could not convert texture:%s",
703 RMessageForError(RErrorCode));
704 RReleaseImage(tiled);
705 goto error;
707 texture->width = tiled->width;
708 texture->height = tiled->height;
710 RReleaseImage(tiled);
712 texture->pixmap = pixmap;
713 } else if (strcasecmp(type, "function")==0) {
714 #ifdef HAVE_DLFCN_H
715 void (*initFunc) (Display*, Colormap);
716 RImage* (*mainFunc) (int, char**, int, int, int);
717 Pixmap pixmap;
718 RImage *image = 0;
719 int success = 0;
720 char *lib, *func, **argv = 0;
721 void *handle = 0;
722 int i, argc;
724 if (count < 3)
725 goto function_cleanup;
727 /* get the library name */
728 GETSTRORGOTO(val, lib, 1, function_cleanup);
730 /* get the function name */
731 GETSTRORGOTO(val, func, 2, function_cleanup);
733 argc = count - 2;
734 argv = (char**)wmalloc(argc * sizeof(char*));
736 /* get the parameters */
737 argv[0] = func;
738 for (i=0; i<argc-1; i++) {
739 GETSTRORGOTO(val, tmp, 3+i, function_cleanup);
740 argv[i+1] = wstrdup(tmp);
743 handle = dlopen(lib, RTLD_LAZY);
744 if (!handle) {
745 wwarning("could not find library %s", lib);
746 goto function_cleanup;
749 initFunc = dlsym(handle, "initWindowMaker");
750 if (!initFunc) {
751 wwarning("could not initialize library %s", lib);
752 goto function_cleanup;
754 initFunc(dpy, DefaultColormap(dpy, scr));
756 mainFunc = dlsym(handle, func);
757 if (!mainFunc) {
758 wwarning("could not find function %s::%s", lib, func);
759 goto function_cleanup;
761 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
763 if (!RConvertImage(rc, image, &pixmap)) {
764 wwarning("could not convert texture:%s",
765 RMessageForError(RErrorCode));
766 goto function_cleanup;
768 texture->width = scrWidth;
769 texture->height = scrHeight;
770 texture->pixmap = pixmap;
771 success = 1;
773 function_cleanup:
774 if (argv) {
775 int i;
776 for (i=0; i<argc; i++) {
777 wfree(argv[i]);
780 if (handle) {
781 dlclose(handle);
783 if (image) {
784 RReleaseImage(image);
786 if (!success) {
787 goto error;
789 #else
790 wwarning("function textures not supported");
791 goto error;
792 #endif
793 } else {
794 wwarning("invalid texture type %s", text);
795 goto error;
798 texture->spec = wstrdup(text);
800 return texture;
802 error:
803 if (texture)
804 wfree(texture);
805 if (texarray)
806 WMReleasePropList(texarray);
808 return NULL;
812 void
813 freeTexture(BackgroundTexture *texture)
815 if (texture->solid) {
816 long pixel[1];
818 pixel[0] = texture->color.pixel;
819 /* dont free black/white pixels */
820 if (pixel[0]!=BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
821 && pixel[0]!=WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
822 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
824 if (texture->pixmap) {
825 XFreePixmap(dpy, texture->pixmap);
827 wfree(texture->spec);
828 wfree(texture);
832 void
833 setupTexture(RContext *rc, BackgroundTexture **textures, int *maxTextures,
834 int workspace, char *texture)
836 BackgroundTexture *newTexture = NULL;
837 int i;
839 /* unset the texture */
840 if (!texture) {
841 if (textures[workspace]!=NULL) {
842 textures[workspace]->refcount--;
844 if (textures[workspace]->refcount == 0)
845 freeTexture(textures[workspace]);
847 textures[workspace] = NULL;
848 return;
851 if (textures[workspace]
852 && strcasecmp(textures[workspace]->spec, texture)==0) {
853 /* texture did not change */
854 return;
857 /* check if the same texture is already created */
858 for (i = 0; i < *maxTextures; i++) {
859 if (textures[i] && strcasecmp(textures[i]->spec, texture)==0) {
860 newTexture = textures[i];
861 break;
865 if (!newTexture) {
866 /* create the texture */
867 newTexture = parseTexture(rc, texture);
869 if (!newTexture)
870 return;
872 if (textures[workspace]!=NULL) {
874 textures[workspace]->refcount--;
876 if (textures[workspace]->refcount == 0)
877 freeTexture(textures[workspace]);
880 newTexture->refcount++;
881 textures[workspace] = newTexture;
883 if (*maxTextures < workspace)
884 *maxTextures = workspace;
889 Pixmap
890 duplicatePixmap(Pixmap pixmap, int width, int height)
892 Display *tmpDpy;
893 Pixmap copyP;
895 /* must open a new display or the RetainPermanent will
896 * leave stuff allocated in RContext unallocated after exit */
897 tmpDpy = XOpenDisplay(display);
898 if (!tmpDpy) {
899 wwarning("could not open display to update background image information");
901 return None;
902 } else {
903 XSync(dpy, False);
905 copyP = XCreatePixmap(tmpDpy, root, width, height,
906 DefaultDepth(tmpDpy, scr));
907 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr),
908 0, 0, width, height, 0, 0);
909 XSync(tmpDpy, False);
911 XSetCloseDownMode(tmpDpy, RetainPermanent);
912 XCloseDisplay(tmpDpy);
915 return copyP;
919 static int
920 dummyErrorHandler(Display *dpy, XErrorEvent *err)
922 return 0;
925 void
926 setPixmapProperty(Pixmap pixmap)
928 static Atom prop = 0;
929 Atom type;
930 int format;
931 unsigned long length, after;
932 unsigned char *data;
933 int mode;
935 if (!prop) {
936 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
939 XGrabServer(dpy);
941 /* Clear out the old pixmap */
942 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
943 &type, &format, &length, &after, &data);
945 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
946 XSetErrorHandler(dummyErrorHandler);
947 XKillClient(dpy, *((Pixmap *)data));
948 XSync(dpy, False);
949 XSetErrorHandler(NULL);
950 mode = PropModeReplace;
951 } else {
952 mode = PropModeAppend;
954 if (pixmap)
955 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode,
956 (unsigned char *) &pixmap, 1);
957 else
958 XDeleteProperty(dpy, root, prop);
961 XUngrabServer(dpy);
962 XFlush(dpy);
967 void
968 changeTexture(BackgroundTexture *texture)
970 if (!texture) {
971 return;
974 if (texture->solid) {
975 XSetWindowBackground(dpy, root, texture->color.pixel);
976 } else {
977 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
979 XClearWindow(dpy, root);
981 XSync(dpy, False);
984 Pixmap pixmap;
986 pixmap = duplicatePixmap(texture->pixmap, texture->width,
987 texture->height);
989 setPixmapProperty(pixmap);
995 readmsg(int fd, unsigned char *buffer, int size)
997 int count;
999 count = 0;
1000 while (size>0) {
1001 count = read(fd, buffer, size);
1002 if (count < 0)
1003 return -1;
1004 size -= count;
1005 buffer += count;
1006 *buffer = 0;
1009 return size;
1014 * Message Format:
1015 * sizeSntexture_spec - sets the texture for workspace n
1016 * sizeCn - change background texture to the one for workspace n
1017 * sizePpath - set the pixmap search path
1019 * n is 4 bytes
1020 * size = 4 bytes for length of the message data
1022 void
1023 helperLoop(RContext *rc)
1025 BackgroundTexture *textures[WORKSPACE_COUNT];
1026 int maxTextures = 0;
1027 unsigned char buffer[2048], buf[8];
1028 int size;
1029 int errcount = 4;
1031 memset(textures, 0, WORKSPACE_COUNT*sizeof(BackgroundTexture*));
1034 while (1) {
1035 int workspace;
1037 /* get length of message */
1038 if (readmsg(0, buffer, 4) < 0) {
1039 wsyserror("error reading message from Window Maker");
1040 errcount--;
1041 if (errcount == 0) {
1042 wfatal("quitting");
1043 exit(1);
1045 continue;
1047 memcpy(buf, buffer, 4);
1048 buf[4] = 0;
1049 size = atoi(buf);
1051 /* get message */
1052 if (readmsg(0, buffer, size) < 0) {
1053 wsyserror("error reading message from Window Maker");
1054 errcount--;
1055 if (errcount == 0) {
1056 wfatal("quitting");
1057 exit(1);
1059 continue;
1061 #ifdef DEBUG
1062 printf("RECEIVED %s\n",buffer);
1063 #endif
1064 if (buffer[0]!='P' && buffer[0]!='K') {
1065 memcpy(buf, &buffer[1], 4);
1066 buf[4] = 0;
1067 workspace = atoi(buf);
1068 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
1069 wwarning("received message with invalid workspace number %i\n",
1070 workspace);
1071 continue;
1075 switch (buffer[0]) {
1076 case 'S':
1077 #ifdef DEBUG
1078 printf("set texture %s\n", &buffer[5]);
1079 #endif
1080 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1081 break;
1083 case 'C':
1084 #ifdef DEBUG
1085 printf("change texture %i\n", workspace);
1086 #endif
1087 if (!textures[workspace]) {
1088 changeTexture(textures[0]);
1089 } else {
1090 changeTexture(textures[workspace]);
1092 break;
1094 case 'P':
1095 #ifdef DEBUG
1096 printf("change pixmappath %s\n", &buffer[1]);
1097 #endif
1098 if (PixmapPath)
1099 wfree(PixmapPath);
1100 PixmapPath = wstrdup(&buffer[1]);
1101 break;
1103 case 'U':
1104 #ifdef DEBUG
1105 printf("unset workspace %i\n", workspace);
1106 #endif
1107 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1108 break;
1110 case 'K':
1111 #ifdef DEBUG
1112 printf("exit command\n");
1113 #endif
1114 exit(0);
1116 default:
1117 wwarning("unknown message received");
1118 break;
1124 void
1125 updateDomain(char *domain, char *key, char *texture)
1127 char *program = "wdwrite";
1129 /* here is a mem leak */
1130 system(wstrconcat("wdwrite ",
1131 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES"
1132 : " SmoothWorkspaceBack NO")));
1134 execlp(program, program, domain, key, texture, NULL);
1135 wwarning("warning could not run \"%s\"", program);
1140 char*
1141 globalDefaultsPathForDomain(char *domain)
1143 char path[1024];
1145 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1147 return wstrdup(path);
1151 static WMPropList*
1152 getValueForKey(char *domain, char *keyName)
1154 char *path;
1155 WMPropList *key, *val, *d;
1157 key = WMCreatePLString(keyName);
1159 /* try to find PixmapPath in user defaults */
1160 path = wdefaultspathfordomain(domain);
1161 d = WMReadPropListFromFile(path);
1162 if (!d) {
1163 wwarning("could not open domain file %s", path);
1165 wfree(path);
1167 if (d && !WMIsPLDictionary(d)) {
1168 WMReleasePropList(d);
1169 d = NULL;
1171 if (d) {
1172 val = WMGetFromPLDictionary(d, key);
1173 } else {
1174 val = NULL;
1176 /* try to find PixmapPath in global defaults */
1177 if (!val) {
1178 path = globalDefaultsPathForDomain(domain);
1179 if (!path) {
1180 wwarning("could not locate file for domain %s", domain);
1181 d = NULL;
1182 } else {
1183 d = WMReadPropListFromFile(path);
1184 wfree(path);
1187 if (d && !WMIsPLDictionary(d)) {
1188 WMReleasePropList(d);
1189 d = NULL;
1191 if (d) {
1192 val = WMGetFromPLDictionary(d, key);
1194 } else {
1195 val = NULL;
1199 if (val)
1200 WMRetainPropList(val);
1202 WMReleasePropList(key);
1203 if (d)
1204 WMReleasePropList(d);
1206 return val;
1211 char*
1212 getPixmapPath(char *domain)
1214 WMPropList *val;
1215 char *ptr, *data;
1216 int len, i, count;
1218 val = getValueForKey(domain, "PixmapPath");
1220 if (!val || !WMIsPLArray(val)) {
1221 if (val)
1222 WMReleasePropList(val);
1223 return wstrdup("");
1226 count = WMGetPropListItemCount(val);
1227 len = 0;
1228 for (i=0; i<count; i++) {
1229 WMPropList *v;
1231 v = WMGetFromPLArray(val, i);
1232 if (!v || !WMIsPLString(v)) {
1233 continue;
1235 len += strlen(WMGetFromPLString(v))+1;
1238 ptr = data = wmalloc(len+1);
1239 *ptr = 0;
1241 for (i=0; i<count; i++) {
1242 WMPropList *v;
1244 v = WMGetFromPLArray(val, i);
1245 if (!v || !WMIsPLString(v)) {
1246 continue;
1248 strcpy(ptr, WMGetFromPLString(v));
1250 ptr += strlen(WMGetFromPLString(v));
1251 *ptr = ':';
1252 ptr++;
1254 if (i>0)
1255 ptr--; *(ptr--) = 0;
1257 WMReleasePropList(val);
1259 return data;
1263 char*
1264 getFullPixmapPath(char *file)
1266 char *tmp;
1268 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1269 int bsize = 512;
1270 char *path = wmalloc(bsize);
1272 while (!getcwd(path, bsize)) {
1273 bsize += bsize/2;
1274 path = wrealloc(path, bsize);
1277 tmp = wstrconcat(path, "/");
1278 wfree(path);
1279 path = wstrconcat(tmp, file);
1280 wfree(tmp);
1282 return path;
1285 /* the file is in the PixmapPath */
1286 wfree(tmp);
1288 return wstrdup(file);
1293 void
1294 wAbort()
1296 wfatal("aborting");
1297 exit(1);
1302 void
1303 print_help(char *ProgName)
1305 printf("Usage: %s [options] [image]\n", ProgName);
1306 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1307 puts("");
1308 #define P(m) puts(m)
1309 P(" -display display to use");
1310 P(" -d, --dither dither image");
1311 P(" -m, --match match colors");
1312 P(" -S, --smooth smooth scaled image");
1313 P(" -b, --back-color <color> background color");
1314 P(" -t, --tile tile image");
1315 P(" -e, --center center image");
1316 P(" -s, --scale scale image (default)");
1317 P(" -a, --maxscale scale image and keep aspect ratio");
1318 P(" -u, --update-wmaker update WindowMaker domain database");
1319 P(" -D, --update-domain <domain> update <domain> database");
1320 P(" -c, --colors <cpc> colors per channel to use");
1321 P(" -p, --parse <texture> proplist style texture specification");
1322 P(" -w, --workspace <workspace> update background for the specified workspace");
1323 P(" --version show version of wmsetbg and exit");
1324 P(" --help show this help and exit");
1325 #undef P
1330 void
1331 changeTextureForWorkspace(char *domain, char *texture, int workspace)
1333 WMPropList *array, *val;
1334 char *value;
1335 int j;
1337 val = WMCreatePropListFromDescription(texture);
1338 if (!val) {
1339 wwarning("could not parse texture %s", texture);
1340 return;
1343 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1345 if (!array) {
1346 array = WMCreatePLArray(NULL, NULL);
1349 j = WMGetPropListItemCount(array);
1350 if (workspace >= j) {
1351 WMPropList *empty;
1353 empty = WMCreatePLArray(NULL, NULL);
1355 while (j++ < workspace-1) {
1356 WMAddToPLArray(array, empty);
1358 WMAddToPLArray(array, val);
1359 } else {
1360 WMDeleteFromPLArray(array, workspace);
1361 WMInsertInPLArray(array, workspace, val);
1364 value = WMGetPropListDescription(array, False);
1365 updateDomain(domain, "WorkspaceSpecificBack", value);
1370 main(int argc, char **argv)
1372 int i;
1373 int helperMode = 0;
1374 RContext *rc;
1375 RContextAttributes rattr;
1376 char *style = "spixmap";
1377 char *back_color = "gray20";
1378 char *image_name = NULL;
1379 char *domain = "WindowMaker";
1380 int update=0, cpc=4, render_mode=RDitheredRendering, obey_user=0;
1381 char *texture = NULL;
1382 int workspace = -1;
1384 signal(SIGINT, SIG_DFL);
1385 signal(SIGTERM, SIG_DFL);
1386 signal(SIGQUIT, SIG_DFL);
1387 signal(SIGSEGV, SIG_DFL);
1388 signal(SIGBUS, SIG_DFL);
1389 signal(SIGFPE, SIG_DFL);
1390 signal(SIGABRT, SIG_DFL);
1391 signal(SIGHUP, SIG_DFL);
1392 signal(SIGPIPE, SIG_DFL);
1393 signal(SIGCHLD, SIG_DFL);
1395 WMInitializeApplication("wmsetbg", &argc, argv);
1397 for (i=1; i<argc; i++) {
1398 if (strcmp(argv[i], "-helper")==0) {
1399 helperMode = 1;
1400 } else if (strcmp(argv[i], "-display")==0) {
1401 i++;
1402 if (i>=argc) {
1403 wfatal("too few arguments for %s\n", argv[i-1]);
1404 exit(1);
1406 display = argv[i];
1407 } else if (strcmp(argv[i], "-s")==0
1408 || strcmp(argv[i], "--scale")==0) {
1409 style = "spixmap";
1410 } else if (strcmp(argv[i], "-t")==0
1411 || strcmp(argv[i], "--tile")==0) {
1412 style = "tpixmap";
1413 } else if (strcmp(argv[i], "-e")==0
1414 || strcmp(argv[i], "--center")==0) {
1415 style = "cpixmap";
1416 } else if (strcmp(argv[i], "-a")==0
1417 || strcmp(argv[i], "--maxscale")==0) {
1418 style = "mpixmap";
1419 } else if (strcmp(argv[i], "-d")==0
1420 || strcmp(argv[i], "--dither")==0) {
1421 render_mode = RDitheredRendering;
1422 obey_user++;
1423 } else if (strcmp(argv[i], "-m")==0
1424 || strcmp(argv[i], "--match")==0) {
1425 render_mode = RBestMatchRendering;
1426 obey_user++;
1427 } else if (strcmp(argv[i], "-S")==0
1428 || strcmp(argv[i], "--smooth")==0) {
1429 smooth = True;
1430 } else if (strcmp(argv[i], "-u")==0
1431 || strcmp(argv[i], "--update-wmaker")==0) {
1432 update++;
1433 } else if (strcmp(argv[i], "-D")==0
1434 || strcmp(argv[i], "--update-domain")==0) {
1435 update++;
1436 i++;
1437 if (i>=argc) {
1438 wfatal("too few arguments for %s\n", argv[i-1]);
1439 exit(1);
1441 domain = wstrdup(argv[i]);
1442 } else if (strcmp(argv[i], "-c")==0
1443 || strcmp(argv[i], "--colors")==0) {
1444 i++;
1445 if (i>=argc) {
1446 wfatal("too few arguments for %s\n", argv[i-1]);
1447 exit(1);
1449 if (sscanf(argv[i], "%i", &cpc)!=1) {
1450 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1451 exit(1);
1453 } else if (strcmp(argv[i], "-b")==0
1454 || strcmp(argv[i], "--back-color")==0) {
1455 i++;
1456 if (i>=argc) {
1457 wfatal("too few arguments for %s\n", argv[i-1]);
1458 exit(1);
1460 back_color = argv[i];
1461 } else if (strcmp(argv[i], "-p")==0
1462 || strcmp(argv[i], "--parse")==0) {
1463 i++;
1464 if (i>=argc) {
1465 wfatal("too few arguments for %s\n", argv[i-1]);
1466 exit(1);
1468 texture = argv[i];
1469 } else if (strcmp(argv[i], "-w")==0
1470 || strcmp(argv[i], "--workspace")==0) {
1471 i++;
1472 if (i>=argc) {
1473 wfatal("too few arguments for %s\n", argv[i-1]);
1474 exit(1);
1476 if (sscanf(argv[i], "%i", &workspace)!=1) {
1477 wfatal("bad value for workspace number: \"%s\"",
1478 argv[i]);
1479 exit(1);
1481 } else if (strcmp(argv[i], "--version")==0) {
1483 printf(PROG_VERSION);
1484 exit(0);
1486 } else if (strcmp(argv[i], "--help")==0) {
1487 print_help(argv[0]);
1488 exit(0);
1489 } else if (argv[i][0] != '-') {
1490 image_name = argv[i];
1491 } else {
1492 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1493 printf("Try '%s --help' for more information\n", argv[0]);
1494 exit(1);
1497 if (!image_name && !texture && !helperMode) {
1498 printf("%s: you must specify a image file name or a texture\n",
1499 argv[0]);
1500 printf("Try '%s --help' for more information\n", argv[0]);
1501 exit(1);
1505 PixmapPath = getPixmapPath(domain);
1506 if (!smooth) {
1507 WMPropList *val;
1508 #if 0 /* some problem with Alpha... TODO: check if its right */
1509 val = WMGetFromPLDictionary(domain,
1510 WMCreatePLString("SmoothWorkspaceBack"));
1511 #else
1512 val = getValueForKey(domain, "SmoothWorkspaceBack");
1513 #endif
1515 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES")==0)
1516 smooth = True;
1519 dpy = XOpenDisplay(display);
1520 if (!dpy) {
1521 wfatal("could not open display");
1522 exit(1);
1524 #if 0
1525 XSynchronize(dpy, 1);
1526 #endif
1528 root = DefaultRootWindow(dpy);
1530 scr = DefaultScreen(dpy);
1532 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1533 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1534 scrX = scrY = 0;
1536 #ifdef XINERAMA
1537 xine_screens = XineramaQueryScreens(dpy, &xine_count);
1538 #endif
1541 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1542 render_mode = RDitheredRendering;
1544 rattr.flags = RC_RenderMode | RC_ColorsPerChannel
1545 | RC_StandardColormap | RC_DefaultVisual;
1546 rattr.render_mode = render_mode;
1547 rattr.colors_per_channel = cpc;
1548 rattr.standard_colormap_mode = RCreateStdColormap;
1550 rc = RCreateContext(dpy, scr, &rattr);
1552 if (!rc) {
1553 rattr.standard_colormap_mode = RIgnoreStdColormap;
1554 rc = RCreateContext(dpy, scr, &rattr);
1557 if (!rc) {
1558 wfatal("could not initialize wrlib: %s",
1559 RMessageForError(RErrorCode));
1560 exit(1);
1563 if (helperMode) {
1564 /* lower priority, so that it wont use all the CPU */
1565 nice(15);
1567 helperLoop(rc);
1568 } else {
1569 BackgroundTexture *tex;
1570 char buffer[4098];
1572 if (!texture) {
1573 char *image_path = getFullPixmapPath(image_name);
1575 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1576 wfree(image_path);
1577 texture = (char*)buffer;
1580 if (update && workspace < 0) {
1581 updateDomain(domain, "WorkspaceBack", texture);
1584 tex = parseTexture(rc, texture);
1585 if (!tex)
1586 exit(1);
1588 if (workspace<0)
1589 changeTexture(tex);
1590 else {
1591 /* always update domain */
1592 changeTextureForWorkspace(domain, texture, workspace);
1596 return 0;