Make ProgName a local variable
[wmaker-crm.git] / util / wmsetbg.c
blob17b0583963ac02acd3ddc8617786232f12f05a12
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
4 * WindowMaker window manager
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 Dan Pascu
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * TODO: rewrite, too dirty
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <X11/Xatom.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <ctype.h>
40 #include "../config.h"
42 #ifdef XINERAMA
43 # ifdef SOLARIS_XINERAMA /* sucks */
44 # include <X11/extensions/xinerama.h>
45 # else
46 # include <X11/extensions/Xinerama.h>
47 # endif
48 #endif
50 #include "../src/wconfig.h"
52 #ifndef GLOBAL_DEFAULTS_SUBDIR
53 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
54 #endif
56 #include <WINGs/WINGs.h>
57 #include <wraster.h>
59 typedef struct {
60 WMRect *screens;
61 int count; /* screen count, 0 = inactive */
62 } WXineramaInfo;
64 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
66 Display *dpy;
67 char *display = "";
68 Window root;
69 int scr;
70 int scrWidth;
71 int scrHeight;
72 int scrX, scrY;
74 WXineramaInfo xineInfo;
76 Bool smooth = False;
77 #ifdef XINERAMA
78 Bool xineStretch = False;
79 #endif
81 Pixmap CurrentPixmap = None;
82 char *PixmapPath = NULL;
84 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
85 extern char *__progname;
87 typedef struct BackgroundTexture {
88 int refcount;
90 int solid;
92 char *spec;
94 XColor color;
95 Pixmap pixmap; /* for all textures, including solid */
96 int width; /* size of the pixmap */
97 int height;
98 } BackgroundTexture;
100 void initXinerama()
102 xineInfo.screens = NULL;
103 xineInfo.count = 0;
104 #ifdef XINERAMA
105 # ifdef SOLARIS_XINERAMA
106 if (XineramaGetState(dpy, scr)) {
107 XRectangle head[MAXFRAMEBUFFERS];
108 unsigned char hints[MAXFRAMEBUFFERS];
109 int i;
111 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
113 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
115 for (i = 0; i < xineInfo.count; i++) {
116 xineInfo.screens[i].pos.x = head[i].x;
117 xineInfo.screens[i].pos.y = head[i].y;
118 xineInfo.screens[i].size.width = head[i].width;
119 xineInfo.screens[i].size.height = head[i].height;
123 # else /* !SOLARIS_XINERAMA */
124 if (XineramaIsActive(dpy)) {
125 XineramaScreenInfo *xine_screens;
126 int i;
128 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
130 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
132 for (i = 0; i < xineInfo.count; i++) {
133 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
134 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
135 xineInfo.screens[i].size.width = xine_screens[i].width;
136 xineInfo.screens[i].size.height = xine_screens[i].height;
138 XFree(xine_screens);
140 # endif /* !SOLARIS_XINERAMA */
141 #endif /* XINERAMA */
144 RImage *loadImage(RContext * rc, char *file)
146 char *path;
147 RImage *image;
149 if (access(file, F_OK) != 0) {
150 path = wfindfile(PixmapPath, file);
151 if (!path) {
152 wwarning("%s:could not find image file used in texture", file);
153 return NULL;
155 } else {
156 path = wstrdup(file);
159 image = RLoadImage(rc, path, 0);
160 if (!image) {
161 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
163 wfree(path);
165 return image;
168 static void
169 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
170 int x, int y, int width, int height)
172 int w, h;
173 Bool fimage = False;
175 switch (toupper(type)) {
176 case 'S':
177 case 'M':
178 if (toupper(type) == 'S') {
179 w = width;
180 h = height;
181 } else {
182 if (image->width * height > image->height * width) {
183 w = width;
184 h = (width * image->height) / image->width;
185 } else {
186 w = (height * image->width) / image->height;
187 h = height;
191 if (w != image->width || h != image->height) {
192 RImage *simage;
194 if (smooth) {
195 simage = RSmoothScaleImage(image, w, h);
196 } else {
197 simage = RScaleImage(image, w, h);
200 if (!simage) {
201 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
202 return;
204 fimage = True;
205 image = simage;
208 /* fall through */
209 case 'C':
211 Pixmap pixmap;
213 if (!RConvertImage(rc, image, &pixmap)) {
214 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
215 return;
218 if (image->width != width || image->height != height) {
219 int sx, sy, w, h;
221 if (image->height < height) {
222 h = image->height;
223 y += (height - h) / 2;
224 sy = 0;
225 } else {
226 sy = (image->height - height) / 2;
227 h = height;
229 if (image->width < width) {
230 w = image->width;
231 x += (width - w) / 2;
232 sx = 0;
233 } else {
234 sx = (image->width - width) / 2;
235 w = width;
238 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
239 } else
240 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
241 x, y);
243 XFreePixmap(dpy, pixmap);
244 if (fimage) {
245 RReleaseImage(image);
248 break;
252 BackgroundTexture *parseTexture(RContext * rc, char *text)
254 BackgroundTexture *texture = NULL;
255 WMPropList *texarray;
256 WMPropList *val;
257 int count;
258 char *tmp;
259 char *type;
261 #define GETSTRORGOTO(val, str, i, label) \
262 val = WMGetFromPLArray(texarray, i);\
263 if (!WMIsPLString(val)) {\
264 wwarning("could not parse texture %s", text);\
265 goto label;\
267 str = WMGetFromPLString(val)
269 texarray = WMCreatePropListFromDescription(text);
270 if (!texarray || !WMIsPLArray(texarray)
271 || (count = WMGetPropListItemCount(texarray)) < 2) {
273 wwarning("could not parse texture %s", text);
274 if (texarray)
275 WMReleasePropList(texarray);
276 return NULL;
279 texture = wmalloc(sizeof(BackgroundTexture));
280 memset(texture, 0, sizeof(BackgroundTexture));
282 GETSTRORGOTO(val, type, 0, error);
284 if (strcasecmp(type, "solid") == 0) {
285 XColor color;
286 Pixmap pixmap;
288 texture->solid = 1;
290 GETSTRORGOTO(val, tmp, 1, error);
292 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
293 wwarning("could not parse color %s in texture %s", tmp, text);
294 goto error;
296 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
298 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
299 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
300 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
302 texture->pixmap = pixmap;
303 texture->color = color;
304 texture->width = 8;
305 texture->height = 8;
306 } else if (strcasecmp(type, "vgradient") == 0
307 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
308 XColor color;
309 RColor color1, color2;
310 RImage *image;
311 Pixmap pixmap;
312 int gtype;
313 int iwidth, iheight;
315 GETSTRORGOTO(val, tmp, 1, error);
317 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
318 wwarning("could not parse color %s in texture %s", tmp, text);
319 goto error;
322 color1.red = color.red >> 8;
323 color1.green = color.green >> 8;
324 color1.blue = color.blue >> 8;
326 GETSTRORGOTO(val, tmp, 2, 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 color2.red = color.red >> 8;
334 color2.green = color.green >> 8;
335 color2.blue = color.blue >> 8;
337 switch (type[0]) {
338 case 'h':
339 case 'H':
340 gtype = RHorizontalGradient;
341 iwidth = scrWidth;
342 iheight = 32;
343 break;
344 case 'V':
345 case 'v':
346 gtype = RVerticalGradient;
347 iwidth = 32;
348 iheight = scrHeight;
349 break;
350 default:
351 gtype = RDiagonalGradient;
352 iwidth = scrWidth;
353 iheight = scrHeight;
354 break;
357 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
359 if (!image) {
360 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
361 goto error;
364 if (!RConvertImage(rc, image, &pixmap)) {
365 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
366 RReleaseImage(image);
367 goto error;
370 texture->width = image->width;
371 texture->height = image->height;
372 RReleaseImage(image);
374 texture->pixmap = pixmap;
375 } else if (strcasecmp(type, "mvgradient") == 0
376 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
377 XColor color;
378 RColor **colors;
379 RImage *image;
380 Pixmap pixmap;
381 int i, j;
382 int gtype;
383 int iwidth, iheight;
385 colors = malloc(sizeof(RColor *) * (count - 1));
386 if (!colors) {
387 wwarning("out of memory while parsing texture");
388 goto error;
390 memset(colors, 0, sizeof(RColor *) * (count - 1));
392 for (i = 2; i < count; i++) {
393 val = WMGetFromPLArray(texarray, i);
394 if (!WMIsPLString(val)) {
395 wwarning("could not parse texture %s", text);
397 for (j = 0; colors[j] != NULL; j++)
398 wfree(colors[j]);
399 wfree(colors);
400 goto error;
402 tmp = WMGetFromPLString(val);
404 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
405 wwarning("could not parse color %s in texture %s", tmp, text);
407 for (j = 0; colors[j] != NULL; j++)
408 wfree(colors[j]);
409 wfree(colors);
410 goto error;
412 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
413 wwarning("out of memory while parsing texture");
415 for (j = 0; colors[j] != NULL; j++)
416 wfree(colors[j]);
417 wfree(colors);
418 goto error;
421 colors[i - 2]->red = color.red >> 8;
422 colors[i - 2]->green = color.green >> 8;
423 colors[i - 2]->blue = color.blue >> 8;
426 switch (type[1]) {
427 case 'h':
428 case 'H':
429 gtype = RHorizontalGradient;
430 iwidth = scrWidth;
431 iheight = 32;
432 break;
433 case 'V':
434 case 'v':
435 gtype = RVerticalGradient;
436 iwidth = 32;
437 iheight = scrHeight;
438 break;
439 default:
440 gtype = RDiagonalGradient;
441 iwidth = scrWidth;
442 iheight = scrHeight;
443 break;
446 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
448 for (j = 0; colors[j] != NULL; j++)
449 wfree(colors[j]);
450 wfree(colors);
452 if (!image) {
453 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
454 goto error;
457 if (!RConvertImage(rc, image, &pixmap)) {
458 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
459 RReleaseImage(image);
460 goto error;
463 texture->width = image->width;
464 texture->height = image->height;
465 RReleaseImage(image);
467 texture->pixmap = pixmap;
468 } else if (strcasecmp(type, "cpixmap") == 0
469 || strcasecmp(type, "spixmap") == 0
470 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
471 XColor color;
472 Pixmap pixmap = None;
473 RImage *image = NULL;
474 int iwidth = 0, iheight = 0;
475 RColor rcolor;
477 GETSTRORGOTO(val, tmp, 1, error);
479 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
480 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
483 if (!pixmap) {
484 image = loadImage(rc, tmp);
485 if (!image) {
486 goto error;
488 iwidth = image->width;
489 iheight = image->height;
492 GETSTRORGOTO(val, tmp, 2, error);
494 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
495 wwarning("could not parse color %s in texture %s", tmp, text);
496 RReleaseImage(image);
497 goto error;
499 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
500 rcolor.red = color.red >> 8;
501 rcolor.green = color.green >> 8;
502 rcolor.blue = color.blue >> 8;
503 RGetClosestXColor(rc, &rcolor, &color);
504 } else {
505 rcolor.red = 0;
506 rcolor.green = 0;
507 rcolor.blue = 0;
509 /* for images with a transparent color */
510 if (image->data[3]) {
511 RCombineImageWithColor(image, &rcolor);
514 switch (toupper(type[0])) {
515 case 'T':
516 texture->width = iwidth;
517 texture->height = iheight;
518 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
519 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
520 RReleaseImage(image);
521 goto error;
523 if (image)
524 RReleaseImage(image);
526 texture->pixmap = pixmap;
527 texture->color = color;
528 break;
529 case 'S':
530 case 'M':
531 case 'C':
533 Pixmap tpixmap =
534 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
535 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
536 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
538 texture->pixmap = tpixmap;
539 texture->color = color;
540 texture->width = scrWidth;
541 texture->height = scrHeight;
543 #ifdef XINERAMA
544 if (xineInfo.count && ! xineStretch) {
545 int i;
546 for (i = 0; i < xineInfo.count; ++i) {
547 applyImage(rc, texture, image, type[0],
548 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
549 xineInfo.screens[i].size.width,
550 xineInfo.screens[i].size.height);
552 } else {
553 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
555 #else /* !XINERAMA */
556 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
557 #endif /* !XINERAMA */
558 RReleaseImage(image);
560 break;
562 } else if (strcasecmp(type, "thgradient") == 0
563 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
564 XColor color;
565 RColor color1, color2;
566 RImage *image;
567 RImage *gradient;
568 RImage *tiled;
569 Pixmap pixmap;
570 int opaq;
571 char *file;
572 int gtype;
573 int twidth, theight;
575 GETSTRORGOTO(val, file, 1, error);
577 GETSTRORGOTO(val, tmp, 2, error);
579 opaq = atoi(tmp);
581 GETSTRORGOTO(val, tmp, 3, error);
583 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
584 wwarning("could not parse color %s in texture %s", tmp, text);
585 goto error;
588 color1.red = color.red >> 8;
589 color1.green = color.green >> 8;
590 color1.blue = color.blue >> 8;
592 GETSTRORGOTO(val, tmp, 4, error);
594 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
595 wwarning("could not parse color %s in texture %s", tmp, text);
596 goto error;
599 color2.red = color.red >> 8;
600 color2.green = color.green >> 8;
601 color2.blue = color.blue >> 8;
603 image = loadImage(rc, file);
604 if (!image) {
605 goto error;
608 switch (type[1]) {
609 case 'h':
610 case 'H':
611 gtype = RHorizontalGradient;
612 twidth = scrWidth;
613 theight = image->height > scrHeight ? scrHeight : image->height;
614 break;
615 case 'V':
616 case 'v':
617 gtype = RVerticalGradient;
618 twidth = image->width > scrWidth ? scrWidth : image->width;
619 theight = scrHeight;
620 break;
621 default:
622 gtype = RDiagonalGradient;
623 twidth = scrWidth;
624 theight = scrHeight;
625 break;
627 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
629 if (!gradient) {
630 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
631 RReleaseImage(gradient);
632 RReleaseImage(image);
633 goto error;
636 tiled = RMakeTiledImage(image, twidth, theight);
637 if (!tiled) {
638 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
639 RReleaseImage(gradient);
640 RReleaseImage(image);
641 goto error;
643 RReleaseImage(image);
645 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
646 RReleaseImage(gradient);
648 if (!RConvertImage(rc, tiled, &pixmap)) {
649 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
650 RReleaseImage(tiled);
651 goto error;
653 texture->width = tiled->width;
654 texture->height = tiled->height;
656 RReleaseImage(tiled);
658 texture->pixmap = pixmap;
659 } else if (strcasecmp(type, "function") == 0) {
660 /* Leave this in to handle the unlikely case of
661 * someone actually having function textures configured */
662 wwarning("function texture support has been removed");
663 goto error;
664 } else {
665 wwarning("invalid texture type %s", text);
666 goto error;
669 texture->spec = wstrdup(text);
671 return texture;
673 error:
674 if (texture)
675 wfree(texture);
676 if (texarray)
677 WMReleasePropList(texarray);
679 return NULL;
682 void freeTexture(BackgroundTexture * texture)
684 if (texture->solid) {
685 unsigned long pixel[1];
687 pixel[0] = texture->color.pixel;
688 /* dont free black/white pixels */
689 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
690 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
691 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
693 if (texture->pixmap) {
694 XFreePixmap(dpy, texture->pixmap);
696 wfree(texture->spec);
697 wfree(texture);
700 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
702 BackgroundTexture *newTexture = NULL;
703 int i;
705 /* unset the texture */
706 if (!texture) {
707 if (textures[workspace] != NULL) {
708 textures[workspace]->refcount--;
710 if (textures[workspace]->refcount == 0)
711 freeTexture(textures[workspace]);
713 textures[workspace] = NULL;
714 return;
717 if (textures[workspace]
718 && strcasecmp(textures[workspace]->spec, texture) == 0) {
719 /* texture did not change */
720 return;
723 /* check if the same texture is already created */
724 for (i = 0; i < *maxTextures; i++) {
725 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
726 newTexture = textures[i];
727 break;
731 if (!newTexture) {
732 /* create the texture */
733 newTexture = parseTexture(rc, texture);
735 if (!newTexture)
736 return;
738 if (textures[workspace] != NULL) {
740 textures[workspace]->refcount--;
742 if (textures[workspace]->refcount == 0)
743 freeTexture(textures[workspace]);
746 newTexture->refcount++;
747 textures[workspace] = newTexture;
749 if (*maxTextures < workspace)
750 *maxTextures = workspace;
753 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
755 Display *tmpDpy;
756 Pixmap copyP;
758 /* must open a new display or the RetainPermanent will
759 * leave stuff allocated in RContext unallocated after exit */
760 tmpDpy = XOpenDisplay(display);
761 if (!tmpDpy) {
762 wwarning("could not open display to update background image information");
764 return None;
765 } else {
766 XSync(dpy, False);
768 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
769 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
770 XSync(tmpDpy, False);
772 XSetCloseDownMode(tmpDpy, RetainPermanent);
773 XCloseDisplay(tmpDpy);
776 return copyP;
779 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
781 return 0;
784 void setPixmapProperty(Pixmap pixmap)
786 static Atom prop = 0;
787 Atom type;
788 int format;
789 unsigned long length, after;
790 unsigned char *data;
791 int mode;
793 if (!prop) {
794 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
797 XGrabServer(dpy);
799 /* Clear out the old pixmap */
800 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
801 &type, &format, &length, &after, &data);
803 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
804 XSetErrorHandler(dummyErrorHandler);
805 XKillClient(dpy, *((Pixmap *) data));
806 XSync(dpy, False);
807 XSetErrorHandler(NULL);
808 mode = PropModeReplace;
809 } else {
810 mode = PropModeAppend;
812 if (pixmap)
813 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
814 else
815 XDeleteProperty(dpy, root, prop);
817 XUngrabServer(dpy);
818 XFlush(dpy);
821 void changeTexture(BackgroundTexture * texture)
823 if (!texture) {
824 return;
827 if (texture->solid) {
828 XSetWindowBackground(dpy, root, texture->color.pixel);
829 } else {
830 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
832 XClearWindow(dpy, root);
834 XSync(dpy, False);
837 Pixmap pixmap;
839 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
841 setPixmapProperty(pixmap);
845 int readmsg(int fd, char *buffer, int size)
847 int count;
849 count = 0;
850 while (size > 0) {
851 count = read(fd, buffer, size);
852 if (count < 0)
853 return -1;
854 size -= count;
855 buffer += count;
856 *buffer = 0;
859 return size;
863 * Message Format:
864 * sizeSntexture_spec - sets the texture for workspace n
865 * sizeCn - change background texture to the one for workspace n
866 * sizePpath - set the pixmap search path
868 * n is 4 bytes
869 * size = 4 bytes for length of the message data
871 void helperLoop(RContext * rc)
873 BackgroundTexture *textures[WORKSPACE_COUNT];
874 int maxTextures = 0;
875 char buffer[2048], buf[8];
876 int size;
877 int errcount = 4;
879 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
881 while (1) {
882 int workspace = -1;
884 /* get length of message */
885 if (readmsg(0, buffer, 4) < 0) {
886 werror("error reading message from Window Maker");
887 errcount--;
888 if (errcount == 0) {
889 wfatal("quitting");
890 exit(1);
892 continue;
894 memcpy(buf, buffer, 4);
895 buf[4] = 0;
896 size = atoi(buf);
898 /* get message */
899 if (readmsg(0, buffer, size) < 0) {
900 werror("error reading message from Window Maker");
901 errcount--;
902 if (errcount == 0) {
903 wfatal("quitting");
904 exit(1);
906 continue;
908 #ifdef DEBUG
909 printf("RECEIVED %s\n", buffer);
910 #endif
911 if (buffer[0] != 'P' && buffer[0] != 'K') {
912 memcpy(buf, &buffer[1], 4);
913 buf[4] = 0;
914 workspace = atoi(buf);
915 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
916 wwarning("received message with invalid workspace number %i", workspace);
917 continue;
921 switch (buffer[0]) {
922 case 'S':
923 #ifdef DEBUG
924 printf("set texture %s\n", &buffer[5]);
925 #endif
926 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
927 break;
929 case 'C':
930 #ifdef DEBUG
931 printf("change texture %i\n", workspace);
932 #endif
933 if (!textures[workspace]) {
934 changeTexture(textures[0]);
935 } else {
936 changeTexture(textures[workspace]);
938 break;
940 case 'P':
941 #ifdef DEBUG
942 printf("change pixmappath %s\n", &buffer[1]);
943 #endif
944 if (PixmapPath)
945 wfree(PixmapPath);
946 PixmapPath = wstrdup(&buffer[1]);
947 break;
949 case 'U':
950 #ifdef DEBUG
951 printf("unset workspace %i\n", workspace);
952 #endif
953 setupTexture(rc, textures, &maxTextures, workspace, NULL);
954 break;
956 case 'K':
957 #ifdef DEBUG
958 printf("exit command\n");
959 #endif
960 exit(0);
962 default:
963 wwarning("unknown message received");
964 break;
969 void updateDomain(char *domain, char *key, char *texture)
971 char *program = "wdwrite";
973 /* here is a mem leak */
974 system(wstrconcat("wdwrite ",
975 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
977 execlp(program, program, domain, key, texture, NULL);
978 wwarning("warning could not run \"%s\"", program);
981 static WMPropList *getValueForKey(char *domain, char *keyName)
983 char *path;
984 WMPropList *key, *val, *d;
986 key = WMCreatePLString(keyName);
988 /* try to find PixmapPath in user defaults */
989 path = wdefaultspathfordomain(domain);
990 d = WMReadPropListFromFile(path);
991 if (!d) {
992 wwarning("could not open domain file %s", path);
994 wfree(path);
996 if (d && !WMIsPLDictionary(d)) {
997 WMReleasePropList(d);
998 d = NULL;
1000 if (d) {
1001 val = WMGetFromPLDictionary(d, key);
1002 } else {
1003 val = NULL;
1005 /* try to find PixmapPath in global defaults */
1006 if (!val) {
1007 path = wglobaldefaultspathfordomain(domain);
1008 if (!path) {
1009 wwarning("could not locate file for domain %s", domain);
1010 d = NULL;
1011 } else {
1012 d = WMReadPropListFromFile(path);
1013 wfree(path);
1016 if (d && !WMIsPLDictionary(d)) {
1017 WMReleasePropList(d);
1018 d = NULL;
1020 if (d) {
1021 val = WMGetFromPLDictionary(d, key);
1023 } else {
1024 val = NULL;
1028 if (val)
1029 WMRetainPropList(val);
1031 WMReleasePropList(key);
1032 if (d)
1033 WMReleasePropList(d);
1035 return val;
1038 char *getPixmapPath(char *domain)
1040 WMPropList *val;
1041 char *ptr, *data;
1042 int len, i, count;
1044 val = getValueForKey(domain, "PixmapPath");
1046 if (!val || !WMIsPLArray(val)) {
1047 if (val)
1048 WMReleasePropList(val);
1049 return wstrdup("");
1052 count = WMGetPropListItemCount(val);
1053 len = 0;
1054 for (i = 0; i < count; i++) {
1055 WMPropList *v;
1057 v = WMGetFromPLArray(val, i);
1058 if (!v || !WMIsPLString(v)) {
1059 continue;
1061 len += strlen(WMGetFromPLString(v)) + 1;
1064 ptr = data = wmalloc(len + 1);
1065 *ptr = 0;
1067 for (i = 0; i < count; i++) {
1068 WMPropList *v;
1070 v = WMGetFromPLArray(val, i);
1071 if (!v || !WMIsPLString(v)) {
1072 continue;
1074 strcpy(ptr, WMGetFromPLString(v));
1076 ptr += strlen(WMGetFromPLString(v));
1077 *ptr = ':';
1078 ptr++;
1080 if (i > 0)
1081 ptr--;
1082 *(ptr--) = 0;
1084 WMReleasePropList(val);
1086 return data;
1089 char *getFullPixmapPath(char *file)
1091 char *tmp;
1093 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1094 int bsize = 512;
1095 char *path = wmalloc(bsize);
1097 while (!getcwd(path, bsize)) {
1098 bsize += bsize / 2;
1099 path = wrealloc(path, bsize);
1102 tmp = wstrconcat(path, "/");
1103 wfree(path);
1104 path = wstrconcat(tmp, file);
1105 wfree(tmp);
1107 return path;
1110 /* the file is in the PixmapPath */
1111 wfree(tmp);
1113 return wstrdup(file);
1116 void wAbort()
1118 wfatal("aborting");
1119 exit(1);
1122 void print_help()
1124 printf("Usage: %s [options] [image]\n", __progname);
1125 puts("Sets the workspace background to the specified image or a texture and");
1126 puts("optionally update Window Maker configuration");
1127 puts("");
1128 puts(" -display display to use");
1129 puts(" -d, --dither dither image");
1130 puts(" -m, --match match colors");
1131 puts(" -S, --smooth smooth scaled image");
1132 #ifdef XINERAMA
1133 puts(" -X, --xinerama stretch image across Xinerama heads");
1134 #endif
1135 puts(" -b, --back-color <color> background color");
1136 puts(" -t, --tile tile image");
1137 puts(" -e, --center center image");
1138 puts(" -s, --scale scale image (default)");
1139 puts(" -a, --maxscale scale image and keep aspect ratio");
1140 puts(" -u, --update-wmaker update WindowMaker domain database");
1141 puts(" -D, --update-domain <domain> update <domain> database");
1142 puts(" -c, --colors <cpc> colors per channel to use");
1143 puts(" -p, --parse <texture> proplist style texture specification");
1144 puts(" -w, --workspace <workspace> update background for the specified workspace");
1145 puts(" -v, --version show version of wmsetbg and exit");
1146 puts(" -h, --help show this help and exit");
1149 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1151 WMPropList *array, *val;
1152 char *value;
1153 int j;
1155 val = WMCreatePropListFromDescription(texture);
1156 if (!val) {
1157 wwarning("could not parse texture %s", texture);
1158 return;
1161 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1163 if (!array) {
1164 array = WMCreatePLArray(NULL, NULL);
1167 j = WMGetPropListItemCount(array);
1168 if (workspace >= j) {
1169 WMPropList *empty;
1171 empty = WMCreatePLArray(NULL, NULL);
1173 while (j++ < workspace - 1) {
1174 WMAddToPLArray(array, empty);
1176 WMAddToPLArray(array, val);
1177 } else {
1178 WMDeleteFromPLArray(array, workspace);
1179 WMInsertInPLArray(array, workspace, val);
1182 value = WMGetPropListDescription(array, False);
1183 updateDomain(domain, "WorkspaceSpecificBack", value);
1186 int main(int argc, char **argv)
1188 int i;
1189 int helperMode = 0;
1190 RContext *rc;
1191 RContextAttributes rattr;
1192 char *style = "spixmap";
1193 char *back_color = "gray20";
1194 char *image_name = NULL;
1195 char *domain = "WindowMaker";
1196 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1197 char *texture = NULL;
1198 int workspace = -1;
1200 signal(SIGINT, SIG_DFL);
1201 signal(SIGTERM, SIG_DFL);
1202 signal(SIGQUIT, SIG_DFL);
1203 signal(SIGSEGV, SIG_DFL);
1204 signal(SIGBUS, SIG_DFL);
1205 signal(SIGFPE, SIG_DFL);
1206 signal(SIGABRT, SIG_DFL);
1207 signal(SIGHUP, SIG_DFL);
1208 signal(SIGPIPE, SIG_DFL);
1209 signal(SIGCHLD, SIG_DFL);
1211 WMInitializeApplication("wmsetbg", &argc, argv);
1213 for (i = 1; i < argc; i++) {
1214 if (strcmp(argv[i], "-helper") == 0) {
1215 helperMode = 1;
1216 } else if (strcmp(argv[i], "-display") == 0) {
1217 i++;
1218 if (i >= argc) {
1219 wfatal("too few arguments for %s", argv[i - 1]);
1220 exit(1);
1222 display = argv[i];
1223 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1224 style = "spixmap";
1225 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1226 style = "tpixmap";
1227 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1228 style = "cpixmap";
1229 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1230 style = "mpixmap";
1231 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1232 render_mode = RDitheredRendering;
1233 obey_user++;
1234 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1235 render_mode = RBestMatchRendering;
1236 obey_user++;
1237 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1238 smooth = True;
1239 #ifdef XINERAMA
1240 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1241 xineStretch = True;
1242 #endif
1243 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1244 update++;
1245 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1246 update++;
1247 i++;
1248 if (i >= argc) {
1249 wfatal("too few arguments for %s", argv[i - 1]);
1250 exit(1);
1252 domain = wstrdup(argv[i]);
1253 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1254 i++;
1255 if (i >= argc) {
1256 wfatal("too few arguments for %s", argv[i - 1]);
1257 exit(1);
1259 if (sscanf(argv[i], "%i", &cpc) != 1) {
1260 wfatal("bad value for colors per channel: \"%s\"", argv[i]);
1261 exit(1);
1263 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1264 i++;
1265 if (i >= argc) {
1266 wfatal("too few arguments for %s", argv[i - 1]);
1267 exit(1);
1269 back_color = argv[i];
1270 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1271 i++;
1272 if (i >= argc) {
1273 wfatal("too few arguments for %s", argv[i - 1]);
1274 exit(1);
1276 texture = argv[i];
1277 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1278 i++;
1279 if (i >= argc) {
1280 wfatal("too few arguments for %s", argv[i - 1]);
1281 exit(1);
1283 if (sscanf(argv[i], "%i", &workspace) != 1) {
1284 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1285 exit(1);
1287 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1288 printf("%s (Window Maker %s)\n", __progname, VERSION);
1289 exit(0);
1290 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1291 print_help();
1292 exit(0);
1293 } else if (argv[i][0] != '-') {
1294 image_name = argv[i];
1295 } else {
1296 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1297 printf("Try '%s --help' for more information\n", __progname);
1298 exit(1);
1301 if (!image_name && !texture && !helperMode) {
1302 printf("%s: you must specify a image file name or a texture\n", __progname);
1303 printf("Try '%s --help' for more information\n", __progname);
1304 exit(1);
1307 PixmapPath = getPixmapPath(domain);
1308 if (!smooth) {
1309 WMPropList *val;
1310 /* carlos, don't remove this */
1311 #if 0 /* some problem with Alpha... TODO: check if its right */
1312 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1313 #else
1314 val = getValueForKey(domain, "SmoothWorkspaceBack");
1315 #endif
1317 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1318 smooth = True;
1321 dpy = XOpenDisplay(display);
1322 if (!dpy) {
1323 wfatal("could not open display");
1324 exit(1);
1326 #if 0
1327 XSynchronize(dpy, 1);
1328 #endif
1330 root = DefaultRootWindow(dpy);
1332 scr = DefaultScreen(dpy);
1334 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1335 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1336 scrX = scrY = 0;
1338 initXinerama();
1340 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1341 render_mode = RDitheredRendering;
1343 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1344 rattr.render_mode = render_mode;
1345 rattr.colors_per_channel = cpc;
1346 rattr.standard_colormap_mode = RCreateStdColormap;
1348 rc = RCreateContext(dpy, scr, &rattr);
1350 if (!rc) {
1351 rattr.standard_colormap_mode = RIgnoreStdColormap;
1352 rc = RCreateContext(dpy, scr, &rattr);
1355 if (!rc) {
1356 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1357 exit(1);
1360 if (helperMode) {
1361 /* lower priority, so that it wont use all the CPU */
1362 nice(15);
1364 helperLoop(rc);
1365 } else {
1366 BackgroundTexture *tex;
1367 char buffer[4098];
1369 if (!texture) {
1370 char *image_path = getFullPixmapPath(image_name);
1372 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1373 wfree(image_path);
1374 texture = (char *)buffer;
1377 if (update && workspace < 0) {
1378 updateDomain(domain, "WorkspaceBack", texture);
1381 tex = parseTexture(rc, texture);
1382 if (!tex)
1383 exit(1);
1385 if (workspace < 0)
1386 changeTexture(tex);
1387 else {
1388 /* always update domain */
1389 changeTextureForWorkspace(domain, texture, workspace);
1393 return 0;