Coverity: fix util wmsetbg resource leak
[wmaker-crm.git] / util / wmsetbg.c
blobdc747d0488c267dd89145dfee916fcc7973a5e15
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"
57 #include <WINGs/WINGs.h>
58 #include <wraster.h>
60 typedef struct {
61 WMRect *screens;
62 int count; /* screen count, 0 = inactive */
63 } WXineramaInfo;
65 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
67 Display *dpy;
68 char *display = "";
69 Window root;
70 int scr;
71 int scrWidth;
72 int scrHeight;
73 int scrX, scrY;
75 WXineramaInfo xineInfo;
77 Bool smooth = False;
78 #ifdef USE_XINERAMA
79 Bool xineStretch = False;
80 #endif
82 Pixmap CurrentPixmap = None;
83 char *PixmapPath = NULL;
85 static const char *prog_name;
87 typedef struct BackgroundTexture {
88 int refcount;
90 int solid;
92 char *spec;
94 XColor color;
95 Pixmap pixmap; /* for all textures, including solid */
96 int width; /* size of the pixmap */
97 int height;
98 } BackgroundTexture;
100 static noreturn void quit(int rcode)
102 WMReleaseApplication();
103 exit(rcode);
106 static void initXinerama(void)
108 xineInfo.screens = NULL;
109 xineInfo.count = 0;
110 #ifdef USE_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 /* USE_XINERAMA */
150 static RImage *loadImage(RContext * rc, const 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 case 'F':
185 if (toupper(type) == 'S') {
186 w = width;
187 h = height;
188 } else if(toupper(type) == 'F') {
189 if (image->width * height > image->height * width) {
190 w = (height * image->width) / image->height;
191 h = height;
192 } else {
193 w = width;
194 h = (width * image->height) / image->width;
196 } else {
197 if (image->width * height > image->height * width) {
198 w = width;
199 h = (width * image->height) / image->width;
200 } else {
201 w = (height * image->width) / image->height;
202 h = height;
206 if (w != image->width || h != image->height) {
207 RImage *simage;
209 if (smooth) {
210 simage = RSmoothScaleImage(image, w, h);
211 } else {
212 simage = RScaleImage(image, w, h);
215 if (!simage) {
216 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
217 return;
219 fimage = True;
220 image = simage;
223 /* fall through */
224 case 'C':
226 Pixmap pixmap;
228 if (!RConvertImage(rc, image, &pixmap)) {
229 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
230 if (fimage)
231 RReleaseImage(image);
232 return;
235 if (image->width != width || image->height != height) {
236 int sx, sy, w, h;
238 if (image->height < height) {
239 h = image->height;
240 y += (height - h) / 2;
241 sy = 0;
242 } else {
243 sy = (image->height - height) / 2;
244 h = height;
246 if (image->width < width) {
247 w = image->width;
248 x += (width - w) / 2;
249 sx = 0;
250 } else {
251 sx = (image->width - width) / 2;
252 w = width;
255 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
256 } else
257 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
258 x, y);
260 XFreePixmap(dpy, pixmap);
261 if (fimage) {
262 RReleaseImage(image);
265 break;
269 static BackgroundTexture *parseTexture(RContext * rc, char *text)
271 BackgroundTexture *texture = NULL;
272 WMPropList *texarray;
273 WMPropList *val;
274 int count;
275 char *tmp;
276 char *type;
278 #define GETSTRORGOTO(val, str, i, label) \
279 val = WMGetFromPLArray(texarray, i);\
280 if (!WMIsPLString(val)) {\
281 wwarning("could not parse texture %s", text);\
282 goto label;\
284 str = WMGetFromPLString(val)
286 texarray = WMCreatePropListFromDescription(text);
287 if (!texarray || !WMIsPLArray(texarray)
288 || (count = WMGetPropListItemCount(texarray)) < 2) {
290 wwarning("could not parse texture %s", text);
291 if (texarray)
292 WMReleasePropList(texarray);
293 return NULL;
296 texture = wmalloc(sizeof(BackgroundTexture));
298 GETSTRORGOTO(val, type, 0, error);
300 if (strcasecmp(type, "solid") == 0) {
301 XColor color;
302 Pixmap pixmap;
304 texture->solid = 1;
306 GETSTRORGOTO(val, tmp, 1, error);
308 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
309 wwarning("could not parse color %s in texture %s", tmp, text);
310 goto error;
312 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
314 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
315 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
316 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
318 texture->pixmap = pixmap;
319 texture->color = color;
320 texture->width = 8;
321 texture->height = 8;
322 } else if (strcasecmp(type, "vgradient") == 0
323 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
324 XColor color;
325 RColor color1, color2;
326 RImage *image;
327 Pixmap pixmap;
328 RGradientStyle gtype;
329 int iwidth, iheight;
331 GETSTRORGOTO(val, tmp, 1, error);
333 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
334 wwarning("could not parse color %s in texture %s", tmp, text);
335 goto error;
338 color1.red = color.red >> 8;
339 color1.green = color.green >> 8;
340 color1.blue = color.blue >> 8;
342 GETSTRORGOTO(val, tmp, 2, error);
344 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
345 wwarning("could not parse color %s in texture %s", tmp, text);
346 goto error;
349 color2.red = color.red >> 8;
350 color2.green = color.green >> 8;
351 color2.blue = color.blue >> 8;
353 switch (type[0]) {
354 case 'h':
355 case 'H':
356 gtype = RHorizontalGradient;
357 iwidth = scrWidth;
358 iheight = 32;
359 break;
360 case 'V':
361 case 'v':
362 gtype = RVerticalGradient;
363 iwidth = 32;
364 iheight = scrHeight;
365 break;
366 default:
367 gtype = RDiagonalGradient;
368 iwidth = scrWidth;
369 iheight = scrHeight;
370 break;
373 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
375 if (!image) {
376 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
377 goto error;
380 if (!RConvertImage(rc, image, &pixmap)) {
381 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
382 RReleaseImage(image);
383 goto error;
386 texture->width = image->width;
387 texture->height = image->height;
388 RReleaseImage(image);
390 texture->pixmap = pixmap;
391 } else if (strcasecmp(type, "mvgradient") == 0
392 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
393 XColor color;
394 RColor **colors;
395 RImage *image;
396 Pixmap pixmap;
397 int i, j;
398 RGradientStyle gtype;
399 int iwidth, iheight;
401 colors = malloc(sizeof(RColor *) * (count - 1));
402 if (!colors) {
403 wwarning("out of memory while parsing texture");
404 goto error;
406 memset(colors, 0, sizeof(RColor *) * (count - 1));
408 for (i = 2; i < count; i++) {
409 val = WMGetFromPLArray(texarray, i);
410 if (!WMIsPLString(val)) {
411 wwarning("could not parse texture %s", text);
413 for (j = 0; colors[j] != NULL; j++)
414 wfree(colors[j]);
415 wfree(colors);
416 goto error;
418 tmp = WMGetFromPLString(val);
420 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
421 wwarning("could not parse color %s in texture %s", tmp, text);
423 for (j = 0; colors[j] != NULL; j++)
424 wfree(colors[j]);
425 wfree(colors);
426 goto error;
428 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
429 wwarning("out of memory while parsing texture");
431 for (j = 0; colors[j] != NULL; j++)
432 wfree(colors[j]);
433 wfree(colors);
434 goto error;
437 colors[i - 2]->red = color.red >> 8;
438 colors[i - 2]->green = color.green >> 8;
439 colors[i - 2]->blue = color.blue >> 8;
442 switch (type[1]) {
443 case 'h':
444 case 'H':
445 gtype = RHorizontalGradient;
446 iwidth = scrWidth;
447 iheight = 32;
448 break;
449 case 'V':
450 case 'v':
451 gtype = RVerticalGradient;
452 iwidth = 32;
453 iheight = scrHeight;
454 break;
455 default:
456 gtype = RDiagonalGradient;
457 iwidth = scrWidth;
458 iheight = scrHeight;
459 break;
462 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
464 for (j = 0; colors[j] != NULL; j++)
465 wfree(colors[j]);
466 wfree(colors);
468 if (!image) {
469 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
470 goto error;
473 if (!RConvertImage(rc, image, &pixmap)) {
474 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
475 RReleaseImage(image);
476 goto error;
479 texture->width = image->width;
480 texture->height = image->height;
481 RReleaseImage(image);
483 texture->pixmap = pixmap;
484 } else if (strcasecmp(type, "cpixmap") == 0
485 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
486 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
487 XColor color;
488 Pixmap pixmap = None;
489 RImage *image = NULL;
490 int iwidth = 0, iheight = 0;
491 RColor rcolor;
493 GETSTRORGOTO(val, tmp, 1, error);
495 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
496 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
499 if (!pixmap) {
500 image = loadImage(rc, tmp);
501 if (!image) {
502 goto error;
504 iwidth = image->width;
505 iheight = image->height;
508 /* cannot use GETSTRORGOTO() here
509 * as we have to free image in case of error */
510 val = WMGetFromPLArray(texarray, 2);
511 if (!WMIsPLString(val)) {
512 wwarning("could not parse texture %s", text);
513 RReleaseImage(image);
514 goto error;
516 tmp = WMGetFromPLString(val);
518 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
519 wwarning("could not parse color %s in texture %s", tmp, text);
520 RReleaseImage(image);
521 goto error;
523 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
524 rcolor.red = color.red >> 8;
525 rcolor.green = color.green >> 8;
526 rcolor.blue = color.blue >> 8;
527 RGetClosestXColor(rc, &rcolor, &color);
528 } else {
529 rcolor.red = 0;
530 rcolor.green = 0;
531 rcolor.blue = 0;
533 /* for images with a transparent color */
534 if (image && image->data[3])
535 RCombineImageWithColor(image, &rcolor);
537 switch (toupper(type[0])) {
538 case 'T':
539 texture->width = iwidth;
540 texture->height = iheight;
541 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
542 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
543 RReleaseImage(image);
544 goto error;
547 texture->pixmap = pixmap;
548 texture->color = color;
549 break;
550 case 'S':
551 case 'M':
552 case 'C':
553 case 'F':
555 Pixmap tpixmap =
556 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
557 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
558 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
560 texture->pixmap = tpixmap;
561 texture->color = color;
562 texture->width = scrWidth;
563 texture->height = scrHeight;
565 if (!image)
566 break;
568 #ifdef USE_XINERAMA
569 if (xineInfo.count && ! xineStretch) {
570 int i;
571 for (i = 0; i < xineInfo.count; ++i) {
572 applyImage(rc, texture, image, type[0],
573 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
574 xineInfo.screens[i].size.width,
575 xineInfo.screens[i].size.height);
577 } else {
578 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
580 #else /* !USE_XINERAMA */
581 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
582 #endif /* !USE_XINERAMA */
584 break;
586 if (image)
587 RReleaseImage(image);
589 } else if (strcasecmp(type, "thgradient") == 0
590 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
591 XColor color;
592 RColor color1, color2;
593 RImage *image;
594 RImage *gradient;
595 RImage *tiled;
596 Pixmap pixmap;
597 int opaq;
598 char *file;
599 RGradientStyle gtype;
600 int twidth, theight;
602 GETSTRORGOTO(val, file, 1, error);
604 GETSTRORGOTO(val, tmp, 2, error);
606 opaq = atoi(tmp);
608 GETSTRORGOTO(val, tmp, 3, error);
610 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
611 wwarning("could not parse color %s in texture %s", tmp, text);
612 goto error;
615 color1.red = color.red >> 8;
616 color1.green = color.green >> 8;
617 color1.blue = color.blue >> 8;
619 GETSTRORGOTO(val, tmp, 4, error);
621 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
622 wwarning("could not parse color %s in texture %s", tmp, text);
623 goto error;
626 color2.red = color.red >> 8;
627 color2.green = color.green >> 8;
628 color2.blue = color.blue >> 8;
630 image = loadImage(rc, file);
631 if (!image) {
632 goto error;
635 switch (type[1]) {
636 case 'h':
637 case 'H':
638 gtype = RHorizontalGradient;
639 twidth = scrWidth;
640 theight = image->height > scrHeight ? scrHeight : image->height;
641 break;
642 case 'V':
643 case 'v':
644 gtype = RVerticalGradient;
645 twidth = image->width > scrWidth ? scrWidth : image->width;
646 theight = scrHeight;
647 break;
648 default:
649 gtype = RDiagonalGradient;
650 twidth = scrWidth;
651 theight = scrHeight;
652 break;
654 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
656 if (!gradient) {
657 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
658 RReleaseImage(image);
659 goto error;
662 tiled = RMakeTiledImage(image, twidth, theight);
663 if (!tiled) {
664 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
665 RReleaseImage(gradient);
666 RReleaseImage(image);
667 goto error;
669 RReleaseImage(image);
671 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
672 RReleaseImage(gradient);
674 if (!RConvertImage(rc, tiled, &pixmap)) {
675 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
676 RReleaseImage(tiled);
677 goto error;
679 texture->width = tiled->width;
680 texture->height = tiled->height;
682 RReleaseImage(tiled);
684 texture->pixmap = pixmap;
685 } else if (strcasecmp(type, "function") == 0) {
686 /* Leave this in to handle the unlikely case of
687 * someone actually having function textures configured */
688 wwarning("function texture support has been removed");
689 goto error;
690 } else {
691 wwarning("invalid texture type %s", text);
692 goto error;
695 texture->spec = wstrdup(text);
697 return texture;
699 error:
700 if (texture)
701 wfree(texture);
702 if (texarray)
703 WMReleasePropList(texarray);
705 return NULL;
708 static void freeTexture(BackgroundTexture * texture)
710 if (texture->solid) {
711 unsigned long pixel[1];
713 pixel[0] = texture->color.pixel;
714 /* dont free black/white pixels */
715 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
716 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
717 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
719 if (texture->pixmap) {
720 XFreePixmap(dpy, texture->pixmap);
722 wfree(texture->spec);
723 wfree(texture);
726 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
728 BackgroundTexture *newTexture = NULL;
729 int i;
731 /* unset the texture */
732 if (!texture) {
733 if (textures[workspace] != NULL) {
734 textures[workspace]->refcount--;
736 if (textures[workspace]->refcount == 0)
737 freeTexture(textures[workspace]);
739 textures[workspace] = NULL;
740 return;
743 if (textures[workspace]
744 && strcasecmp(textures[workspace]->spec, texture) == 0) {
745 /* texture did not change */
746 return;
749 /* check if the same texture is already created */
750 for (i = 0; i < *maxTextures; i++) {
751 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
752 newTexture = textures[i];
753 break;
757 if (!newTexture) {
758 /* create the texture */
759 newTexture = parseTexture(rc, texture);
761 if (!newTexture)
762 return;
764 if (textures[workspace] != NULL) {
766 textures[workspace]->refcount--;
768 if (textures[workspace]->refcount == 0)
769 freeTexture(textures[workspace]);
772 newTexture->refcount++;
773 textures[workspace] = newTexture;
775 if (*maxTextures < workspace)
776 *maxTextures = workspace;
779 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
781 Display *tmpDpy;
782 Pixmap copyP;
784 /* must open a new display or the RetainPermanent will
785 * leave stuff allocated in RContext unallocated after exit */
786 tmpDpy = XOpenDisplay(display);
787 if (!tmpDpy) {
788 wwarning("could not open display to update background image information");
790 return None;
791 } else {
792 XSync(dpy, False);
794 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
795 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
796 XSync(tmpDpy, False);
798 XSetCloseDownMode(tmpDpy, RetainPermanent);
799 XCloseDisplay(tmpDpy);
802 return copyP;
805 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
807 /* Parameter not used, but tell the compiler that it is ok */
808 (void) dpy;
809 (void) err;
811 return 0;
814 static void setPixmapProperty(Pixmap pixmap)
816 static Atom prop = 0;
817 Atom type;
818 int format;
819 unsigned long length, after;
820 unsigned char *data;
821 int mode;
823 if (!prop) {
824 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
827 XGrabServer(dpy);
829 /* Clear out the old pixmap */
830 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
831 &type, &format, &length, &after, &data);
833 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
834 XSetErrorHandler(dummyErrorHandler);
835 XKillClient(dpy, *((Pixmap *) data));
836 XSync(dpy, False);
837 XSetErrorHandler(NULL);
838 mode = PropModeReplace;
839 } else {
840 mode = PropModeAppend;
842 if (pixmap)
843 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
844 else
845 XDeleteProperty(dpy, root, prop);
847 XUngrabServer(dpy);
848 XFlush(dpy);
851 static void changeTexture(BackgroundTexture * texture)
853 if (!texture) {
854 return;
857 if (texture->solid) {
858 XSetWindowBackground(dpy, root, texture->color.pixel);
859 } else {
860 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
862 XClearWindow(dpy, root);
864 XSync(dpy, False);
867 Pixmap pixmap;
869 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
871 setPixmapProperty(pixmap);
875 static int readmsg(int fd, char *buffer, int size)
877 int count;
879 while (size > 0) {
880 count = read(fd, buffer, size);
881 if (count < 0)
882 return -1;
883 size -= count;
884 buffer += count;
885 *buffer = 0;
888 return size;
892 * Message Format:
893 * sizeSntexture_spec - sets the texture for workspace n
894 * sizeCn - change background texture to the one for workspace n
895 * sizePpath - set the pixmap search path
897 * n is 4 bytes
898 * size = 4 bytes for length of the message data
900 static noreturn void helperLoop(RContext * rc)
902 BackgroundTexture *textures[WORKSPACE_COUNT];
903 int maxTextures = 0;
904 char buffer[2048], buf[8];
905 int size;
906 int errcount = 4;
908 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
909 memset(buffer, 0, sizeof(buffer));
911 while (1) {
912 int workspace = -1;
914 /* get length of message */
915 if (readmsg(0, buffer, 4) < 0) {
916 werror("error reading message from Window Maker");
917 errcount--;
918 if (errcount == 0) {
919 wfatal("quitting");
920 quit(1);
922 continue;
924 memcpy(buf, buffer, 4);
925 buf[4] = 0;
926 size = atoi(buf);
927 if (size < 0 || size > sizeof(buffer)) {
928 wfatal("received invalid size %d for message from WindowMaker", size);
929 quit(1);
931 if (size == 0) {
932 werror("received 0-sized message from WindowMaker, trying to continue");
933 continue;
936 /* get message */
937 if (readmsg(0, buffer, size) < 0) {
938 werror("error reading message from Window Maker");
939 errcount--;
940 if (errcount == 0) {
941 wfatal("quitting");
942 quit(1);
944 continue;
946 #ifdef DEBUG
947 printf("RECEIVED %s\n", buffer);
948 #endif
949 if (buffer[0] != 'P' && buffer[0] != 'K') {
950 memcpy(buf, &buffer[1], 4);
951 buf[4] = 0;
952 workspace = atoi(buf);
953 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
954 wwarning("received message with invalid workspace number %i", workspace);
955 continue;
959 switch (buffer[0]) {
960 case 'S':
961 #ifdef DEBUG
962 printf("set texture %s\n", &buffer[5]);
963 #endif
964 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
965 break;
967 case 'C':
968 #ifdef DEBUG
969 printf("change texture %i\n", workspace);
970 #endif
971 if (!textures[workspace]) {
972 changeTexture(textures[0]);
973 } else {
974 changeTexture(textures[workspace]);
976 break;
978 case 'P':
979 #ifdef DEBUG
980 printf("change pixmappath %s\n", &buffer[1]);
981 #endif
982 if (PixmapPath)
983 wfree(PixmapPath);
984 PixmapPath = wstrdup(&buffer[1]);
985 break;
987 case 'U':
988 #ifdef DEBUG
989 printf("unset workspace %i\n", workspace);
990 #endif
991 setupTexture(rc, textures, &maxTextures, workspace, NULL);
992 break;
994 case 'K':
995 #ifdef DEBUG
996 printf("exit command\n");
997 #endif
998 quit(0);
1000 default:
1001 wwarning("unknown message received");
1002 break;
1007 static void updateDomain(const char *domain, const char *key, const char *texture)
1009 int result;
1010 char *program = "wdwrite";
1011 char cmd_smooth[1024];
1013 snprintf(cmd_smooth, sizeof(cmd_smooth),
1014 "wdwrite %s SmoothWorkspaceBack %s",
1015 domain, smooth ? "YES" : "NO");
1016 result = system(cmd_smooth);
1017 if (result == -1)
1018 werror("error executing system(\"%s\")", cmd_smooth);
1020 execlp(program, program, domain, key, texture, NULL);
1021 wwarning("warning could not run \"%s\"", program);
1024 static WMPropList *getValueForKey(const char *domain, const char *keyName)
1026 char *path;
1027 WMPropList *key, *val, *d;
1029 key = WMCreatePLString(keyName);
1031 /* try to find PixmapPath in user defaults */
1032 path = wdefaultspathfordomain(domain);
1033 d = WMReadPropListFromFile(path);
1034 if (!d) {
1035 wwarning("could not open domain file %s", path);
1037 wfree(path);
1039 if (d && !WMIsPLDictionary(d)) {
1040 WMReleasePropList(d);
1041 d = NULL;
1043 if (d) {
1044 val = WMGetFromPLDictionary(d, key);
1045 } else {
1046 val = NULL;
1048 /* try to find PixmapPath in global defaults */
1049 if (!val) {
1050 path = wglobaldefaultspathfordomain(domain);
1051 if (!path) {
1052 wwarning("could not locate file for domain %s", domain);
1053 d = NULL;
1054 } else {
1055 d = WMReadPropListFromFile(path);
1056 wfree(path);
1059 if (d && !WMIsPLDictionary(d)) {
1060 WMReleasePropList(d);
1061 d = NULL;
1063 if (d) {
1064 val = WMGetFromPLDictionary(d, key);
1066 } else {
1067 val = NULL;
1071 if (val)
1072 WMRetainPropList(val);
1074 WMReleasePropList(key);
1075 if (d)
1076 WMReleasePropList(d);
1078 return val;
1081 static char *getPixmapPath(const char *domain)
1083 WMPropList *val;
1084 char *ptr, *data;
1085 int len, i, count;
1087 val = getValueForKey(domain, "PixmapPath");
1089 if (!val || !WMIsPLArray(val)) {
1090 if (val)
1091 WMReleasePropList(val);
1092 return wstrdup("");
1095 count = WMGetPropListItemCount(val);
1096 len = 0;
1097 for (i = 0; i < count; i++) {
1098 WMPropList *v;
1100 v = WMGetFromPLArray(val, i);
1101 if (!v || !WMIsPLString(v)) {
1102 continue;
1104 len += strlen(WMGetFromPLString(v)) + 1;
1107 ptr = data = wmalloc(len + 1);
1108 *ptr = 0;
1110 for (i = 0; i < count; i++) {
1111 WMPropList *v;
1113 v = WMGetFromPLArray(val, i);
1114 if (!v || !WMIsPLString(v)) {
1115 continue;
1117 strcpy(ptr, WMGetFromPLString(v));
1119 ptr += strlen(WMGetFromPLString(v));
1120 *ptr = ':';
1121 ptr++;
1123 if (i > 0)
1124 ptr--;
1125 *(ptr--) = 0;
1127 WMReleasePropList(val);
1129 return data;
1132 static char *getFullPixmapPath(const char *file)
1134 char *tmp;
1136 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1137 int bsize = 512;
1138 char *path = wmalloc(bsize);
1140 while (!getcwd(path, bsize)) {
1141 bsize += bsize / 2;
1142 path = wrealloc(path, bsize);
1145 tmp = wstrconcat(path, "/");
1146 wfree(path);
1147 path = wstrconcat(tmp, file);
1148 wfree(tmp);
1150 return path;
1153 /* the file is in the PixmapPath */
1154 wfree(tmp);
1156 return wstrdup(file);
1159 static void print_help(void)
1161 printf("Usage: %s [options] [image]\n", prog_name);
1162 puts("Sets the workspace background to the specified image or a texture and");
1163 puts("optionally update Window Maker configuration");
1164 puts("");
1165 puts(" -display <display> display to use");
1166 puts(" -d, --dither dither image");
1167 puts(" -m, --match match colors");
1168 puts(" -S, --smooth smooth scaled image");
1169 #ifdef USE_XINERAMA
1170 puts(" -X, --xinerama stretch image across Xinerama heads");
1171 #endif
1172 puts(" -b, --back-color <color> background color");
1173 puts(" -t, --tile tile image");
1174 puts(" -e, --center center image");
1175 puts(" -s, --scale scale image (default)");
1176 puts(" -a, --maxscale scale image and keep aspect ratio");
1177 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1178 puts(" -u, --update-wmaker update WindowMaker domain database");
1179 puts(" -D, --update-domain <domain> update <domain> database");
1180 puts(" -c, --colors <cpc> colors per channel to use");
1181 puts(" -p, --parse <texture> proplist style texture specification");
1182 puts(" -w, --workspace <workspace> update background for the specified workspace");
1183 puts(" -v, --version show version of wmsetbg and exit");
1184 puts(" -h, --help show this help and exit");
1187 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1189 WMPropList *array, *val;
1190 char *value;
1191 int j;
1193 val = WMCreatePropListFromDescription(texture);
1194 if (!val) {
1195 wwarning("could not parse texture %s", texture);
1196 return;
1199 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1201 if (!array) {
1202 array = WMCreatePLArray(NULL, NULL);
1205 j = WMGetPropListItemCount(array);
1206 if (workspace >= j) {
1207 WMPropList *empty;
1209 empty = WMCreatePLArray(NULL, NULL);
1211 while (j++ < workspace - 1) {
1212 WMAddToPLArray(array, empty);
1214 WMAddToPLArray(array, val);
1216 WMReleasePropList(empty);
1217 } else {
1218 WMDeleteFromPLArray(array, workspace);
1219 WMInsertInPLArray(array, workspace, val);
1222 value = WMGetPropListDescription(array, False);
1223 updateDomain(domain, "WorkspaceSpecificBack", value);
1225 WMReleasePropList(array);
1228 int main(int argc, char **argv)
1230 int i;
1231 int helperMode = 0;
1232 RContext *rc;
1233 RContextAttributes rattr;
1234 char *style = "spixmap";
1235 char *back_color = "gray20";
1236 char *image_name = NULL;
1237 char *domain = "WindowMaker";
1238 int update = 0, cpc = 4, obey_user = 0;
1239 RRenderingMode render_mode = RDitheredRendering;
1240 char *texture = NULL;
1241 int workspace = -1;
1243 signal(SIGINT, SIG_DFL);
1244 signal(SIGTERM, SIG_DFL);
1245 signal(SIGQUIT, SIG_DFL);
1246 signal(SIGSEGV, SIG_DFL);
1247 signal(SIGBUS, SIG_DFL);
1248 signal(SIGFPE, SIG_DFL);
1249 signal(SIGABRT, SIG_DFL);
1250 signal(SIGHUP, SIG_DFL);
1251 signal(SIGPIPE, SIG_DFL);
1252 signal(SIGCHLD, SIG_DFL);
1254 WMInitializeApplication("wmsetbg", &argc, argv);
1256 prog_name = argv[0];
1257 for (i = 1; i < argc; i++) {
1258 if (strcmp(argv[i], "-helper") == 0) {
1259 helperMode = 1;
1260 } else if (strcmp(argv[i], "-display") == 0) {
1261 i++;
1262 if (i >= argc) {
1263 wfatal("too few arguments for %s", argv[i - 1]);
1264 quit(1);
1266 display = argv[i];
1267 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1268 style = "spixmap";
1269 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1270 style = "tpixmap";
1271 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1272 style = "cpixmap";
1273 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1274 style = "mpixmap";
1275 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1276 style = "fpixmap";
1277 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1278 render_mode = RDitheredRendering;
1279 obey_user++;
1280 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1281 render_mode = RBestMatchRendering;
1282 obey_user++;
1283 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1284 smooth = True;
1285 #ifdef USE_XINERAMA
1286 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1287 xineStretch = True;
1288 #endif
1289 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1290 update++;
1291 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1292 update++;
1293 i++;
1294 if (i >= argc) {
1295 wfatal("too few arguments for %s", argv[i - 1]);
1296 quit(1);
1298 domain = wstrdup(argv[i]);
1299 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 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", &cpc) != 1) {
1306 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1307 quit(1);
1309 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1310 i++;
1311 if (i >= argc) {
1312 wfatal("too few arguments for %s", argv[i - 1]);
1313 quit(1);
1315 back_color = argv[i];
1316 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1317 i++;
1318 if (i >= argc) {
1319 wfatal("too few arguments for %s", argv[i - 1]);
1320 quit(1);
1322 texture = argv[i];
1323 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1324 i++;
1325 if (i >= argc) {
1326 wfatal("too few arguments for %s", argv[i - 1]);
1327 quit(1);
1329 if (sscanf(argv[i], "%i", &workspace) != 1) {
1330 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1331 quit(1);
1333 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1334 printf("%s (Window Maker %s)\n", prog_name, VERSION);
1335 quit(0);
1336 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1337 print_help();
1338 quit(0);
1339 } else if (argv[i][0] != '-') {
1340 image_name = argv[i];
1341 } else {
1342 printf("%s: invalid argument '%s'\n", prog_name, argv[i]);
1343 printf("Try '%s --help' for more information\n", prog_name);
1344 quit(1);
1347 if (!image_name && !texture && !helperMode) {
1348 printf("%s: you must specify a image file name or a texture\n", prog_name);
1349 printf("Try '%s --help' for more information\n", prog_name);
1350 quit(1);
1353 PixmapPath = getPixmapPath(domain);
1354 if (!smooth) {
1355 WMPropList *val;
1356 /* carlos, don't remove this */
1357 #if 0 /* some problem with Alpha... TODO: check if its right */
1358 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1359 #else
1360 val = getValueForKey(domain, "SmoothWorkspaceBack");
1361 #endif
1363 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1364 smooth = True;
1367 dpy = XOpenDisplay(display);
1368 if (!dpy) {
1369 wfatal("could not open display");
1370 quit(1);
1372 #if 0
1373 XSynchronize(dpy, 1);
1374 #endif
1376 root = DefaultRootWindow(dpy);
1378 scr = DefaultScreen(dpy);
1380 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1381 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1382 scrX = scrY = 0;
1384 initXinerama();
1386 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1387 render_mode = RDitheredRendering;
1389 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1390 rattr.render_mode = render_mode;
1391 rattr.colors_per_channel = cpc;
1392 rattr.standard_colormap_mode = RCreateStdColormap;
1394 rc = RCreateContext(dpy, scr, &rattr);
1396 if (!rc) {
1397 rattr.standard_colormap_mode = RIgnoreStdColormap;
1398 rc = RCreateContext(dpy, scr, &rattr);
1401 if (!rc) {
1402 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1403 quit(1);
1406 if (helperMode) {
1407 int result;
1409 /* lower priority, so that it wont use all the CPU */
1410 result = nice(15);
1411 if (result == -1)
1412 wwarning("error could not nice process");
1414 helperLoop(rc);
1415 } else {
1416 BackgroundTexture *tex;
1417 char buffer[4098];
1419 if (!texture) {
1420 char *image_path = getFullPixmapPath(image_name);
1421 snprintf(buffer, sizeof(buffer), "(%s, \"%s\", %s)", style, image_path, back_color);
1422 wfree(image_path);
1423 texture = (char *)buffer;
1426 if (update && workspace < 0) {
1427 updateDomain(domain, "WorkspaceBack", texture);
1430 tex = parseTexture(rc, texture);
1431 if (!tex)
1432 quit(1);
1434 if (workspace < 0)
1435 changeTexture(tex);
1436 else {
1437 /* always update domain */
1438 changeTextureForWorkspace(domain, texture, workspace);
1442 WMReleaseApplication();
1443 return 0;