Idle fixes
[wmaker-crm.git] / util / wmsetbg.c
blob1037bf236b021e512a6ce44c94752ae68d6ddee0
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
4 * WindowMaker window manager
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 Dan Pascu
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22 * USA.
26 * TODO: rewrite, too dirty
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xatom.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <ctype.h>
40 #include "../src/config.h"
42 #ifdef XINERAMA
43 # ifdef SOLARIS_XINERAMA /* sucks */
44 # include <X11/extensions/xinerama.h>
45 # else
46 # include <X11/extensions/Xinerama.h>
47 # endif
48 #endif
50 #ifdef HAVE_DLFCN_H
51 #include <dlfcn.h>
52 #endif
54 #include "../src/wconfig.h"
56 #ifndef GLOBAL_DEFAULTS_SUBDIR
57 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
58 #endif
60 #include <WINGs/WINGs.h>
61 #include <wraster.h>
63 typedef struct {
64 WMRect *screens;
65 int count; /* screen count, 0 = inactive */
66 } WXineramaInfo;
68 #define PROG_VERSION "wmsetbg (Window Maker) 2.8"
70 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
72 Display *dpy;
73 char *display = "";
74 Window root;
75 int scr;
76 int scrWidth;
77 int scrHeight;
78 int scrX, scrY;
80 WXineramaInfo xineInfo;
82 Bool smooth = False;
83 #ifdef XINERAMA
84 Bool xineStretch = False;
85 #endif
87 Pixmap CurrentPixmap = None;
88 char *PixmapPath = NULL;
90 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
91 extern char *__progname;
93 typedef struct BackgroundTexture {
94 int refcount;
96 int solid;
98 char *spec;
100 XColor color;
101 Pixmap pixmap; /* for all textures, including solid */
102 int width; /* size of the pixmap */
103 int height;
104 } BackgroundTexture;
106 void initXinerama()
108 xineInfo.screens = NULL;
109 xineInfo.count = 0;
110 #ifdef XINERAMA
111 # ifdef SOLARIS_XINERAMA
112 if (XineramaGetState(dpy, scr)) {
113 XRectangle head[MAXFRAMEBUFFERS];
114 unsigned char hints[MAXFRAMEBUFFERS];
115 int i;
117 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
119 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
121 for (i = 0; i < xineInfo.count; i++) {
122 xineInfo.screens[i].pos.x = head[i].x;
123 xineInfo.screens[i].pos.y = head[i].y;
124 xineInfo.screens[i].size.width = head[i].width;
125 xineInfo.screens[i].size.height = head[i].height;
129 # else /* !SOLARIS_XINERAMA */
130 if (XineramaIsActive(dpy)) {
131 XineramaScreenInfo *xine_screens;
132 int i;
134 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
136 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
138 for (i = 0; i < xineInfo.count; i++) {
139 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
140 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
141 xineInfo.screens[i].size.width = xine_screens[i].width;
142 xineInfo.screens[i].size.height = xine_screens[i].height;
144 XFree(xine_screens);
146 # endif /* !SOLARIS_XINERAMA */
147 #endif /* XINERAMA */
150 RImage *loadImage(RContext * rc, char *file)
152 char *path;
153 RImage *image;
155 if (access(file, F_OK) != 0) {
156 path = wfindfile(PixmapPath, file);
157 if (!path) {
158 wwarning("%s:could not find image file used in texture", file);
159 return NULL;
161 } else {
162 path = wstrdup(file);
165 image = RLoadImage(rc, path, 0);
166 if (!image) {
167 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
169 wfree(path);
171 return image;
174 static void
175 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
176 int x, int y, int width, int height)
178 int w, h;
179 Bool fimage = False;
181 switch (toupper(type)) {
182 case 'S':
183 case 'M':
184 if (toupper(type) == 'S') {
185 w = width;
186 h = height;
187 } else {
188 if (image->width * height > image->height * width) {
189 w = width;
190 h = (width * image->height) / image->width;
191 } else {
192 w = (height * image->width) / image->height;
193 h = height;
197 if (w != image->width || h != image->height) {
198 RImage *simage;
200 if (smooth) {
201 simage = RSmoothScaleImage(image, w, h);
202 } else {
203 simage = RScaleImage(image, w, h);
206 if (!simage) {
207 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
208 return;
210 fimage = True;
211 image = simage;
214 /* fall through */
215 case 'C':
217 Pixmap pixmap;
219 if (!RConvertImage(rc, image, &pixmap)) {
220 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
221 return;
224 if (image->width != width || image->height != height) {
225 int sx, sy, w, h;
227 if (image->height < height) {
228 h = image->height;
229 y += (height - h) / 2;
230 sy = 0;
231 } else {
232 sy = (image->height - height) / 2;
233 h = height;
235 if (image->width < width) {
236 w = image->width;
237 x += (width - w) / 2;
238 sx = 0;
239 } else {
240 sx = (image->width - width) / 2;
241 w = width;
244 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
245 } else
246 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
247 x, y);
249 XFreePixmap(dpy, pixmap);
250 if (fimage) {
251 RReleaseImage(image);
254 break;
258 BackgroundTexture *parseTexture(RContext * rc, char *text)
260 BackgroundTexture *texture = NULL;
261 WMPropList *texarray;
262 WMPropList *val;
263 int count;
264 char *tmp;
265 char *type;
267 #define GETSTRORGOTO(val, str, i, label) \
268 val = WMGetFromPLArray(texarray, i);\
269 if (!WMIsPLString(val)) {\
270 wwarning("could not parse texture %s", text);\
271 goto label;\
273 str = WMGetFromPLString(val)
275 texarray = WMCreatePropListFromDescription(text);
276 if (!texarray || !WMIsPLArray(texarray)
277 || (count = WMGetPropListItemCount(texarray)) < 2) {
279 wwarning("could not parse texture %s", text);
280 if (texarray)
281 WMReleasePropList(texarray);
282 return NULL;
285 texture = wmalloc(sizeof(BackgroundTexture));
286 memset(texture, 0, sizeof(BackgroundTexture));
288 GETSTRORGOTO(val, type, 0, error);
290 if (strcasecmp(type, "solid") == 0) {
291 XColor color;
292 Pixmap pixmap;
294 texture->solid = 1;
296 GETSTRORGOTO(val, tmp, 1, error);
298 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
299 wwarning("could not parse color %s in texture %s", tmp, text);
300 goto error;
302 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
304 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
305 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
306 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
308 texture->pixmap = pixmap;
309 texture->color = color;
310 texture->width = 8;
311 texture->height = 8;
312 } else if (strcasecmp(type, "vgradient") == 0
313 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
314 XColor color;
315 RColor color1, color2;
316 RImage *image;
317 Pixmap pixmap;
318 int gtype;
319 int iwidth, iheight;
321 GETSTRORGOTO(val, tmp, 1, error);
323 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
324 wwarning("could not parse color %s in texture %s", tmp, text);
325 goto error;
328 color1.red = color.red >> 8;
329 color1.green = color.green >> 8;
330 color1.blue = color.blue >> 8;
332 GETSTRORGOTO(val, tmp, 2, error);
334 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
335 wwarning("could not parse color %s in texture %s", tmp, text);
336 goto error;
339 color2.red = color.red >> 8;
340 color2.green = color.green >> 8;
341 color2.blue = color.blue >> 8;
343 switch (type[0]) {
344 case 'h':
345 case 'H':
346 gtype = RHorizontalGradient;
347 iwidth = scrWidth;
348 iheight = 32;
349 break;
350 case 'V':
351 case 'v':
352 gtype = RVerticalGradient;
353 iwidth = 32;
354 iheight = scrHeight;
355 break;
356 default:
357 gtype = RDiagonalGradient;
358 iwidth = scrWidth;
359 iheight = scrHeight;
360 break;
363 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
365 if (!image) {
366 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
367 goto error;
370 if (!RConvertImage(rc, image, &pixmap)) {
371 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
372 RReleaseImage(image);
373 goto error;
376 texture->width = image->width;
377 texture->height = image->height;
378 RReleaseImage(image);
380 texture->pixmap = pixmap;
381 } else if (strcasecmp(type, "mvgradient") == 0
382 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
383 XColor color;
384 RColor **colors;
385 RImage *image;
386 Pixmap pixmap;
387 int i, j;
388 int gtype;
389 int iwidth, iheight;
391 colors = malloc(sizeof(RColor *) * (count - 1));
392 if (!colors) {
393 wwarning("out of memory while parsing texture");
394 goto error;
396 memset(colors, 0, sizeof(RColor *) * (count - 1));
398 for (i = 2; i < count; i++) {
399 val = WMGetFromPLArray(texarray, i);
400 if (!WMIsPLString(val)) {
401 wwarning("could not parse texture %s", text);
403 for (j = 0; colors[j] != NULL; j++)
404 wfree(colors[j]);
405 wfree(colors);
406 goto error;
408 tmp = WMGetFromPLString(val);
410 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
411 wwarning("could not parse color %s in texture %s", tmp, text);
413 for (j = 0; colors[j] != NULL; j++)
414 wfree(colors[j]);
415 wfree(colors);
416 goto error;
418 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
419 wwarning("out of memory while parsing texture");
421 for (j = 0; colors[j] != NULL; j++)
422 wfree(colors[j]);
423 wfree(colors);
424 goto error;
427 colors[i - 2]->red = color.red >> 8;
428 colors[i - 2]->green = color.green >> 8;
429 colors[i - 2]->blue = color.blue >> 8;
432 switch (type[1]) {
433 case 'h':
434 case 'H':
435 gtype = RHorizontalGradient;
436 iwidth = scrWidth;
437 iheight = 32;
438 break;
439 case 'V':
440 case 'v':
441 gtype = RVerticalGradient;
442 iwidth = 32;
443 iheight = scrHeight;
444 break;
445 default:
446 gtype = RDiagonalGradient;
447 iwidth = scrWidth;
448 iheight = scrHeight;
449 break;
452 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
454 for (j = 0; colors[j] != NULL; j++)
455 wfree(colors[j]);
456 wfree(colors);
458 if (!image) {
459 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
460 goto error;
463 if (!RConvertImage(rc, image, &pixmap)) {
464 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
465 RReleaseImage(image);
466 goto error;
469 texture->width = image->width;
470 texture->height = image->height;
471 RReleaseImage(image);
473 texture->pixmap = pixmap;
474 } else if (strcasecmp(type, "cpixmap") == 0
475 || strcasecmp(type, "spixmap") == 0
476 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
477 XColor color;
478 Pixmap pixmap = None;
479 RImage *image = NULL;
480 int iwidth, iheight;
481 RColor rcolor;
483 GETSTRORGOTO(val, tmp, 1, error);
485 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
486 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
489 if (!pixmap) {
490 image = loadImage(rc, tmp);
491 if (!image) {
492 goto error;
494 iwidth = image->width;
495 iheight = image->height;
498 GETSTRORGOTO(val, tmp, 2, error);
500 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
501 wwarning("could not parse color %s in texture %s\n", tmp, text);
502 RReleaseImage(image);
503 goto error;
505 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
506 rcolor.red = color.red >> 8;
507 rcolor.green = color.green >> 8;
508 rcolor.blue = color.blue >> 8;
509 RGetClosestXColor(rc, &rcolor, &color);
510 } else {
511 rcolor.red = 0;
512 rcolor.green = 0;
513 rcolor.blue = 0;
515 /* for images with a transparent color */
516 if (image->data[3]) {
517 RCombineImageWithColor(image, &rcolor);
520 switch (toupper(type[0])) {
521 case 'T':
522 texture->width = iwidth;
523 texture->height = iheight;
524 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
525 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
526 RReleaseImage(image);
527 goto error;
529 if (image)
530 RReleaseImage(image);
532 texture->pixmap = pixmap;
533 texture->color = color;
534 break;
535 case 'S':
536 case 'M':
537 case 'C':
539 Pixmap tpixmap =
540 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
541 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
542 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
544 texture->pixmap = tpixmap;
545 texture->color = color;
546 texture->width = scrWidth;
547 texture->height = scrHeight;
549 #ifdef XINERAMA
550 if (xineInfo.count && ! xineStretch) {
551 int i;
552 for (i = 0; i < xineInfo.count; ++i) {
553 applyImage(rc, texture, image, type[0],
554 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
555 xineInfo.screens[i].size.width,
556 xineInfo.screens[i].size.height);
558 } else {
559 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
561 #else /* !XINERAMA */
562 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
563 #endif /* !XINERAMA */
564 RReleaseImage(image);
566 break;
568 } else if (strcasecmp(type, "thgradient") == 0
569 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
570 XColor color;
571 RColor color1, color2;
572 RImage *image;
573 RImage *gradient;
574 RImage *tiled;
575 Pixmap pixmap;
576 int opaq;
577 char *file;
578 int gtype;
579 int twidth, theight;
581 GETSTRORGOTO(val, file, 1, error);
583 GETSTRORGOTO(val, tmp, 2, error);
585 opaq = atoi(tmp);
587 GETSTRORGOTO(val, tmp, 3, error);
589 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
590 wwarning("could not parse color %s in texture %s", tmp, text);
591 goto error;
594 color1.red = color.red >> 8;
595 color1.green = color.green >> 8;
596 color1.blue = color.blue >> 8;
598 GETSTRORGOTO(val, tmp, 4, error);
600 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
601 wwarning("could not parse color %s in texture %s", tmp, text);
602 goto error;
605 color2.red = color.red >> 8;
606 color2.green = color.green >> 8;
607 color2.blue = color.blue >> 8;
609 image = loadImage(rc, file);
610 if (!image) {
611 goto error;
614 switch (type[1]) {
615 case 'h':
616 case 'H':
617 gtype = RHorizontalGradient;
618 twidth = scrWidth;
619 theight = image->height > scrHeight ? scrHeight : image->height;
620 break;
621 case 'V':
622 case 'v':
623 gtype = RVerticalGradient;
624 twidth = image->width > scrWidth ? scrWidth : image->width;
625 theight = scrHeight;
626 break;
627 default:
628 gtype = RDiagonalGradient;
629 twidth = scrWidth;
630 theight = scrHeight;
631 break;
633 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
635 if (!gradient) {
636 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
637 RReleaseImage(gradient);
638 RReleaseImage(image);
639 goto error;
642 tiled = RMakeTiledImage(image, twidth, theight);
643 if (!tiled) {
644 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
645 RReleaseImage(gradient);
646 RReleaseImage(image);
647 goto error;
649 RReleaseImage(image);
651 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
652 RReleaseImage(gradient);
654 if (!RConvertImage(rc, tiled, &pixmap)) {
655 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
656 RReleaseImage(tiled);
657 goto error;
659 texture->width = tiled->width;
660 texture->height = tiled->height;
662 RReleaseImage(tiled);
664 texture->pixmap = pixmap;
665 } else if (strcasecmp(type, "function") == 0) {
666 #ifdef HAVE_DLFCN_H
667 void (*initFunc) (Display *, Colormap);
668 RImage *(*mainFunc) (int, char **, int, int, int);
669 Pixmap pixmap;
670 RImage *image = 0;
671 int success = 0;
672 char *lib, *func, **argv = 0;
673 void *handle = 0;
674 int i, argc;
676 if (count < 3)
677 goto function_cleanup;
679 /* get the library name */
680 GETSTRORGOTO(val, lib, 1, function_cleanup);
682 /* get the function name */
683 GETSTRORGOTO(val, func, 2, function_cleanup);
685 argc = count - 2;
686 argv = (char **)wmalloc(argc * sizeof(char *));
688 /* get the parameters */
689 argv[0] = func;
690 for (i = 0; i < argc - 1; i++) {
691 GETSTRORGOTO(val, tmp, 3 + i, function_cleanup);
692 argv[i + 1] = wstrdup(tmp);
695 handle = dlopen(lib, RTLD_LAZY);
696 if (!handle) {
697 wwarning("could not find library %s", lib);
698 goto function_cleanup;
701 initFunc = dlsym(handle, "initWindowMaker");
702 if (!initFunc) {
703 wwarning("could not initialize library %s", lib);
704 goto function_cleanup;
706 initFunc(dpy, DefaultColormap(dpy, scr));
708 mainFunc = dlsym(handle, func);
709 if (!mainFunc) {
710 wwarning("could not find function %s::%s", lib, func);
711 goto function_cleanup;
713 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
715 if (!RConvertImage(rc, image, &pixmap)) {
716 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
717 goto function_cleanup;
719 texture->width = scrWidth;
720 texture->height = scrHeight;
721 texture->pixmap = pixmap;
722 success = 1;
724 function_cleanup:
725 if (argv) {
726 int i;
727 for (i = 0; i < argc; i++) {
728 wfree(argv[i]);
731 if (handle) {
732 dlclose(handle);
734 if (image) {
735 RReleaseImage(image);
737 if (!success) {
738 goto error;
740 #else
741 wwarning("function textures not supported");
742 goto error;
743 #endif
744 } else {
745 wwarning("invalid texture type %s", text);
746 goto error;
749 texture->spec = wstrdup(text);
751 return texture;
753 error:
754 if (texture)
755 wfree(texture);
756 if (texarray)
757 WMReleasePropList(texarray);
759 return NULL;
762 void freeTexture(BackgroundTexture * texture)
764 if (texture->solid) {
765 unsigned long pixel[1];
767 pixel[0] = texture->color.pixel;
768 /* dont free black/white pixels */
769 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
770 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
771 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
773 if (texture->pixmap) {
774 XFreePixmap(dpy, texture->pixmap);
776 wfree(texture->spec);
777 wfree(texture);
780 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
782 BackgroundTexture *newTexture = NULL;
783 int i;
785 /* unset the texture */
786 if (!texture) {
787 if (textures[workspace] != NULL) {
788 textures[workspace]->refcount--;
790 if (textures[workspace]->refcount == 0)
791 freeTexture(textures[workspace]);
793 textures[workspace] = NULL;
794 return;
797 if (textures[workspace]
798 && strcasecmp(textures[workspace]->spec, texture) == 0) {
799 /* texture did not change */
800 return;
803 /* check if the same texture is already created */
804 for (i = 0; i < *maxTextures; i++) {
805 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
806 newTexture = textures[i];
807 break;
811 if (!newTexture) {
812 /* create the texture */
813 newTexture = parseTexture(rc, texture);
815 if (!newTexture)
816 return;
818 if (textures[workspace] != NULL) {
820 textures[workspace]->refcount--;
822 if (textures[workspace]->refcount == 0)
823 freeTexture(textures[workspace]);
826 newTexture->refcount++;
827 textures[workspace] = newTexture;
829 if (*maxTextures < workspace)
830 *maxTextures = workspace;
833 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
835 Display *tmpDpy;
836 Pixmap copyP;
838 /* must open a new display or the RetainPermanent will
839 * leave stuff allocated in RContext unallocated after exit */
840 tmpDpy = XOpenDisplay(display);
841 if (!tmpDpy) {
842 wwarning("could not open display to update background image information");
844 return None;
845 } else {
846 XSync(dpy, False);
848 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
849 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
850 XSync(tmpDpy, False);
852 XSetCloseDownMode(tmpDpy, RetainPermanent);
853 XCloseDisplay(tmpDpy);
856 return copyP;
859 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
861 return 0;
864 void setPixmapProperty(Pixmap pixmap)
866 static Atom prop = 0;
867 Atom type;
868 int format;
869 unsigned long length, after;
870 unsigned char *data;
871 int mode;
873 if (!prop) {
874 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
877 XGrabServer(dpy);
879 /* Clear out the old pixmap */
880 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
881 &type, &format, &length, &after, &data);
883 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
884 XSetErrorHandler(dummyErrorHandler);
885 XKillClient(dpy, *((Pixmap *) data));
886 XSync(dpy, False);
887 XSetErrorHandler(NULL);
888 mode = PropModeReplace;
889 } else {
890 mode = PropModeAppend;
892 if (pixmap)
893 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
894 else
895 XDeleteProperty(dpy, root, prop);
897 XUngrabServer(dpy);
898 XFlush(dpy);
901 void changeTexture(BackgroundTexture * texture)
903 if (!texture) {
904 return;
907 if (texture->solid) {
908 XSetWindowBackground(dpy, root, texture->color.pixel);
909 } else {
910 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
912 XClearWindow(dpy, root);
914 XSync(dpy, False);
917 Pixmap pixmap;
919 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
921 setPixmapProperty(pixmap);
925 int readmsg(int fd, char *buffer, int size)
927 int count;
929 count = 0;
930 while (size > 0) {
931 count = read(fd, buffer, size);
932 if (count < 0)
933 return -1;
934 size -= count;
935 buffer += count;
936 *buffer = 0;
939 return size;
943 * Message Format:
944 * sizeSntexture_spec - sets the texture for workspace n
945 * sizeCn - change background texture to the one for workspace n
946 * sizePpath - set the pixmap search path
948 * n is 4 bytes
949 * size = 4 bytes for length of the message data
951 void helperLoop(RContext * rc)
953 BackgroundTexture *textures[WORKSPACE_COUNT];
954 int maxTextures = 0;
955 char buffer[2048], buf[8];
956 int size;
957 int errcount = 4;
959 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
961 while (1) {
962 int workspace;
964 /* get length of message */
965 if (readmsg(0, buffer, 4) < 0) {
966 wsyserror("error reading message from Window Maker");
967 errcount--;
968 if (errcount == 0) {
969 wfatal("quitting");
970 exit(1);
972 continue;
974 memcpy(buf, buffer, 4);
975 buf[4] = 0;
976 size = atoi(buf);
978 /* get message */
979 if (readmsg(0, buffer, size) < 0) {
980 wsyserror("error reading message from Window Maker");
981 errcount--;
982 if (errcount == 0) {
983 wfatal("quitting");
984 exit(1);
986 continue;
988 #ifdef DEBUG
989 printf("RECEIVED %s\n", buffer);
990 #endif
991 if (buffer[0] != 'P' && buffer[0] != 'K') {
992 memcpy(buf, &buffer[1], 4);
993 buf[4] = 0;
994 workspace = atoi(buf);
995 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
996 wwarning("received message with invalid workspace number %i\n", workspace);
997 continue;
1001 switch (buffer[0]) {
1002 case 'S':
1003 #ifdef DEBUG
1004 printf("set texture %s\n", &buffer[5]);
1005 #endif
1006 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1007 break;
1009 case 'C':
1010 #ifdef DEBUG
1011 printf("change texture %i\n", workspace);
1012 #endif
1013 if (!textures[workspace]) {
1014 changeTexture(textures[0]);
1015 } else {
1016 changeTexture(textures[workspace]);
1018 break;
1020 case 'P':
1021 #ifdef DEBUG
1022 printf("change pixmappath %s\n", &buffer[1]);
1023 #endif
1024 if (PixmapPath)
1025 wfree(PixmapPath);
1026 PixmapPath = wstrdup(&buffer[1]);
1027 break;
1029 case 'U':
1030 #ifdef DEBUG
1031 printf("unset workspace %i\n", workspace);
1032 #endif
1033 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1034 break;
1036 case 'K':
1037 #ifdef DEBUG
1038 printf("exit command\n");
1039 #endif
1040 exit(0);
1042 default:
1043 wwarning("unknown message received");
1044 break;
1049 void updateDomain(char *domain, char *key, char *texture)
1051 char *program = "wdwrite";
1053 /* here is a mem leak */
1054 system(wstrconcat("wdwrite ",
1055 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
1057 execlp(program, program, domain, key, texture, NULL);
1058 wwarning("warning could not run \"%s\"", program);
1061 static WMPropList *getValueForKey(char *domain, char *keyName)
1063 char *path;
1064 WMPropList *key, *val, *d;
1066 key = WMCreatePLString(keyName);
1068 /* try to find PixmapPath in user defaults */
1069 path = wdefaultspathfordomain(domain);
1070 d = WMReadPropListFromFile(path);
1071 if (!d) {
1072 wwarning("could not open domain file %s", path);
1074 wfree(path);
1076 if (d && !WMIsPLDictionary(d)) {
1077 WMReleasePropList(d);
1078 d = NULL;
1080 if (d) {
1081 val = WMGetFromPLDictionary(d, key);
1082 } else {
1083 val = NULL;
1085 /* try to find PixmapPath in global defaults */
1086 if (!val) {
1087 path = wglobaldefaultspathfordomain(domain);
1088 if (!path) {
1089 wwarning("could not locate file for domain %s", domain);
1090 d = NULL;
1091 } else {
1092 d = WMReadPropListFromFile(path);
1093 wfree(path);
1096 if (d && !WMIsPLDictionary(d)) {
1097 WMReleasePropList(d);
1098 d = NULL;
1100 if (d) {
1101 val = WMGetFromPLDictionary(d, key);
1103 } else {
1104 val = NULL;
1108 if (val)
1109 WMRetainPropList(val);
1111 WMReleasePropList(key);
1112 if (d)
1113 WMReleasePropList(d);
1115 return val;
1118 char *getPixmapPath(char *domain)
1120 WMPropList *val;
1121 char *ptr, *data;
1122 int len, i, count;
1124 val = getValueForKey(domain, "PixmapPath");
1126 if (!val || !WMIsPLArray(val)) {
1127 if (val)
1128 WMReleasePropList(val);
1129 return wstrdup("");
1132 count = WMGetPropListItemCount(val);
1133 len = 0;
1134 for (i = 0; i < count; i++) {
1135 WMPropList *v;
1137 v = WMGetFromPLArray(val, i);
1138 if (!v || !WMIsPLString(v)) {
1139 continue;
1141 len += strlen(WMGetFromPLString(v)) + 1;
1144 ptr = data = wmalloc(len + 1);
1145 *ptr = 0;
1147 for (i = 0; i < count; i++) {
1148 WMPropList *v;
1150 v = WMGetFromPLArray(val, i);
1151 if (!v || !WMIsPLString(v)) {
1152 continue;
1154 strcpy(ptr, WMGetFromPLString(v));
1156 ptr += strlen(WMGetFromPLString(v));
1157 *ptr = ':';
1158 ptr++;
1160 if (i > 0)
1161 ptr--;
1162 *(ptr--) = 0;
1164 WMReleasePropList(val);
1166 return data;
1169 char *getFullPixmapPath(char *file)
1171 char *tmp;
1173 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1174 int bsize = 512;
1175 char *path = wmalloc(bsize);
1177 while (!getcwd(path, bsize)) {
1178 bsize += bsize / 2;
1179 path = wrealloc(path, bsize);
1182 tmp = wstrconcat(path, "/");
1183 wfree(path);
1184 path = wstrconcat(tmp, file);
1185 wfree(tmp);
1187 return path;
1190 /* the file is in the PixmapPath */
1191 wfree(tmp);
1193 return wstrdup(file);
1196 void wAbort()
1198 wfatal("aborting");
1199 exit(1);
1202 void print_help()
1204 printf("Usage: %s [options] [image]\n", __progname);
1205 puts("Sets the workspace background to the specified image or a texture and");
1206 puts("optionally update Window Maker configuration");
1207 puts("");
1208 puts(" -display display to use");
1209 puts(" -d, --dither dither image");
1210 puts(" -m, --match match colors");
1211 puts(" -S, --smooth smooth scaled image");
1212 #ifdef XINERAMA
1213 puts(" -X, --xinerama stretch image across Xinerama heads");
1214 #endif
1215 puts(" -b, --back-color <color> background color");
1216 puts(" -t, --tile tile image");
1217 puts(" -e, --center center image");
1218 puts(" -s, --scale scale image (default)");
1219 puts(" -a, --maxscale scale image and keep aspect ratio");
1220 puts(" -u, --update-wmaker update WindowMaker domain database");
1221 puts(" -D, --update-domain <domain> update <domain> database");
1222 puts(" -c, --colors <cpc> colors per channel to use");
1223 puts(" -p, --parse <texture> proplist style texture specification");
1224 puts(" -w, --workspace <workspace> update background for the specified workspace");
1225 puts(" -v, --version show version of wmsetbg and exit");
1226 puts(" -h, --help show this help and exit");
1229 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1231 WMPropList *array, *val;
1232 char *value;
1233 int j;
1235 val = WMCreatePropListFromDescription(texture);
1236 if (!val) {
1237 wwarning("could not parse texture %s", texture);
1238 return;
1241 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1243 if (!array) {
1244 array = WMCreatePLArray(NULL, NULL);
1247 j = WMGetPropListItemCount(array);
1248 if (workspace >= j) {
1249 WMPropList *empty;
1251 empty = WMCreatePLArray(NULL, NULL);
1253 while (j++ < workspace - 1) {
1254 WMAddToPLArray(array, empty);
1256 WMAddToPLArray(array, val);
1257 } else {
1258 WMDeleteFromPLArray(array, workspace);
1259 WMInsertInPLArray(array, workspace, val);
1262 value = WMGetPropListDescription(array, False);
1263 updateDomain(domain, "WorkspaceSpecificBack", value);
1266 int main(int argc, char **argv)
1268 int i;
1269 int helperMode = 0;
1270 RContext *rc;
1271 RContextAttributes rattr;
1272 char *style = "spixmap";
1273 char *back_color = "gray20";
1274 char *image_name = NULL;
1275 char *domain = "WindowMaker";
1276 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1277 char *texture = NULL;
1278 int workspace = -1;
1280 signal(SIGINT, SIG_DFL);
1281 signal(SIGTERM, SIG_DFL);
1282 signal(SIGQUIT, SIG_DFL);
1283 signal(SIGSEGV, SIG_DFL);
1284 signal(SIGBUS, SIG_DFL);
1285 signal(SIGFPE, SIG_DFL);
1286 signal(SIGABRT, SIG_DFL);
1287 signal(SIGHUP, SIG_DFL);
1288 signal(SIGPIPE, SIG_DFL);
1289 signal(SIGCHLD, SIG_DFL);
1291 WMInitializeApplication("wmsetbg", &argc, argv);
1293 for (i = 1; i < argc; i++) {
1294 if (strcmp(argv[i], "-helper") == 0) {
1295 helperMode = 1;
1296 } else if (strcmp(argv[i], "-display") == 0) {
1297 i++;
1298 if (i >= argc) {
1299 wfatal("too few arguments for %s\n", argv[i - 1]);
1300 exit(1);
1302 display = argv[i];
1303 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1304 style = "spixmap";
1305 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1306 style = "tpixmap";
1307 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1308 style = "cpixmap";
1309 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1310 style = "mpixmap";
1311 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1312 render_mode = RDitheredRendering;
1313 obey_user++;
1314 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1315 render_mode = RBestMatchRendering;
1316 obey_user++;
1317 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1318 smooth = True;
1319 #ifdef XINERAMA
1320 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1321 xineStretch = True;
1322 #endif
1323 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1324 update++;
1325 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1326 update++;
1327 i++;
1328 if (i >= argc) {
1329 wfatal("too few arguments for %s\n", argv[i - 1]);
1330 exit(1);
1332 domain = wstrdup(argv[i]);
1333 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1334 i++;
1335 if (i >= argc) {
1336 wfatal("too few arguments for %s\n", argv[i - 1]);
1337 exit(1);
1339 if (sscanf(argv[i], "%i", &cpc) != 1) {
1340 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1341 exit(1);
1343 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1344 i++;
1345 if (i >= argc) {
1346 wfatal("too few arguments for %s\n", argv[i - 1]);
1347 exit(1);
1349 back_color = argv[i];
1350 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1351 i++;
1352 if (i >= argc) {
1353 wfatal("too few arguments for %s\n", argv[i - 1]);
1354 exit(1);
1356 texture = argv[i];
1357 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1358 i++;
1359 if (i >= argc) {
1360 wfatal("too few arguments for %s\n", argv[i - 1]);
1361 exit(1);
1363 if (sscanf(argv[i], "%i", &workspace) != 1) {
1364 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1365 exit(1);
1367 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1368 printf(PROG_VERSION);
1369 exit(0);
1370 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1371 print_help();
1372 exit(0);
1373 } else if (argv[i][0] != '-') {
1374 image_name = argv[i];
1375 } else {
1376 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1377 printf("Try '%s --help' for more information\n", __progname);
1378 exit(1);
1381 if (!image_name && !texture && !helperMode) {
1382 printf("%s: you must specify a image file name or a texture\n", __progname);
1383 printf("Try '%s --help' for more information\n", __progname);
1384 exit(1);
1387 PixmapPath = getPixmapPath(domain);
1388 if (!smooth) {
1389 WMPropList *val;
1390 /* carlos, don't remove this */
1391 #if 0 /* some problem with Alpha... TODO: check if its right */
1392 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1393 #else
1394 val = getValueForKey(domain, "SmoothWorkspaceBack");
1395 #endif
1397 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1398 smooth = True;
1401 dpy = XOpenDisplay(display);
1402 if (!dpy) {
1403 wfatal("could not open display");
1404 exit(1);
1406 #if 0
1407 XSynchronize(dpy, 1);
1408 #endif
1410 root = DefaultRootWindow(dpy);
1412 scr = DefaultScreen(dpy);
1414 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1415 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1416 scrX = scrY = 0;
1418 initXinerama();
1420 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1421 render_mode = RDitheredRendering;
1423 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1424 rattr.render_mode = render_mode;
1425 rattr.colors_per_channel = cpc;
1426 rattr.standard_colormap_mode = RCreateStdColormap;
1428 rc = RCreateContext(dpy, scr, &rattr);
1430 if (!rc) {
1431 rattr.standard_colormap_mode = RIgnoreStdColormap;
1432 rc = RCreateContext(dpy, scr, &rattr);
1435 if (!rc) {
1436 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1437 exit(1);
1440 if (helperMode) {
1441 /* lower priority, so that it wont use all the CPU */
1442 nice(15);
1444 helperLoop(rc);
1445 } else {
1446 BackgroundTexture *tex;
1447 char buffer[4098];
1449 if (!texture) {
1450 char *image_path = getFullPixmapPath(image_name);
1452 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1453 wfree(image_path);
1454 texture = (char *)buffer;
1457 if (update && workspace < 0) {
1458 updateDomain(domain, "WorkspaceBack", texture);
1461 tex = parseTexture(rc, texture);
1462 if (!tex)
1463 exit(1);
1465 if (workspace < 0)
1466 changeTexture(tex);
1467 else {
1468 /* always update domain */
1469 changeTextureForWorkspace(domain, texture, workspace);
1473 return 0;