Coverity: fix dialog dereference null return value
[wmaker-crm.git] / util / wmsetbg.c
blobe5d1b157d6438a376c3fd54c71b23a6d8945467f
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 GETSTRORGOTO(val, tmp, 2, error);
510 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
511 wwarning("could not parse color %s in texture %s", tmp, text);
512 RReleaseImage(image);
513 goto error;
515 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
516 rcolor.red = color.red >> 8;
517 rcolor.green = color.green >> 8;
518 rcolor.blue = color.blue >> 8;
519 RGetClosestXColor(rc, &rcolor, &color);
520 } else {
521 rcolor.red = 0;
522 rcolor.green = 0;
523 rcolor.blue = 0;
525 /* for images with a transparent color */
526 if (image && image->data[3])
527 RCombineImageWithColor(image, &rcolor);
529 switch (toupper(type[0])) {
530 case 'T':
531 texture->width = iwidth;
532 texture->height = iheight;
533 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
534 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
535 RReleaseImage(image);
536 goto error;
539 texture->pixmap = pixmap;
540 texture->color = color;
541 break;
542 case 'S':
543 case 'M':
544 case 'C':
545 case 'F':
547 Pixmap tpixmap =
548 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
549 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
550 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
552 texture->pixmap = tpixmap;
553 texture->color = color;
554 texture->width = scrWidth;
555 texture->height = scrHeight;
557 if (!image)
558 break;
560 #ifdef USE_XINERAMA
561 if (xineInfo.count && ! xineStretch) {
562 int i;
563 for (i = 0; i < xineInfo.count; ++i) {
564 applyImage(rc, texture, image, type[0],
565 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
566 xineInfo.screens[i].size.width,
567 xineInfo.screens[i].size.height);
569 } else {
570 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
572 #else /* !USE_XINERAMA */
573 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
574 #endif /* !USE_XINERAMA */
576 break;
578 if (image)
579 RReleaseImage(image);
581 } else if (strcasecmp(type, "thgradient") == 0
582 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
583 XColor color;
584 RColor color1, color2;
585 RImage *image;
586 RImage *gradient;
587 RImage *tiled;
588 Pixmap pixmap;
589 int opaq;
590 char *file;
591 RGradientStyle gtype;
592 int twidth, theight;
594 GETSTRORGOTO(val, file, 1, error);
596 GETSTRORGOTO(val, tmp, 2, error);
598 opaq = atoi(tmp);
600 GETSTRORGOTO(val, tmp, 3, error);
602 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
603 wwarning("could not parse color %s in texture %s", tmp, text);
604 goto error;
607 color1.red = color.red >> 8;
608 color1.green = color.green >> 8;
609 color1.blue = color.blue >> 8;
611 GETSTRORGOTO(val, tmp, 4, error);
613 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
614 wwarning("could not parse color %s in texture %s", tmp, text);
615 goto error;
618 color2.red = color.red >> 8;
619 color2.green = color.green >> 8;
620 color2.blue = color.blue >> 8;
622 image = loadImage(rc, file);
623 if (!image) {
624 goto error;
627 switch (type[1]) {
628 case 'h':
629 case 'H':
630 gtype = RHorizontalGradient;
631 twidth = scrWidth;
632 theight = image->height > scrHeight ? scrHeight : image->height;
633 break;
634 case 'V':
635 case 'v':
636 gtype = RVerticalGradient;
637 twidth = image->width > scrWidth ? scrWidth : image->width;
638 theight = scrHeight;
639 break;
640 default:
641 gtype = RDiagonalGradient;
642 twidth = scrWidth;
643 theight = scrHeight;
644 break;
646 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
648 if (!gradient) {
649 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
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 *));
901 memset(buffer, 0, sizeof(buffer));
903 while (1) {
904 int workspace = -1;
906 /* get length of message */
907 if (readmsg(0, buffer, 4) < 0) {
908 werror("error reading message from Window Maker");
909 errcount--;
910 if (errcount == 0) {
911 wfatal("quitting");
912 quit(1);
914 continue;
916 memcpy(buf, buffer, 4);
917 buf[4] = 0;
918 size = atoi(buf);
919 if (size < 0 || size > sizeof(buffer)) {
920 wfatal("received invalid size %d for message from WindowMaker", size);
921 quit(1);
923 if (size == 0) {
924 werror("received 0-sized message from WindowMaker, trying to continue");
925 continue;
928 /* get message */
929 if (readmsg(0, buffer, size) < 0) {
930 werror("error reading message from Window Maker");
931 errcount--;
932 if (errcount == 0) {
933 wfatal("quitting");
934 quit(1);
936 continue;
938 #ifdef DEBUG
939 printf("RECEIVED %s\n", buffer);
940 #endif
941 if (buffer[0] != 'P' && buffer[0] != 'K') {
942 memcpy(buf, &buffer[1], 4);
943 buf[4] = 0;
944 workspace = atoi(buf);
945 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
946 wwarning("received message with invalid workspace number %i", workspace);
947 continue;
951 switch (buffer[0]) {
952 case 'S':
953 #ifdef DEBUG
954 printf("set texture %s\n", &buffer[5]);
955 #endif
956 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
957 break;
959 case 'C':
960 #ifdef DEBUG
961 printf("change texture %i\n", workspace);
962 #endif
963 if (!textures[workspace]) {
964 changeTexture(textures[0]);
965 } else {
966 changeTexture(textures[workspace]);
968 break;
970 case 'P':
971 #ifdef DEBUG
972 printf("change pixmappath %s\n", &buffer[1]);
973 #endif
974 if (PixmapPath)
975 wfree(PixmapPath);
976 PixmapPath = wstrdup(&buffer[1]);
977 break;
979 case 'U':
980 #ifdef DEBUG
981 printf("unset workspace %i\n", workspace);
982 #endif
983 setupTexture(rc, textures, &maxTextures, workspace, NULL);
984 break;
986 case 'K':
987 #ifdef DEBUG
988 printf("exit command\n");
989 #endif
990 quit(0);
992 default:
993 wwarning("unknown message received");
994 break;
999 static void updateDomain(const char *domain, const char *key, const char *texture)
1001 int result;
1002 char *program = "wdwrite";
1003 char cmd_smooth[1024];
1005 snprintf(cmd_smooth, sizeof(cmd_smooth),
1006 "wdwrite %s SmoothWorkspaceBack %s",
1007 domain, smooth ? "YES" : "NO");
1008 result = system(cmd_smooth);
1009 if (result == -1)
1010 werror("error executing system(\"%s\")", cmd_smooth);
1012 execlp(program, program, domain, key, texture, NULL);
1013 wwarning("warning could not run \"%s\"", program);
1016 static WMPropList *getValueForKey(const char *domain, const char *keyName)
1018 char *path;
1019 WMPropList *key, *val, *d;
1021 key = WMCreatePLString(keyName);
1023 /* try to find PixmapPath in user defaults */
1024 path = wdefaultspathfordomain(domain);
1025 d = WMReadPropListFromFile(path);
1026 if (!d) {
1027 wwarning("could not open domain file %s", path);
1029 wfree(path);
1031 if (d && !WMIsPLDictionary(d)) {
1032 WMReleasePropList(d);
1033 d = NULL;
1035 if (d) {
1036 val = WMGetFromPLDictionary(d, key);
1037 } else {
1038 val = NULL;
1040 /* try to find PixmapPath in global defaults */
1041 if (!val) {
1042 path = wglobaldefaultspathfordomain(domain);
1043 if (!path) {
1044 wwarning("could not locate file for domain %s", domain);
1045 d = NULL;
1046 } else {
1047 d = WMReadPropListFromFile(path);
1048 wfree(path);
1051 if (d && !WMIsPLDictionary(d)) {
1052 WMReleasePropList(d);
1053 d = NULL;
1055 if (d) {
1056 val = WMGetFromPLDictionary(d, key);
1058 } else {
1059 val = NULL;
1063 if (val)
1064 WMRetainPropList(val);
1066 WMReleasePropList(key);
1067 if (d)
1068 WMReleasePropList(d);
1070 return val;
1073 static char *getPixmapPath(const char *domain)
1075 WMPropList *val;
1076 char *ptr, *data;
1077 int len, i, count;
1079 val = getValueForKey(domain, "PixmapPath");
1081 if (!val || !WMIsPLArray(val)) {
1082 if (val)
1083 WMReleasePropList(val);
1084 return wstrdup("");
1087 count = WMGetPropListItemCount(val);
1088 len = 0;
1089 for (i = 0; i < count; i++) {
1090 WMPropList *v;
1092 v = WMGetFromPLArray(val, i);
1093 if (!v || !WMIsPLString(v)) {
1094 continue;
1096 len += strlen(WMGetFromPLString(v)) + 1;
1099 ptr = data = wmalloc(len + 1);
1100 *ptr = 0;
1102 for (i = 0; i < count; i++) {
1103 WMPropList *v;
1105 v = WMGetFromPLArray(val, i);
1106 if (!v || !WMIsPLString(v)) {
1107 continue;
1109 strcpy(ptr, WMGetFromPLString(v));
1111 ptr += strlen(WMGetFromPLString(v));
1112 *ptr = ':';
1113 ptr++;
1115 if (i > 0)
1116 ptr--;
1117 *(ptr--) = 0;
1119 WMReleasePropList(val);
1121 return data;
1124 static char *getFullPixmapPath(const char *file)
1126 char *tmp;
1128 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1129 int bsize = 512;
1130 char *path = wmalloc(bsize);
1132 while (!getcwd(path, bsize)) {
1133 bsize += bsize / 2;
1134 path = wrealloc(path, bsize);
1137 tmp = wstrconcat(path, "/");
1138 wfree(path);
1139 path = wstrconcat(tmp, file);
1140 wfree(tmp);
1142 return path;
1145 /* the file is in the PixmapPath */
1146 wfree(tmp);
1148 return wstrdup(file);
1151 static void print_help(void)
1153 printf("Usage: %s [options] [image]\n", prog_name);
1154 puts("Sets the workspace background to the specified image or a texture and");
1155 puts("optionally update Window Maker configuration");
1156 puts("");
1157 puts(" -display <display> display to use");
1158 puts(" -d, --dither dither image");
1159 puts(" -m, --match match colors");
1160 puts(" -S, --smooth smooth scaled image");
1161 #ifdef USE_XINERAMA
1162 puts(" -X, --xinerama stretch image across Xinerama heads");
1163 #endif
1164 puts(" -b, --back-color <color> background color");
1165 puts(" -t, --tile tile image");
1166 puts(" -e, --center center image");
1167 puts(" -s, --scale scale image (default)");
1168 puts(" -a, --maxscale scale image and keep aspect ratio");
1169 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1170 puts(" -u, --update-wmaker update WindowMaker domain database");
1171 puts(" -D, --update-domain <domain> update <domain> database");
1172 puts(" -c, --colors <cpc> colors per channel to use");
1173 puts(" -p, --parse <texture> proplist style texture specification");
1174 puts(" -w, --workspace <workspace> update background for the specified workspace");
1175 puts(" -v, --version show version of wmsetbg and exit");
1176 puts(" -h, --help show this help and exit");
1179 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1181 WMPropList *array, *val;
1182 char *value;
1183 int j;
1185 val = WMCreatePropListFromDescription(texture);
1186 if (!val) {
1187 wwarning("could not parse texture %s", texture);
1188 return;
1191 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1193 if (!array) {
1194 array = WMCreatePLArray(NULL, NULL);
1197 j = WMGetPropListItemCount(array);
1198 if (workspace >= j) {
1199 WMPropList *empty;
1201 empty = WMCreatePLArray(NULL, NULL);
1203 while (j++ < workspace - 1) {
1204 WMAddToPLArray(array, empty);
1206 WMAddToPLArray(array, val);
1208 WMReleasePropList(empty);
1209 } else {
1210 WMDeleteFromPLArray(array, workspace);
1211 WMInsertInPLArray(array, workspace, val);
1214 value = WMGetPropListDescription(array, False);
1215 updateDomain(domain, "WorkspaceSpecificBack", value);
1217 WMReleasePropList(array);
1220 int main(int argc, char **argv)
1222 int i;
1223 int helperMode = 0;
1224 RContext *rc;
1225 RContextAttributes rattr;
1226 char *style = "spixmap";
1227 char *back_color = "gray20";
1228 char *image_name = NULL;
1229 char *domain = "WindowMaker";
1230 int update = 0, cpc = 4, obey_user = 0;
1231 RRenderingMode render_mode = RDitheredRendering;
1232 char *texture = NULL;
1233 int workspace = -1;
1235 signal(SIGINT, SIG_DFL);
1236 signal(SIGTERM, SIG_DFL);
1237 signal(SIGQUIT, SIG_DFL);
1238 signal(SIGSEGV, SIG_DFL);
1239 signal(SIGBUS, SIG_DFL);
1240 signal(SIGFPE, SIG_DFL);
1241 signal(SIGABRT, SIG_DFL);
1242 signal(SIGHUP, SIG_DFL);
1243 signal(SIGPIPE, SIG_DFL);
1244 signal(SIGCHLD, SIG_DFL);
1246 WMInitializeApplication("wmsetbg", &argc, argv);
1248 prog_name = argv[0];
1249 for (i = 1; i < argc; i++) {
1250 if (strcmp(argv[i], "-helper") == 0) {
1251 helperMode = 1;
1252 } else if (strcmp(argv[i], "-display") == 0) {
1253 i++;
1254 if (i >= argc) {
1255 wfatal("too few arguments for %s", argv[i - 1]);
1256 quit(1);
1258 display = argv[i];
1259 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1260 style = "spixmap";
1261 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1262 style = "tpixmap";
1263 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1264 style = "cpixmap";
1265 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1266 style = "mpixmap";
1267 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1268 style = "fpixmap";
1269 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1270 render_mode = RDitheredRendering;
1271 obey_user++;
1272 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1273 render_mode = RBestMatchRendering;
1274 obey_user++;
1275 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1276 smooth = True;
1277 #ifdef USE_XINERAMA
1278 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1279 xineStretch = True;
1280 #endif
1281 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1282 update++;
1283 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1284 update++;
1285 i++;
1286 if (i >= argc) {
1287 wfatal("too few arguments for %s", argv[i - 1]);
1288 quit(1);
1290 domain = wstrdup(argv[i]);
1291 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1292 i++;
1293 if (i >= argc) {
1294 wfatal("too few arguments for %s", argv[i - 1]);
1295 quit(1);
1297 if (sscanf(argv[i], "%i", &cpc) != 1) {
1298 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1299 quit(1);
1301 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1302 i++;
1303 if (i >= argc) {
1304 wfatal("too few arguments for %s", argv[i - 1]);
1305 quit(1);
1307 back_color = argv[i];
1308 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1309 i++;
1310 if (i >= argc) {
1311 wfatal("too few arguments for %s", argv[i - 1]);
1312 quit(1);
1314 texture = argv[i];
1315 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1316 i++;
1317 if (i >= argc) {
1318 wfatal("too few arguments for %s", argv[i - 1]);
1319 quit(1);
1321 if (sscanf(argv[i], "%i", &workspace) != 1) {
1322 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1323 quit(1);
1325 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1326 printf("%s (Window Maker %s)\n", prog_name, VERSION);
1327 quit(0);
1328 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1329 print_help();
1330 quit(0);
1331 } else if (argv[i][0] != '-') {
1332 image_name = argv[i];
1333 } else {
1334 printf("%s: invalid argument '%s'\n", prog_name, argv[i]);
1335 printf("Try '%s --help' for more information\n", prog_name);
1336 quit(1);
1339 if (!image_name && !texture && !helperMode) {
1340 printf("%s: you must specify a image file name or a texture\n", prog_name);
1341 printf("Try '%s --help' for more information\n", prog_name);
1342 quit(1);
1345 PixmapPath = getPixmapPath(domain);
1346 if (!smooth) {
1347 WMPropList *val;
1348 /* carlos, don't remove this */
1349 #if 0 /* some problem with Alpha... TODO: check if its right */
1350 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1351 #else
1352 val = getValueForKey(domain, "SmoothWorkspaceBack");
1353 #endif
1355 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1356 smooth = True;
1359 dpy = XOpenDisplay(display);
1360 if (!dpy) {
1361 wfatal("could not open display");
1362 quit(1);
1364 #if 0
1365 XSynchronize(dpy, 1);
1366 #endif
1368 root = DefaultRootWindow(dpy);
1370 scr = DefaultScreen(dpy);
1372 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1373 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1374 scrX = scrY = 0;
1376 initXinerama();
1378 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1379 render_mode = RDitheredRendering;
1381 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1382 rattr.render_mode = render_mode;
1383 rattr.colors_per_channel = cpc;
1384 rattr.standard_colormap_mode = RCreateStdColormap;
1386 rc = RCreateContext(dpy, scr, &rattr);
1388 if (!rc) {
1389 rattr.standard_colormap_mode = RIgnoreStdColormap;
1390 rc = RCreateContext(dpy, scr, &rattr);
1393 if (!rc) {
1394 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1395 quit(1);
1398 if (helperMode) {
1399 int result;
1401 /* lower priority, so that it wont use all the CPU */
1402 result = nice(15);
1403 if (result == -1)
1404 wwarning("error could not nice process");
1406 helperLoop(rc);
1407 } else {
1408 BackgroundTexture *tex;
1409 char buffer[4098];
1411 if (!texture) {
1412 char *image_path = getFullPixmapPath(image_name);
1413 snprintf(buffer, sizeof(buffer), "(%s, \"%s\", %s)", style, image_path, back_color);
1414 wfree(image_path);
1415 texture = (char *)buffer;
1418 if (update && workspace < 0) {
1419 updateDomain(domain, "WorkspaceBack", texture);
1422 tex = parseTexture(rc, texture);
1423 if (!tex)
1424 quit(1);
1426 if (workspace < 0)
1427 changeTexture(tex);
1428 else {
1429 /* always update domain */
1430 changeTextureForWorkspace(domain, texture, workspace);
1434 WMReleaseApplication();
1435 return 0;