Add GLOBAL_DEFAULTS_SUBDIR and fix a syntax error in wmlib/Makefile.am
[wmaker-crm.git] / util / wmsetbg.c
blobd5328bca3a28fcf23cde68b4166112b5eea228ef
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;
84 Pixmap CurrentPixmap = None;
85 char *PixmapPath = NULL;
87 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
89 typedef struct BackgroundTexture {
90 int refcount;
92 int solid;
94 char *spec;
96 XColor color;
97 Pixmap pixmap; /* for all textures, including solid */
98 int width; /* size of the pixmap */
99 int height;
100 } BackgroundTexture;
102 void initXinerama()
104 xineInfo.screens = NULL;
105 xineInfo.count = 0;
106 #ifdef XINERAMA
107 # ifdef SOLARIS_XINERAMA
108 if (XineramaGetState(dpy, scr)) {
109 XRectangle head[MAXFRAMEBUFFERS];
110 unsigned char hints[MAXFRAMEBUFFERS];
111 int i;
113 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
115 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
117 for (i = 0; i < xineInfo.count; i++) {
118 xineInfo.screens[i].pos.x = head[i].x;
119 xineInfo.screens[i].pos.y = head[i].y;
120 xineInfo.screens[i].size.width = head[i].width;
121 xineInfo.screens[i].size.height = head[i].height;
125 # else /* !SOLARIS_XINERAMA */
126 if (XineramaIsActive(dpy)) {
127 XineramaScreenInfo *xine_screens;
128 int i;
130 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
132 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
134 for (i = 0; i < xineInfo.count; i++) {
135 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
136 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
137 xineInfo.screens[i].size.width = xine_screens[i].width;
138 xineInfo.screens[i].size.height = xine_screens[i].height;
140 XFree(xine_screens);
142 # endif /* !SOLARIS_XINERAMA */
143 #endif /* XINERAMA */
146 RImage *loadImage(RContext * rc, char *file)
148 char *path;
149 RImage *image;
151 if (access(file, F_OK) != 0) {
152 path = wfindfile(PixmapPath, file);
153 if (!path) {
154 wwarning("%s:could not find image file used in texture", file);
155 return NULL;
157 } else {
158 path = wstrdup(file);
161 image = RLoadImage(rc, path, 0);
162 if (!image) {
163 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
165 wfree(path);
167 return image;
170 static void
171 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
172 int x, int y, int width, int height)
174 int w, h;
175 Bool fimage = False;
177 switch (toupper(type)) {
178 case 'S':
179 case 'M':
180 if (toupper(type) == 'S') {
181 w = width;
182 h = height;
183 } else {
184 if (image->width * height > image->height * width) {
185 w = width;
186 h = (width * image->height) / image->width;
187 } else {
188 w = (height * image->width) / image->height;
189 h = height;
193 if (w != image->width || h != image->height) {
194 RImage *simage;
196 if (smooth) {
197 simage = RSmoothScaleImage(image, w, h);
198 } else {
199 simage = RScaleImage(image, w, h);
202 if (!simage) {
203 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
204 return;
206 fimage = True;
207 image = simage;
210 /* fall through */
211 case 'C':
213 Pixmap pixmap;
215 if (!RConvertImage(rc, image, &pixmap)) {
216 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
217 return;
220 if (image->width != width || image->height != height) {
221 int sx, sy, w, h;
223 if (image->height < height) {
224 h = image->height;
225 y += (height - h) / 2;
226 sy = 0;
227 } else {
228 sy = (image->height - height) / 2;
229 h = height;
231 if (image->width < width) {
232 w = image->width;
233 x += (width - w) / 2;
234 sx = 0;
235 } else {
236 sx = (image->width - width) / 2;
237 w = width;
240 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
241 } else
242 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
243 x, y);
245 XFreePixmap(dpy, pixmap);
246 if (fimage) {
247 RReleaseImage(image);
250 break;
254 BackgroundTexture *parseTexture(RContext * rc, char *text)
256 BackgroundTexture *texture = NULL;
257 WMPropList *texarray;
258 WMPropList *val;
259 int count;
260 char *tmp;
261 char *type;
263 #define GETSTRORGOTO(val, str, i, label) \
264 val = WMGetFromPLArray(texarray, i);\
265 if (!WMIsPLString(val)) {\
266 wwarning("could not parse texture %s", text);\
267 goto label;\
269 str = WMGetFromPLString(val)
271 texarray = WMCreatePropListFromDescription(text);
272 if (!texarray || !WMIsPLArray(texarray)
273 || (count = WMGetPropListItemCount(texarray)) < 2) {
275 wwarning("could not parse texture %s", text);
276 if (texarray)
277 WMReleasePropList(texarray);
278 return NULL;
281 texture = wmalloc(sizeof(BackgroundTexture));
282 memset(texture, 0, sizeof(BackgroundTexture));
284 GETSTRORGOTO(val, type, 0, error);
286 if (strcasecmp(type, "solid") == 0) {
287 XColor color;
288 Pixmap pixmap;
290 texture->solid = 1;
292 GETSTRORGOTO(val, tmp, 1, error);
294 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
295 wwarning("could not parse color %s in texture %s", tmp, text);
296 goto error;
298 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
300 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
301 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
302 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
304 texture->pixmap = pixmap;
305 texture->color = color;
306 texture->width = 8;
307 texture->height = 8;
308 } else if (strcasecmp(type, "vgradient") == 0
309 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
310 XColor color;
311 RColor color1, color2;
312 RImage *image;
313 Pixmap pixmap;
314 int gtype;
315 int iwidth, iheight;
317 GETSTRORGOTO(val, tmp, 1, error);
319 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
320 wwarning("could not parse color %s in texture %s", tmp, text);
321 goto error;
324 color1.red = color.red >> 8;
325 color1.green = color.green >> 8;
326 color1.blue = color.blue >> 8;
328 GETSTRORGOTO(val, tmp, 2, error);
330 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
331 wwarning("could not parse color %s in texture %s", tmp, text);
332 goto error;
335 color2.red = color.red >> 8;
336 color2.green = color.green >> 8;
337 color2.blue = color.blue >> 8;
339 switch (type[0]) {
340 case 'h':
341 case 'H':
342 gtype = RHorizontalGradient;
343 iwidth = scrWidth;
344 iheight = 32;
345 break;
346 case 'V':
347 case 'v':
348 gtype = RVerticalGradient;
349 iwidth = 32;
350 iheight = scrHeight;
351 break;
352 default:
353 gtype = RDiagonalGradient;
354 iwidth = scrWidth;
355 iheight = scrHeight;
356 break;
359 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
361 if (!image) {
362 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
363 goto error;
366 if (!RConvertImage(rc, image, &pixmap)) {
367 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
368 RReleaseImage(image);
369 goto error;
372 texture->width = image->width;
373 texture->height = image->height;
374 RReleaseImage(image);
376 texture->pixmap = pixmap;
377 } else if (strcasecmp(type, "mvgradient") == 0
378 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
379 XColor color;
380 RColor **colors;
381 RImage *image;
382 Pixmap pixmap;
383 int i, j;
384 int gtype;
385 int iwidth, iheight;
387 colors = malloc(sizeof(RColor *) * (count - 1));
388 if (!colors) {
389 wwarning("out of memory while parsing texture");
390 goto error;
392 memset(colors, 0, sizeof(RColor *) * (count - 1));
394 for (i = 2; i < count; i++) {
395 val = WMGetFromPLArray(texarray, i);
396 if (!WMIsPLString(val)) {
397 wwarning("could not parse texture %s", text);
399 for (j = 0; colors[j] != NULL; j++)
400 wfree(colors[j]);
401 wfree(colors);
402 goto error;
404 tmp = WMGetFromPLString(val);
406 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
407 wwarning("could not parse color %s in texture %s", tmp, text);
409 for (j = 0; colors[j] != NULL; j++)
410 wfree(colors[j]);
411 wfree(colors);
412 goto error;
414 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
415 wwarning("out of memory while parsing texture");
417 for (j = 0; colors[j] != NULL; j++)
418 wfree(colors[j]);
419 wfree(colors);
420 goto error;
423 colors[i - 2]->red = color.red >> 8;
424 colors[i - 2]->green = color.green >> 8;
425 colors[i - 2]->blue = color.blue >> 8;
428 switch (type[1]) {
429 case 'h':
430 case 'H':
431 gtype = RHorizontalGradient;
432 iwidth = scrWidth;
433 iheight = 32;
434 break;
435 case 'V':
436 case 'v':
437 gtype = RVerticalGradient;
438 iwidth = 32;
439 iheight = scrHeight;
440 break;
441 default:
442 gtype = RDiagonalGradient;
443 iwidth = scrWidth;
444 iheight = scrHeight;
445 break;
448 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
450 for (j = 0; colors[j] != NULL; j++)
451 wfree(colors[j]);
452 wfree(colors);
454 if (!image) {
455 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
456 goto error;
459 if (!RConvertImage(rc, image, &pixmap)) {
460 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
461 RReleaseImage(image);
462 goto error;
465 texture->width = image->width;
466 texture->height = image->height;
467 RReleaseImage(image);
469 texture->pixmap = pixmap;
470 } else if (strcasecmp(type, "cpixmap") == 0
471 || strcasecmp(type, "spixmap") == 0
472 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
473 XColor color;
474 Pixmap pixmap = None;
475 RImage *image = NULL;
476 int iwidth, iheight;
477 RColor rcolor;
479 GETSTRORGOTO(val, tmp, 1, error);
481 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
482 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
485 if (!pixmap) {
486 image = loadImage(rc, tmp);
487 if (!image) {
488 goto error;
490 iwidth = image->width;
491 iheight = image->height;
494 GETSTRORGOTO(val, tmp, 2, error);
496 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
497 wwarning("could not parse color %s in texture %s\n", tmp, text);
498 RReleaseImage(image);
499 goto error;
501 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
502 rcolor.red = color.red >> 8;
503 rcolor.green = color.green >> 8;
504 rcolor.blue = color.blue >> 8;
505 RGetClosestXColor(rc, &rcolor, &color);
506 } else {
507 rcolor.red = 0;
508 rcolor.green = 0;
509 rcolor.blue = 0;
511 /* for images with a transparent color */
512 if (image->data[3]) {
513 RCombineImageWithColor(image, &rcolor);
516 switch (toupper(type[0])) {
517 case 'T':
518 texture->width = iwidth;
519 texture->height = iheight;
520 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
521 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
522 RReleaseImage(image);
523 goto error;
525 if (image)
526 RReleaseImage(image);
528 texture->pixmap = pixmap;
529 texture->color = color;
530 break;
531 case 'S':
532 case 'M':
533 case 'C':
535 Pixmap tpixmap =
536 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
537 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
538 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
540 texture->pixmap = tpixmap;
541 texture->color = color;
542 texture->width = scrWidth;
543 texture->height = scrHeight;
545 #ifdef XINERAMA
546 if (xineInfo.count) {
547 int i;
548 for (i = 0; i < xineInfo.count; ++i) {
549 applyImage(rc, texture, image, type[0],
550 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
551 xineInfo.screens[i].size.width,
552 xineInfo.screens[i].size.height);
554 } else {
555 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
557 #else /* !XINERAMA */
558 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
559 #endif /* !XINERAMA */
560 RReleaseImage(image);
562 break;
564 } else if (strcasecmp(type, "thgradient") == 0
565 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
566 XColor color;
567 RColor color1, color2;
568 RImage *image;
569 RImage *gradient;
570 RImage *tiled;
571 Pixmap pixmap;
572 int opaq;
573 char *file;
574 int gtype;
575 int twidth, theight;
577 GETSTRORGOTO(val, file, 1, error);
579 GETSTRORGOTO(val, tmp, 2, error);
581 opaq = atoi(tmp);
583 GETSTRORGOTO(val, tmp, 3, error);
585 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
586 wwarning("could not parse color %s in texture %s", tmp, text);
587 goto error;
590 color1.red = color.red >> 8;
591 color1.green = color.green >> 8;
592 color1.blue = color.blue >> 8;
594 GETSTRORGOTO(val, tmp, 4, error);
596 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
597 wwarning("could not parse color %s in texture %s", tmp, text);
598 goto error;
601 color2.red = color.red >> 8;
602 color2.green = color.green >> 8;
603 color2.blue = color.blue >> 8;
605 image = loadImage(rc, file);
606 if (!image) {
607 goto error;
610 switch (type[1]) {
611 case 'h':
612 case 'H':
613 gtype = RHorizontalGradient;
614 twidth = scrWidth;
615 theight = image->height > scrHeight ? scrHeight : image->height;
616 break;
617 case 'V':
618 case 'v':
619 gtype = RVerticalGradient;
620 twidth = image->width > scrWidth ? scrWidth : image->width;
621 theight = scrHeight;
622 break;
623 default:
624 gtype = RDiagonalGradient;
625 twidth = scrWidth;
626 theight = scrHeight;
627 break;
629 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
631 if (!gradient) {
632 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
633 RReleaseImage(gradient);
634 RReleaseImage(image);
635 goto error;
638 tiled = RMakeTiledImage(image, twidth, theight);
639 if (!tiled) {
640 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
641 RReleaseImage(gradient);
642 RReleaseImage(image);
643 goto error;
645 RReleaseImage(image);
647 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
648 RReleaseImage(gradient);
650 if (!RConvertImage(rc, tiled, &pixmap)) {
651 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
652 RReleaseImage(tiled);
653 goto error;
655 texture->width = tiled->width;
656 texture->height = tiled->height;
658 RReleaseImage(tiled);
660 texture->pixmap = pixmap;
661 } else if (strcasecmp(type, "function") == 0) {
662 #ifdef HAVE_DLFCN_H
663 void (*initFunc) (Display *, Colormap);
664 RImage *(*mainFunc) (int, char **, int, int, int);
665 Pixmap pixmap;
666 RImage *image = 0;
667 int success = 0;
668 char *lib, *func, **argv = 0;
669 void *handle = 0;
670 int i, argc;
672 if (count < 3)
673 goto function_cleanup;
675 /* get the library name */
676 GETSTRORGOTO(val, lib, 1, function_cleanup);
678 /* get the function name */
679 GETSTRORGOTO(val, func, 2, function_cleanup);
681 argc = count - 2;
682 argv = (char **)wmalloc(argc * sizeof(char *));
684 /* get the parameters */
685 argv[0] = func;
686 for (i = 0; i < argc - 1; i++) {
687 GETSTRORGOTO(val, tmp, 3 + i, function_cleanup);
688 argv[i + 1] = wstrdup(tmp);
691 handle = dlopen(lib, RTLD_LAZY);
692 if (!handle) {
693 wwarning("could not find library %s", lib);
694 goto function_cleanup;
697 initFunc = dlsym(handle, "initWindowMaker");
698 if (!initFunc) {
699 wwarning("could not initialize library %s", lib);
700 goto function_cleanup;
702 initFunc(dpy, DefaultColormap(dpy, scr));
704 mainFunc = dlsym(handle, func);
705 if (!mainFunc) {
706 wwarning("could not find function %s::%s", lib, func);
707 goto function_cleanup;
709 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
711 if (!RConvertImage(rc, image, &pixmap)) {
712 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
713 goto function_cleanup;
715 texture->width = scrWidth;
716 texture->height = scrHeight;
717 texture->pixmap = pixmap;
718 success = 1;
720 function_cleanup:
721 if (argv) {
722 int i;
723 for (i = 0; i < argc; i++) {
724 wfree(argv[i]);
727 if (handle) {
728 dlclose(handle);
730 if (image) {
731 RReleaseImage(image);
733 if (!success) {
734 goto error;
736 #else
737 wwarning("function textures not supported");
738 goto error;
739 #endif
740 } else {
741 wwarning("invalid texture type %s", text);
742 goto error;
745 texture->spec = wstrdup(text);
747 return texture;
749 error:
750 if (texture)
751 wfree(texture);
752 if (texarray)
753 WMReleasePropList(texarray);
755 return NULL;
758 void freeTexture(BackgroundTexture * texture)
760 if (texture->solid) {
761 unsigned long pixel[1];
763 pixel[0] = texture->color.pixel;
764 /* dont free black/white pixels */
765 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
766 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
767 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
769 if (texture->pixmap) {
770 XFreePixmap(dpy, texture->pixmap);
772 wfree(texture->spec);
773 wfree(texture);
776 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
778 BackgroundTexture *newTexture = NULL;
779 int i;
781 /* unset the texture */
782 if (!texture) {
783 if (textures[workspace] != NULL) {
784 textures[workspace]->refcount--;
786 if (textures[workspace]->refcount == 0)
787 freeTexture(textures[workspace]);
789 textures[workspace] = NULL;
790 return;
793 if (textures[workspace]
794 && strcasecmp(textures[workspace]->spec, texture) == 0) {
795 /* texture did not change */
796 return;
799 /* check if the same texture is already created */
800 for (i = 0; i < *maxTextures; i++) {
801 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
802 newTexture = textures[i];
803 break;
807 if (!newTexture) {
808 /* create the texture */
809 newTexture = parseTexture(rc, texture);
811 if (!newTexture)
812 return;
814 if (textures[workspace] != NULL) {
816 textures[workspace]->refcount--;
818 if (textures[workspace]->refcount == 0)
819 freeTexture(textures[workspace]);
822 newTexture->refcount++;
823 textures[workspace] = newTexture;
825 if (*maxTextures < workspace)
826 *maxTextures = workspace;
829 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
831 Display *tmpDpy;
832 Pixmap copyP;
834 /* must open a new display or the RetainPermanent will
835 * leave stuff allocated in RContext unallocated after exit */
836 tmpDpy = XOpenDisplay(display);
837 if (!tmpDpy) {
838 wwarning("could not open display to update background image information");
840 return None;
841 } else {
842 XSync(dpy, False);
844 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
845 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
846 XSync(tmpDpy, False);
848 XSetCloseDownMode(tmpDpy, RetainPermanent);
849 XCloseDisplay(tmpDpy);
852 return copyP;
855 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
857 return 0;
860 void setPixmapProperty(Pixmap pixmap)
862 static Atom prop = 0;
863 Atom type;
864 int format;
865 unsigned long length, after;
866 unsigned char *data;
867 int mode;
869 if (!prop) {
870 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
873 XGrabServer(dpy);
875 /* Clear out the old pixmap */
876 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
877 &type, &format, &length, &after, &data);
879 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
880 XSetErrorHandler(dummyErrorHandler);
881 XKillClient(dpy, *((Pixmap *) data));
882 XSync(dpy, False);
883 XSetErrorHandler(NULL);
884 mode = PropModeReplace;
885 } else {
886 mode = PropModeAppend;
888 if (pixmap)
889 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
890 else
891 XDeleteProperty(dpy, root, prop);
893 XUngrabServer(dpy);
894 XFlush(dpy);
897 void changeTexture(BackgroundTexture * texture)
899 if (!texture) {
900 return;
903 if (texture->solid) {
904 XSetWindowBackground(dpy, root, texture->color.pixel);
905 } else {
906 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
908 XClearWindow(dpy, root);
910 XSync(dpy, False);
913 Pixmap pixmap;
915 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
917 setPixmapProperty(pixmap);
921 int readmsg(int fd, char *buffer, int size)
923 int count;
925 count = 0;
926 while (size > 0) {
927 count = read(fd, buffer, size);
928 if (count < 0)
929 return -1;
930 size -= count;
931 buffer += count;
932 *buffer = 0;
935 return size;
939 * Message Format:
940 * sizeSntexture_spec - sets the texture for workspace n
941 * sizeCn - change background texture to the one for workspace n
942 * sizePpath - set the pixmap search path
944 * n is 4 bytes
945 * size = 4 bytes for length of the message data
947 void helperLoop(RContext * rc)
949 BackgroundTexture *textures[WORKSPACE_COUNT];
950 int maxTextures = 0;
951 char buffer[2048], buf[8];
952 int size;
953 int errcount = 4;
955 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
957 while (1) {
958 int workspace;
960 /* get length of message */
961 if (readmsg(0, buffer, 4) < 0) {
962 wsyserror("error reading message from Window Maker");
963 errcount--;
964 if (errcount == 0) {
965 wfatal("quitting");
966 exit(1);
968 continue;
970 memcpy(buf, buffer, 4);
971 buf[4] = 0;
972 size = atoi(buf);
974 /* get message */
975 if (readmsg(0, buffer, size) < 0) {
976 wsyserror("error reading message from Window Maker");
977 errcount--;
978 if (errcount == 0) {
979 wfatal("quitting");
980 exit(1);
982 continue;
984 #ifdef DEBUG
985 printf("RECEIVED %s\n", buffer);
986 #endif
987 if (buffer[0] != 'P' && buffer[0] != 'K') {
988 memcpy(buf, &buffer[1], 4);
989 buf[4] = 0;
990 workspace = atoi(buf);
991 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
992 wwarning("received message with invalid workspace number %i\n", workspace);
993 continue;
997 switch (buffer[0]) {
998 case 'S':
999 #ifdef DEBUG
1000 printf("set texture %s\n", &buffer[5]);
1001 #endif
1002 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1003 break;
1005 case 'C':
1006 #ifdef DEBUG
1007 printf("change texture %i\n", workspace);
1008 #endif
1009 if (!textures[workspace]) {
1010 changeTexture(textures[0]);
1011 } else {
1012 changeTexture(textures[workspace]);
1014 break;
1016 case 'P':
1017 #ifdef DEBUG
1018 printf("change pixmappath %s\n", &buffer[1]);
1019 #endif
1020 if (PixmapPath)
1021 wfree(PixmapPath);
1022 PixmapPath = wstrdup(&buffer[1]);
1023 break;
1025 case 'U':
1026 #ifdef DEBUG
1027 printf("unset workspace %i\n", workspace);
1028 #endif
1029 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1030 break;
1032 case 'K':
1033 #ifdef DEBUG
1034 printf("exit command\n");
1035 #endif
1036 exit(0);
1038 default:
1039 wwarning("unknown message received");
1040 break;
1045 void updateDomain(char *domain, char *key, char *texture)
1047 char *program = "wdwrite";
1049 /* here is a mem leak */
1050 system(wstrconcat("wdwrite ",
1051 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
1053 execlp(program, program, domain, key, texture, NULL);
1054 wwarning("warning could not run \"%s\"", program);
1057 char *globalDefaultsPathForDomain(char *domain)
1059 char path[1024];
1061 sprintf(path, "%s/%s/%s", SYSCONFDIR, GLOBAL_DEFAULTS_SUBDIR, domain);
1063 return wstrdup(path);
1066 static WMPropList *getValueForKey(char *domain, char *keyName)
1068 char *path;
1069 WMPropList *key, *val, *d;
1071 key = WMCreatePLString(keyName);
1073 /* try to find PixmapPath in user defaults */
1074 path = wdefaultspathfordomain(domain);
1075 d = WMReadPropListFromFile(path);
1076 if (!d) {
1077 wwarning("could not open domain file %s", path);
1079 wfree(path);
1081 if (d && !WMIsPLDictionary(d)) {
1082 WMReleasePropList(d);
1083 d = NULL;
1085 if (d) {
1086 val = WMGetFromPLDictionary(d, key);
1087 } else {
1088 val = NULL;
1090 /* try to find PixmapPath in global defaults */
1091 if (!val) {
1092 path = globalDefaultsPathForDomain(domain);
1093 if (!path) {
1094 wwarning("could not locate file for domain %s", domain);
1095 d = NULL;
1096 } else {
1097 d = WMReadPropListFromFile(path);
1098 wfree(path);
1101 if (d && !WMIsPLDictionary(d)) {
1102 WMReleasePropList(d);
1103 d = NULL;
1105 if (d) {
1106 val = WMGetFromPLDictionary(d, key);
1108 } else {
1109 val = NULL;
1113 if (val)
1114 WMRetainPropList(val);
1116 WMReleasePropList(key);
1117 if (d)
1118 WMReleasePropList(d);
1120 return val;
1123 char *getPixmapPath(char *domain)
1125 WMPropList *val;
1126 char *ptr, *data;
1127 int len, i, count;
1129 val = getValueForKey(domain, "PixmapPath");
1131 if (!val || !WMIsPLArray(val)) {
1132 if (val)
1133 WMReleasePropList(val);
1134 return wstrdup("");
1137 count = WMGetPropListItemCount(val);
1138 len = 0;
1139 for (i = 0; i < count; i++) {
1140 WMPropList *v;
1142 v = WMGetFromPLArray(val, i);
1143 if (!v || !WMIsPLString(v)) {
1144 continue;
1146 len += strlen(WMGetFromPLString(v)) + 1;
1149 ptr = data = wmalloc(len + 1);
1150 *ptr = 0;
1152 for (i = 0; i < count; i++) {
1153 WMPropList *v;
1155 v = WMGetFromPLArray(val, i);
1156 if (!v || !WMIsPLString(v)) {
1157 continue;
1159 strcpy(ptr, WMGetFromPLString(v));
1161 ptr += strlen(WMGetFromPLString(v));
1162 *ptr = ':';
1163 ptr++;
1165 if (i > 0)
1166 ptr--;
1167 *(ptr--) = 0;
1169 WMReleasePropList(val);
1171 return data;
1174 char *getFullPixmapPath(char *file)
1176 char *tmp;
1178 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1179 int bsize = 512;
1180 char *path = wmalloc(bsize);
1182 while (!getcwd(path, bsize)) {
1183 bsize += bsize / 2;
1184 path = wrealloc(path, bsize);
1187 tmp = wstrconcat(path, "/");
1188 wfree(path);
1189 path = wstrconcat(tmp, file);
1190 wfree(tmp);
1192 return path;
1195 /* the file is in the PixmapPath */
1196 wfree(tmp);
1198 return wstrdup(file);
1201 void wAbort()
1203 wfatal("aborting");
1204 exit(1);
1207 void print_help(char *ProgName)
1209 printf("Usage: %s [options] [image]\n", ProgName);
1210 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1211 puts("");
1212 #define P(m) puts(m)
1213 P(" -display display to use");
1214 P(" -d, --dither dither image");
1215 P(" -m, --match match colors");
1216 P(" -S, --smooth smooth scaled image");
1217 P(" -b, --back-color <color> background color");
1218 P(" -t, --tile tile image");
1219 P(" -e, --center center image");
1220 P(" -s, --scale scale image (default)");
1221 P(" -a, --maxscale scale image and keep aspect ratio");
1222 P(" -u, --update-wmaker update WindowMaker domain database");
1223 P(" -D, --update-domain <domain> update <domain> database");
1224 P(" -c, --colors <cpc> colors per channel to use");
1225 P(" -p, --parse <texture> proplist style texture specification");
1226 P(" -w, --workspace <workspace> update background for the specified workspace");
1227 P(" --version show version of wmsetbg and exit");
1228 P(" --help show this help and exit");
1229 #undef P
1232 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1234 WMPropList *array, *val;
1235 char *value;
1236 int j;
1238 val = WMCreatePropListFromDescription(texture);
1239 if (!val) {
1240 wwarning("could not parse texture %s", texture);
1241 return;
1244 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1246 if (!array) {
1247 array = WMCreatePLArray(NULL, NULL);
1250 j = WMGetPropListItemCount(array);
1251 if (workspace >= j) {
1252 WMPropList *empty;
1254 empty = WMCreatePLArray(NULL, NULL);
1256 while (j++ < workspace - 1) {
1257 WMAddToPLArray(array, empty);
1259 WMAddToPLArray(array, val);
1260 } else {
1261 WMDeleteFromPLArray(array, workspace);
1262 WMInsertInPLArray(array, workspace, val);
1265 value = WMGetPropListDescription(array, False);
1266 updateDomain(domain, "WorkspaceSpecificBack", value);
1269 int main(int argc, char **argv)
1271 int i;
1272 int helperMode = 0;
1273 RContext *rc;
1274 RContextAttributes rattr;
1275 char *style = "spixmap";
1276 char *back_color = "gray20";
1277 char *image_name = NULL;
1278 char *domain = "WindowMaker";
1279 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1280 char *texture = NULL;
1281 int workspace = -1;
1283 signal(SIGINT, SIG_DFL);
1284 signal(SIGTERM, SIG_DFL);
1285 signal(SIGQUIT, SIG_DFL);
1286 signal(SIGSEGV, SIG_DFL);
1287 signal(SIGBUS, SIG_DFL);
1288 signal(SIGFPE, SIG_DFL);
1289 signal(SIGABRT, SIG_DFL);
1290 signal(SIGHUP, SIG_DFL);
1291 signal(SIGPIPE, SIG_DFL);
1292 signal(SIGCHLD, SIG_DFL);
1294 WMInitializeApplication("wmsetbg", &argc, argv);
1296 for (i = 1; i < argc; i++) {
1297 if (strcmp(argv[i], "-helper") == 0) {
1298 helperMode = 1;
1299 } else if (strcmp(argv[i], "-display") == 0) {
1300 i++;
1301 if (i >= argc) {
1302 wfatal("too few arguments for %s\n", argv[i - 1]);
1303 exit(1);
1305 display = argv[i];
1306 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1307 style = "spixmap";
1308 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1309 style = "tpixmap";
1310 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1311 style = "cpixmap";
1312 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1313 style = "mpixmap";
1314 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1315 render_mode = RDitheredRendering;
1316 obey_user++;
1317 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1318 render_mode = RBestMatchRendering;
1319 obey_user++;
1320 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1321 smooth = True;
1322 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1323 update++;
1324 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1325 update++;
1326 i++;
1327 if (i >= argc) {
1328 wfatal("too few arguments for %s\n", argv[i - 1]);
1329 exit(1);
1331 domain = wstrdup(argv[i]);
1332 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1333 i++;
1334 if (i >= argc) {
1335 wfatal("too few arguments for %s\n", argv[i - 1]);
1336 exit(1);
1338 if (sscanf(argv[i], "%i", &cpc) != 1) {
1339 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1340 exit(1);
1342 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1343 i++;
1344 if (i >= argc) {
1345 wfatal("too few arguments for %s\n", argv[i - 1]);
1346 exit(1);
1348 back_color = argv[i];
1349 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1350 i++;
1351 if (i >= argc) {
1352 wfatal("too few arguments for %s\n", argv[i - 1]);
1353 exit(1);
1355 texture = argv[i];
1356 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1357 i++;
1358 if (i >= argc) {
1359 wfatal("too few arguments for %s\n", argv[i - 1]);
1360 exit(1);
1362 if (sscanf(argv[i], "%i", &workspace) != 1) {
1363 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1364 exit(1);
1366 } else if (strcmp(argv[i], "--version") == 0) {
1368 printf(PROG_VERSION);
1369 exit(0);
1371 } else if (strcmp(argv[i], "--help") == 0) {
1372 print_help(argv[0]);
1373 exit(0);
1374 } else if (argv[i][0] != '-') {
1375 image_name = argv[i];
1376 } else {
1377 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1378 printf("Try '%s --help' for more information\n", argv[0]);
1379 exit(1);
1382 if (!image_name && !texture && !helperMode) {
1383 printf("%s: you must specify a image file name or a texture\n", argv[0]);
1384 printf("Try '%s --help' for more information\n", argv[0]);
1385 exit(1);
1388 PixmapPath = getPixmapPath(domain);
1389 if (!smooth) {
1390 WMPropList *val;
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;