Prevent border drifting.
[wmaker-crm.git] / util / wmsetbg.c
bloba75ae951377a2b6cbb67be989d40d9bbe445f742
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 <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <X11/Xatom.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <ctype.h>
40 #include "../config.h"
42 #ifdef 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 #include "../src/wconfig.h"
52 #ifndef GLOBAL_DEFAULTS_SUBDIR
53 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
54 #endif
56 #include <WINGs/WINGs.h>
57 #include <wraster.h>
59 typedef struct {
60 WMRect *screens;
61 int count; /* screen count, 0 = inactive */
62 } WXineramaInfo;
64 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
66 Display *dpy;
67 char *display = "";
68 Window root;
69 int scr;
70 int scrWidth;
71 int scrHeight;
72 int scrX, scrY;
74 WXineramaInfo xineInfo;
76 Bool smooth = False;
77 #ifdef XINERAMA
78 Bool xineStretch = False;
79 #endif
81 Pixmap CurrentPixmap = None;
82 char *PixmapPath = NULL;
84 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
85 extern char *__progname;
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 void initXinerama()
102 xineInfo.screens = NULL;
103 xineInfo.count = 0;
104 #ifdef XINERAMA
105 # ifdef SOLARIS_XINERAMA
106 if (XineramaGetState(dpy, scr)) {
107 XRectangle head[MAXFRAMEBUFFERS];
108 unsigned char hints[MAXFRAMEBUFFERS];
109 int i;
111 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
113 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
115 for (i = 0; i < xineInfo.count; i++) {
116 xineInfo.screens[i].pos.x = head[i].x;
117 xineInfo.screens[i].pos.y = head[i].y;
118 xineInfo.screens[i].size.width = head[i].width;
119 xineInfo.screens[i].size.height = head[i].height;
123 # else /* !SOLARIS_XINERAMA */
124 if (XineramaIsActive(dpy)) {
125 XineramaScreenInfo *xine_screens;
126 int i;
128 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
130 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
132 for (i = 0; i < xineInfo.count; i++) {
133 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
134 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
135 xineInfo.screens[i].size.width = xine_screens[i].width;
136 xineInfo.screens[i].size.height = xine_screens[i].height;
138 XFree(xine_screens);
140 # endif /* !SOLARIS_XINERAMA */
141 #endif /* XINERAMA */
144 RImage *loadImage(RContext * rc, char *file)
146 char *path;
147 RImage *image;
149 if (access(file, F_OK) != 0) {
150 path = wfindfile(PixmapPath, file);
151 if (!path) {
152 wwarning("%s:could not find image file used in texture", file);
153 return NULL;
155 } else {
156 path = wstrdup(file);
159 image = RLoadImage(rc, path, 0);
160 if (!image) {
161 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
163 wfree(path);
165 return image;
168 static void
169 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
170 int x, int y, int width, int height)
172 int w, h;
173 Bool fimage = False;
175 switch (toupper(type)) {
176 case 'S':
177 case 'M':
178 case 'F':
179 if (toupper(type) == 'S') {
180 w = width;
181 h = height;
182 } else if(toupper(type) == 'F') {
183 if (image->width * height > image->height * width) {
184 w = (height * image->width) / image->height;
185 h = height;
186 } else {
187 w = width;
188 h = (width * image->height) / image->width;
190 } else {
191 if (image->width * height > image->height * width) {
192 w = width;
193 h = (width * image->height) / image->width;
194 } else {
195 w = (height * image->width) / image->height;
196 h = height;
200 if (w != image->width || h != image->height) {
201 RImage *simage;
203 if (smooth) {
204 simage = RSmoothScaleImage(image, w, h);
205 } else {
206 simage = RScaleImage(image, w, h);
209 if (!simage) {
210 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
211 return;
213 fimage = True;
214 image = simage;
217 /* fall through */
218 case 'C':
220 Pixmap pixmap;
222 if (!RConvertImage(rc, image, &pixmap)) {
223 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
224 return;
227 if (image->width != width || image->height != height) {
228 int sx, sy, w, h;
230 if (image->height < height) {
231 h = image->height;
232 y += (height - h) / 2;
233 sy = 0;
234 } else {
235 sy = (image->height - height) / 2;
236 h = height;
238 if (image->width < width) {
239 w = image->width;
240 x += (width - w) / 2;
241 sx = 0;
242 } else {
243 sx = (image->width - width) / 2;
244 w = width;
247 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
248 } else
249 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
250 x, y);
252 XFreePixmap(dpy, pixmap);
253 if (fimage) {
254 RReleaseImage(image);
257 break;
261 BackgroundTexture *parseTexture(RContext * rc, char *text)
263 BackgroundTexture *texture = NULL;
264 WMPropList *texarray;
265 WMPropList *val;
266 int count;
267 char *tmp;
268 char *type;
270 #define GETSTRORGOTO(val, str, i, label) \
271 val = WMGetFromPLArray(texarray, i);\
272 if (!WMIsPLString(val)) {\
273 wwarning("could not parse texture %s", text);\
274 goto label;\
276 str = WMGetFromPLString(val)
278 texarray = WMCreatePropListFromDescription(text);
279 if (!texarray || !WMIsPLArray(texarray)
280 || (count = WMGetPropListItemCount(texarray)) < 2) {
282 wwarning("could not parse texture %s", text);
283 if (texarray)
284 WMReleasePropList(texarray);
285 return NULL;
288 texture = wmalloc(sizeof(BackgroundTexture));
290 GETSTRORGOTO(val, type, 0, error);
292 if (strcasecmp(type, "solid") == 0) {
293 XColor color;
294 Pixmap pixmap;
296 texture->solid = 1;
298 GETSTRORGOTO(val, tmp, 1, error);
300 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
301 wwarning("could not parse color %s in texture %s", tmp, text);
302 goto error;
304 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
306 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
307 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
308 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
310 texture->pixmap = pixmap;
311 texture->color = color;
312 texture->width = 8;
313 texture->height = 8;
314 } else if (strcasecmp(type, "vgradient") == 0
315 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
316 XColor color;
317 RColor color1, color2;
318 RImage *image;
319 Pixmap pixmap;
320 int gtype;
321 int iwidth, iheight;
323 GETSTRORGOTO(val, tmp, 1, error);
325 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
326 wwarning("could not parse color %s in texture %s", tmp, text);
327 goto error;
330 color1.red = color.red >> 8;
331 color1.green = color.green >> 8;
332 color1.blue = color.blue >> 8;
334 GETSTRORGOTO(val, tmp, 2, 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 color2.red = color.red >> 8;
342 color2.green = color.green >> 8;
343 color2.blue = color.blue >> 8;
345 switch (type[0]) {
346 case 'h':
347 case 'H':
348 gtype = RHorizontalGradient;
349 iwidth = scrWidth;
350 iheight = 32;
351 break;
352 case 'V':
353 case 'v':
354 gtype = RVerticalGradient;
355 iwidth = 32;
356 iheight = scrHeight;
357 break;
358 default:
359 gtype = RDiagonalGradient;
360 iwidth = scrWidth;
361 iheight = scrHeight;
362 break;
365 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
367 if (!image) {
368 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
369 goto error;
372 if (!RConvertImage(rc, image, &pixmap)) {
373 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
374 RReleaseImage(image);
375 goto error;
378 texture->width = image->width;
379 texture->height = image->height;
380 RReleaseImage(image);
382 texture->pixmap = pixmap;
383 } else if (strcasecmp(type, "mvgradient") == 0
384 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
385 XColor color;
386 RColor **colors;
387 RImage *image;
388 Pixmap pixmap;
389 int i, j;
390 int gtype;
391 int iwidth, iheight;
393 colors = malloc(sizeof(RColor *) * (count - 1));
394 if (!colors) {
395 wwarning("out of memory while parsing texture");
396 goto error;
398 memset(colors, 0, sizeof(RColor *) * (count - 1));
400 for (i = 2; i < count; i++) {
401 val = WMGetFromPLArray(texarray, i);
402 if (!WMIsPLString(val)) {
403 wwarning("could not parse texture %s", text);
405 for (j = 0; colors[j] != NULL; j++)
406 wfree(colors[j]);
407 wfree(colors);
408 goto error;
410 tmp = WMGetFromPLString(val);
412 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
413 wwarning("could not parse color %s in texture %s", tmp, text);
415 for (j = 0; colors[j] != NULL; j++)
416 wfree(colors[j]);
417 wfree(colors);
418 goto error;
420 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
421 wwarning("out of memory while parsing texture");
423 for (j = 0; colors[j] != NULL; j++)
424 wfree(colors[j]);
425 wfree(colors);
426 goto error;
429 colors[i - 2]->red = color.red >> 8;
430 colors[i - 2]->green = color.green >> 8;
431 colors[i - 2]->blue = color.blue >> 8;
434 switch (type[1]) {
435 case 'h':
436 case 'H':
437 gtype = RHorizontalGradient;
438 iwidth = scrWidth;
439 iheight = 32;
440 break;
441 case 'V':
442 case 'v':
443 gtype = RVerticalGradient;
444 iwidth = 32;
445 iheight = scrHeight;
446 break;
447 default:
448 gtype = RDiagonalGradient;
449 iwidth = scrWidth;
450 iheight = scrHeight;
451 break;
454 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
456 for (j = 0; colors[j] != NULL; j++)
457 wfree(colors[j]);
458 wfree(colors);
460 if (!image) {
461 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
462 goto error;
465 if (!RConvertImage(rc, image, &pixmap)) {
466 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
467 RReleaseImage(image);
468 goto error;
471 texture->width = image->width;
472 texture->height = image->height;
473 RReleaseImage(image);
475 texture->pixmap = pixmap;
476 } else if (strcasecmp(type, "cpixmap") == 0
477 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
478 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
479 XColor color;
480 Pixmap pixmap = None;
481 RImage *image = NULL;
482 int iwidth = 0, iheight = 0;
483 RColor rcolor;
485 GETSTRORGOTO(val, tmp, 1, error);
487 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
488 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
491 if (!pixmap) {
492 image = loadImage(rc, tmp);
493 if (!image) {
494 goto error;
496 iwidth = image->width;
497 iheight = image->height;
500 GETSTRORGOTO(val, tmp, 2, error);
502 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
503 wwarning("could not parse color %s in texture %s", tmp, text);
504 RReleaseImage(image);
505 goto error;
507 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
508 rcolor.red = color.red >> 8;
509 rcolor.green = color.green >> 8;
510 rcolor.blue = color.blue >> 8;
511 RGetClosestXColor(rc, &rcolor, &color);
512 } else {
513 rcolor.red = 0;
514 rcolor.green = 0;
515 rcolor.blue = 0;
517 /* for images with a transparent color */
518 if (image->data[3]) {
519 RCombineImageWithColor(image, &rcolor);
522 switch (toupper(type[0])) {
523 case 'T':
524 texture->width = iwidth;
525 texture->height = iheight;
526 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
527 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
528 RReleaseImage(image);
529 goto error;
531 if (image)
532 RReleaseImage(image);
534 texture->pixmap = pixmap;
535 texture->color = color;
536 break;
537 case 'S':
538 case 'M':
539 case 'C':
540 case 'F':
542 Pixmap tpixmap =
543 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
544 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
545 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
547 texture->pixmap = tpixmap;
548 texture->color = color;
549 texture->width = scrWidth;
550 texture->height = scrHeight;
552 #ifdef XINERAMA
553 if (xineInfo.count && ! xineStretch) {
554 int i;
555 for (i = 0; i < xineInfo.count; ++i) {
556 applyImage(rc, texture, image, type[0],
557 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
558 xineInfo.screens[i].size.width,
559 xineInfo.screens[i].size.height);
561 } else {
562 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
564 #else /* !XINERAMA */
565 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
566 #endif /* !XINERAMA */
567 RReleaseImage(image);
569 break;
571 } else if (strcasecmp(type, "thgradient") == 0
572 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
573 XColor color;
574 RColor color1, color2;
575 RImage *image;
576 RImage *gradient;
577 RImage *tiled;
578 Pixmap pixmap;
579 int opaq;
580 char *file;
581 int gtype;
582 int twidth, theight;
584 GETSTRORGOTO(val, file, 1, error);
586 GETSTRORGOTO(val, tmp, 2, error);
588 opaq = atoi(tmp);
590 GETSTRORGOTO(val, tmp, 3, error);
592 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
593 wwarning("could not parse color %s in texture %s", tmp, text);
594 goto error;
597 color1.red = color.red >> 8;
598 color1.green = color.green >> 8;
599 color1.blue = color.blue >> 8;
601 GETSTRORGOTO(val, tmp, 4, error);
603 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
604 wwarning("could not parse color %s in texture %s", tmp, text);
605 goto error;
608 color2.red = color.red >> 8;
609 color2.green = color.green >> 8;
610 color2.blue = color.blue >> 8;
612 image = loadImage(rc, file);
613 if (!image) {
614 goto error;
617 switch (type[1]) {
618 case 'h':
619 case 'H':
620 gtype = RHorizontalGradient;
621 twidth = scrWidth;
622 theight = image->height > scrHeight ? scrHeight : image->height;
623 break;
624 case 'V':
625 case 'v':
626 gtype = RVerticalGradient;
627 twidth = image->width > scrWidth ? scrWidth : image->width;
628 theight = scrHeight;
629 break;
630 default:
631 gtype = RDiagonalGradient;
632 twidth = scrWidth;
633 theight = scrHeight;
634 break;
636 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
638 if (!gradient) {
639 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
640 RReleaseImage(gradient);
641 RReleaseImage(image);
642 goto error;
645 tiled = RMakeTiledImage(image, twidth, theight);
646 if (!tiled) {
647 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
648 RReleaseImage(gradient);
649 RReleaseImage(image);
650 goto error;
652 RReleaseImage(image);
654 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
655 RReleaseImage(gradient);
657 if (!RConvertImage(rc, tiled, &pixmap)) {
658 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
659 RReleaseImage(tiled);
660 goto error;
662 texture->width = tiled->width;
663 texture->height = tiled->height;
665 RReleaseImage(tiled);
667 texture->pixmap = pixmap;
668 } else if (strcasecmp(type, "function") == 0) {
669 /* Leave this in to handle the unlikely case of
670 * someone actually having function textures configured */
671 wwarning("function texture support has been removed");
672 goto error;
673 } else {
674 wwarning("invalid texture type %s", text);
675 goto error;
678 texture->spec = wstrdup(text);
680 return texture;
682 error:
683 if (texture)
684 wfree(texture);
685 if (texarray)
686 WMReleasePropList(texarray);
688 return NULL;
691 void freeTexture(BackgroundTexture * texture)
693 if (texture->solid) {
694 unsigned long pixel[1];
696 pixel[0] = texture->color.pixel;
697 /* dont free black/white pixels */
698 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
699 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
700 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
702 if (texture->pixmap) {
703 XFreePixmap(dpy, texture->pixmap);
705 wfree(texture->spec);
706 wfree(texture);
709 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
711 BackgroundTexture *newTexture = NULL;
712 int i;
714 /* unset the texture */
715 if (!texture) {
716 if (textures[workspace] != NULL) {
717 textures[workspace]->refcount--;
719 if (textures[workspace]->refcount == 0)
720 freeTexture(textures[workspace]);
722 textures[workspace] = NULL;
723 return;
726 if (textures[workspace]
727 && strcasecmp(textures[workspace]->spec, texture) == 0) {
728 /* texture did not change */
729 return;
732 /* check if the same texture is already created */
733 for (i = 0; i < *maxTextures; i++) {
734 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
735 newTexture = textures[i];
736 break;
740 if (!newTexture) {
741 /* create the texture */
742 newTexture = parseTexture(rc, texture);
744 if (!newTexture)
745 return;
747 if (textures[workspace] != NULL) {
749 textures[workspace]->refcount--;
751 if (textures[workspace]->refcount == 0)
752 freeTexture(textures[workspace]);
755 newTexture->refcount++;
756 textures[workspace] = newTexture;
758 if (*maxTextures < workspace)
759 *maxTextures = workspace;
762 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
764 Display *tmpDpy;
765 Pixmap copyP;
767 /* must open a new display or the RetainPermanent will
768 * leave stuff allocated in RContext unallocated after exit */
769 tmpDpy = XOpenDisplay(display);
770 if (!tmpDpy) {
771 wwarning("could not open display to update background image information");
773 return None;
774 } else {
775 XSync(dpy, False);
777 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
778 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
779 XSync(tmpDpy, False);
781 XSetCloseDownMode(tmpDpy, RetainPermanent);
782 XCloseDisplay(tmpDpy);
785 return copyP;
788 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
790 return 0;
793 void setPixmapProperty(Pixmap pixmap)
795 static Atom prop = 0;
796 Atom type;
797 int format;
798 unsigned long length, after;
799 unsigned char *data;
800 int mode;
802 if (!prop) {
803 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
806 XGrabServer(dpy);
808 /* Clear out the old pixmap */
809 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
810 &type, &format, &length, &after, &data);
812 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
813 XSetErrorHandler(dummyErrorHandler);
814 XKillClient(dpy, *((Pixmap *) data));
815 XSync(dpy, False);
816 XSetErrorHandler(NULL);
817 mode = PropModeReplace;
818 } else {
819 mode = PropModeAppend;
821 if (pixmap)
822 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
823 else
824 XDeleteProperty(dpy, root, prop);
826 XUngrabServer(dpy);
827 XFlush(dpy);
830 void changeTexture(BackgroundTexture * texture)
832 if (!texture) {
833 return;
836 if (texture->solid) {
837 XSetWindowBackground(dpy, root, texture->color.pixel);
838 } else {
839 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
841 XClearWindow(dpy, root);
843 XSync(dpy, False);
846 Pixmap pixmap;
848 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
850 setPixmapProperty(pixmap);
854 int readmsg(int fd, char *buffer, int size)
856 int count;
858 count = 0;
859 while (size > 0) {
860 count = read(fd, buffer, size);
861 if (count < 0)
862 return -1;
863 size -= count;
864 buffer += count;
865 *buffer = 0;
868 return size;
872 * Message Format:
873 * sizeSntexture_spec - sets the texture for workspace n
874 * sizeCn - change background texture to the one for workspace n
875 * sizePpath - set the pixmap search path
877 * n is 4 bytes
878 * size = 4 bytes for length of the message data
880 void helperLoop(RContext * rc)
882 BackgroundTexture *textures[WORKSPACE_COUNT];
883 int maxTextures = 0;
884 char buffer[2048], buf[8];
885 int size;
886 int errcount = 4;
888 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
890 while (1) {
891 int workspace = -1;
893 /* get length of message */
894 if (readmsg(0, buffer, 4) < 0) {
895 werror("error reading message from Window Maker");
896 errcount--;
897 if (errcount == 0) {
898 wfatal("quitting");
899 exit(1);
901 continue;
903 memcpy(buf, buffer, 4);
904 buf[4] = 0;
905 size = atoi(buf);
907 /* get message */
908 if (readmsg(0, buffer, size) < 0) {
909 werror("error reading message from Window Maker");
910 errcount--;
911 if (errcount == 0) {
912 wfatal("quitting");
913 exit(1);
915 continue;
917 #ifdef DEBUG
918 printf("RECEIVED %s\n", buffer);
919 #endif
920 if (buffer[0] != 'P' && buffer[0] != 'K') {
921 memcpy(buf, &buffer[1], 4);
922 buf[4] = 0;
923 workspace = atoi(buf);
924 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
925 wwarning("received message with invalid workspace number %i", workspace);
926 continue;
930 switch (buffer[0]) {
931 case 'S':
932 #ifdef DEBUG
933 printf("set texture %s\n", &buffer[5]);
934 #endif
935 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
936 break;
938 case 'C':
939 #ifdef DEBUG
940 printf("change texture %i\n", workspace);
941 #endif
942 if (!textures[workspace]) {
943 changeTexture(textures[0]);
944 } else {
945 changeTexture(textures[workspace]);
947 break;
949 case 'P':
950 #ifdef DEBUG
951 printf("change pixmappath %s\n", &buffer[1]);
952 #endif
953 if (PixmapPath)
954 wfree(PixmapPath);
955 PixmapPath = wstrdup(&buffer[1]);
956 break;
958 case 'U':
959 #ifdef DEBUG
960 printf("unset workspace %i\n", workspace);
961 #endif
962 setupTexture(rc, textures, &maxTextures, workspace, NULL);
963 break;
965 case 'K':
966 #ifdef DEBUG
967 printf("exit command\n");
968 #endif
969 exit(0);
971 default:
972 wwarning("unknown message received");
973 break;
978 void updateDomain(char *domain, char *key, char *texture)
980 char *program = "wdwrite";
982 /* here is a mem leak */
983 system(wstrconcat("wdwrite ",
984 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
986 execlp(program, program, domain, key, texture, NULL);
987 wwarning("warning could not run \"%s\"", program);
990 static WMPropList *getValueForKey(char *domain, char *keyName)
992 char *path;
993 WMPropList *key, *val, *d;
995 key = WMCreatePLString(keyName);
997 /* try to find PixmapPath in user defaults */
998 path = wdefaultspathfordomain(domain);
999 d = WMReadPropListFromFile(path);
1000 if (!d) {
1001 wwarning("could not open domain file %s", path);
1003 wfree(path);
1005 if (d && !WMIsPLDictionary(d)) {
1006 WMReleasePropList(d);
1007 d = NULL;
1009 if (d) {
1010 val = WMGetFromPLDictionary(d, key);
1011 } else {
1012 val = NULL;
1014 /* try to find PixmapPath in global defaults */
1015 if (!val) {
1016 path = wglobaldefaultspathfordomain(domain);
1017 if (!path) {
1018 wwarning("could not locate file for domain %s", domain);
1019 d = NULL;
1020 } else {
1021 d = WMReadPropListFromFile(path);
1022 wfree(path);
1025 if (d && !WMIsPLDictionary(d)) {
1026 WMReleasePropList(d);
1027 d = NULL;
1029 if (d) {
1030 val = WMGetFromPLDictionary(d, key);
1032 } else {
1033 val = NULL;
1037 if (val)
1038 WMRetainPropList(val);
1040 WMReleasePropList(key);
1041 if (d)
1042 WMReleasePropList(d);
1044 return val;
1047 char *getPixmapPath(char *domain)
1049 WMPropList *val;
1050 char *ptr, *data;
1051 int len, i, count;
1053 val = getValueForKey(domain, "PixmapPath");
1055 if (!val || !WMIsPLArray(val)) {
1056 if (val)
1057 WMReleasePropList(val);
1058 return wstrdup("");
1061 count = WMGetPropListItemCount(val);
1062 len = 0;
1063 for (i = 0; i < count; i++) {
1064 WMPropList *v;
1066 v = WMGetFromPLArray(val, i);
1067 if (!v || !WMIsPLString(v)) {
1068 continue;
1070 len += strlen(WMGetFromPLString(v)) + 1;
1073 ptr = data = wmalloc(len + 1);
1074 *ptr = 0;
1076 for (i = 0; i < count; i++) {
1077 WMPropList *v;
1079 v = WMGetFromPLArray(val, i);
1080 if (!v || !WMIsPLString(v)) {
1081 continue;
1083 strcpy(ptr, WMGetFromPLString(v));
1085 ptr += strlen(WMGetFromPLString(v));
1086 *ptr = ':';
1087 ptr++;
1089 if (i > 0)
1090 ptr--;
1091 *(ptr--) = 0;
1093 WMReleasePropList(val);
1095 return data;
1098 char *getFullPixmapPath(char *file)
1100 char *tmp;
1102 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1103 int bsize = 512;
1104 char *path = wmalloc(bsize);
1106 while (!getcwd(path, bsize)) {
1107 bsize += bsize / 2;
1108 path = wrealloc(path, bsize);
1111 tmp = wstrconcat(path, "/");
1112 wfree(path);
1113 path = wstrconcat(tmp, file);
1114 wfree(tmp);
1116 return path;
1119 /* the file is in the PixmapPath */
1120 wfree(tmp);
1122 return wstrdup(file);
1125 void wAbort()
1127 wfatal("aborting");
1128 exit(1);
1131 void print_help()
1133 printf("Usage: %s [options] [image]\n", __progname);
1134 puts("Sets the workspace background to the specified image or a texture and");
1135 puts("optionally update Window Maker configuration");
1136 puts("");
1137 puts(" -display display to use");
1138 puts(" -d, --dither dither image");
1139 puts(" -m, --match match colors");
1140 puts(" -S, --smooth smooth scaled image");
1141 #ifdef XINERAMA
1142 puts(" -X, --xinerama stretch image across Xinerama heads");
1143 #endif
1144 puts(" -b, --back-color <color> background color");
1145 puts(" -t, --tile tile image");
1146 puts(" -e, --center center image");
1147 puts(" -s, --scale scale image (default)");
1148 puts(" -a, --maxscale scale image and keep aspect ratio");
1149 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1150 puts(" -u, --update-wmaker update WindowMaker domain database");
1151 puts(" -D, --update-domain <domain> update <domain> database");
1152 puts(" -c, --colors <cpc> colors per channel to use");
1153 puts(" -p, --parse <texture> proplist style texture specification");
1154 puts(" -w, --workspace <workspace> update background for the specified workspace");
1155 puts(" -v, --version show version of wmsetbg and exit");
1156 puts(" -h, --help show this help and exit");
1159 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1161 WMPropList *array, *val;
1162 char *value;
1163 int j;
1165 val = WMCreatePropListFromDescription(texture);
1166 if (!val) {
1167 wwarning("could not parse texture %s", texture);
1168 return;
1171 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1173 if (!array) {
1174 array = WMCreatePLArray(NULL, NULL);
1177 j = WMGetPropListItemCount(array);
1178 if (workspace >= j) {
1179 WMPropList *empty;
1181 empty = WMCreatePLArray(NULL, NULL);
1183 while (j++ < workspace - 1) {
1184 WMAddToPLArray(array, empty);
1186 WMAddToPLArray(array, val);
1187 } else {
1188 WMDeleteFromPLArray(array, workspace);
1189 WMInsertInPLArray(array, workspace, val);
1192 value = WMGetPropListDescription(array, False);
1193 updateDomain(domain, "WorkspaceSpecificBack", value);
1196 int main(int argc, char **argv)
1198 int i;
1199 int helperMode = 0;
1200 RContext *rc;
1201 RContextAttributes rattr;
1202 char *style = "spixmap";
1203 char *back_color = "gray20";
1204 char *image_name = NULL;
1205 char *domain = "WindowMaker";
1206 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1207 char *texture = NULL;
1208 int workspace = -1;
1210 signal(SIGINT, SIG_DFL);
1211 signal(SIGTERM, SIG_DFL);
1212 signal(SIGQUIT, SIG_DFL);
1213 signal(SIGSEGV, SIG_DFL);
1214 signal(SIGBUS, SIG_DFL);
1215 signal(SIGFPE, SIG_DFL);
1216 signal(SIGABRT, SIG_DFL);
1217 signal(SIGHUP, SIG_DFL);
1218 signal(SIGPIPE, SIG_DFL);
1219 signal(SIGCHLD, SIG_DFL);
1221 WMInitializeApplication("wmsetbg", &argc, argv);
1223 for (i = 1; i < argc; i++) {
1224 if (strcmp(argv[i], "-helper") == 0) {
1225 helperMode = 1;
1226 } else if (strcmp(argv[i], "-display") == 0) {
1227 i++;
1228 if (i >= argc) {
1229 wfatal("too few arguments for %s", argv[i - 1]);
1230 exit(1);
1232 display = argv[i];
1233 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1234 style = "spixmap";
1235 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1236 style = "tpixmap";
1237 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1238 style = "cpixmap";
1239 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1240 style = "mpixmap";
1241 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1242 style = "fpixmap";
1243 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1244 render_mode = RDitheredRendering;
1245 obey_user++;
1246 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1247 render_mode = RBestMatchRendering;
1248 obey_user++;
1249 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1250 smooth = True;
1251 #ifdef XINERAMA
1252 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1253 xineStretch = True;
1254 #endif
1255 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1256 update++;
1257 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1258 update++;
1259 i++;
1260 if (i >= argc) {
1261 wfatal("too few arguments for %s", argv[i - 1]);
1262 exit(1);
1264 domain = wstrdup(argv[i]);
1265 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1266 i++;
1267 if (i >= argc) {
1268 wfatal("too few arguments for %s", argv[i - 1]);
1269 exit(1);
1271 if (sscanf(argv[i], "%i", &cpc) != 1) {
1272 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1273 exit(1);
1275 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1276 i++;
1277 if (i >= argc) {
1278 wfatal("too few arguments for %s", argv[i - 1]);
1279 exit(1);
1281 back_color = argv[i];
1282 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1283 i++;
1284 if (i >= argc) {
1285 wfatal("too few arguments for %s", argv[i - 1]);
1286 exit(1);
1288 texture = argv[i];
1289 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1290 i++;
1291 if (i >= argc) {
1292 wfatal("too few arguments for %s", argv[i - 1]);
1293 exit(1);
1295 if (sscanf(argv[i], "%i", &workspace) != 1) {
1296 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1297 exit(1);
1299 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1300 printf("%s (Window Maker %s)\n", __progname, VERSION);
1301 exit(0);
1302 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1303 print_help();
1304 exit(0);
1305 } else if (argv[i][0] != '-') {
1306 image_name = argv[i];
1307 } else {
1308 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1309 printf("Try '%s --help' for more information\n", __progname);
1310 exit(1);
1313 if (!image_name && !texture && !helperMode) {
1314 printf("%s: you must specify a image file name or a texture\n", __progname);
1315 printf("Try '%s --help' for more information\n", __progname);
1316 exit(1);
1319 PixmapPath = getPixmapPath(domain);
1320 if (!smooth) {
1321 WMPropList *val;
1322 /* carlos, don't remove this */
1323 #if 0 /* some problem with Alpha... TODO: check if its right */
1324 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1325 #else
1326 val = getValueForKey(domain, "SmoothWorkspaceBack");
1327 #endif
1329 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1330 smooth = True;
1333 dpy = XOpenDisplay(display);
1334 if (!dpy) {
1335 wfatal("could not open display");
1336 exit(1);
1338 #if 0
1339 XSynchronize(dpy, 1);
1340 #endif
1342 root = DefaultRootWindow(dpy);
1344 scr = DefaultScreen(dpy);
1346 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1347 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1348 scrX = scrY = 0;
1350 initXinerama();
1352 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1353 render_mode = RDitheredRendering;
1355 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1356 rattr.render_mode = render_mode;
1357 rattr.colors_per_channel = cpc;
1358 rattr.standard_colormap_mode = RCreateStdColormap;
1360 rc = RCreateContext(dpy, scr, &rattr);
1362 if (!rc) {
1363 rattr.standard_colormap_mode = RIgnoreStdColormap;
1364 rc = RCreateContext(dpy, scr, &rattr);
1367 if (!rc) {
1368 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1369 exit(1);
1372 if (helperMode) {
1373 /* lower priority, so that it wont use all the CPU */
1374 nice(15);
1376 helperLoop(rc);
1377 } else {
1378 BackgroundTexture *tex;
1379 char buffer[4098];
1381 if (!texture) {
1382 char *image_path = getFullPixmapPath(image_name);
1384 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1385 wfree(image_path);
1386 texture = (char *)buffer;
1389 if (update && workspace < 0) {
1390 updateDomain(domain, "WorkspaceBack", texture);
1393 tex = parseTexture(rc, texture);
1394 if (!tex)
1395 exit(1);
1397 if (workspace < 0)
1398 changeTexture(tex);
1399 else {
1400 /* always update domain */
1401 changeTextureForWorkspace(domain, texture, workspace);
1405 return 0;