WPrefs: grouped the choices for Window Resize Display in a single place
[wmaker-crm.git] / util / wmsetbg.c
blob7253517e891b0dc89a08f9dbdfe2aa08841674d4
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
4 * WindowMaker window manager
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 Dan Pascu
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * TODO: rewrite, too dirty
27 #include "config.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <ctype.h>
42 #ifdef USE_XINERAMA
43 # ifdef SOLARIS_XINERAMA /* sucks */
44 # include <X11/extensions/xinerama.h>
45 # else
46 # include <X11/extensions/Xinerama.h>
47 # endif
48 #endif
50 #ifdef HAVE_STDNORETURN
51 #include <stdnoreturn.h>
52 #endif
54 #include "../src/wconfig.h"
57 #include <WINGs/WINGs.h>
58 #include <wraster.h>
60 typedef struct {
61 WMRect *screens;
62 int count; /* screen count, 0 = inactive */
63 } WXineramaInfo;
65 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
67 Display *dpy;
68 char *display = "";
69 Window root;
70 int scr;
71 int scrWidth;
72 int scrHeight;
73 int scrX, scrY;
75 WXineramaInfo xineInfo;
77 Bool smooth = False;
78 #ifdef USE_XINERAMA
79 Bool xineStretch = False;
80 #endif
82 Pixmap CurrentPixmap = None;
83 char *PixmapPath = NULL;
85 static const char *prog_name;
87 typedef struct BackgroundTexture {
88 int refcount;
90 int solid;
92 char *spec;
94 XColor color;
95 Pixmap pixmap; /* for all textures, including solid */
96 int width; /* size of the pixmap */
97 int height;
98 } BackgroundTexture;
100 static noreturn void quit(int rcode)
102 WMReleaseApplication();
103 exit(rcode);
106 static void initXinerama(void)
108 xineInfo.screens = NULL;
109 xineInfo.count = 0;
110 #ifdef USE_XINERAMA
111 # ifdef SOLARIS_XINERAMA
112 if (XineramaGetState(dpy, scr)) {
113 XRectangle head[MAXFRAMEBUFFERS];
114 unsigned char hints[MAXFRAMEBUFFERS];
115 int i;
117 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
119 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
121 for (i = 0; i < xineInfo.count; i++) {
122 xineInfo.screens[i].pos.x = head[i].x;
123 xineInfo.screens[i].pos.y = head[i].y;
124 xineInfo.screens[i].size.width = head[i].width;
125 xineInfo.screens[i].size.height = head[i].height;
129 # else /* !SOLARIS_XINERAMA */
130 if (XineramaIsActive(dpy)) {
131 XineramaScreenInfo *xine_screens;
132 int i;
134 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
136 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
138 for (i = 0; i < xineInfo.count; i++) {
139 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
140 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
141 xineInfo.screens[i].size.width = xine_screens[i].width;
142 xineInfo.screens[i].size.height = xine_screens[i].height;
144 XFree(xine_screens);
146 # endif /* !SOLARIS_XINERAMA */
147 #endif /* USE_XINERAMA */
150 static RImage *loadImage(RContext * rc, const char *file)
152 char *path;
153 RImage *image;
155 if (access(file, F_OK) != 0) {
156 path = wfindfile(PixmapPath, file);
157 if (!path) {
158 wwarning("%s:could not find image file used in texture", file);
159 return NULL;
161 } else {
162 path = wstrdup(file);
165 image = RLoadImage(rc, path, 0);
166 if (!image) {
167 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
169 wfree(path);
171 return image;
174 static void
175 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
176 int x, int y, int width, int height)
178 int w, h;
179 Bool fimage = False;
181 switch (toupper(type)) {
182 case 'S':
183 case 'M':
184 case 'F':
185 if (toupper(type) == 'S') {
186 w = width;
187 h = height;
188 } else if(toupper(type) == 'F') {
189 if (image->width * height > image->height * width) {
190 w = (height * image->width) / image->height;
191 h = height;
192 } else {
193 w = width;
194 h = (width * image->height) / image->width;
196 } else {
197 if (image->width * height > image->height * width) {
198 w = width;
199 h = (width * image->height) / image->width;
200 } else {
201 w = (height * image->width) / image->height;
202 h = height;
206 if (w != image->width || h != image->height) {
207 RImage *simage;
209 if (smooth) {
210 simage = RSmoothScaleImage(image, w, h);
211 } else {
212 simage = RScaleImage(image, w, h);
215 if (!simage) {
216 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
217 return;
219 fimage = True;
220 image = simage;
223 /* fall through */
224 case 'C':
226 Pixmap pixmap;
228 if (!RConvertImage(rc, image, &pixmap)) {
229 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
230 if (fimage)
231 RReleaseImage(image);
232 return;
235 if (image->width != width || image->height != height) {
236 int sx, sy, w, h;
238 if (image->height < height) {
239 h = image->height;
240 y += (height - h) / 2;
241 sy = 0;
242 } else {
243 sy = (image->height - height) / 2;
244 h = height;
246 if (image->width < width) {
247 w = image->width;
248 x += (width - w) / 2;
249 sx = 0;
250 } else {
251 sx = (image->width - width) / 2;
252 w = width;
255 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
256 } else
257 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
258 x, y);
260 XFreePixmap(dpy, pixmap);
261 if (fimage) {
262 RReleaseImage(image);
265 break;
269 static BackgroundTexture *parseTexture(RContext * rc, char *text)
271 BackgroundTexture *texture = NULL;
272 WMPropList *texarray;
273 WMPropList *val;
274 int count;
275 char *tmp;
276 char *type;
278 #define GETSTRORGOTO(val, str, i, label) \
279 val = WMGetFromPLArray(texarray, i);\
280 if (!WMIsPLString(val)) {\
281 wwarning("could not parse texture %s", text);\
282 goto label;\
284 str = WMGetFromPLString(val)
286 texarray = WMCreatePropListFromDescription(text);
287 if (!texarray || !WMIsPLArray(texarray)
288 || (count = WMGetPropListItemCount(texarray)) < 2) {
290 wwarning("could not parse texture %s", text);
291 if (texarray)
292 WMReleasePropList(texarray);
293 return NULL;
296 texture = wmalloc(sizeof(BackgroundTexture));
298 GETSTRORGOTO(val, type, 0, error);
300 if (strcasecmp(type, "solid") == 0) {
301 XColor color;
302 Pixmap pixmap;
304 texture->solid = 1;
306 GETSTRORGOTO(val, tmp, 1, error);
308 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
309 wwarning("could not parse color %s in texture %s", tmp, text);
310 goto error;
312 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
314 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
315 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
316 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
318 texture->pixmap = pixmap;
319 texture->color = color;
320 texture->width = 8;
321 texture->height = 8;
322 } else if (strcasecmp(type, "vgradient") == 0
323 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
324 XColor color;
325 RColor color1, color2;
326 RImage *image;
327 Pixmap pixmap;
328 RGradientStyle gtype;
329 int iwidth, iheight;
331 GETSTRORGOTO(val, tmp, 1, error);
333 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
334 wwarning("could not parse color %s in texture %s", tmp, text);
335 goto error;
338 color1.red = color.red >> 8;
339 color1.green = color.green >> 8;
340 color1.blue = color.blue >> 8;
342 GETSTRORGOTO(val, tmp, 2, error);
344 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
345 wwarning("could not parse color %s in texture %s", tmp, text);
346 goto error;
349 color2.red = color.red >> 8;
350 color2.green = color.green >> 8;
351 color2.blue = color.blue >> 8;
353 switch (type[0]) {
354 case 'h':
355 case 'H':
356 gtype = RHorizontalGradient;
357 iwidth = scrWidth;
358 iheight = 32;
359 break;
360 case 'V':
361 case 'v':
362 gtype = RVerticalGradient;
363 iwidth = 32;
364 iheight = scrHeight;
365 break;
366 default:
367 gtype = RDiagonalGradient;
368 iwidth = scrWidth;
369 iheight = scrHeight;
370 break;
373 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
375 if (!image) {
376 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
377 goto error;
380 if (!RConvertImage(rc, image, &pixmap)) {
381 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
382 RReleaseImage(image);
383 goto error;
386 texture->width = image->width;
387 texture->height = image->height;
388 RReleaseImage(image);
390 texture->pixmap = pixmap;
391 } else if (strcasecmp(type, "mvgradient") == 0
392 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
393 XColor color;
394 RColor **colors;
395 RImage *image;
396 Pixmap pixmap;
397 int i, j;
398 RGradientStyle gtype;
399 int iwidth, iheight;
401 colors = malloc(sizeof(RColor *) * (count - 1));
402 if (!colors) {
403 wwarning("out of memory while parsing texture");
404 goto error;
406 memset(colors, 0, sizeof(RColor *) * (count - 1));
408 for (i = 2; i < count; i++) {
409 val = WMGetFromPLArray(texarray, i);
410 if (!WMIsPLString(val)) {
411 wwarning("could not parse texture %s", text);
413 for (j = 0; colors[j] != NULL; j++)
414 wfree(colors[j]);
415 wfree(colors);
416 goto error;
418 tmp = WMGetFromPLString(val);
420 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
421 wwarning("could not parse color %s in texture %s", tmp, text);
423 for (j = 0; colors[j] != NULL; j++)
424 wfree(colors[j]);
425 wfree(colors);
426 goto error;
428 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
429 wwarning("out of memory while parsing texture");
431 for (j = 0; colors[j] != NULL; j++)
432 wfree(colors[j]);
433 wfree(colors);
434 goto error;
437 colors[i - 2]->red = color.red >> 8;
438 colors[i - 2]->green = color.green >> 8;
439 colors[i - 2]->blue = color.blue >> 8;
442 switch (type[1]) {
443 case 'h':
444 case 'H':
445 gtype = RHorizontalGradient;
446 iwidth = scrWidth;
447 iheight = 32;
448 break;
449 case 'V':
450 case 'v':
451 gtype = RVerticalGradient;
452 iwidth = 32;
453 iheight = scrHeight;
454 break;
455 default:
456 gtype = RDiagonalGradient;
457 iwidth = scrWidth;
458 iheight = scrHeight;
459 break;
462 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
464 for (j = 0; colors[j] != NULL; j++)
465 wfree(colors[j]);
466 wfree(colors);
468 if (!image) {
469 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
470 goto error;
473 if (!RConvertImage(rc, image, &pixmap)) {
474 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
475 RReleaseImage(image);
476 goto error;
479 texture->width = image->width;
480 texture->height = image->height;
481 RReleaseImage(image);
483 texture->pixmap = pixmap;
484 } else if (strcasecmp(type, "cpixmap") == 0
485 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
486 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
487 XColor color;
488 Pixmap pixmap = None;
489 RImage *image = NULL;
490 int iwidth = 0, iheight = 0;
491 RColor rcolor;
493 GETSTRORGOTO(val, tmp, 1, error);
495 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
496 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
499 if (!pixmap) {
500 image = loadImage(rc, tmp);
501 if (!image) {
502 goto error;
504 iwidth = image->width;
505 iheight = image->height;
508 GETSTRORGOTO(val, tmp, 2, error);
510 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
511 wwarning("could not parse color %s in texture %s", tmp, text);
512 RReleaseImage(image);
513 goto error;
515 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
516 rcolor.red = color.red >> 8;
517 rcolor.green = color.green >> 8;
518 rcolor.blue = color.blue >> 8;
519 RGetClosestXColor(rc, &rcolor, &color);
520 } else {
521 rcolor.red = 0;
522 rcolor.green = 0;
523 rcolor.blue = 0;
525 /* for images with a transparent color */
526 if (image && image->data[3])
527 RCombineImageWithColor(image, &rcolor);
529 switch (toupper(type[0])) {
530 case 'T':
531 texture->width = iwidth;
532 texture->height = iheight;
533 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
534 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
535 RReleaseImage(image);
536 goto error;
539 texture->pixmap = pixmap;
540 texture->color = color;
541 break;
542 case 'S':
543 case 'M':
544 case 'C':
545 case 'F':
547 Pixmap tpixmap =
548 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
549 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
550 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
552 texture->pixmap = tpixmap;
553 texture->color = color;
554 texture->width = scrWidth;
555 texture->height = scrHeight;
557 if (!image)
558 break;
560 #ifdef USE_XINERAMA
561 if (xineInfo.count && ! xineStretch) {
562 int i;
563 for (i = 0; i < xineInfo.count; ++i) {
564 applyImage(rc, texture, image, type[0],
565 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
566 xineInfo.screens[i].size.width,
567 xineInfo.screens[i].size.height);
569 } else {
570 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
572 #else /* !USE_XINERAMA */
573 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
574 #endif /* !USE_XINERAMA */
576 break;
578 if (image)
579 RReleaseImage(image);
581 } else if (strcasecmp(type, "thgradient") == 0
582 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
583 XColor color;
584 RColor color1, color2;
585 RImage *image;
586 RImage *gradient;
587 RImage *tiled;
588 Pixmap pixmap;
589 int opaq;
590 char *file;
591 RGradientStyle gtype;
592 int twidth, theight;
594 GETSTRORGOTO(val, file, 1, error);
596 GETSTRORGOTO(val, tmp, 2, error);
598 opaq = atoi(tmp);
600 GETSTRORGOTO(val, tmp, 3, error);
602 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
603 wwarning("could not parse color %s in texture %s", tmp, text);
604 goto error;
607 color1.red = color.red >> 8;
608 color1.green = color.green >> 8;
609 color1.blue = color.blue >> 8;
611 GETSTRORGOTO(val, tmp, 4, error);
613 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
614 wwarning("could not parse color %s in texture %s", tmp, text);
615 goto error;
618 color2.red = color.red >> 8;
619 color2.green = color.green >> 8;
620 color2.blue = color.blue >> 8;
622 image = loadImage(rc, file);
623 if (!image) {
624 goto error;
627 switch (type[1]) {
628 case 'h':
629 case 'H':
630 gtype = RHorizontalGradient;
631 twidth = scrWidth;
632 theight = image->height > scrHeight ? scrHeight : image->height;
633 break;
634 case 'V':
635 case 'v':
636 gtype = RVerticalGradient;
637 twidth = image->width > scrWidth ? scrWidth : image->width;
638 theight = scrHeight;
639 break;
640 default:
641 gtype = RDiagonalGradient;
642 twidth = scrWidth;
643 theight = scrHeight;
644 break;
646 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
648 if (!gradient) {
649 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
650 RReleaseImage(image);
651 goto error;
654 tiled = RMakeTiledImage(image, twidth, theight);
655 if (!tiled) {
656 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
657 RReleaseImage(gradient);
658 RReleaseImage(image);
659 goto error;
661 RReleaseImage(image);
663 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
664 RReleaseImage(gradient);
666 if (!RConvertImage(rc, tiled, &pixmap)) {
667 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
668 RReleaseImage(tiled);
669 goto error;
671 texture->width = tiled->width;
672 texture->height = tiled->height;
674 RReleaseImage(tiled);
676 texture->pixmap = pixmap;
677 } else if (strcasecmp(type, "function") == 0) {
678 /* Leave this in to handle the unlikely case of
679 * someone actually having function textures configured */
680 wwarning("function texture support has been removed");
681 goto error;
682 } else {
683 wwarning("invalid texture type %s", text);
684 goto error;
687 texture->spec = wstrdup(text);
689 return texture;
691 error:
692 if (texture)
693 wfree(texture);
694 if (texarray)
695 WMReleasePropList(texarray);
697 return NULL;
700 static void freeTexture(BackgroundTexture * texture)
702 if (texture->solid) {
703 unsigned long pixel[1];
705 pixel[0] = texture->color.pixel;
706 /* dont free black/white pixels */
707 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
708 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
709 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
711 if (texture->pixmap) {
712 XFreePixmap(dpy, texture->pixmap);
714 wfree(texture->spec);
715 wfree(texture);
718 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
720 BackgroundTexture *newTexture = NULL;
721 int i;
723 /* unset the texture */
724 if (!texture) {
725 if (textures[workspace] != NULL) {
726 textures[workspace]->refcount--;
728 if (textures[workspace]->refcount == 0)
729 freeTexture(textures[workspace]);
731 textures[workspace] = NULL;
732 return;
735 if (textures[workspace]
736 && strcasecmp(textures[workspace]->spec, texture) == 0) {
737 /* texture did not change */
738 return;
741 /* check if the same texture is already created */
742 for (i = 0; i < *maxTextures; i++) {
743 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
744 newTexture = textures[i];
745 break;
749 if (!newTexture) {
750 /* create the texture */
751 newTexture = parseTexture(rc, texture);
753 if (!newTexture)
754 return;
756 if (textures[workspace] != NULL) {
758 textures[workspace]->refcount--;
760 if (textures[workspace]->refcount == 0)
761 freeTexture(textures[workspace]);
764 newTexture->refcount++;
765 textures[workspace] = newTexture;
767 if (*maxTextures < workspace)
768 *maxTextures = workspace;
771 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
773 Display *tmpDpy;
774 Pixmap copyP;
776 /* must open a new display or the RetainPermanent will
777 * leave stuff allocated in RContext unallocated after exit */
778 tmpDpy = XOpenDisplay(display);
779 if (!tmpDpy) {
780 wwarning("could not open display to update background image information");
782 return None;
783 } else {
784 XSync(dpy, False);
786 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
787 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
788 XSync(tmpDpy, False);
790 XSetCloseDownMode(tmpDpy, RetainPermanent);
791 XCloseDisplay(tmpDpy);
794 return copyP;
797 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
799 /* Parameter not used, but tell the compiler that it is ok */
800 (void) dpy;
801 (void) err;
803 return 0;
806 static void setPixmapProperty(Pixmap pixmap)
808 static Atom prop = 0;
809 Atom type;
810 int format;
811 unsigned long length, after;
812 unsigned char *data;
813 int mode;
815 if (!prop) {
816 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
819 XGrabServer(dpy);
821 /* Clear out the old pixmap */
822 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
823 &type, &format, &length, &after, &data);
825 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
826 XSetErrorHandler(dummyErrorHandler);
827 XKillClient(dpy, *((Pixmap *) data));
828 XSync(dpy, False);
829 XSetErrorHandler(NULL);
830 mode = PropModeReplace;
831 } else {
832 mode = PropModeAppend;
834 if (pixmap)
835 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
836 else
837 XDeleteProperty(dpy, root, prop);
839 XUngrabServer(dpy);
840 XFlush(dpy);
843 static void changeTexture(BackgroundTexture * texture)
845 if (!texture) {
846 return;
849 if (texture->solid) {
850 XSetWindowBackground(dpy, root, texture->color.pixel);
851 } else {
852 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
854 XClearWindow(dpy, root);
856 XSync(dpy, False);
859 Pixmap pixmap;
861 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
863 setPixmapProperty(pixmap);
867 static int readmsg(int fd, char *buffer, int size)
869 int count;
871 while (size > 0) {
872 count = read(fd, buffer, size);
873 if (count < 0)
874 return -1;
875 size -= count;
876 buffer += count;
877 *buffer = 0;
880 return size;
884 * Message Format:
885 * sizeSntexture_spec - sets the texture for workspace n
886 * sizeCn - change background texture to the one for workspace n
887 * sizePpath - set the pixmap search path
889 * n is 4 bytes
890 * size = 4 bytes for length of the message data
892 static noreturn void helperLoop(RContext * rc)
894 BackgroundTexture *textures[WORKSPACE_COUNT];
895 int maxTextures = 0;
896 char buffer[2048], buf[8];
897 int size;
898 int errcount = 4;
900 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
902 while (1) {
903 int workspace = -1;
905 /* get length of message */
906 if (readmsg(0, buffer, 4) < 0) {
907 werror("error reading message from Window Maker");
908 errcount--;
909 if (errcount == 0) {
910 wfatal("quitting");
911 quit(1);
913 continue;
915 memcpy(buf, buffer, 4);
916 buf[4] = 0;
917 size = atoi(buf);
918 if (size < 0 || size > sizeof(buffer)) {
919 wfatal("received invalid size %d for message from WindowMaker", size);
920 quit(1);
922 if (size == 0) {
923 werror("received 0-sized message from WindowMaker, trying to continue");
924 continue;
927 /* get message */
928 if (readmsg(0, buffer, size) < 0) {
929 werror("error reading message from Window Maker");
930 errcount--;
931 if (errcount == 0) {
932 wfatal("quitting");
933 quit(1);
935 continue;
937 #ifdef DEBUG
938 printf("RECEIVED %s\n", buffer);
939 #endif
940 if (buffer[0] != 'P' && buffer[0] != 'K') {
941 memcpy(buf, &buffer[1], 4);
942 buf[4] = 0;
943 workspace = atoi(buf);
944 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
945 wwarning("received message with invalid workspace number %i", workspace);
946 continue;
950 switch (buffer[0]) {
951 case 'S':
952 #ifdef DEBUG
953 printf("set texture %s\n", &buffer[5]);
954 #endif
955 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
956 break;
958 case 'C':
959 #ifdef DEBUG
960 printf("change texture %i\n", workspace);
961 #endif
962 if (!textures[workspace]) {
963 changeTexture(textures[0]);
964 } else {
965 changeTexture(textures[workspace]);
967 break;
969 case 'P':
970 #ifdef DEBUG
971 printf("change pixmappath %s\n", &buffer[1]);
972 #endif
973 if (PixmapPath)
974 wfree(PixmapPath);
975 PixmapPath = wstrdup(&buffer[1]);
976 break;
978 case 'U':
979 #ifdef DEBUG
980 printf("unset workspace %i\n", workspace);
981 #endif
982 setupTexture(rc, textures, &maxTextures, workspace, NULL);
983 break;
985 case 'K':
986 #ifdef DEBUG
987 printf("exit command\n");
988 #endif
989 quit(0);
991 default:
992 wwarning("unknown message received");
993 break;
998 static void updateDomain(const char *domain, const char *key, const char *texture)
1000 int result;
1001 char *program = "wdwrite";
1002 char cmd_smooth[1024];
1004 snprintf(cmd_smooth, sizeof(cmd_smooth),
1005 "wdwrite %s SmoothWorkspaceBack %s",
1006 domain, smooth ? "YES" : "NO");
1007 result = system(cmd_smooth);
1008 if (result == -1)
1009 werror("error executing system(\"%s\")", cmd_smooth);
1011 execlp(program, program, domain, key, texture, NULL);
1012 wwarning("warning could not run \"%s\"", program);
1015 static WMPropList *getValueForKey(const char *domain, const char *keyName)
1017 char *path;
1018 WMPropList *key, *val, *d;
1020 key = WMCreatePLString(keyName);
1022 /* try to find PixmapPath in user defaults */
1023 path = wdefaultspathfordomain(domain);
1024 d = WMReadPropListFromFile(path);
1025 if (!d) {
1026 wwarning("could not open domain file %s", path);
1028 wfree(path);
1030 if (d && !WMIsPLDictionary(d)) {
1031 WMReleasePropList(d);
1032 d = NULL;
1034 if (d) {
1035 val = WMGetFromPLDictionary(d, key);
1036 } else {
1037 val = NULL;
1039 /* try to find PixmapPath in global defaults */
1040 if (!val) {
1041 path = wglobaldefaultspathfordomain(domain);
1042 if (!path) {
1043 wwarning("could not locate file for domain %s", domain);
1044 d = NULL;
1045 } else {
1046 d = WMReadPropListFromFile(path);
1047 wfree(path);
1050 if (d && !WMIsPLDictionary(d)) {
1051 WMReleasePropList(d);
1052 d = NULL;
1054 if (d) {
1055 val = WMGetFromPLDictionary(d, key);
1057 } else {
1058 val = NULL;
1062 if (val)
1063 WMRetainPropList(val);
1065 WMReleasePropList(key);
1066 if (d)
1067 WMReleasePropList(d);
1069 return val;
1072 static char *getPixmapPath(const char *domain)
1074 WMPropList *val;
1075 char *ptr, *data;
1076 int len, i, count;
1078 val = getValueForKey(domain, "PixmapPath");
1080 if (!val || !WMIsPLArray(val)) {
1081 if (val)
1082 WMReleasePropList(val);
1083 return wstrdup("");
1086 count = WMGetPropListItemCount(val);
1087 len = 0;
1088 for (i = 0; i < count; i++) {
1089 WMPropList *v;
1091 v = WMGetFromPLArray(val, i);
1092 if (!v || !WMIsPLString(v)) {
1093 continue;
1095 len += strlen(WMGetFromPLString(v)) + 1;
1098 ptr = data = wmalloc(len + 1);
1099 *ptr = 0;
1101 for (i = 0; i < count; i++) {
1102 WMPropList *v;
1104 v = WMGetFromPLArray(val, i);
1105 if (!v || !WMIsPLString(v)) {
1106 continue;
1108 strcpy(ptr, WMGetFromPLString(v));
1110 ptr += strlen(WMGetFromPLString(v));
1111 *ptr = ':';
1112 ptr++;
1114 if (i > 0)
1115 ptr--;
1116 *(ptr--) = 0;
1118 WMReleasePropList(val);
1120 return data;
1123 static char *getFullPixmapPath(const char *file)
1125 char *tmp;
1127 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1128 int bsize = 512;
1129 char *path = wmalloc(bsize);
1131 while (!getcwd(path, bsize)) {
1132 bsize += bsize / 2;
1133 path = wrealloc(path, bsize);
1136 tmp = wstrconcat(path, "/");
1137 wfree(path);
1138 path = wstrconcat(tmp, file);
1139 wfree(tmp);
1141 return path;
1144 /* the file is in the PixmapPath */
1145 wfree(tmp);
1147 return wstrdup(file);
1150 static void print_help(void)
1152 printf("Usage: %s [options] [image]\n", prog_name);
1153 puts("Sets the workspace background to the specified image or a texture and");
1154 puts("optionally update Window Maker configuration");
1155 puts("");
1156 puts(" -display display to use");
1157 puts(" -d, --dither dither image");
1158 puts(" -m, --match match colors");
1159 puts(" -S, --smooth smooth scaled image");
1160 #ifdef USE_XINERAMA
1161 puts(" -X, --xinerama stretch image across Xinerama heads");
1162 #endif
1163 puts(" -b, --back-color <color> background color");
1164 puts(" -t, --tile tile image");
1165 puts(" -e, --center center image");
1166 puts(" -s, --scale scale image (default)");
1167 puts(" -a, --maxscale scale image and keep aspect ratio");
1168 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1169 puts(" -u, --update-wmaker update WindowMaker domain database");
1170 puts(" -D, --update-domain <domain> update <domain> database");
1171 puts(" -c, --colors <cpc> colors per channel to use");
1172 puts(" -p, --parse <texture> proplist style texture specification");
1173 puts(" -w, --workspace <workspace> update background for the specified workspace");
1174 puts(" -v, --version show version of wmsetbg and exit");
1175 puts(" -h, --help show this help and exit");
1178 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1180 WMPropList *array, *val;
1181 char *value;
1182 int j;
1184 val = WMCreatePropListFromDescription(texture);
1185 if (!val) {
1186 wwarning("could not parse texture %s", texture);
1187 return;
1190 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1192 if (!array) {
1193 array = WMCreatePLArray(NULL, NULL);
1196 j = WMGetPropListItemCount(array);
1197 if (workspace >= j) {
1198 WMPropList *empty;
1200 empty = WMCreatePLArray(NULL, NULL);
1202 while (j++ < workspace - 1) {
1203 WMAddToPLArray(array, empty);
1205 WMAddToPLArray(array, val);
1207 WMReleasePropList(empty);
1208 } else {
1209 WMDeleteFromPLArray(array, workspace);
1210 WMInsertInPLArray(array, workspace, val);
1213 value = WMGetPropListDescription(array, False);
1214 updateDomain(domain, "WorkspaceSpecificBack", value);
1216 WMReleasePropList(array);
1219 int main(int argc, char **argv)
1221 int i;
1222 int helperMode = 0;
1223 RContext *rc;
1224 RContextAttributes rattr;
1225 char *style = "spixmap";
1226 char *back_color = "gray20";
1227 char *image_name = NULL;
1228 char *domain = "WindowMaker";
1229 int update = 0, cpc = 4, obey_user = 0;
1230 RRenderingMode render_mode = RDitheredRendering;
1231 char *texture = NULL;
1232 int workspace = -1;
1234 signal(SIGINT, SIG_DFL);
1235 signal(SIGTERM, SIG_DFL);
1236 signal(SIGQUIT, SIG_DFL);
1237 signal(SIGSEGV, SIG_DFL);
1238 signal(SIGBUS, SIG_DFL);
1239 signal(SIGFPE, SIG_DFL);
1240 signal(SIGABRT, SIG_DFL);
1241 signal(SIGHUP, SIG_DFL);
1242 signal(SIGPIPE, SIG_DFL);
1243 signal(SIGCHLD, SIG_DFL);
1245 WMInitializeApplication("wmsetbg", &argc, argv);
1247 prog_name = argv[0];
1248 for (i = 1; i < argc; i++) {
1249 if (strcmp(argv[i], "-helper") == 0) {
1250 helperMode = 1;
1251 } else if (strcmp(argv[i], "-display") == 0) {
1252 i++;
1253 if (i >= argc) {
1254 wfatal("too few arguments for %s", argv[i - 1]);
1255 quit(1);
1257 display = argv[i];
1258 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1259 style = "spixmap";
1260 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1261 style = "tpixmap";
1262 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1263 style = "cpixmap";
1264 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1265 style = "mpixmap";
1266 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1267 style = "fpixmap";
1268 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1269 render_mode = RDitheredRendering;
1270 obey_user++;
1271 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1272 render_mode = RBestMatchRendering;
1273 obey_user++;
1274 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1275 smooth = True;
1276 #ifdef USE_XINERAMA
1277 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1278 xineStretch = True;
1279 #endif
1280 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1281 update++;
1282 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1283 update++;
1284 i++;
1285 if (i >= argc) {
1286 wfatal("too few arguments for %s", argv[i - 1]);
1287 quit(1);
1289 domain = wstrdup(argv[i]);
1290 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1291 i++;
1292 if (i >= argc) {
1293 wfatal("too few arguments for %s", argv[i - 1]);
1294 quit(1);
1296 if (sscanf(argv[i], "%i", &cpc) != 1) {
1297 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1298 quit(1);
1300 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1301 i++;
1302 if (i >= argc) {
1303 wfatal("too few arguments for %s", argv[i - 1]);
1304 quit(1);
1306 back_color = argv[i];
1307 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1308 i++;
1309 if (i >= argc) {
1310 wfatal("too few arguments for %s", argv[i - 1]);
1311 quit(1);
1313 texture = argv[i];
1314 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1315 i++;
1316 if (i >= argc) {
1317 wfatal("too few arguments for %s", argv[i - 1]);
1318 quit(1);
1320 if (sscanf(argv[i], "%i", &workspace) != 1) {
1321 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1322 quit(1);
1324 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1325 printf("%s (Window Maker %s)\n", prog_name, VERSION);
1326 quit(0);
1327 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1328 print_help();
1329 quit(0);
1330 } else if (argv[i][0] != '-') {
1331 image_name = argv[i];
1332 } else {
1333 printf("%s: invalid argument '%s'\n", prog_name, argv[i]);
1334 printf("Try '%s --help' for more information\n", prog_name);
1335 quit(1);
1338 if (!image_name && !texture && !helperMode) {
1339 printf("%s: you must specify a image file name or a texture\n", prog_name);
1340 printf("Try '%s --help' for more information\n", prog_name);
1341 quit(1);
1344 PixmapPath = getPixmapPath(domain);
1345 if (!smooth) {
1346 WMPropList *val;
1347 /* carlos, don't remove this */
1348 #if 0 /* some problem with Alpha... TODO: check if its right */
1349 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1350 #else
1351 val = getValueForKey(domain, "SmoothWorkspaceBack");
1352 #endif
1354 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1355 smooth = True;
1358 dpy = XOpenDisplay(display);
1359 if (!dpy) {
1360 wfatal("could not open display");
1361 quit(1);
1363 #if 0
1364 XSynchronize(dpy, 1);
1365 #endif
1367 root = DefaultRootWindow(dpy);
1369 scr = DefaultScreen(dpy);
1371 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1372 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1373 scrX = scrY = 0;
1375 initXinerama();
1377 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1378 render_mode = RDitheredRendering;
1380 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1381 rattr.render_mode = render_mode;
1382 rattr.colors_per_channel = cpc;
1383 rattr.standard_colormap_mode = RCreateStdColormap;
1385 rc = RCreateContext(dpy, scr, &rattr);
1387 if (!rc) {
1388 rattr.standard_colormap_mode = RIgnoreStdColormap;
1389 rc = RCreateContext(dpy, scr, &rattr);
1392 if (!rc) {
1393 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1394 quit(1);
1397 if (helperMode) {
1398 int result;
1400 /* lower priority, so that it wont use all the CPU */
1401 result = nice(15);
1402 if (result == -1)
1403 wwarning("error could not nice process");
1405 helperLoop(rc);
1406 } else {
1407 BackgroundTexture *tex;
1408 char buffer[4098];
1410 if (!texture) {
1411 char *image_path = getFullPixmapPath(image_name);
1412 snprintf(buffer, sizeof(buffer), "(%s, \"%s\", %s)", style, image_path, back_color);
1413 wfree(image_path);
1414 texture = (char *)buffer;
1417 if (update && workspace < 0) {
1418 updateDomain(domain, "WorkspaceBack", texture);
1421 tex = parseTexture(rc, texture);
1422 if (!tex)
1423 quit(1);
1425 if (workspace < 0)
1426 changeTexture(tex);
1427 else {
1428 /* always update domain */
1429 changeTextureForWorkspace(domain, texture, workspace);
1433 WMReleaseApplication();
1434 return 0;