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