util: add check for size validity (Coverity #50224)
[wmaker-crm.git] / util / wmsetbg.c
blobc25d60bf969a23a2851306a6881cb4b42b6bd69c
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 if (fimage)
234 RReleaseImage(image);
235 return;
238 if (image->width != width || image->height != height) {
239 int sx, sy, w, h;
241 if (image->height < height) {
242 h = image->height;
243 y += (height - h) / 2;
244 sy = 0;
245 } else {
246 sy = (image->height - height) / 2;
247 h = height;
249 if (image->width < width) {
250 w = image->width;
251 x += (width - w) / 2;
252 sx = 0;
253 } else {
254 sx = (image->width - width) / 2;
255 w = width;
258 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
259 } else
260 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
261 x, y);
263 XFreePixmap(dpy, pixmap);
264 if (fimage) {
265 RReleaseImage(image);
268 break;
272 static BackgroundTexture *parseTexture(RContext * rc, char *text)
274 BackgroundTexture *texture = NULL;
275 WMPropList *texarray;
276 WMPropList *val;
277 int count;
278 char *tmp;
279 char *type;
281 #define GETSTRORGOTO(val, str, i, label) \
282 val = WMGetFromPLArray(texarray, i);\
283 if (!WMIsPLString(val)) {\
284 wwarning("could not parse texture %s", text);\
285 goto label;\
287 str = WMGetFromPLString(val)
289 texarray = WMCreatePropListFromDescription(text);
290 if (!texarray || !WMIsPLArray(texarray)
291 || (count = WMGetPropListItemCount(texarray)) < 2) {
293 wwarning("could not parse texture %s", text);
294 if (texarray)
295 WMReleasePropList(texarray);
296 return NULL;
299 texture = wmalloc(sizeof(BackgroundTexture));
301 GETSTRORGOTO(val, type, 0, error);
303 if (strcasecmp(type, "solid") == 0) {
304 XColor color;
305 Pixmap pixmap;
307 texture->solid = 1;
309 GETSTRORGOTO(val, tmp, 1, error);
311 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
312 wwarning("could not parse color %s in texture %s", tmp, text);
313 goto error;
315 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
317 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
318 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
319 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
321 texture->pixmap = pixmap;
322 texture->color = color;
323 texture->width = 8;
324 texture->height = 8;
325 } else if (strcasecmp(type, "vgradient") == 0
326 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
327 XColor color;
328 RColor color1, color2;
329 RImage *image;
330 Pixmap pixmap;
331 RGradientStyle gtype;
332 int iwidth, iheight;
334 GETSTRORGOTO(val, tmp, 1, error);
336 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
337 wwarning("could not parse color %s in texture %s", tmp, text);
338 goto error;
341 color1.red = color.red >> 8;
342 color1.green = color.green >> 8;
343 color1.blue = color.blue >> 8;
345 GETSTRORGOTO(val, tmp, 2, error);
347 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
348 wwarning("could not parse color %s in texture %s", tmp, text);
349 goto error;
352 color2.red = color.red >> 8;
353 color2.green = color.green >> 8;
354 color2.blue = color.blue >> 8;
356 switch (type[0]) {
357 case 'h':
358 case 'H':
359 gtype = RHorizontalGradient;
360 iwidth = scrWidth;
361 iheight = 32;
362 break;
363 case 'V':
364 case 'v':
365 gtype = RVerticalGradient;
366 iwidth = 32;
367 iheight = scrHeight;
368 break;
369 default:
370 gtype = RDiagonalGradient;
371 iwidth = scrWidth;
372 iheight = scrHeight;
373 break;
376 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
378 if (!image) {
379 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
380 goto error;
383 if (!RConvertImage(rc, image, &pixmap)) {
384 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
385 RReleaseImage(image);
386 goto error;
389 texture->width = image->width;
390 texture->height = image->height;
391 RReleaseImage(image);
393 texture->pixmap = pixmap;
394 } else if (strcasecmp(type, "mvgradient") == 0
395 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
396 XColor color;
397 RColor **colors;
398 RImage *image;
399 Pixmap pixmap;
400 int i, j;
401 RGradientStyle gtype;
402 int iwidth, iheight;
404 colors = malloc(sizeof(RColor *) * (count - 1));
405 if (!colors) {
406 wwarning("out of memory while parsing texture");
407 goto error;
409 memset(colors, 0, sizeof(RColor *) * (count - 1));
411 for (i = 2; i < count; i++) {
412 val = WMGetFromPLArray(texarray, i);
413 if (!WMIsPLString(val)) {
414 wwarning("could not parse texture %s", text);
416 for (j = 0; colors[j] != NULL; j++)
417 wfree(colors[j]);
418 wfree(colors);
419 goto error;
421 tmp = WMGetFromPLString(val);
423 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
424 wwarning("could not parse color %s in texture %s", tmp, text);
426 for (j = 0; colors[j] != NULL; j++)
427 wfree(colors[j]);
428 wfree(colors);
429 goto error;
431 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
432 wwarning("out of memory while parsing texture");
434 for (j = 0; colors[j] != NULL; j++)
435 wfree(colors[j]);
436 wfree(colors);
437 goto error;
440 colors[i - 2]->red = color.red >> 8;
441 colors[i - 2]->green = color.green >> 8;
442 colors[i - 2]->blue = color.blue >> 8;
445 switch (type[1]) {
446 case 'h':
447 case 'H':
448 gtype = RHorizontalGradient;
449 iwidth = scrWidth;
450 iheight = 32;
451 break;
452 case 'V':
453 case 'v':
454 gtype = RVerticalGradient;
455 iwidth = 32;
456 iheight = scrHeight;
457 break;
458 default:
459 gtype = RDiagonalGradient;
460 iwidth = scrWidth;
461 iheight = scrHeight;
462 break;
465 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
467 for (j = 0; colors[j] != NULL; j++)
468 wfree(colors[j]);
469 wfree(colors);
471 if (!image) {
472 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
473 goto error;
476 if (!RConvertImage(rc, image, &pixmap)) {
477 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
478 RReleaseImage(image);
479 goto error;
482 texture->width = image->width;
483 texture->height = image->height;
484 RReleaseImage(image);
486 texture->pixmap = pixmap;
487 } else if (strcasecmp(type, "cpixmap") == 0
488 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
489 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
490 XColor color;
491 Pixmap pixmap = None;
492 RImage *image = NULL;
493 int iwidth = 0, iheight = 0;
494 RColor rcolor;
496 GETSTRORGOTO(val, tmp, 1, error);
498 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
499 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
502 if (!pixmap) {
503 image = loadImage(rc, tmp);
504 if (!image) {
505 goto error;
507 iwidth = image->width;
508 iheight = image->height;
511 GETSTRORGOTO(val, tmp, 2, error);
513 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
514 wwarning("could not parse color %s in texture %s", tmp, text);
515 RReleaseImage(image);
516 goto error;
518 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
519 rcolor.red = color.red >> 8;
520 rcolor.green = color.green >> 8;
521 rcolor.blue = color.blue >> 8;
522 RGetClosestXColor(rc, &rcolor, &color);
523 } else {
524 rcolor.red = 0;
525 rcolor.green = 0;
526 rcolor.blue = 0;
528 /* for images with a transparent color */
529 if (image && image->data[3])
530 RCombineImageWithColor(image, &rcolor);
532 switch (toupper(type[0])) {
533 case 'T':
534 texture->width = iwidth;
535 texture->height = iheight;
536 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
537 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
538 RReleaseImage(image);
539 goto error;
542 texture->pixmap = pixmap;
543 texture->color = color;
544 break;
545 case 'S':
546 case 'M':
547 case 'C':
548 case 'F':
550 Pixmap tpixmap =
551 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
552 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
553 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
555 texture->pixmap = tpixmap;
556 texture->color = color;
557 texture->width = scrWidth;
558 texture->height = scrHeight;
560 if (!image)
561 break;
563 #ifdef USE_XINERAMA
564 if (xineInfo.count && ! xineStretch) {
565 int i;
566 for (i = 0; i < xineInfo.count; ++i) {
567 applyImage(rc, texture, image, type[0],
568 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
569 xineInfo.screens[i].size.width,
570 xineInfo.screens[i].size.height);
572 } else {
573 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
575 #else /* !USE_XINERAMA */
576 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
577 #endif /* !USE_XINERAMA */
579 break;
581 if (image)
582 RReleaseImage(image);
584 } else if (strcasecmp(type, "thgradient") == 0
585 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
586 XColor color;
587 RColor color1, color2;
588 RImage *image;
589 RImage *gradient;
590 RImage *tiled;
591 Pixmap pixmap;
592 int opaq;
593 char *file;
594 RGradientStyle gtype;
595 int twidth, theight;
597 GETSTRORGOTO(val, file, 1, error);
599 GETSTRORGOTO(val, tmp, 2, error);
601 opaq = atoi(tmp);
603 GETSTRORGOTO(val, tmp, 3, error);
605 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
606 wwarning("could not parse color %s in texture %s", tmp, text);
607 goto error;
610 color1.red = color.red >> 8;
611 color1.green = color.green >> 8;
612 color1.blue = color.blue >> 8;
614 GETSTRORGOTO(val, tmp, 4, error);
616 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
617 wwarning("could not parse color %s in texture %s", tmp, text);
618 goto error;
621 color2.red = color.red >> 8;
622 color2.green = color.green >> 8;
623 color2.blue = color.blue >> 8;
625 image = loadImage(rc, file);
626 if (!image) {
627 goto error;
630 switch (type[1]) {
631 case 'h':
632 case 'H':
633 gtype = RHorizontalGradient;
634 twidth = scrWidth;
635 theight = image->height > scrHeight ? scrHeight : image->height;
636 break;
637 case 'V':
638 case 'v':
639 gtype = RVerticalGradient;
640 twidth = image->width > scrWidth ? scrWidth : image->width;
641 theight = scrHeight;
642 break;
643 default:
644 gtype = RDiagonalGradient;
645 twidth = scrWidth;
646 theight = scrHeight;
647 break;
649 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
651 if (!gradient) {
652 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
653 RReleaseImage(image);
654 goto error;
657 tiled = RMakeTiledImage(image, twidth, theight);
658 if (!tiled) {
659 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
660 RReleaseImage(gradient);
661 RReleaseImage(image);
662 goto error;
664 RReleaseImage(image);
666 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
667 RReleaseImage(gradient);
669 if (!RConvertImage(rc, tiled, &pixmap)) {
670 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
671 RReleaseImage(tiled);
672 goto error;
674 texture->width = tiled->width;
675 texture->height = tiled->height;
677 RReleaseImage(tiled);
679 texture->pixmap = pixmap;
680 } else if (strcasecmp(type, "function") == 0) {
681 /* Leave this in to handle the unlikely case of
682 * someone actually having function textures configured */
683 wwarning("function texture support has been removed");
684 goto error;
685 } else {
686 wwarning("invalid texture type %s", text);
687 goto error;
690 texture->spec = wstrdup(text);
692 return texture;
694 error:
695 if (texture)
696 wfree(texture);
697 if (texarray)
698 WMReleasePropList(texarray);
700 return NULL;
703 static void freeTexture(BackgroundTexture * texture)
705 if (texture->solid) {
706 unsigned long pixel[1];
708 pixel[0] = texture->color.pixel;
709 /* dont free black/white pixels */
710 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
711 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
712 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
714 if (texture->pixmap) {
715 XFreePixmap(dpy, texture->pixmap);
717 wfree(texture->spec);
718 wfree(texture);
721 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
723 BackgroundTexture *newTexture = NULL;
724 int i;
726 /* unset the texture */
727 if (!texture) {
728 if (textures[workspace] != NULL) {
729 textures[workspace]->refcount--;
731 if (textures[workspace]->refcount == 0)
732 freeTexture(textures[workspace]);
734 textures[workspace] = NULL;
735 return;
738 if (textures[workspace]
739 && strcasecmp(textures[workspace]->spec, texture) == 0) {
740 /* texture did not change */
741 return;
744 /* check if the same texture is already created */
745 for (i = 0; i < *maxTextures; i++) {
746 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
747 newTexture = textures[i];
748 break;
752 if (!newTexture) {
753 /* create the texture */
754 newTexture = parseTexture(rc, texture);
756 if (!newTexture)
757 return;
759 if (textures[workspace] != NULL) {
761 textures[workspace]->refcount--;
763 if (textures[workspace]->refcount == 0)
764 freeTexture(textures[workspace]);
767 newTexture->refcount++;
768 textures[workspace] = newTexture;
770 if (*maxTextures < workspace)
771 *maxTextures = workspace;
774 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
776 Display *tmpDpy;
777 Pixmap copyP;
779 /* must open a new display or the RetainPermanent will
780 * leave stuff allocated in RContext unallocated after exit */
781 tmpDpy = XOpenDisplay(display);
782 if (!tmpDpy) {
783 wwarning("could not open display to update background image information");
785 return None;
786 } else {
787 XSync(dpy, False);
789 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
790 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
791 XSync(tmpDpy, False);
793 XSetCloseDownMode(tmpDpy, RetainPermanent);
794 XCloseDisplay(tmpDpy);
797 return copyP;
800 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
802 /* Parameter not used, but tell the compiler that it is ok */
803 (void) dpy;
804 (void) err;
806 return 0;
809 static void setPixmapProperty(Pixmap pixmap)
811 static Atom prop = 0;
812 Atom type;
813 int format;
814 unsigned long length, after;
815 unsigned char *data;
816 int mode;
818 if (!prop) {
819 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
822 XGrabServer(dpy);
824 /* Clear out the old pixmap */
825 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
826 &type, &format, &length, &after, &data);
828 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
829 XSetErrorHandler(dummyErrorHandler);
830 XKillClient(dpy, *((Pixmap *) data));
831 XSync(dpy, False);
832 XSetErrorHandler(NULL);
833 mode = PropModeReplace;
834 } else {
835 mode = PropModeAppend;
837 if (pixmap)
838 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
839 else
840 XDeleteProperty(dpy, root, prop);
842 XUngrabServer(dpy);
843 XFlush(dpy);
846 static void changeTexture(BackgroundTexture * texture)
848 if (!texture) {
849 return;
852 if (texture->solid) {
853 XSetWindowBackground(dpy, root, texture->color.pixel);
854 } else {
855 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
857 XClearWindow(dpy, root);
859 XSync(dpy, False);
862 Pixmap pixmap;
864 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
866 setPixmapProperty(pixmap);
870 static int readmsg(int fd, char *buffer, int size)
872 int count;
874 while (size > 0) {
875 count = read(fd, buffer, size);
876 if (count < 0)
877 return -1;
878 size -= count;
879 buffer += count;
880 *buffer = 0;
883 return size;
887 * Message Format:
888 * sizeSntexture_spec - sets the texture for workspace n
889 * sizeCn - change background texture to the one for workspace n
890 * sizePpath - set the pixmap search path
892 * n is 4 bytes
893 * size = 4 bytes for length of the message data
895 static noreturn void helperLoop(RContext * rc)
897 BackgroundTexture *textures[WORKSPACE_COUNT];
898 int maxTextures = 0;
899 char buffer[2048], buf[8];
900 int size;
901 int errcount = 4;
903 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
905 while (1) {
906 int workspace = -1;
908 /* get length of message */
909 if (readmsg(0, buffer, 4) < 0) {
910 werror("error reading message from Window Maker");
911 errcount--;
912 if (errcount == 0) {
913 wfatal("quitting");
914 quit(1);
916 continue;
918 memcpy(buf, buffer, 4);
919 buf[4] = 0;
920 size = atoi(buf);
921 if (size < 0 || size > sizeof(buffer)) {
922 wfatal("received invalid size %d for message from WindowMaker", size);
923 quit(1);
925 if (size == 0) {
926 werror("received 0-sized message from WindowMaker, trying to continue");
927 continue;
930 /* get message */
931 if (readmsg(0, buffer, size) < 0) {
932 werror("error reading message from Window Maker");
933 errcount--;
934 if (errcount == 0) {
935 wfatal("quitting");
936 quit(1);
938 continue;
940 #ifdef DEBUG
941 printf("RECEIVED %s\n", buffer);
942 #endif
943 if (buffer[0] != 'P' && buffer[0] != 'K') {
944 memcpy(buf, &buffer[1], 4);
945 buf[4] = 0;
946 workspace = atoi(buf);
947 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
948 wwarning("received message with invalid workspace number %i", workspace);
949 continue;
953 switch (buffer[0]) {
954 case 'S':
955 #ifdef DEBUG
956 printf("set texture %s\n", &buffer[5]);
957 #endif
958 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
959 break;
961 case 'C':
962 #ifdef DEBUG
963 printf("change texture %i\n", workspace);
964 #endif
965 if (!textures[workspace]) {
966 changeTexture(textures[0]);
967 } else {
968 changeTexture(textures[workspace]);
970 break;
972 case 'P':
973 #ifdef DEBUG
974 printf("change pixmappath %s\n", &buffer[1]);
975 #endif
976 if (PixmapPath)
977 wfree(PixmapPath);
978 PixmapPath = wstrdup(&buffer[1]);
979 break;
981 case 'U':
982 #ifdef DEBUG
983 printf("unset workspace %i\n", workspace);
984 #endif
985 setupTexture(rc, textures, &maxTextures, workspace, NULL);
986 break;
988 case 'K':
989 #ifdef DEBUG
990 printf("exit command\n");
991 #endif
992 quit(0);
994 default:
995 wwarning("unknown message received");
996 break;
1001 static void updateDomain(const char *domain, const char *key, const char *texture)
1003 int result;
1004 char *program = "wdwrite";
1005 char cmd_smooth[1024];
1007 snprintf(cmd_smooth, sizeof(cmd_smooth),
1008 "wdwrite %s SmoothWorkspaceBack %s",
1009 domain, smooth ? "YES" : "NO");
1010 result = system(cmd_smooth);
1011 if (result == -1)
1012 werror("error executing system(\"%s\")", cmd_smooth);
1014 execlp(program, program, domain, key, texture, NULL);
1015 wwarning("warning could not run \"%s\"", program);
1018 static WMPropList *getValueForKey(const char *domain, const char *keyName)
1020 char *path;
1021 WMPropList *key, *val, *d;
1023 key = WMCreatePLString(keyName);
1025 /* try to find PixmapPath in user defaults */
1026 path = wdefaultspathfordomain(domain);
1027 d = WMReadPropListFromFile(path);
1028 if (!d) {
1029 wwarning("could not open domain file %s", path);
1031 wfree(path);
1033 if (d && !WMIsPLDictionary(d)) {
1034 WMReleasePropList(d);
1035 d = NULL;
1037 if (d) {
1038 val = WMGetFromPLDictionary(d, key);
1039 } else {
1040 val = NULL;
1042 /* try to find PixmapPath in global defaults */
1043 if (!val) {
1044 path = wglobaldefaultspathfordomain(domain);
1045 if (!path) {
1046 wwarning("could not locate file for domain %s", domain);
1047 d = NULL;
1048 } else {
1049 d = WMReadPropListFromFile(path);
1050 wfree(path);
1053 if (d && !WMIsPLDictionary(d)) {
1054 WMReleasePropList(d);
1055 d = NULL;
1057 if (d) {
1058 val = WMGetFromPLDictionary(d, key);
1060 } else {
1061 val = NULL;
1065 if (val)
1066 WMRetainPropList(val);
1068 WMReleasePropList(key);
1069 if (d)
1070 WMReleasePropList(d);
1072 return val;
1075 static char *getPixmapPath(const char *domain)
1077 WMPropList *val;
1078 char *ptr, *data;
1079 int len, i, count;
1081 val = getValueForKey(domain, "PixmapPath");
1083 if (!val || !WMIsPLArray(val)) {
1084 if (val)
1085 WMReleasePropList(val);
1086 return wstrdup("");
1089 count = WMGetPropListItemCount(val);
1090 len = 0;
1091 for (i = 0; i < count; i++) {
1092 WMPropList *v;
1094 v = WMGetFromPLArray(val, i);
1095 if (!v || !WMIsPLString(v)) {
1096 continue;
1098 len += strlen(WMGetFromPLString(v)) + 1;
1101 ptr = data = wmalloc(len + 1);
1102 *ptr = 0;
1104 for (i = 0; i < count; i++) {
1105 WMPropList *v;
1107 v = WMGetFromPLArray(val, i);
1108 if (!v || !WMIsPLString(v)) {
1109 continue;
1111 strcpy(ptr, WMGetFromPLString(v));
1113 ptr += strlen(WMGetFromPLString(v));
1114 *ptr = ':';
1115 ptr++;
1117 if (i > 0)
1118 ptr--;
1119 *(ptr--) = 0;
1121 WMReleasePropList(val);
1123 return data;
1126 static char *getFullPixmapPath(const char *file)
1128 char *tmp;
1130 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1131 int bsize = 512;
1132 char *path = wmalloc(bsize);
1134 while (!getcwd(path, bsize)) {
1135 bsize += bsize / 2;
1136 path = wrealloc(path, bsize);
1139 tmp = wstrconcat(path, "/");
1140 wfree(path);
1141 path = wstrconcat(tmp, file);
1142 wfree(tmp);
1144 return path;
1147 /* the file is in the PixmapPath */
1148 wfree(tmp);
1150 return wstrdup(file);
1153 static void print_help(void)
1155 printf("Usage: %s [options] [image]\n", __progname);
1156 puts("Sets the workspace background to the specified image or a texture and");
1157 puts("optionally update Window Maker configuration");
1158 puts("");
1159 puts(" -display display to use");
1160 puts(" -d, --dither dither image");
1161 puts(" -m, --match match colors");
1162 puts(" -S, --smooth smooth scaled image");
1163 #ifdef USE_XINERAMA
1164 puts(" -X, --xinerama stretch image across Xinerama heads");
1165 #endif
1166 puts(" -b, --back-color <color> background color");
1167 puts(" -t, --tile tile image");
1168 puts(" -e, --center center image");
1169 puts(" -s, --scale scale image (default)");
1170 puts(" -a, --maxscale scale image and keep aspect ratio");
1171 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1172 puts(" -u, --update-wmaker update WindowMaker domain database");
1173 puts(" -D, --update-domain <domain> update <domain> database");
1174 puts(" -c, --colors <cpc> colors per channel to use");
1175 puts(" -p, --parse <texture> proplist style texture specification");
1176 puts(" -w, --workspace <workspace> update background for the specified workspace");
1177 puts(" -v, --version show version of wmsetbg and exit");
1178 puts(" -h, --help show this help and exit");
1181 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1183 WMPropList *array, *val;
1184 char *value;
1185 int j;
1187 val = WMCreatePropListFromDescription(texture);
1188 if (!val) {
1189 wwarning("could not parse texture %s", texture);
1190 return;
1193 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1195 if (!array) {
1196 array = WMCreatePLArray(NULL, NULL);
1199 j = WMGetPropListItemCount(array);
1200 if (workspace >= j) {
1201 WMPropList *empty;
1203 empty = WMCreatePLArray(NULL, NULL);
1205 while (j++ < workspace - 1) {
1206 WMAddToPLArray(array, empty);
1208 WMAddToPLArray(array, val);
1210 WMReleasePropList(empty);
1211 } else {
1212 WMDeleteFromPLArray(array, workspace);
1213 WMInsertInPLArray(array, workspace, val);
1216 value = WMGetPropListDescription(array, False);
1217 updateDomain(domain, "WorkspaceSpecificBack", value);
1219 WMReleasePropList(array);
1222 int main(int argc, char **argv)
1224 int i;
1225 int helperMode = 0;
1226 RContext *rc;
1227 RContextAttributes rattr;
1228 char *style = "spixmap";
1229 char *back_color = "gray20";
1230 char *image_name = NULL;
1231 char *domain = "WindowMaker";
1232 int update = 0, cpc = 4, obey_user = 0;
1233 RRenderingMode render_mode = RDitheredRendering;
1234 char *texture = NULL;
1235 int workspace = -1;
1237 signal(SIGINT, SIG_DFL);
1238 signal(SIGTERM, SIG_DFL);
1239 signal(SIGQUIT, SIG_DFL);
1240 signal(SIGSEGV, SIG_DFL);
1241 signal(SIGBUS, SIG_DFL);
1242 signal(SIGFPE, SIG_DFL);
1243 signal(SIGABRT, SIG_DFL);
1244 signal(SIGHUP, SIG_DFL);
1245 signal(SIGPIPE, SIG_DFL);
1246 signal(SIGCHLD, SIG_DFL);
1248 WMInitializeApplication("wmsetbg", &argc, argv);
1250 for (i = 1; i < argc; i++) {
1251 if (strcmp(argv[i], "-helper") == 0) {
1252 helperMode = 1;
1253 } else if (strcmp(argv[i], "-display") == 0) {
1254 i++;
1255 if (i >= argc) {
1256 wfatal("too few arguments for %s", argv[i - 1]);
1257 quit(1);
1259 display = argv[i];
1260 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1261 style = "spixmap";
1262 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1263 style = "tpixmap";
1264 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1265 style = "cpixmap";
1266 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1267 style = "mpixmap";
1268 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1269 style = "fpixmap";
1270 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1271 render_mode = RDitheredRendering;
1272 obey_user++;
1273 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1274 render_mode = RBestMatchRendering;
1275 obey_user++;
1276 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1277 smooth = True;
1278 #ifdef USE_XINERAMA
1279 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1280 xineStretch = True;
1281 #endif
1282 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1283 update++;
1284 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1285 update++;
1286 i++;
1287 if (i >= argc) {
1288 wfatal("too few arguments for %s", argv[i - 1]);
1289 quit(1);
1291 domain = wstrdup(argv[i]);
1292 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1293 i++;
1294 if (i >= argc) {
1295 wfatal("too few arguments for %s", argv[i - 1]);
1296 quit(1);
1298 if (sscanf(argv[i], "%i", &cpc) != 1) {
1299 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1300 quit(1);
1302 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1303 i++;
1304 if (i >= argc) {
1305 wfatal("too few arguments for %s", argv[i - 1]);
1306 quit(1);
1308 back_color = argv[i];
1309 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1310 i++;
1311 if (i >= argc) {
1312 wfatal("too few arguments for %s", argv[i - 1]);
1313 quit(1);
1315 texture = argv[i];
1316 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1317 i++;
1318 if (i >= argc) {
1319 wfatal("too few arguments for %s", argv[i - 1]);
1320 quit(1);
1322 if (sscanf(argv[i], "%i", &workspace) != 1) {
1323 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1324 quit(1);
1326 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1327 printf("%s (Window Maker %s)\n", __progname, VERSION);
1328 quit(0);
1329 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1330 print_help();
1331 quit(0);
1332 } else if (argv[i][0] != '-') {
1333 image_name = argv[i];
1334 } else {
1335 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1336 printf("Try '%s --help' for more information\n", __progname);
1337 quit(1);
1340 if (!image_name && !texture && !helperMode) {
1341 printf("%s: you must specify a image file name or a texture\n", __progname);
1342 printf("Try '%s --help' for more information\n", __progname);
1343 quit(1);
1346 PixmapPath = getPixmapPath(domain);
1347 if (!smooth) {
1348 WMPropList *val;
1349 /* carlos, don't remove this */
1350 #if 0 /* some problem with Alpha... TODO: check if its right */
1351 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1352 #else
1353 val = getValueForKey(domain, "SmoothWorkspaceBack");
1354 #endif
1356 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1357 smooth = True;
1360 dpy = XOpenDisplay(display);
1361 if (!dpy) {
1362 wfatal("could not open display");
1363 quit(1);
1365 #if 0
1366 XSynchronize(dpy, 1);
1367 #endif
1369 root = DefaultRootWindow(dpy);
1371 scr = DefaultScreen(dpy);
1373 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1374 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1375 scrX = scrY = 0;
1377 initXinerama();
1379 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1380 render_mode = RDitheredRendering;
1382 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1383 rattr.render_mode = render_mode;
1384 rattr.colors_per_channel = cpc;
1385 rattr.standard_colormap_mode = RCreateStdColormap;
1387 rc = RCreateContext(dpy, scr, &rattr);
1389 if (!rc) {
1390 rattr.standard_colormap_mode = RIgnoreStdColormap;
1391 rc = RCreateContext(dpy, scr, &rattr);
1394 if (!rc) {
1395 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1396 quit(1);
1399 if (helperMode) {
1400 int result;
1402 /* lower priority, so that it wont use all the CPU */
1403 result = nice(15);
1404 if (result == -1)
1405 wwarning("error could not nice process");
1407 helperLoop(rc);
1408 } else {
1409 BackgroundTexture *tex;
1410 char buffer[4098];
1412 if (!texture) {
1413 char *image_path = getFullPixmapPath(image_name);
1414 snprintf(buffer, sizeof(buffer), "(%s, \"%s\", %s)", style, image_path, back_color);
1415 wfree(image_path);
1416 texture = (char *)buffer;
1419 if (update && workspace < 0) {
1420 updateDomain(domain, "WorkspaceBack", texture);
1423 tex = parseTexture(rc, texture);
1424 if (!tex)
1425 quit(1);
1427 if (workspace < 0)
1428 changeTexture(tex);
1429 else {
1430 /* always update domain */
1431 changeTextureForWorkspace(domain, texture, workspace);
1435 WMReleaseApplication();
1436 return 0;