Maximization regression
[wmaker-crm.git] / util / wmsetbg.c
blob8c15ea1d60eb28d18423db2100adbf86e03e666d
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 char *__progname;
86 typedef struct BackgroundTexture {
87 int refcount;
89 int solid;
91 char *spec;
93 XColor color;
94 Pixmap pixmap; /* for all textures, including solid */
95 int width; /* size of the pixmap */
96 int height;
97 } BackgroundTexture;
99 static void initXinerama(void)
101 xineInfo.screens = NULL;
102 xineInfo.count = 0;
103 #ifdef XINERAMA
104 # ifdef SOLARIS_XINERAMA
105 if (XineramaGetState(dpy, scr)) {
106 XRectangle head[MAXFRAMEBUFFERS];
107 unsigned char hints[MAXFRAMEBUFFERS];
108 int i;
110 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
112 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
114 for (i = 0; i < xineInfo.count; i++) {
115 xineInfo.screens[i].pos.x = head[i].x;
116 xineInfo.screens[i].pos.y = head[i].y;
117 xineInfo.screens[i].size.width = head[i].width;
118 xineInfo.screens[i].size.height = head[i].height;
122 # else /* !SOLARIS_XINERAMA */
123 if (XineramaIsActive(dpy)) {
124 XineramaScreenInfo *xine_screens;
125 int i;
127 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
129 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
131 for (i = 0; i < xineInfo.count; i++) {
132 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
133 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
134 xineInfo.screens[i].size.width = xine_screens[i].width;
135 xineInfo.screens[i].size.height = xine_screens[i].height;
137 XFree(xine_screens);
139 # endif /* !SOLARIS_XINERAMA */
140 #endif /* XINERAMA */
143 static RImage *loadImage(RContext * rc, const char *file)
145 char *path;
146 RImage *image;
148 if (access(file, F_OK) != 0) {
149 path = wfindfile(PixmapPath, file);
150 if (!path) {
151 wwarning("%s:could not find image file used in texture", file);
152 return NULL;
154 } else {
155 path = wstrdup(file);
158 image = RLoadImage(rc, path, 0);
159 if (!image) {
160 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
162 wfree(path);
164 return image;
167 static void
168 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
169 int x, int y, int width, int height)
171 int w, h;
172 Bool fimage = False;
174 switch (toupper(type)) {
175 case 'S':
176 case 'M':
177 case 'F':
178 if (toupper(type) == 'S') {
179 w = width;
180 h = height;
181 } else if(toupper(type) == 'F') {
182 if (image->width * height > image->height * width) {
183 w = (height * image->width) / image->height;
184 h = height;
185 } else {
186 w = width;
187 h = (width * image->height) / image->width;
189 } else {
190 if (image->width * height > image->height * width) {
191 w = width;
192 h = (width * image->height) / image->width;
193 } else {
194 w = (height * image->width) / image->height;
195 h = height;
199 if (w != image->width || h != image->height) {
200 RImage *simage;
202 if (smooth) {
203 simage = RSmoothScaleImage(image, w, h);
204 } else {
205 simage = RScaleImage(image, w, h);
208 if (!simage) {
209 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
210 return;
212 fimage = True;
213 image = simage;
216 /* fall through */
217 case 'C':
219 Pixmap pixmap;
221 if (!RConvertImage(rc, image, &pixmap)) {
222 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
223 return;
226 if (image->width != width || image->height != height) {
227 int sx, sy, w, h;
229 if (image->height < height) {
230 h = image->height;
231 y += (height - h) / 2;
232 sy = 0;
233 } else {
234 sy = (image->height - height) / 2;
235 h = height;
237 if (image->width < width) {
238 w = image->width;
239 x += (width - w) / 2;
240 sx = 0;
241 } else {
242 sx = (image->width - width) / 2;
243 w = width;
246 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
247 } else
248 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
249 x, y);
251 XFreePixmap(dpy, pixmap);
252 if (fimage) {
253 RReleaseImage(image);
256 break;
260 static BackgroundTexture *parseTexture(RContext * rc, char *text)
262 BackgroundTexture *texture = NULL;
263 WMPropList *texarray;
264 WMPropList *val;
265 int count;
266 char *tmp;
267 char *type;
269 #define GETSTRORGOTO(val, str, i, label) \
270 val = WMGetFromPLArray(texarray, i);\
271 if (!WMIsPLString(val)) {\
272 wwarning("could not parse texture %s", text);\
273 goto label;\
275 str = WMGetFromPLString(val)
277 texarray = WMCreatePropListFromDescription(text);
278 if (!texarray || !WMIsPLArray(texarray)
279 || (count = WMGetPropListItemCount(texarray)) < 2) {
281 wwarning("could not parse texture %s", text);
282 if (texarray)
283 WMReleasePropList(texarray);
284 return NULL;
287 texture = wmalloc(sizeof(BackgroundTexture));
289 GETSTRORGOTO(val, type, 0, error);
291 if (strcasecmp(type, "solid") == 0) {
292 XColor color;
293 Pixmap pixmap;
295 texture->solid = 1;
297 GETSTRORGOTO(val, tmp, 1, error);
299 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
300 wwarning("could not parse color %s in texture %s", tmp, text);
301 goto error;
303 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
305 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
306 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
307 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
309 texture->pixmap = pixmap;
310 texture->color = color;
311 texture->width = 8;
312 texture->height = 8;
313 } else if (strcasecmp(type, "vgradient") == 0
314 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
315 XColor color;
316 RColor color1, color2;
317 RImage *image;
318 Pixmap pixmap;
319 int gtype;
320 int iwidth, iheight;
322 GETSTRORGOTO(val, tmp, 1, error);
324 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
325 wwarning("could not parse color %s in texture %s", tmp, text);
326 goto error;
329 color1.red = color.red >> 8;
330 color1.green = color.green >> 8;
331 color1.blue = color.blue >> 8;
333 GETSTRORGOTO(val, tmp, 2, error);
335 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
336 wwarning("could not parse color %s in texture %s", tmp, text);
337 goto error;
340 color2.red = color.red >> 8;
341 color2.green = color.green >> 8;
342 color2.blue = color.blue >> 8;
344 switch (type[0]) {
345 case 'h':
346 case 'H':
347 gtype = RHorizontalGradient;
348 iwidth = scrWidth;
349 iheight = 32;
350 break;
351 case 'V':
352 case 'v':
353 gtype = RVerticalGradient;
354 iwidth = 32;
355 iheight = scrHeight;
356 break;
357 default:
358 gtype = RDiagonalGradient;
359 iwidth = scrWidth;
360 iheight = scrHeight;
361 break;
364 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
366 if (!image) {
367 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
368 goto error;
371 if (!RConvertImage(rc, image, &pixmap)) {
372 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
373 RReleaseImage(image);
374 goto error;
377 texture->width = image->width;
378 texture->height = image->height;
379 RReleaseImage(image);
381 texture->pixmap = pixmap;
382 } else if (strcasecmp(type, "mvgradient") == 0
383 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
384 XColor color;
385 RColor **colors;
386 RImage *image;
387 Pixmap pixmap;
388 int i, j;
389 int gtype;
390 int iwidth, iheight;
392 colors = malloc(sizeof(RColor *) * (count - 1));
393 if (!colors) {
394 wwarning("out of memory while parsing texture");
395 goto error;
397 memset(colors, 0, sizeof(RColor *) * (count - 1));
399 for (i = 2; i < count; i++) {
400 val = WMGetFromPLArray(texarray, i);
401 if (!WMIsPLString(val)) {
402 wwarning("could not parse texture %s", text);
404 for (j = 0; colors[j] != NULL; j++)
405 wfree(colors[j]);
406 wfree(colors);
407 goto error;
409 tmp = WMGetFromPLString(val);
411 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
412 wwarning("could not parse color %s in texture %s", tmp, text);
414 for (j = 0; colors[j] != NULL; j++)
415 wfree(colors[j]);
416 wfree(colors);
417 goto error;
419 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
420 wwarning("out of memory while parsing texture");
422 for (j = 0; colors[j] != NULL; j++)
423 wfree(colors[j]);
424 wfree(colors);
425 goto error;
428 colors[i - 2]->red = color.red >> 8;
429 colors[i - 2]->green = color.green >> 8;
430 colors[i - 2]->blue = color.blue >> 8;
433 switch (type[1]) {
434 case 'h':
435 case 'H':
436 gtype = RHorizontalGradient;
437 iwidth = scrWidth;
438 iheight = 32;
439 break;
440 case 'V':
441 case 'v':
442 gtype = RVerticalGradient;
443 iwidth = 32;
444 iheight = scrHeight;
445 break;
446 default:
447 gtype = RDiagonalGradient;
448 iwidth = scrWidth;
449 iheight = scrHeight;
450 break;
453 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
455 for (j = 0; colors[j] != NULL; j++)
456 wfree(colors[j]);
457 wfree(colors);
459 if (!image) {
460 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
461 goto error;
464 if (!RConvertImage(rc, image, &pixmap)) {
465 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
466 RReleaseImage(image);
467 goto error;
470 texture->width = image->width;
471 texture->height = image->height;
472 RReleaseImage(image);
474 texture->pixmap = pixmap;
475 } else if (strcasecmp(type, "cpixmap") == 0
476 || strcasecmp(type, "spixmap") == 0 || strcasecmp(type, "fpixmap") == 0
477 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
478 XColor color;
479 Pixmap pixmap = None;
480 RImage *image = NULL;
481 int iwidth = 0, iheight = 0;
482 RColor rcolor;
484 GETSTRORGOTO(val, tmp, 1, error);
486 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
487 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
490 if (!pixmap) {
491 image = loadImage(rc, tmp);
492 if (!image) {
493 goto error;
495 iwidth = image->width;
496 iheight = image->height;
499 GETSTRORGOTO(val, tmp, 2, error);
501 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
502 wwarning("could not parse color %s in texture %s", tmp, text);
503 RReleaseImage(image);
504 goto error;
506 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
507 rcolor.red = color.red >> 8;
508 rcolor.green = color.green >> 8;
509 rcolor.blue = color.blue >> 8;
510 RGetClosestXColor(rc, &rcolor, &color);
511 } else {
512 rcolor.red = 0;
513 rcolor.green = 0;
514 rcolor.blue = 0;
516 /* for images with a transparent color */
517 if (image->data[3]) {
518 RCombineImageWithColor(image, &rcolor);
521 switch (toupper(type[0])) {
522 case 'T':
523 texture->width = iwidth;
524 texture->height = iheight;
525 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
526 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
527 RReleaseImage(image);
528 goto error;
530 if (image)
531 RReleaseImage(image);
533 texture->pixmap = pixmap;
534 texture->color = color;
535 break;
536 case 'S':
537 case 'M':
538 case 'C':
539 case 'F':
541 Pixmap tpixmap =
542 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
543 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
544 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
546 texture->pixmap = tpixmap;
547 texture->color = color;
548 texture->width = scrWidth;
549 texture->height = scrHeight;
551 #ifdef XINERAMA
552 if (xineInfo.count && ! xineStretch) {
553 int i;
554 for (i = 0; i < xineInfo.count; ++i) {
555 applyImage(rc, texture, image, type[0],
556 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
557 xineInfo.screens[i].size.width,
558 xineInfo.screens[i].size.height);
560 } else {
561 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
563 #else /* !XINERAMA */
564 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
565 #endif /* !XINERAMA */
566 RReleaseImage(image);
568 break;
570 } else if (strcasecmp(type, "thgradient") == 0
571 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
572 XColor color;
573 RColor color1, color2;
574 RImage *image;
575 RImage *gradient;
576 RImage *tiled;
577 Pixmap pixmap;
578 int opaq;
579 char *file;
580 int gtype;
581 int twidth, theight;
583 GETSTRORGOTO(val, file, 1, error);
585 GETSTRORGOTO(val, tmp, 2, error);
587 opaq = atoi(tmp);
589 GETSTRORGOTO(val, tmp, 3, error);
591 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
592 wwarning("could not parse color %s in texture %s", tmp, text);
593 goto error;
596 color1.red = color.red >> 8;
597 color1.green = color.green >> 8;
598 color1.blue = color.blue >> 8;
600 GETSTRORGOTO(val, tmp, 4, 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 color2.red = color.red >> 8;
608 color2.green = color.green >> 8;
609 color2.blue = color.blue >> 8;
611 image = loadImage(rc, file);
612 if (!image) {
613 goto error;
616 switch (type[1]) {
617 case 'h':
618 case 'H':
619 gtype = RHorizontalGradient;
620 twidth = scrWidth;
621 theight = image->height > scrHeight ? scrHeight : image->height;
622 break;
623 case 'V':
624 case 'v':
625 gtype = RVerticalGradient;
626 twidth = image->width > scrWidth ? scrWidth : image->width;
627 theight = scrHeight;
628 break;
629 default:
630 gtype = RDiagonalGradient;
631 twidth = scrWidth;
632 theight = scrHeight;
633 break;
635 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
637 if (!gradient) {
638 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
639 RReleaseImage(gradient);
640 RReleaseImage(image);
641 goto error;
644 tiled = RMakeTiledImage(image, twidth, theight);
645 if (!tiled) {
646 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
647 RReleaseImage(gradient);
648 RReleaseImage(image);
649 goto error;
651 RReleaseImage(image);
653 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
654 RReleaseImage(gradient);
656 if (!RConvertImage(rc, tiled, &pixmap)) {
657 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
658 RReleaseImage(tiled);
659 goto error;
661 texture->width = tiled->width;
662 texture->height = tiled->height;
664 RReleaseImage(tiled);
666 texture->pixmap = pixmap;
667 } else if (strcasecmp(type, "function") == 0) {
668 /* Leave this in to handle the unlikely case of
669 * someone actually having function textures configured */
670 wwarning("function texture support has been removed");
671 goto error;
672 } else {
673 wwarning("invalid texture type %s", text);
674 goto error;
677 texture->spec = wstrdup(text);
679 return texture;
681 error:
682 if (texture)
683 wfree(texture);
684 if (texarray)
685 WMReleasePropList(texarray);
687 return NULL;
690 static void freeTexture(BackgroundTexture * texture)
692 if (texture->solid) {
693 unsigned long pixel[1];
695 pixel[0] = texture->color.pixel;
696 /* dont free black/white pixels */
697 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
698 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
699 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
701 if (texture->pixmap) {
702 XFreePixmap(dpy, texture->pixmap);
704 wfree(texture->spec);
705 wfree(texture);
708 static void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
710 BackgroundTexture *newTexture = NULL;
711 int i;
713 /* unset the texture */
714 if (!texture) {
715 if (textures[workspace] != NULL) {
716 textures[workspace]->refcount--;
718 if (textures[workspace]->refcount == 0)
719 freeTexture(textures[workspace]);
721 textures[workspace] = NULL;
722 return;
725 if (textures[workspace]
726 && strcasecmp(textures[workspace]->spec, texture) == 0) {
727 /* texture did not change */
728 return;
731 /* check if the same texture is already created */
732 for (i = 0; i < *maxTextures; i++) {
733 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
734 newTexture = textures[i];
735 break;
739 if (!newTexture) {
740 /* create the texture */
741 newTexture = parseTexture(rc, texture);
743 if (!newTexture)
744 return;
746 if (textures[workspace] != NULL) {
748 textures[workspace]->refcount--;
750 if (textures[workspace]->refcount == 0)
751 freeTexture(textures[workspace]);
754 newTexture->refcount++;
755 textures[workspace] = newTexture;
757 if (*maxTextures < workspace)
758 *maxTextures = workspace;
761 static Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
763 Display *tmpDpy;
764 Pixmap copyP;
766 /* must open a new display or the RetainPermanent will
767 * leave stuff allocated in RContext unallocated after exit */
768 tmpDpy = XOpenDisplay(display);
769 if (!tmpDpy) {
770 wwarning("could not open display to update background image information");
772 return None;
773 } else {
774 XSync(dpy, False);
776 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
777 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
778 XSync(tmpDpy, False);
780 XSetCloseDownMode(tmpDpy, RetainPermanent);
781 XCloseDisplay(tmpDpy);
784 return copyP;
787 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
789 return 0;
792 static void setPixmapProperty(Pixmap pixmap)
794 static Atom prop = 0;
795 Atom type;
796 int format;
797 unsigned long length, after;
798 unsigned char *data;
799 int mode;
801 if (!prop) {
802 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
805 XGrabServer(dpy);
807 /* Clear out the old pixmap */
808 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
809 &type, &format, &length, &after, &data);
811 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
812 XSetErrorHandler(dummyErrorHandler);
813 XKillClient(dpy, *((Pixmap *) data));
814 XSync(dpy, False);
815 XSetErrorHandler(NULL);
816 mode = PropModeReplace;
817 } else {
818 mode = PropModeAppend;
820 if (pixmap)
821 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
822 else
823 XDeleteProperty(dpy, root, prop);
825 XUngrabServer(dpy);
826 XFlush(dpy);
829 static void changeTexture(BackgroundTexture * texture)
831 if (!texture) {
832 return;
835 if (texture->solid) {
836 XSetWindowBackground(dpy, root, texture->color.pixel);
837 } else {
838 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
840 XClearWindow(dpy, root);
842 XSync(dpy, False);
845 Pixmap pixmap;
847 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
849 setPixmapProperty(pixmap);
853 static int readmsg(int fd, char *buffer, int size)
855 int count;
857 count = 0;
858 while (size > 0) {
859 count = read(fd, buffer, size);
860 if (count < 0)
861 return -1;
862 size -= count;
863 buffer += count;
864 *buffer = 0;
867 return size;
871 * Message Format:
872 * sizeSntexture_spec - sets the texture for workspace n
873 * sizeCn - change background texture to the one for workspace n
874 * sizePpath - set the pixmap search path
876 * n is 4 bytes
877 * size = 4 bytes for length of the message data
879 static void helperLoop(RContext * rc)
881 BackgroundTexture *textures[WORKSPACE_COUNT];
882 int maxTextures = 0;
883 char buffer[2048], buf[8];
884 int size;
885 int errcount = 4;
887 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
889 while (1) {
890 int workspace = -1;
892 /* get length of message */
893 if (readmsg(0, buffer, 4) < 0) {
894 werror("error reading message from Window Maker");
895 errcount--;
896 if (errcount == 0) {
897 wfatal("quitting");
898 exit(1);
900 continue;
902 memcpy(buf, buffer, 4);
903 buf[4] = 0;
904 size = atoi(buf);
906 /* get message */
907 if (readmsg(0, buffer, size) < 0) {
908 werror("error reading message from Window Maker");
909 errcount--;
910 if (errcount == 0) {
911 wfatal("quitting");
912 exit(1);
914 continue;
916 #ifdef DEBUG
917 printf("RECEIVED %s\n", buffer);
918 #endif
919 if (buffer[0] != 'P' && buffer[0] != 'K') {
920 memcpy(buf, &buffer[1], 4);
921 buf[4] = 0;
922 workspace = atoi(buf);
923 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
924 wwarning("received message with invalid workspace number %i", workspace);
925 continue;
929 switch (buffer[0]) {
930 case 'S':
931 #ifdef DEBUG
932 printf("set texture %s\n", &buffer[5]);
933 #endif
934 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
935 break;
937 case 'C':
938 #ifdef DEBUG
939 printf("change texture %i\n", workspace);
940 #endif
941 if (!textures[workspace]) {
942 changeTexture(textures[0]);
943 } else {
944 changeTexture(textures[workspace]);
946 break;
948 case 'P':
949 #ifdef DEBUG
950 printf("change pixmappath %s\n", &buffer[1]);
951 #endif
952 if (PixmapPath)
953 wfree(PixmapPath);
954 PixmapPath = wstrdup(&buffer[1]);
955 break;
957 case 'U':
958 #ifdef DEBUG
959 printf("unset workspace %i\n", workspace);
960 #endif
961 setupTexture(rc, textures, &maxTextures, workspace, NULL);
962 break;
964 case 'K':
965 #ifdef DEBUG
966 printf("exit command\n");
967 #endif
968 exit(0);
970 default:
971 wwarning("unknown message received");
972 break;
977 static void updateDomain(const char *domain, const char *key, const char *texture)
979 char *program = "wdwrite";
981 /* here is a mem leak */
982 system(wstrconcat("wdwrite ",
983 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
985 execlp(program, program, domain, key, texture, NULL);
986 wwarning("warning could not run \"%s\"", program);
989 static WMPropList *getValueForKey(const char *domain, const char *keyName)
991 char *path;
992 WMPropList *key, *val, *d;
994 key = WMCreatePLString(keyName);
996 /* try to find PixmapPath in user defaults */
997 path = wdefaultspathfordomain(domain);
998 d = WMReadPropListFromFile(path);
999 if (!d) {
1000 wwarning("could not open domain file %s", path);
1002 wfree(path);
1004 if (d && !WMIsPLDictionary(d)) {
1005 WMReleasePropList(d);
1006 d = NULL;
1008 if (d) {
1009 val = WMGetFromPLDictionary(d, key);
1010 } else {
1011 val = NULL;
1013 /* try to find PixmapPath in global defaults */
1014 if (!val) {
1015 path = wglobaldefaultspathfordomain(domain);
1016 if (!path) {
1017 wwarning("could not locate file for domain %s", domain);
1018 d = NULL;
1019 } else {
1020 d = WMReadPropListFromFile(path);
1021 wfree(path);
1024 if (d && !WMIsPLDictionary(d)) {
1025 WMReleasePropList(d);
1026 d = NULL;
1028 if (d) {
1029 val = WMGetFromPLDictionary(d, key);
1031 } else {
1032 val = NULL;
1036 if (val)
1037 WMRetainPropList(val);
1039 WMReleasePropList(key);
1040 if (d)
1041 WMReleasePropList(d);
1043 return val;
1046 static char *getPixmapPath(const char *domain)
1048 WMPropList *val;
1049 char *ptr, *data;
1050 int len, i, count;
1052 val = getValueForKey(domain, "PixmapPath");
1054 if (!val || !WMIsPLArray(val)) {
1055 if (val)
1056 WMReleasePropList(val);
1057 return wstrdup("");
1060 count = WMGetPropListItemCount(val);
1061 len = 0;
1062 for (i = 0; i < count; i++) {
1063 WMPropList *v;
1065 v = WMGetFromPLArray(val, i);
1066 if (!v || !WMIsPLString(v)) {
1067 continue;
1069 len += strlen(WMGetFromPLString(v)) + 1;
1072 ptr = data = wmalloc(len + 1);
1073 *ptr = 0;
1075 for (i = 0; i < count; i++) {
1076 WMPropList *v;
1078 v = WMGetFromPLArray(val, i);
1079 if (!v || !WMIsPLString(v)) {
1080 continue;
1082 strcpy(ptr, WMGetFromPLString(v));
1084 ptr += strlen(WMGetFromPLString(v));
1085 *ptr = ':';
1086 ptr++;
1088 if (i > 0)
1089 ptr--;
1090 *(ptr--) = 0;
1092 WMReleasePropList(val);
1094 return data;
1097 static char *getFullPixmapPath(const char *file)
1099 char *tmp;
1101 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1102 int bsize = 512;
1103 char *path = wmalloc(bsize);
1105 while (!getcwd(path, bsize)) {
1106 bsize += bsize / 2;
1107 path = wrealloc(path, bsize);
1110 tmp = wstrconcat(path, "/");
1111 wfree(path);
1112 path = wstrconcat(tmp, file);
1113 wfree(tmp);
1115 return path;
1118 /* the file is in the PixmapPath */
1119 wfree(tmp);
1121 return wstrdup(file);
1124 void wAbort(void)
1126 wfatal("aborting");
1127 exit(1);
1130 static void print_help(void)
1132 printf("Usage: %s [options] [image]\n", __progname);
1133 puts("Sets the workspace background to the specified image or a texture and");
1134 puts("optionally update Window Maker configuration");
1135 puts("");
1136 puts(" -display display to use");
1137 puts(" -d, --dither dither image");
1138 puts(" -m, --match match colors");
1139 puts(" -S, --smooth smooth scaled image");
1140 #ifdef XINERAMA
1141 puts(" -X, --xinerama stretch image across Xinerama heads");
1142 #endif
1143 puts(" -b, --back-color <color> background color");
1144 puts(" -t, --tile tile image");
1145 puts(" -e, --center center image");
1146 puts(" -s, --scale scale image (default)");
1147 puts(" -a, --maxscale scale image and keep aspect ratio");
1148 puts(" -f, --fillscale scale image to fill screen and keep aspect ratio");
1149 puts(" -u, --update-wmaker update WindowMaker domain database");
1150 puts(" -D, --update-domain <domain> update <domain> database");
1151 puts(" -c, --colors <cpc> colors per channel to use");
1152 puts(" -p, --parse <texture> proplist style texture specification");
1153 puts(" -w, --workspace <workspace> update background for the specified workspace");
1154 puts(" -v, --version show version of wmsetbg and exit");
1155 puts(" -h, --help show this help and exit");
1158 static void changeTextureForWorkspace(const char *domain, char *texture, int workspace)
1160 WMPropList *array, *val;
1161 char *value;
1162 int j;
1164 val = WMCreatePropListFromDescription(texture);
1165 if (!val) {
1166 wwarning("could not parse texture %s", texture);
1167 return;
1170 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1172 if (!array) {
1173 array = WMCreatePLArray(NULL, NULL);
1176 j = WMGetPropListItemCount(array);
1177 if (workspace >= j) {
1178 WMPropList *empty;
1180 empty = WMCreatePLArray(NULL, NULL);
1182 while (j++ < workspace - 1) {
1183 WMAddToPLArray(array, empty);
1185 WMAddToPLArray(array, val);
1186 } else {
1187 WMDeleteFromPLArray(array, workspace);
1188 WMInsertInPLArray(array, workspace, val);
1191 value = WMGetPropListDescription(array, False);
1192 updateDomain(domain, "WorkspaceSpecificBack", value);
1195 int main(int argc, char **argv)
1197 int i;
1198 int helperMode = 0;
1199 RContext *rc;
1200 RContextAttributes rattr;
1201 char *style = "spixmap";
1202 char *back_color = "gray20";
1203 char *image_name = NULL;
1204 char *domain = "WindowMaker";
1205 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1206 char *texture = NULL;
1207 int workspace = -1;
1209 signal(SIGINT, SIG_DFL);
1210 signal(SIGTERM, SIG_DFL);
1211 signal(SIGQUIT, SIG_DFL);
1212 signal(SIGSEGV, SIG_DFL);
1213 signal(SIGBUS, SIG_DFL);
1214 signal(SIGFPE, SIG_DFL);
1215 signal(SIGABRT, SIG_DFL);
1216 signal(SIGHUP, SIG_DFL);
1217 signal(SIGPIPE, SIG_DFL);
1218 signal(SIGCHLD, SIG_DFL);
1220 WMInitializeApplication("wmsetbg", &argc, argv);
1222 for (i = 1; i < argc; i++) {
1223 if (strcmp(argv[i], "-helper") == 0) {
1224 helperMode = 1;
1225 } else if (strcmp(argv[i], "-display") == 0) {
1226 i++;
1227 if (i >= argc) {
1228 wfatal("too few arguments for %s", argv[i - 1]);
1229 exit(1);
1231 display = argv[i];
1232 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1233 style = "spixmap";
1234 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1235 style = "tpixmap";
1236 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1237 style = "cpixmap";
1238 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1239 style = "mpixmap";
1240 } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fillscale") == 0) {
1241 style = "fpixmap";
1242 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1243 render_mode = RDitheredRendering;
1244 obey_user++;
1245 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1246 render_mode = RBestMatchRendering;
1247 obey_user++;
1248 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1249 smooth = True;
1250 #ifdef XINERAMA
1251 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1252 xineStretch = True;
1253 #endif
1254 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1255 update++;
1256 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1257 update++;
1258 i++;
1259 if (i >= argc) {
1260 wfatal("too few arguments for %s", argv[i - 1]);
1261 exit(1);
1263 domain = wstrdup(argv[i]);
1264 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1265 i++;
1266 if (i >= argc) {
1267 wfatal("too few arguments for %s", argv[i - 1]);
1268 exit(1);
1270 if (sscanf(argv[i], "%i", &cpc) != 1) {
1271 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1272 exit(1);
1274 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1275 i++;
1276 if (i >= argc) {
1277 wfatal("too few arguments for %s", argv[i - 1]);
1278 exit(1);
1280 back_color = argv[i];
1281 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1282 i++;
1283 if (i >= argc) {
1284 wfatal("too few arguments for %s", argv[i - 1]);
1285 exit(1);
1287 texture = argv[i];
1288 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1289 i++;
1290 if (i >= argc) {
1291 wfatal("too few arguments for %s", argv[i - 1]);
1292 exit(1);
1294 if (sscanf(argv[i], "%i", &workspace) != 1) {
1295 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1296 exit(1);
1298 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1299 printf("%s (Window Maker %s)\n", __progname, VERSION);
1300 exit(0);
1301 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1302 print_help();
1303 exit(0);
1304 } else if (argv[i][0] != '-') {
1305 image_name = argv[i];
1306 } else {
1307 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1308 printf("Try '%s --help' for more information\n", __progname);
1309 exit(1);
1312 if (!image_name && !texture && !helperMode) {
1313 printf("%s: you must specify a image file name or a texture\n", __progname);
1314 printf("Try '%s --help' for more information\n", __progname);
1315 exit(1);
1318 PixmapPath = getPixmapPath(domain);
1319 if (!smooth) {
1320 WMPropList *val;
1321 /* carlos, don't remove this */
1322 #if 0 /* some problem with Alpha... TODO: check if its right */
1323 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1324 #else
1325 val = getValueForKey(domain, "SmoothWorkspaceBack");
1326 #endif
1328 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1329 smooth = True;
1332 dpy = XOpenDisplay(display);
1333 if (!dpy) {
1334 wfatal("could not open display");
1335 exit(1);
1337 #if 0
1338 XSynchronize(dpy, 1);
1339 #endif
1341 root = DefaultRootWindow(dpy);
1343 scr = DefaultScreen(dpy);
1345 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1346 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1347 scrX = scrY = 0;
1349 initXinerama();
1351 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1352 render_mode = RDitheredRendering;
1354 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1355 rattr.render_mode = render_mode;
1356 rattr.colors_per_channel = cpc;
1357 rattr.standard_colormap_mode = RCreateStdColormap;
1359 rc = RCreateContext(dpy, scr, &rattr);
1361 if (!rc) {
1362 rattr.standard_colormap_mode = RIgnoreStdColormap;
1363 rc = RCreateContext(dpy, scr, &rattr);
1366 if (!rc) {
1367 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1368 exit(1);
1371 if (helperMode) {
1372 /* lower priority, so that it wont use all the CPU */
1373 nice(15);
1375 helperLoop(rc);
1376 } else {
1377 BackgroundTexture *tex;
1378 char buffer[4098];
1380 if (!texture) {
1381 char *image_path = getFullPixmapPath(image_name);
1383 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1384 wfree(image_path);
1385 texture = (char *)buffer;
1388 if (update && workspace < 0) {
1389 updateDomain(domain, "WorkspaceBack", texture);
1392 tex = parseTexture(rc, texture);
1393 if (!tex)
1394 exit(1);
1396 if (workspace < 0)
1397 changeTexture(tex);
1398 else {
1399 /* always update domain */
1400 changeTextureForWorkspace(domain, texture, workspace);
1404 return 0;