Code refactoring: replaced macro 'XINERAMA' by 'USE_XINERAMA' for consistency
[wmaker-crm.git] / util / wmsetbg.c
blob644c85d7fa79a227b97c8a08aba7b0efd30a4fa7
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 initXinerama(void)
105 xineInfo.screens = NULL;
106 xineInfo.count = 0;
107 #ifdef USE_XINERAMA
108 # ifdef SOLARIS_XINERAMA
109 if (XineramaGetState(dpy, scr)) {
110 XRectangle head[MAXFRAMEBUFFERS];
111 unsigned char hints[MAXFRAMEBUFFERS];
112 int i;
114 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
116 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
118 for (i = 0; i < xineInfo.count; i++) {
119 xineInfo.screens[i].pos.x = head[i].x;
120 xineInfo.screens[i].pos.y = head[i].y;
121 xineInfo.screens[i].size.width = head[i].width;
122 xineInfo.screens[i].size.height = head[i].height;
126 # else /* !SOLARIS_XINERAMA */
127 if (XineramaIsActive(dpy)) {
128 XineramaScreenInfo *xine_screens;
129 int i;
131 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
133 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
135 for (i = 0; i < xineInfo.count; i++) {
136 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
137 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
138 xineInfo.screens[i].size.width = xine_screens[i].width;
139 xineInfo.screens[i].size.height = xine_screens[i].height;
141 XFree(xine_screens);
143 # endif /* !SOLARIS_XINERAMA */
144 #endif /* USE_XINERAMA */
147 static RImage *loadImage(RContext * rc, const char *file)
149 char *path;
150 RImage *image;
152 if (access(file, F_OK) != 0) {
153 path = wfindfile(PixmapPath, file);
154 if (!path) {
155 wwarning("%s:could not find image file used in texture", file);
156 return NULL;
158 } else {
159 path = wstrdup(file);
162 image = RLoadImage(rc, path, 0);
163 if (!image) {
164 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
166 wfree(path);
168 return image;
171 static void
172 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
173 int x, int y, int width, int height)
175 int w, h;
176 Bool fimage = False;
178 switch (toupper(type)) {
179 case 'S':
180 case 'M':
181 case 'F':
182 if (toupper(type) == 'S') {
183 w = width;
184 h = height;
185 } else if(toupper(type) == 'F') {
186 if (image->width * height > image->height * width) {
187 w = (height * image->width) / image->height;
188 h = height;
189 } else {
190 w = width;
191 h = (width * image->height) / image->width;
193 } else {
194 if (image->width * height > image->height * width) {
195 w = width;
196 h = (width * image->height) / image->width;
197 } else {
198 w = (height * image->width) / image->height;
199 h = height;
203 if (w != image->width || h != image->height) {
204 RImage *simage;
206 if (smooth) {
207 simage = RSmoothScaleImage(image, w, h);
208 } else {
209 simage = RScaleImage(image, w, h);
212 if (!simage) {
213 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
214 return;
216 fimage = True;
217 image = simage;
220 /* fall through */
221 case 'C':
223 Pixmap pixmap;
225 if (!RConvertImage(rc, image, &pixmap)) {
226 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
227 return;
230 if (image->width != width || image->height != height) {
231 int sx, sy, w, h;
233 if (image->height < height) {
234 h = image->height;
235 y += (height - h) / 2;
236 sy = 0;
237 } else {
238 sy = (image->height - height) / 2;
239 h = height;
241 if (image->width < width) {
242 w = image->width;
243 x += (width - w) / 2;
244 sx = 0;
245 } else {
246 sx = (image->width - width) / 2;
247 w = width;
250 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
251 } else
252 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
253 x, y);
255 XFreePixmap(dpy, pixmap);
256 if (fimage) {
257 RReleaseImage(image);
260 break;
264 static BackgroundTexture *parseTexture(RContext * rc, char *text)
266 BackgroundTexture *texture = NULL;
267 WMPropList *texarray;
268 WMPropList *val;
269 int count;
270 char *tmp;
271 char *type;
273 #define GETSTRORGOTO(val, str, i, label) \
274 val = WMGetFromPLArray(texarray, i);\
275 if (!WMIsPLString(val)) {\
276 wwarning("could not parse texture %s", text);\
277 goto label;\
279 str = WMGetFromPLString(val)
281 texarray = WMCreatePropListFromDescription(text);
282 if (!texarray || !WMIsPLArray(texarray)
283 || (count = WMGetPropListItemCount(texarray)) < 2) {
285 wwarning("could not parse texture %s", text);
286 if (texarray)
287 WMReleasePropList(texarray);
288 return NULL;
291 texture = wmalloc(sizeof(BackgroundTexture));
293 GETSTRORGOTO(val, type, 0, error);
295 if (strcasecmp(type, "solid") == 0) {
296 XColor color;
297 Pixmap pixmap;
299 texture->solid = 1;
301 GETSTRORGOTO(val, tmp, 1, error);
303 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
304 wwarning("could not parse color %s in texture %s", tmp, text);
305 goto error;
307 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
309 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
310 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
311 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
313 texture->pixmap = pixmap;
314 texture->color = color;
315 texture->width = 8;
316 texture->height = 8;
317 } else if (strcasecmp(type, "vgradient") == 0
318 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
319 XColor color;
320 RColor color1, color2;
321 RImage *image;
322 Pixmap pixmap;
323 int gtype;
324 int iwidth, iheight;
326 GETSTRORGOTO(val, tmp, 1, error);
328 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
329 wwarning("could not parse color %s in texture %s", tmp, text);
330 goto error;
333 color1.red = color.red >> 8;
334 color1.green = color.green >> 8;
335 color1.blue = color.blue >> 8;
337 GETSTRORGOTO(val, tmp, 2, error);
339 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
340 wwarning("could not parse color %s in texture %s", tmp, text);
341 goto error;
344 color2.red = color.red >> 8;
345 color2.green = color.green >> 8;
346 color2.blue = color.blue >> 8;
348 switch (type[0]) {
349 case 'h':
350 case 'H':
351 gtype = RHorizontalGradient;
352 iwidth = scrWidth;
353 iheight = 32;
354 break;
355 case 'V':
356 case 'v':
357 gtype = RVerticalGradient;
358 iwidth = 32;
359 iheight = scrHeight;
360 break;
361 default:
362 gtype = RDiagonalGradient;
363 iwidth = scrWidth;
364 iheight = scrHeight;
365 break;
368 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
370 if (!image) {
371 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
372 goto error;
375 if (!RConvertImage(rc, image, &pixmap)) {
376 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
377 RReleaseImage(image);
378 goto error;
381 texture->width = image->width;
382 texture->height = image->height;
383 RReleaseImage(image);
385 texture->pixmap = pixmap;
386 } else if (strcasecmp(type, "mvgradient") == 0
387 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
388 XColor color;
389 RColor **colors;
390 RImage *image;
391 Pixmap pixmap;
392 int i, j;
393 int gtype;
394 int iwidth, iheight;
396 colors = malloc(sizeof(RColor *) * (count - 1));
397 if (!colors) {
398 wwarning("out of memory while parsing texture");
399 goto error;
401 memset(colors, 0, sizeof(RColor *) * (count - 1));
403 for (i = 2; i < count; i++) {
404 val = WMGetFromPLArray(texarray, i);
405 if (!WMIsPLString(val)) {
406 wwarning("could not parse texture %s", text);
408 for (j = 0; colors[j] != NULL; j++)
409 wfree(colors[j]);
410 wfree(colors);
411 goto error;
413 tmp = WMGetFromPLString(val);
415 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
416 wwarning("could not parse color %s in texture %s", tmp, text);
418 for (j = 0; colors[j] != NULL; j++)
419 wfree(colors[j]);
420 wfree(colors);
421 goto error;
423 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
424 wwarning("out of memory while parsing texture");
426 for (j = 0; colors[j] != NULL; j++)
427 wfree(colors[j]);
428 wfree(colors);
429 goto error;
432 colors[i - 2]->red = color.red >> 8;
433 colors[i - 2]->green = color.green >> 8;
434 colors[i - 2]->blue = color.blue >> 8;
437 switch (type[1]) {
438 case 'h':
439 case 'H':
440 gtype = RHorizontalGradient;
441 iwidth = scrWidth;
442 iheight = 32;
443 break;
444 case 'V':
445 case 'v':
446 gtype = RVerticalGradient;
447 iwidth = 32;
448 iheight = scrHeight;
449 break;
450 default:
451 gtype = RDiagonalGradient;
452 iwidth = scrWidth;
453 iheight = scrHeight;
454 break;
457 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
459 for (j = 0; colors[j] != NULL; j++)
460 wfree(colors[j]);
461 wfree(colors);
463 if (!image) {
464 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
465 goto error;
468 if (!RConvertImage(rc, image, &pixmap)) {
469 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
470 RReleaseImage(image);
471 goto error;
474 texture->width = image->width;
475 texture->height = image->height;
476 RReleaseImage(image);
478 texture->pixmap = pixmap;
479 } else if (strcasecmp(type, "cpixmap") == 0
480 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
481 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
482 XColor color;
483 Pixmap pixmap = None;
484 RImage *image = NULL;
485 int iwidth = 0, iheight = 0;
486 RColor rcolor;
488 GETSTRORGOTO(val, tmp, 1, error);
490 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
491 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
494 if (!pixmap) {
495 image = loadImage(rc, tmp);
496 if (!image) {
497 goto error;
499 iwidth = image->width;
500 iheight = image->height;
503 GETSTRORGOTO(val, tmp, 2, error);
505 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
506 wwarning("could not parse color %s in texture %s", tmp, text);
507 RReleaseImage(image);
508 goto error;
510 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
511 rcolor.red = color.red >> 8;
512 rcolor.green = color.green >> 8;
513 rcolor.blue = color.blue >> 8;
514 RGetClosestXColor(rc, &rcolor, &color);
515 } else {
516 rcolor.red = 0;
517 rcolor.green = 0;
518 rcolor.blue = 0;
520 /* for images with a transparent color */
521 if (image->data[3]) {
522 RCombineImageWithColor(image, &rcolor);
525 switch (toupper(type[0])) {
526 case 'T':
527 texture->width = iwidth;
528 texture->height = iheight;
529 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
530 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
531 RReleaseImage(image);
532 goto error;
534 if (image)
535 RReleaseImage(image);
537 texture->pixmap = pixmap;
538 texture->color = color;
539 break;
540 case 'S':
541 case 'M':
542 case 'C':
543 case 'F':
545 Pixmap tpixmap =
546 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
547 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
548 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
550 texture->pixmap = tpixmap;
551 texture->color = color;
552 texture->width = scrWidth;
553 texture->height = scrHeight;
555 #ifdef USE_XINERAMA
556 if (xineInfo.count && ! xineStretch) {
557 int i;
558 for (i = 0; i < xineInfo.count; ++i) {
559 applyImage(rc, texture, image, type[0],
560 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
561 xineInfo.screens[i].size.width,
562 xineInfo.screens[i].size.height);
564 } else {
565 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
567 #else /* !USE_XINERAMA */
568 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
569 #endif /* !USE_XINERAMA */
570 RReleaseImage(image);
572 break;
574 } else if (strcasecmp(type, "thgradient") == 0
575 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
576 XColor color;
577 RColor color1, color2;
578 RImage *image;
579 RImage *gradient;
580 RImage *tiled;
581 Pixmap pixmap;
582 int opaq;
583 char *file;
584 int gtype;
585 int twidth, theight;
587 GETSTRORGOTO(val, file, 1, error);
589 GETSTRORGOTO(val, tmp, 2, error);
591 opaq = atoi(tmp);
593 GETSTRORGOTO(val, tmp, 3, error);
595 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
596 wwarning("could not parse color %s in texture %s", tmp, text);
597 goto error;
600 color1.red = color.red >> 8;
601 color1.green = color.green >> 8;
602 color1.blue = color.blue >> 8;
604 GETSTRORGOTO(val, tmp, 4, error);
606 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
607 wwarning("could not parse color %s in texture %s", tmp, text);
608 goto error;
611 color2.red = color.red >> 8;
612 color2.green = color.green >> 8;
613 color2.blue = color.blue >> 8;
615 image = loadImage(rc, file);
616 if (!image) {
617 goto error;
620 switch (type[1]) {
621 case 'h':
622 case 'H':
623 gtype = RHorizontalGradient;
624 twidth = scrWidth;
625 theight = image->height > scrHeight ? scrHeight : image->height;
626 break;
627 case 'V':
628 case 'v':
629 gtype = RVerticalGradient;
630 twidth = image->width > scrWidth ? scrWidth : image->width;
631 theight = scrHeight;
632 break;
633 default:
634 gtype = RDiagonalGradient;
635 twidth = scrWidth;
636 theight = scrHeight;
637 break;
639 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
641 if (!gradient) {
642 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
643 RReleaseImage(gradient);
644 RReleaseImage(image);
645 goto error;
648 tiled = RMakeTiledImage(image, twidth, theight);
649 if (!tiled) {
650 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
651 RReleaseImage(gradient);
652 RReleaseImage(image);
653 goto error;
655 RReleaseImage(image);
657 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
658 RReleaseImage(gradient);
660 if (!RConvertImage(rc, tiled, &pixmap)) {
661 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
662 RReleaseImage(tiled);
663 goto error;
665 texture->width = tiled->width;
666 texture->height = tiled->height;
668 RReleaseImage(tiled);
670 texture->pixmap = pixmap;
671 } else if (strcasecmp(type, "function") == 0) {
672 /* Leave this in to handle the unlikely case of
673 * someone actually having function textures configured */
674 wwarning("function texture support has been removed");
675 goto error;
676 } else {
677 wwarning("invalid texture type %s", text);
678 goto error;
681 texture->spec = wstrdup(text);
683 return texture;
685 error:
686 if (texture)
687 wfree(texture);
688 if (texarray)
689 WMReleasePropList(texarray);
691 return NULL;
694 static void freeTexture(BackgroundTexture * texture)
696 if (texture->solid) {
697 unsigned long pixel[1];
699 pixel[0] = texture->color.pixel;
700 /* dont free black/white pixels */
701 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
702 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
703 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
705 if (texture->pixmap) {
706 XFreePixmap(dpy, texture->pixmap);
708 wfree(texture->spec);
709 wfree(texture);
712 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
714 BackgroundTexture *newTexture = NULL;
715 int i;
717 /* unset the texture */
718 if (!texture) {
719 if (textures[workspace] != NULL) {
720 textures[workspace]->refcount--;
722 if (textures[workspace]->refcount == 0)
723 freeTexture(textures[workspace]);
725 textures[workspace] = NULL;
726 return;
729 if (textures[workspace]
730 && strcasecmp(textures[workspace]->spec, texture) == 0) {
731 /* texture did not change */
732 return;
735 /* check if the same texture is already created */
736 for (i = 0; i < *maxTextures; i++) {
737 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
738 newTexture = textures[i];
739 break;
743 if (!newTexture) {
744 /* create the texture */
745 newTexture = parseTexture(rc, texture);
747 if (!newTexture)
748 return;
750 if (textures[workspace] != NULL) {
752 textures[workspace]->refcount--;
754 if (textures[workspace]->refcount == 0)
755 freeTexture(textures[workspace]);
758 newTexture->refcount++;
759 textures[workspace] = newTexture;
761 if (*maxTextures < workspace)
762 *maxTextures = workspace;
765 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
767 Display *tmpDpy;
768 Pixmap copyP;
770 /* must open a new display or the RetainPermanent will
771 * leave stuff allocated in RContext unallocated after exit */
772 tmpDpy = XOpenDisplay(display);
773 if (!tmpDpy) {
774 wwarning("could not open display to update background image information");
776 return None;
777 } else {
778 XSync(dpy, False);
780 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
781 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
782 XSync(tmpDpy, False);
784 XSetCloseDownMode(tmpDpy, RetainPermanent);
785 XCloseDisplay(tmpDpy);
788 return copyP;
791 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
793 /* Parameter not used, but tell the compiler that it is ok */
794 (void) dpy;
795 (void) err;
797 return 0;
800 static void setPixmapProperty(Pixmap pixmap)
802 static Atom prop = 0;
803 Atom type;
804 int format;
805 unsigned long length, after;
806 unsigned char *data;
807 int mode;
809 if (!prop) {
810 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
813 XGrabServer(dpy);
815 /* Clear out the old pixmap */
816 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
817 &type, &format, &length, &after, &data);
819 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
820 XSetErrorHandler(dummyErrorHandler);
821 XKillClient(dpy, *((Pixmap *) data));
822 XSync(dpy, False);
823 XSetErrorHandler(NULL);
824 mode = PropModeReplace;
825 } else {
826 mode = PropModeAppend;
828 if (pixmap)
829 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
830 else
831 XDeleteProperty(dpy, root, prop);
833 XUngrabServer(dpy);
834 XFlush(dpy);
837 static void changeTexture(BackgroundTexture * texture)
839 if (!texture) {
840 return;
843 if (texture->solid) {
844 XSetWindowBackground(dpy, root, texture->color.pixel);
845 } else {
846 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
848 XClearWindow(dpy, root);
850 XSync(dpy, False);
853 Pixmap pixmap;
855 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
857 setPixmapProperty(pixmap);
861 static int readmsg(int fd, char *buffer, int size)
863 int count;
865 count = 0;
866 while (size > 0) {
867 count = read(fd, buffer, size);
868 if (count < 0)
869 return -1;
870 size -= count;
871 buffer += count;
872 *buffer = 0;
875 return size;
879 * Message Format:
880 * sizeSntexture_spec - sets the texture for workspace n
881 * sizeCn - change background texture to the one for workspace n
882 * sizePpath - set the pixmap search path
884 * n is 4 bytes
885 * size = 4 bytes for length of the message data
887 static noreturn void helperLoop(RContext * rc)
889 BackgroundTexture *textures[WORKSPACE_COUNT];
890 int maxTextures = 0;
891 char buffer[2048], buf[8];
892 int size;
893 int errcount = 4;
895 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
897 while (1) {
898 int workspace = -1;
900 /* get length of message */
901 if (readmsg(0, buffer, 4) < 0) {
902 werror("error reading message from Window Maker");
903 errcount--;
904 if (errcount == 0) {
905 wfatal("quitting");
906 exit(1);
908 continue;
910 memcpy(buf, buffer, 4);
911 buf[4] = 0;
912 size = atoi(buf);
914 /* get message */
915 if (readmsg(0, buffer, size) < 0) {
916 werror("error reading message from Window Maker");
917 errcount--;
918 if (errcount == 0) {
919 wfatal("quitting");
920 exit(1);
922 continue;
924 #ifdef DEBUG
925 printf("RECEIVED %s\n", buffer);
926 #endif
927 if (buffer[0] != 'P' && buffer[0] != 'K') {
928 memcpy(buf, &buffer[1], 4);
929 buf[4] = 0;
930 workspace = atoi(buf);
931 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
932 wwarning("received message with invalid workspace number %i", workspace);
933 continue;
937 switch (buffer[0]) {
938 case 'S':
939 #ifdef DEBUG
940 printf("set texture %s\n", &buffer[5]);
941 #endif
942 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
943 break;
945 case 'C':
946 #ifdef DEBUG
947 printf("change texture %i\n", workspace);
948 #endif
949 if (!textures[workspace]) {
950 changeTexture(textures[0]);
951 } else {
952 changeTexture(textures[workspace]);
954 break;
956 case 'P':
957 #ifdef DEBUG
958 printf("change pixmappath %s\n", &buffer[1]);
959 #endif
960 if (PixmapPath)
961 wfree(PixmapPath);
962 PixmapPath = wstrdup(&buffer[1]);
963 break;
965 case 'U':
966 #ifdef DEBUG
967 printf("unset workspace %i\n", workspace);
968 #endif
969 setupTexture(rc, textures, &maxTextures, workspace, NULL);
970 break;
972 case 'K':
973 #ifdef DEBUG
974 printf("exit command\n");
975 #endif
976 exit(0);
978 default:
979 wwarning("unknown message received");
980 break;
985 static void updateDomain(const char *domain, const char *key, const char *texture)
987 char *program = "wdwrite";
989 /* here is a mem leak */
990 system(wstrconcat("wdwrite ",
991 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
993 execlp(program, program, domain, key, texture, NULL);
994 wwarning("warning could not run \"%s\"", program);
997 static WMPropList *getValueForKey(const char *domain, const char *keyName)
999 char *path;
1000 WMPropList *key, *val, *d;
1002 key = WMCreatePLString(keyName);
1004 /* try to find PixmapPath in user defaults */
1005 path = wdefaultspathfordomain(domain);
1006 d = WMReadPropListFromFile(path);
1007 if (!d) {
1008 wwarning("could not open domain file %s", path);
1010 wfree(path);
1012 if (d && !WMIsPLDictionary(d)) {
1013 WMReleasePropList(d);
1014 d = NULL;
1016 if (d) {
1017 val = WMGetFromPLDictionary(d, key);
1018 } else {
1019 val = NULL;
1021 /* try to find PixmapPath in global defaults */
1022 if (!val) {
1023 path = wglobaldefaultspathfordomain(domain);
1024 if (!path) {
1025 wwarning("could not locate file for domain %s", domain);
1026 d = NULL;
1027 } else {
1028 d = WMReadPropListFromFile(path);
1029 wfree(path);
1032 if (d && !WMIsPLDictionary(d)) {
1033 WMReleasePropList(d);
1034 d = NULL;
1036 if (d) {
1037 val = WMGetFromPLDictionary(d, key);
1039 } else {
1040 val = NULL;
1044 if (val)
1045 WMRetainPropList(val);
1047 WMReleasePropList(key);
1048 if (d)
1049 WMReleasePropList(d);
1051 return val;
1054 static char *getPixmapPath(const char *domain)
1056 WMPropList *val;
1057 char *ptr, *data;
1058 int len, i, count;
1060 val = getValueForKey(domain, "PixmapPath");
1062 if (!val || !WMIsPLArray(val)) {
1063 if (val)
1064 WMReleasePropList(val);
1065 return wstrdup("");
1068 count = WMGetPropListItemCount(val);
1069 len = 0;
1070 for (i = 0; i < count; i++) {
1071 WMPropList *v;
1073 v = WMGetFromPLArray(val, i);
1074 if (!v || !WMIsPLString(v)) {
1075 continue;
1077 len += strlen(WMGetFromPLString(v)) + 1;
1080 ptr = data = wmalloc(len + 1);
1081 *ptr = 0;
1083 for (i = 0; i < count; i++) {
1084 WMPropList *v;
1086 v = WMGetFromPLArray(val, i);
1087 if (!v || !WMIsPLString(v)) {
1088 continue;
1090 strcpy(ptr, WMGetFromPLString(v));
1092 ptr += strlen(WMGetFromPLString(v));
1093 *ptr = ':';
1094 ptr++;
1096 if (i > 0)
1097 ptr--;
1098 *(ptr--) = 0;
1100 WMReleasePropList(val);
1102 return data;
1105 static char *getFullPixmapPath(const char *file)
1107 char *tmp;
1109 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1110 int bsize = 512;
1111 char *path = wmalloc(bsize);
1113 while (!getcwd(path, bsize)) {
1114 bsize += bsize / 2;
1115 path = wrealloc(path, bsize);
1118 tmp = wstrconcat(path, "/");
1119 wfree(path);
1120 path = wstrconcat(tmp, file);
1121 wfree(tmp);
1123 return path;
1126 /* the file is in the PixmapPath */
1127 wfree(tmp);
1129 return wstrdup(file);
1132 static void print_help(void)
1134 printf("Usage: %s [options] [image]\n", __progname);
1135 puts("Sets the workspace background to the specified image or a texture and");
1136 puts("optionally update Window Maker configuration");
1137 puts("");
1138 puts(" -display display to use");
1139 puts(" -d, --dither dither image");
1140 puts(" -m, --match match colors");
1141 puts(" -S, --smooth smooth scaled image");
1142 #ifdef USE_XINERAMA
1143 puts(" -X, --xinerama stretch image across Xinerama heads");
1144 #endif
1145 puts(" -b, --back-color <color> background color");
1146 puts(" -t, --tile tile image");
1147 puts(" -e, --center center image");
1148 puts(" -s, --scale scale image (default)");
1149 puts(" -a, --maxscale scale image and keep aspect ratio");
1150 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1151 puts(" -u, --update-wmaker update WindowMaker domain database");
1152 puts(" -D, --update-domain <domain> update <domain> database");
1153 puts(" -c, --colors <cpc> colors per channel to use");
1154 puts(" -p, --parse <texture> proplist style texture specification");
1155 puts(" -w, --workspace <workspace> update background for the specified workspace");
1156 puts(" -v, --version show version of wmsetbg and exit");
1157 puts(" -h, --help show this help and exit");
1160 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1162 WMPropList *array, *val;
1163 char *value;
1164 int j;
1166 val = WMCreatePropListFromDescription(texture);
1167 if (!val) {
1168 wwarning("could not parse texture %s", texture);
1169 return;
1172 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1174 if (!array) {
1175 array = WMCreatePLArray(NULL, NULL);
1178 j = WMGetPropListItemCount(array);
1179 if (workspace >= j) {
1180 WMPropList *empty;
1182 empty = WMCreatePLArray(NULL, NULL);
1184 while (j++ < workspace - 1) {
1185 WMAddToPLArray(array, empty);
1187 WMAddToPLArray(array, val);
1188 } else {
1189 WMDeleteFromPLArray(array, workspace);
1190 WMInsertInPLArray(array, workspace, val);
1193 value = WMGetPropListDescription(array, False);
1194 updateDomain(domain, "WorkspaceSpecificBack", value);
1197 int main(int argc, char **argv)
1199 int i;
1200 int helperMode = 0;
1201 RContext *rc;
1202 RContextAttributes rattr;
1203 char *style = "spixmap";
1204 char *back_color = "gray20";
1205 char *image_name = NULL;
1206 char *domain = "WindowMaker";
1207 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1208 char *texture = NULL;
1209 int workspace = -1;
1211 signal(SIGINT, SIG_DFL);
1212 signal(SIGTERM, SIG_DFL);
1213 signal(SIGQUIT, SIG_DFL);
1214 signal(SIGSEGV, SIG_DFL);
1215 signal(SIGBUS, SIG_DFL);
1216 signal(SIGFPE, SIG_DFL);
1217 signal(SIGABRT, SIG_DFL);
1218 signal(SIGHUP, SIG_DFL);
1219 signal(SIGPIPE, SIG_DFL);
1220 signal(SIGCHLD, SIG_DFL);
1222 WMInitializeApplication("wmsetbg", &argc, argv);
1224 for (i = 1; i < argc; i++) {
1225 if (strcmp(argv[i], "-helper") == 0) {
1226 helperMode = 1;
1227 } else if (strcmp(argv[i], "-display") == 0) {
1228 i++;
1229 if (i >= argc) {
1230 wfatal("too few arguments for %s", argv[i - 1]);
1231 exit(1);
1233 display = argv[i];
1234 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1235 style = "spixmap";
1236 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1237 style = "tpixmap";
1238 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1239 style = "cpixmap";
1240 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1241 style = "mpixmap";
1242 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1243 style = "fpixmap";
1244 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1245 render_mode = RDitheredRendering;
1246 obey_user++;
1247 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1248 render_mode = RBestMatchRendering;
1249 obey_user++;
1250 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1251 smooth = True;
1252 #ifdef USE_XINERAMA
1253 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1254 xineStretch = True;
1255 #endif
1256 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1257 update++;
1258 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1259 update++;
1260 i++;
1261 if (i >= argc) {
1262 wfatal("too few arguments for %s", argv[i - 1]);
1263 exit(1);
1265 domain = wstrdup(argv[i]);
1266 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1267 i++;
1268 if (i >= argc) {
1269 wfatal("too few arguments for %s", argv[i - 1]);
1270 exit(1);
1272 if (sscanf(argv[i], "%i", &cpc) != 1) {
1273 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1274 exit(1);
1276 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1277 i++;
1278 if (i >= argc) {
1279 wfatal("too few arguments for %s", argv[i - 1]);
1280 exit(1);
1282 back_color = argv[i];
1283 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1284 i++;
1285 if (i >= argc) {
1286 wfatal("too few arguments for %s", argv[i - 1]);
1287 exit(1);
1289 texture = argv[i];
1290 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1291 i++;
1292 if (i >= argc) {
1293 wfatal("too few arguments for %s", argv[i - 1]);
1294 exit(1);
1296 if (sscanf(argv[i], "%i", &workspace) != 1) {
1297 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1298 exit(1);
1300 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1301 printf("%s (Window Maker %s)\n", __progname, VERSION);
1302 exit(0);
1303 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1304 print_help();
1305 exit(0);
1306 } else if (argv[i][0] != '-') {
1307 image_name = argv[i];
1308 } else {
1309 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1310 printf("Try '%s --help' for more information\n", __progname);
1311 exit(1);
1314 if (!image_name && !texture && !helperMode) {
1315 printf("%s: you must specify a image file name or a texture\n", __progname);
1316 printf("Try '%s --help' for more information\n", __progname);
1317 exit(1);
1320 PixmapPath = getPixmapPath(domain);
1321 if (!smooth) {
1322 WMPropList *val;
1323 /* carlos, don't remove this */
1324 #if 0 /* some problem with Alpha... TODO: check if its right */
1325 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1326 #else
1327 val = getValueForKey(domain, "SmoothWorkspaceBack");
1328 #endif
1330 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1331 smooth = True;
1334 dpy = XOpenDisplay(display);
1335 if (!dpy) {
1336 wfatal("could not open display");
1337 exit(1);
1339 #if 0
1340 XSynchronize(dpy, 1);
1341 #endif
1343 root = DefaultRootWindow(dpy);
1345 scr = DefaultScreen(dpy);
1347 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1348 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1349 scrX = scrY = 0;
1351 initXinerama();
1353 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1354 render_mode = RDitheredRendering;
1356 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1357 rattr.render_mode = render_mode;
1358 rattr.colors_per_channel = cpc;
1359 rattr.standard_colormap_mode = RCreateStdColormap;
1361 rc = RCreateContext(dpy, scr, &rattr);
1363 if (!rc) {
1364 rattr.standard_colormap_mode = RIgnoreStdColormap;
1365 rc = RCreateContext(dpy, scr, &rattr);
1368 if (!rc) {
1369 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1370 exit(1);
1373 if (helperMode) {
1374 /* lower priority, so that it wont use all the CPU */
1375 nice(15);
1377 helperLoop(rc);
1378 } else {
1379 BackgroundTexture *tex;
1380 char buffer[4098];
1382 if (!texture) {
1383 char *image_path = getFullPixmapPath(image_name);
1385 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1386 wfree(image_path);
1387 texture = (char *)buffer;
1390 if (update && workspace < 0) {
1391 updateDomain(domain, "WorkspaceBack", texture);
1394 tex = parseTexture(rc, texture);
1395 if (!tex)
1396 exit(1);
1398 if (workspace < 0)
1399 changeTexture(tex);
1400 else {
1401 /* always update domain */
1402 changeTextureForWorkspace(domain, texture, workspace);
1406 return 0;