util: Value stored to 'count' is never read
[wmaker-crm.git] / util / wmsetbg.c
blob775cc19435ebe7b9f6e19f84f93626eb6847f3b5
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 along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * TODO: rewrite, too dirty
27 #include "config.h"
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 <strings.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <ctype.h>
42 #ifdef USE_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_STDNORETURN
51 #include <stdnoreturn.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 WORKSPACE_COUNT (MAX_WORKSPACES+1)
70 Display *dpy;
71 char *display = "";
72 Window root;
73 int scr;
74 int scrWidth;
75 int scrHeight;
76 int scrX, scrY;
78 WXineramaInfo xineInfo;
80 Bool smooth = False;
81 #ifdef USE_XINERAMA
82 Bool xineStretch = False;
83 #endif
85 Pixmap CurrentPixmap = None;
86 char *PixmapPath = NULL;
88 extern char *__progname;
90 typedef struct BackgroundTexture {
91 int refcount;
93 int solid;
95 char *spec;
97 XColor color;
98 Pixmap pixmap; /* for all textures, including solid */
99 int width; /* size of the pixmap */
100 int height;
101 } BackgroundTexture;
103 static noreturn void quit(int rcode)
105 WMReleaseApplication();
106 exit(rcode);
109 static void initXinerama(void)
111 xineInfo.screens = NULL;
112 xineInfo.count = 0;
113 #ifdef USE_XINERAMA
114 # ifdef SOLARIS_XINERAMA
115 if (XineramaGetState(dpy, scr)) {
116 XRectangle head[MAXFRAMEBUFFERS];
117 unsigned char hints[MAXFRAMEBUFFERS];
118 int i;
120 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
122 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
124 for (i = 0; i < xineInfo.count; i++) {
125 xineInfo.screens[i].pos.x = head[i].x;
126 xineInfo.screens[i].pos.y = head[i].y;
127 xineInfo.screens[i].size.width = head[i].width;
128 xineInfo.screens[i].size.height = head[i].height;
132 # else /* !SOLARIS_XINERAMA */
133 if (XineramaIsActive(dpy)) {
134 XineramaScreenInfo *xine_screens;
135 int i;
137 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
139 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
141 for (i = 0; i < xineInfo.count; i++) {
142 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
143 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
144 xineInfo.screens[i].size.width = xine_screens[i].width;
145 xineInfo.screens[i].size.height = xine_screens[i].height;
147 XFree(xine_screens);
149 # endif /* !SOLARIS_XINERAMA */
150 #endif /* USE_XINERAMA */
153 static RImage *loadImage(RContext * rc, const char *file)
155 char *path;
156 RImage *image;
158 if (access(file, F_OK) != 0) {
159 path = wfindfile(PixmapPath, file);
160 if (!path) {
161 wwarning("%s:could not find image file used in texture", file);
162 return NULL;
164 } else {
165 path = wstrdup(file);
168 image = RLoadImage(rc, path, 0);
169 if (!image) {
170 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
172 wfree(path);
174 return image;
177 static void
178 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
179 int x, int y, int width, int height)
181 int w, h;
182 Bool fimage = False;
184 switch (toupper(type)) {
185 case 'S':
186 case 'M':
187 case 'F':
188 if (toupper(type) == 'S') {
189 w = width;
190 h = height;
191 } else if(toupper(type) == 'F') {
192 if (image->width * height > image->height * width) {
193 w = (height * image->width) / image->height;
194 h = height;
195 } else {
196 w = width;
197 h = (width * image->height) / image->width;
199 } else {
200 if (image->width * height > image->height * width) {
201 w = width;
202 h = (width * image->height) / image->width;
203 } else {
204 w = (height * image->width) / image->height;
205 h = height;
209 if (w != image->width || h != image->height) {
210 RImage *simage;
212 if (smooth) {
213 simage = RSmoothScaleImage(image, w, h);
214 } else {
215 simage = RScaleImage(image, w, h);
218 if (!simage) {
219 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
220 return;
222 fimage = True;
223 image = simage;
226 /* fall through */
227 case 'C':
229 Pixmap pixmap;
231 if (!RConvertImage(rc, image, &pixmap)) {
232 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
233 return;
236 if (image->width != width || image->height != height) {
237 int sx, sy, w, h;
239 if (image->height < height) {
240 h = image->height;
241 y += (height - h) / 2;
242 sy = 0;
243 } else {
244 sy = (image->height - height) / 2;
245 h = height;
247 if (image->width < width) {
248 w = image->width;
249 x += (width - w) / 2;
250 sx = 0;
251 } else {
252 sx = (image->width - width) / 2;
253 w = width;
256 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
257 } else
258 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
259 x, y);
261 XFreePixmap(dpy, pixmap);
262 if (fimage) {
263 RReleaseImage(image);
266 break;
270 static BackgroundTexture *parseTexture(RContext * rc, char *text)
272 BackgroundTexture *texture = NULL;
273 WMPropList *texarray;
274 WMPropList *val;
275 int count;
276 char *tmp;
277 char *type;
279 #define GETSTRORGOTO(val, str, i, label) \
280 val = WMGetFromPLArray(texarray, i);\
281 if (!WMIsPLString(val)) {\
282 wwarning("could not parse texture %s", text);\
283 goto label;\
285 str = WMGetFromPLString(val)
287 texarray = WMCreatePropListFromDescription(text);
288 if (!texarray || !WMIsPLArray(texarray)
289 || (count = WMGetPropListItemCount(texarray)) < 2) {
291 wwarning("could not parse texture %s", text);
292 if (texarray)
293 WMReleasePropList(texarray);
294 return NULL;
297 texture = wmalloc(sizeof(BackgroundTexture));
299 GETSTRORGOTO(val, type, 0, error);
301 if (strcasecmp(type, "solid") == 0) {
302 XColor color;
303 Pixmap pixmap;
305 texture->solid = 1;
307 GETSTRORGOTO(val, tmp, 1, error);
309 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
310 wwarning("could not parse color %s in texture %s", tmp, text);
311 goto error;
313 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
315 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
316 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
317 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
319 texture->pixmap = pixmap;
320 texture->color = color;
321 texture->width = 8;
322 texture->height = 8;
323 } else if (strcasecmp(type, "vgradient") == 0
324 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
325 XColor color;
326 RColor color1, color2;
327 RImage *image;
328 Pixmap pixmap;
329 int gtype;
330 int iwidth, iheight;
332 GETSTRORGOTO(val, tmp, 1, 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 color1.red = color.red >> 8;
340 color1.green = color.green >> 8;
341 color1.blue = color.blue >> 8;
343 GETSTRORGOTO(val, tmp, 2, error);
345 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
346 wwarning("could not parse color %s in texture %s", tmp, text);
347 goto error;
350 color2.red = color.red >> 8;
351 color2.green = color.green >> 8;
352 color2.blue = color.blue >> 8;
354 switch (type[0]) {
355 case 'h':
356 case 'H':
357 gtype = RHorizontalGradient;
358 iwidth = scrWidth;
359 iheight = 32;
360 break;
361 case 'V':
362 case 'v':
363 gtype = RVerticalGradient;
364 iwidth = 32;
365 iheight = scrHeight;
366 break;
367 default:
368 gtype = RDiagonalGradient;
369 iwidth = scrWidth;
370 iheight = scrHeight;
371 break;
374 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
376 if (!image) {
377 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
378 goto error;
381 if (!RConvertImage(rc, image, &pixmap)) {
382 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
383 RReleaseImage(image);
384 goto error;
387 texture->width = image->width;
388 texture->height = image->height;
389 RReleaseImage(image);
391 texture->pixmap = pixmap;
392 } else if (strcasecmp(type, "mvgradient") == 0
393 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
394 XColor color;
395 RColor **colors;
396 RImage *image;
397 Pixmap pixmap;
398 int i, j;
399 int gtype;
400 int iwidth, iheight;
402 colors = malloc(sizeof(RColor *) * (count - 1));
403 if (!colors) {
404 wwarning("out of memory while parsing texture");
405 goto error;
407 memset(colors, 0, sizeof(RColor *) * (count - 1));
409 for (i = 2; i < count; i++) {
410 val = WMGetFromPLArray(texarray, i);
411 if (!WMIsPLString(val)) {
412 wwarning("could not parse texture %s", text);
414 for (j = 0; colors[j] != NULL; j++)
415 wfree(colors[j]);
416 wfree(colors);
417 goto error;
419 tmp = WMGetFromPLString(val);
421 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
422 wwarning("could not parse color %s in texture %s", tmp, text);
424 for (j = 0; colors[j] != NULL; j++)
425 wfree(colors[j]);
426 wfree(colors);
427 goto error;
429 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
430 wwarning("out of memory while parsing texture");
432 for (j = 0; colors[j] != NULL; j++)
433 wfree(colors[j]);
434 wfree(colors);
435 goto error;
438 colors[i - 2]->red = color.red >> 8;
439 colors[i - 2]->green = color.green >> 8;
440 colors[i - 2]->blue = color.blue >> 8;
443 switch (type[1]) {
444 case 'h':
445 case 'H':
446 gtype = RHorizontalGradient;
447 iwidth = scrWidth;
448 iheight = 32;
449 break;
450 case 'V':
451 case 'v':
452 gtype = RVerticalGradient;
453 iwidth = 32;
454 iheight = scrHeight;
455 break;
456 default:
457 gtype = RDiagonalGradient;
458 iwidth = scrWidth;
459 iheight = scrHeight;
460 break;
463 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
465 for (j = 0; colors[j] != NULL; j++)
466 wfree(colors[j]);
467 wfree(colors);
469 if (!image) {
470 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
471 goto error;
474 if (!RConvertImage(rc, image, &pixmap)) {
475 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
476 RReleaseImage(image);
477 goto error;
480 texture->width = image->width;
481 texture->height = image->height;
482 RReleaseImage(image);
484 texture->pixmap = pixmap;
485 } else if (strcasecmp(type, "cpixmap") == 0
486 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
487 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
488 XColor color;
489 Pixmap pixmap = None;
490 RImage *image = NULL;
491 int iwidth = 0, iheight = 0;
492 RColor rcolor;
494 GETSTRORGOTO(val, tmp, 1, error);
496 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
497 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
500 if (!pixmap) {
501 image = loadImage(rc, tmp);
502 if (!image) {
503 goto error;
505 iwidth = image->width;
506 iheight = image->height;
509 GETSTRORGOTO(val, tmp, 2, error);
511 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
512 wwarning("could not parse color %s in texture %s", tmp, text);
513 RReleaseImage(image);
514 goto error;
516 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
517 rcolor.red = color.red >> 8;
518 rcolor.green = color.green >> 8;
519 rcolor.blue = color.blue >> 8;
520 RGetClosestXColor(rc, &rcolor, &color);
521 } else {
522 rcolor.red = 0;
523 rcolor.green = 0;
524 rcolor.blue = 0;
526 /* for images with a transparent color */
527 if (image->data[3]) {
528 RCombineImageWithColor(image, &rcolor);
531 switch (toupper(type[0])) {
532 case 'T':
533 texture->width = iwidth;
534 texture->height = iheight;
535 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
536 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
537 RReleaseImage(image);
538 goto error;
540 if (image)
541 RReleaseImage(image);
543 texture->pixmap = pixmap;
544 texture->color = color;
545 break;
546 case 'S':
547 case 'M':
548 case 'C':
549 case 'F':
551 Pixmap tpixmap =
552 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
553 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
554 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
556 texture->pixmap = tpixmap;
557 texture->color = color;
558 texture->width = scrWidth;
559 texture->height = scrHeight;
561 #ifdef USE_XINERAMA
562 if (xineInfo.count && ! xineStretch) {
563 int i;
564 for (i = 0; i < xineInfo.count; ++i) {
565 applyImage(rc, texture, image, type[0],
566 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
567 xineInfo.screens[i].size.width,
568 xineInfo.screens[i].size.height);
570 } else {
571 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
573 #else /* !USE_XINERAMA */
574 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
575 #endif /* !USE_XINERAMA */
576 RReleaseImage(image);
578 break;
580 } else if (strcasecmp(type, "thgradient") == 0
581 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
582 XColor color;
583 RColor color1, color2;
584 RImage *image;
585 RImage *gradient;
586 RImage *tiled;
587 Pixmap pixmap;
588 int opaq;
589 char *file;
590 int gtype;
591 int twidth, theight;
593 GETSTRORGOTO(val, file, 1, error);
595 GETSTRORGOTO(val, tmp, 2, error);
597 opaq = atoi(tmp);
599 GETSTRORGOTO(val, tmp, 3, error);
601 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
602 wwarning("could not parse color %s in texture %s", tmp, text);
603 goto error;
606 color1.red = color.red >> 8;
607 color1.green = color.green >> 8;
608 color1.blue = color.blue >> 8;
610 GETSTRORGOTO(val, tmp, 4, error);
612 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
613 wwarning("could not parse color %s in texture %s", tmp, text);
614 goto error;
617 color2.red = color.red >> 8;
618 color2.green = color.green >> 8;
619 color2.blue = color.blue >> 8;
621 image = loadImage(rc, file);
622 if (!image) {
623 goto error;
626 switch (type[1]) {
627 case 'h':
628 case 'H':
629 gtype = RHorizontalGradient;
630 twidth = scrWidth;
631 theight = image->height > scrHeight ? scrHeight : image->height;
632 break;
633 case 'V':
634 case 'v':
635 gtype = RVerticalGradient;
636 twidth = image->width > scrWidth ? scrWidth : image->width;
637 theight = scrHeight;
638 break;
639 default:
640 gtype = RDiagonalGradient;
641 twidth = scrWidth;
642 theight = scrHeight;
643 break;
645 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
647 if (!gradient) {
648 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
649 RReleaseImage(gradient);
650 RReleaseImage(image);
651 goto error;
654 tiled = RMakeTiledImage(image, twidth, theight);
655 if (!tiled) {
656 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
657 RReleaseImage(gradient);
658 RReleaseImage(image);
659 goto error;
661 RReleaseImage(image);
663 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
664 RReleaseImage(gradient);
666 if (!RConvertImage(rc, tiled, &pixmap)) {
667 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
668 RReleaseImage(tiled);
669 goto error;
671 texture->width = tiled->width;
672 texture->height = tiled->height;
674 RReleaseImage(tiled);
676 texture->pixmap = pixmap;
677 } else if (strcasecmp(type, "function") == 0) {
678 /* Leave this in to handle the unlikely case of
679 * someone actually having function textures configured */
680 wwarning("function texture support has been removed");
681 goto error;
682 } else {
683 wwarning("invalid texture type %s", text);
684 goto error;
687 texture->spec = wstrdup(text);
689 return texture;
691 error:
692 if (texture)
693 wfree(texture);
694 if (texarray)
695 WMReleasePropList(texarray);
697 return NULL;
700 static void freeTexture(BackgroundTexture * texture)
702 if (texture->solid) {
703 unsigned long pixel[1];
705 pixel[0] = texture->color.pixel;
706 /* dont free black/white pixels */
707 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
708 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
709 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
711 if (texture->pixmap) {
712 XFreePixmap(dpy, texture->pixmap);
714 wfree(texture->spec);
715 wfree(texture);
718 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
720 BackgroundTexture *newTexture = NULL;
721 int i;
723 /* unset the texture */
724 if (!texture) {
725 if (textures[workspace] != NULL) {
726 textures[workspace]->refcount--;
728 if (textures[workspace]->refcount == 0)
729 freeTexture(textures[workspace]);
731 textures[workspace] = NULL;
732 return;
735 if (textures[workspace]
736 && strcasecmp(textures[workspace]->spec, texture) == 0) {
737 /* texture did not change */
738 return;
741 /* check if the same texture is already created */
742 for (i = 0; i < *maxTextures; i++) {
743 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
744 newTexture = textures[i];
745 break;
749 if (!newTexture) {
750 /* create the texture */
751 newTexture = parseTexture(rc, texture);
753 if (!newTexture)
754 return;
756 if (textures[workspace] != NULL) {
758 textures[workspace]->refcount--;
760 if (textures[workspace]->refcount == 0)
761 freeTexture(textures[workspace]);
764 newTexture->refcount++;
765 textures[workspace] = newTexture;
767 if (*maxTextures < workspace)
768 *maxTextures = workspace;
771 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
773 Display *tmpDpy;
774 Pixmap copyP;
776 /* must open a new display or the RetainPermanent will
777 * leave stuff allocated in RContext unallocated after exit */
778 tmpDpy = XOpenDisplay(display);
779 if (!tmpDpy) {
780 wwarning("could not open display to update background image information");
782 return None;
783 } else {
784 XSync(dpy, False);
786 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
787 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
788 XSync(tmpDpy, False);
790 XSetCloseDownMode(tmpDpy, RetainPermanent);
791 XCloseDisplay(tmpDpy);
794 return copyP;
797 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
799 /* Parameter not used, but tell the compiler that it is ok */
800 (void) dpy;
801 (void) err;
803 return 0;
806 static void setPixmapProperty(Pixmap pixmap)
808 static Atom prop = 0;
809 Atom type;
810 int format;
811 unsigned long length, after;
812 unsigned char *data;
813 int mode;
815 if (!prop) {
816 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
819 XGrabServer(dpy);
821 /* Clear out the old pixmap */
822 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
823 &type, &format, &length, &after, &data);
825 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
826 XSetErrorHandler(dummyErrorHandler);
827 XKillClient(dpy, *((Pixmap *) data));
828 XSync(dpy, False);
829 XSetErrorHandler(NULL);
830 mode = PropModeReplace;
831 } else {
832 mode = PropModeAppend;
834 if (pixmap)
835 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
836 else
837 XDeleteProperty(dpy, root, prop);
839 XUngrabServer(dpy);
840 XFlush(dpy);
843 static void changeTexture(BackgroundTexture * texture)
845 if (!texture) {
846 return;
849 if (texture->solid) {
850 XSetWindowBackground(dpy, root, texture->color.pixel);
851 } else {
852 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
854 XClearWindow(dpy, root);
856 XSync(dpy, False);
859 Pixmap pixmap;
861 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
863 setPixmapProperty(pixmap);
867 static int readmsg(int fd, char *buffer, int size)
869 int count;
871 while (size > 0) {
872 count = read(fd, buffer, size);
873 if (count < 0)
874 return -1;
875 size -= count;
876 buffer += count;
877 *buffer = 0;
880 return size;
884 * Message Format:
885 * sizeSntexture_spec - sets the texture for workspace n
886 * sizeCn - change background texture to the one for workspace n
887 * sizePpath - set the pixmap search path
889 * n is 4 bytes
890 * size = 4 bytes for length of the message data
892 static noreturn void helperLoop(RContext * rc)
894 BackgroundTexture *textures[WORKSPACE_COUNT];
895 int maxTextures = 0;
896 char buffer[2048], buf[8];
897 int size;
898 int errcount = 4;
900 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
902 while (1) {
903 int workspace = -1;
905 /* get length of message */
906 if (readmsg(0, buffer, 4) < 0) {
907 werror("error reading message from Window Maker");
908 errcount--;
909 if (errcount == 0) {
910 wfatal("quitting");
911 quit(1);
913 continue;
915 memcpy(buf, buffer, 4);
916 buf[4] = 0;
917 size = atoi(buf);
919 /* get message */
920 if (readmsg(0, buffer, size) < 0) {
921 werror("error reading message from Window Maker");
922 errcount--;
923 if (errcount == 0) {
924 wfatal("quitting");
925 quit(1);
927 continue;
929 #ifdef DEBUG
930 printf("RECEIVED %s\n", buffer);
931 #endif
932 if (buffer[0] != 'P' && buffer[0] != 'K') {
933 memcpy(buf, &buffer[1], 4);
934 buf[4] = 0;
935 workspace = atoi(buf);
936 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
937 wwarning("received message with invalid workspace number %i", workspace);
938 continue;
942 switch (buffer[0]) {
943 case 'S':
944 #ifdef DEBUG
945 printf("set texture %s\n", &buffer[5]);
946 #endif
947 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
948 break;
950 case 'C':
951 #ifdef DEBUG
952 printf("change texture %i\n", workspace);
953 #endif
954 if (!textures[workspace]) {
955 changeTexture(textures[0]);
956 } else {
957 changeTexture(textures[workspace]);
959 break;
961 case 'P':
962 #ifdef DEBUG
963 printf("change pixmappath %s\n", &buffer[1]);
964 #endif
965 if (PixmapPath)
966 wfree(PixmapPath);
967 PixmapPath = wstrdup(&buffer[1]);
968 break;
970 case 'U':
971 #ifdef DEBUG
972 printf("unset workspace %i\n", workspace);
973 #endif
974 setupTexture(rc, textures, &maxTextures, workspace, NULL);
975 break;
977 case 'K':
978 #ifdef DEBUG
979 printf("exit command\n");
980 #endif
981 quit(0);
983 default:
984 wwarning("unknown message received");
985 break;
990 static void updateDomain(const char *domain, const char *key, const char *texture)
992 int result;
993 char *program = "wdwrite";
995 /* here is a mem leak */
996 result = system(wstrconcat("wdwrite ",
997 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
999 if (result == -1)
1000 werror("error executing system command");
1002 execlp(program, program, domain, key, texture, NULL);
1003 wwarning("warning could not run \"%s\"", program);
1006 static WMPropList *getValueForKey(const char *domain, const char *keyName)
1008 char *path;
1009 WMPropList *key, *val, *d;
1011 key = WMCreatePLString(keyName);
1013 /* try to find PixmapPath in user defaults */
1014 path = wdefaultspathfordomain(domain);
1015 d = WMReadPropListFromFile(path);
1016 if (!d) {
1017 wwarning("could not open domain file %s", path);
1019 wfree(path);
1021 if (d && !WMIsPLDictionary(d)) {
1022 WMReleasePropList(d);
1023 d = NULL;
1025 if (d) {
1026 val = WMGetFromPLDictionary(d, key);
1027 } else {
1028 val = NULL;
1030 /* try to find PixmapPath in global defaults */
1031 if (!val) {
1032 path = wglobaldefaultspathfordomain(domain);
1033 if (!path) {
1034 wwarning("could not locate file for domain %s", domain);
1035 d = NULL;
1036 } else {
1037 d = WMReadPropListFromFile(path);
1038 wfree(path);
1041 if (d && !WMIsPLDictionary(d)) {
1042 WMReleasePropList(d);
1043 d = NULL;
1045 if (d) {
1046 val = WMGetFromPLDictionary(d, key);
1048 } else {
1049 val = NULL;
1053 if (val)
1054 WMRetainPropList(val);
1056 WMReleasePropList(key);
1057 if (d)
1058 WMReleasePropList(d);
1060 return val;
1063 static char *getPixmapPath(const char *domain)
1065 WMPropList *val;
1066 char *ptr, *data;
1067 int len, i, count;
1069 val = getValueForKey(domain, "PixmapPath");
1071 if (!val || !WMIsPLArray(val)) {
1072 if (val)
1073 WMReleasePropList(val);
1074 return wstrdup("");
1077 count = WMGetPropListItemCount(val);
1078 len = 0;
1079 for (i = 0; i < count; i++) {
1080 WMPropList *v;
1082 v = WMGetFromPLArray(val, i);
1083 if (!v || !WMIsPLString(v)) {
1084 continue;
1086 len += strlen(WMGetFromPLString(v)) + 1;
1089 ptr = data = wmalloc(len + 1);
1090 *ptr = 0;
1092 for (i = 0; i < count; i++) {
1093 WMPropList *v;
1095 v = WMGetFromPLArray(val, i);
1096 if (!v || !WMIsPLString(v)) {
1097 continue;
1099 strcpy(ptr, WMGetFromPLString(v));
1101 ptr += strlen(WMGetFromPLString(v));
1102 *ptr = ':';
1103 ptr++;
1105 if (i > 0)
1106 ptr--;
1107 *(ptr--) = 0;
1109 WMReleasePropList(val);
1111 return data;
1114 static char *getFullPixmapPath(const char *file)
1116 char *tmp;
1118 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1119 int bsize = 512;
1120 char *path = wmalloc(bsize);
1122 while (!getcwd(path, bsize)) {
1123 bsize += bsize / 2;
1124 path = wrealloc(path, bsize);
1127 tmp = wstrconcat(path, "/");
1128 wfree(path);
1129 path = wstrconcat(tmp, file);
1130 wfree(tmp);
1132 return path;
1135 /* the file is in the PixmapPath */
1136 wfree(tmp);
1138 return wstrdup(file);
1141 static void print_help(void)
1143 printf("Usage: %s [options] [image]\n", __progname);
1144 puts("Sets the workspace background to the specified image or a texture and");
1145 puts("optionally update Window Maker configuration");
1146 puts("");
1147 puts(" -display display to use");
1148 puts(" -d, --dither dither image");
1149 puts(" -m, --match match colors");
1150 puts(" -S, --smooth smooth scaled image");
1151 #ifdef USE_XINERAMA
1152 puts(" -X, --xinerama stretch image across Xinerama heads");
1153 #endif
1154 puts(" -b, --back-color <color> background color");
1155 puts(" -t, --tile tile image");
1156 puts(" -e, --center center image");
1157 puts(" -s, --scale scale image (default)");
1158 puts(" -a, --maxscale scale image and keep aspect ratio");
1159 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1160 puts(" -u, --update-wmaker update WindowMaker domain database");
1161 puts(" -D, --update-domain <domain> update <domain> database");
1162 puts(" -c, --colors <cpc> colors per channel to use");
1163 puts(" -p, --parse <texture> proplist style texture specification");
1164 puts(" -w, --workspace <workspace> update background for the specified workspace");
1165 puts(" -v, --version show version of wmsetbg and exit");
1166 puts(" -h, --help show this help and exit");
1169 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1171 WMPropList *array, *val;
1172 char *value;
1173 int j;
1175 val = WMCreatePropListFromDescription(texture);
1176 if (!val) {
1177 wwarning("could not parse texture %s", texture);
1178 return;
1181 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1183 if (!array) {
1184 array = WMCreatePLArray(NULL, NULL);
1187 j = WMGetPropListItemCount(array);
1188 if (workspace >= j) {
1189 WMPropList *empty;
1191 empty = WMCreatePLArray(NULL, NULL);
1193 while (j++ < workspace - 1) {
1194 WMAddToPLArray(array, empty);
1196 WMAddToPLArray(array, val);
1197 } else {
1198 WMDeleteFromPLArray(array, workspace);
1199 WMInsertInPLArray(array, workspace, val);
1202 value = WMGetPropListDescription(array, False);
1203 updateDomain(domain, "WorkspaceSpecificBack", value);
1206 int main(int argc, char **argv)
1208 int i;
1209 int helperMode = 0;
1210 RContext *rc;
1211 RContextAttributes rattr;
1212 char *style = "spixmap";
1213 char *back_color = "gray20";
1214 char *image_name = NULL;
1215 char *domain = "WindowMaker";
1216 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1217 char *texture = NULL;
1218 int workspace = -1;
1220 signal(SIGINT, SIG_DFL);
1221 signal(SIGTERM, SIG_DFL);
1222 signal(SIGQUIT, SIG_DFL);
1223 signal(SIGSEGV, SIG_DFL);
1224 signal(SIGBUS, SIG_DFL);
1225 signal(SIGFPE, SIG_DFL);
1226 signal(SIGABRT, SIG_DFL);
1227 signal(SIGHUP, SIG_DFL);
1228 signal(SIGPIPE, SIG_DFL);
1229 signal(SIGCHLD, SIG_DFL);
1231 WMInitializeApplication("wmsetbg", &argc, argv);
1233 for (i = 1; i < argc; i++) {
1234 if (strcmp(argv[i], "-helper") == 0) {
1235 helperMode = 1;
1236 } else if (strcmp(argv[i], "-display") == 0) {
1237 i++;
1238 if (i >= argc) {
1239 wfatal("too few arguments for %s", argv[i - 1]);
1240 quit(1);
1242 display = argv[i];
1243 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1244 style = "spixmap";
1245 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1246 style = "tpixmap";
1247 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1248 style = "cpixmap";
1249 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1250 style = "mpixmap";
1251 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1252 style = "fpixmap";
1253 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1254 render_mode = RDitheredRendering;
1255 obey_user++;
1256 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1257 render_mode = RBestMatchRendering;
1258 obey_user++;
1259 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1260 smooth = True;
1261 #ifdef USE_XINERAMA
1262 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1263 xineStretch = True;
1264 #endif
1265 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1266 update++;
1267 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1268 update++;
1269 i++;
1270 if (i >= argc) {
1271 wfatal("too few arguments for %s", argv[i - 1]);
1272 quit(1);
1274 domain = wstrdup(argv[i]);
1275 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1276 i++;
1277 if (i >= argc) {
1278 wfatal("too few arguments for %s", argv[i - 1]);
1279 quit(1);
1281 if (sscanf(argv[i], "%i", &cpc) != 1) {
1282 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1283 quit(1);
1285 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1286 i++;
1287 if (i >= argc) {
1288 wfatal("too few arguments for %s", argv[i - 1]);
1289 quit(1);
1291 back_color = argv[i];
1292 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1293 i++;
1294 if (i >= argc) {
1295 wfatal("too few arguments for %s", argv[i - 1]);
1296 quit(1);
1298 texture = argv[i];
1299 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1300 i++;
1301 if (i >= argc) {
1302 wfatal("too few arguments for %s", argv[i - 1]);
1303 quit(1);
1305 if (sscanf(argv[i], "%i", &workspace) != 1) {
1306 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1307 quit(1);
1309 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1310 printf("%s (Window Maker %s)\n", __progname, VERSION);
1311 quit(0);
1312 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1313 print_help();
1314 quit(0);
1315 } else if (argv[i][0] != '-') {
1316 image_name = argv[i];
1317 } else {
1318 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1319 printf("Try '%s --help' for more information\n", __progname);
1320 quit(1);
1323 if (!image_name && !texture && !helperMode) {
1324 printf("%s: you must specify a image file name or a texture\n", __progname);
1325 printf("Try '%s --help' for more information\n", __progname);
1326 quit(1);
1329 PixmapPath = getPixmapPath(domain);
1330 if (!smooth) {
1331 WMPropList *val;
1332 /* carlos, don't remove this */
1333 #if 0 /* some problem with Alpha... TODO: check if its right */
1334 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1335 #else
1336 val = getValueForKey(domain, "SmoothWorkspaceBack");
1337 #endif
1339 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1340 smooth = True;
1343 dpy = XOpenDisplay(display);
1344 if (!dpy) {
1345 wfatal("could not open display");
1346 quit(1);
1348 #if 0
1349 XSynchronize(dpy, 1);
1350 #endif
1352 root = DefaultRootWindow(dpy);
1354 scr = DefaultScreen(dpy);
1356 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1357 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1358 scrX = scrY = 0;
1360 initXinerama();
1362 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1363 render_mode = RDitheredRendering;
1365 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1366 rattr.render_mode = render_mode;
1367 rattr.colors_per_channel = cpc;
1368 rattr.standard_colormap_mode = RCreateStdColormap;
1370 rc = RCreateContext(dpy, scr, &rattr);
1372 if (!rc) {
1373 rattr.standard_colormap_mode = RIgnoreStdColormap;
1374 rc = RCreateContext(dpy, scr, &rattr);
1377 if (!rc) {
1378 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1379 quit(1);
1382 if (helperMode) {
1383 int result;
1385 /* lower priority, so that it wont use all the CPU */
1386 result = nice(15);
1387 if (result == -1)
1388 wwarning("error could not nice process");
1390 helperLoop(rc);
1391 } else {
1392 BackgroundTexture *tex;
1393 char buffer[4098];
1395 if (!texture) {
1396 char *image_path = getFullPixmapPath(image_name);
1397 snprintf(buffer, sizeof(buffer), "(%s, \"%s\", %s)", style, image_path, back_color);
1398 wfree(image_path);
1399 texture = (char *)buffer;
1402 if (update && workspace < 0) {
1403 updateDomain(domain, "WorkspaceBack", texture);
1406 tex = parseTexture(rc, texture);
1407 if (!tex)
1408 quit(1);
1410 if (workspace < 0)
1411 changeTexture(tex);
1412 else {
1413 /* always update domain */
1414 changeTextureForWorkspace(domain, texture, workspace);
1418 WMReleaseApplication();
1419 return 0;