Remove texture plugins
[wmaker-crm.git] / util / wmsetbg.c
blob312e6195feeb58cd644577d237102444ab18fce8
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
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22 * USA.
26 * TODO: rewrite, too dirty
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xatom.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <ctype.h>
40 #include "../src/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 PROG_VERSION "wmsetbg (Window Maker) 2.8"
66 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
68 Display *dpy;
69 char *display = "";
70 Window root;
71 int scr;
72 int scrWidth;
73 int scrHeight;
74 int scrX, scrY;
76 WXineramaInfo xineInfo;
78 Bool smooth = False;
79 #ifdef XINERAMA
80 Bool xineStretch = False;
81 #endif
83 Pixmap CurrentPixmap = None;
84 char *PixmapPath = NULL;
86 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
87 extern char *__progname;
89 typedef struct BackgroundTexture {
90 int refcount;
92 int solid;
94 char *spec;
96 XColor color;
97 Pixmap pixmap; /* for all textures, including solid */
98 int width; /* size of the pixmap */
99 int height;
100 } BackgroundTexture;
102 void initXinerama()
104 xineInfo.screens = NULL;
105 xineInfo.count = 0;
106 #ifdef XINERAMA
107 # ifdef SOLARIS_XINERAMA
108 if (XineramaGetState(dpy, scr)) {
109 XRectangle head[MAXFRAMEBUFFERS];
110 unsigned char hints[MAXFRAMEBUFFERS];
111 int i;
113 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
115 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
117 for (i = 0; i < xineInfo.count; i++) {
118 xineInfo.screens[i].pos.x = head[i].x;
119 xineInfo.screens[i].pos.y = head[i].y;
120 xineInfo.screens[i].size.width = head[i].width;
121 xineInfo.screens[i].size.height = head[i].height;
125 # else /* !SOLARIS_XINERAMA */
126 if (XineramaIsActive(dpy)) {
127 XineramaScreenInfo *xine_screens;
128 int i;
130 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
132 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
134 for (i = 0; i < xineInfo.count; i++) {
135 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
136 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
137 xineInfo.screens[i].size.width = xine_screens[i].width;
138 xineInfo.screens[i].size.height = xine_screens[i].height;
140 XFree(xine_screens);
142 # endif /* !SOLARIS_XINERAMA */
143 #endif /* XINERAMA */
146 RImage *loadImage(RContext * rc, char *file)
148 char *path;
149 RImage *image;
151 if (access(file, F_OK) != 0) {
152 path = wfindfile(PixmapPath, file);
153 if (!path) {
154 wwarning("%s:could not find image file used in texture", file);
155 return NULL;
157 } else {
158 path = wstrdup(file);
161 image = RLoadImage(rc, path, 0);
162 if (!image) {
163 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
165 wfree(path);
167 return image;
170 static void
171 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
172 int x, int y, int width, int height)
174 int w, h;
175 Bool fimage = False;
177 switch (toupper(type)) {
178 case 'S':
179 case 'M':
180 if (toupper(type) == 'S') {
181 w = width;
182 h = height;
183 } else {
184 if (image->width * height > image->height * width) {
185 w = width;
186 h = (width * image->height) / image->width;
187 } else {
188 w = (height * image->width) / image->height;
189 h = height;
193 if (w != image->width || h != image->height) {
194 RImage *simage;
196 if (smooth) {
197 simage = RSmoothScaleImage(image, w, h);
198 } else {
199 simage = RScaleImage(image, w, h);
202 if (!simage) {
203 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
204 return;
206 fimage = True;
207 image = simage;
210 /* fall through */
211 case 'C':
213 Pixmap pixmap;
215 if (!RConvertImage(rc, image, &pixmap)) {
216 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
217 return;
220 if (image->width != width || image->height != height) {
221 int sx, sy, w, h;
223 if (image->height < height) {
224 h = image->height;
225 y += (height - h) / 2;
226 sy = 0;
227 } else {
228 sy = (image->height - height) / 2;
229 h = height;
231 if (image->width < width) {
232 w = image->width;
233 x += (width - w) / 2;
234 sx = 0;
235 } else {
236 sx = (image->width - width) / 2;
237 w = width;
240 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
241 } else
242 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
243 x, y);
245 XFreePixmap(dpy, pixmap);
246 if (fimage) {
247 RReleaseImage(image);
250 break;
254 BackgroundTexture *parseTexture(RContext * rc, char *text)
256 BackgroundTexture *texture = NULL;
257 WMPropList *texarray;
258 WMPropList *val;
259 int count;
260 char *tmp;
261 char *type;
263 #define GETSTRORGOTO(val, str, i, label) \
264 val = WMGetFromPLArray(texarray, i);\
265 if (!WMIsPLString(val)) {\
266 wwarning("could not parse texture %s", text);\
267 goto label;\
269 str = WMGetFromPLString(val)
271 texarray = WMCreatePropListFromDescription(text);
272 if (!texarray || !WMIsPLArray(texarray)
273 || (count = WMGetPropListItemCount(texarray)) < 2) {
275 wwarning("could not parse texture %s", text);
276 if (texarray)
277 WMReleasePropList(texarray);
278 return NULL;
281 texture = wmalloc(sizeof(BackgroundTexture));
282 memset(texture, 0, sizeof(BackgroundTexture));
284 GETSTRORGOTO(val, type, 0, error);
286 if (strcasecmp(type, "solid") == 0) {
287 XColor color;
288 Pixmap pixmap;
290 texture->solid = 1;
292 GETSTRORGOTO(val, tmp, 1, error);
294 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
295 wwarning("could not parse color %s in texture %s", tmp, text);
296 goto error;
298 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
300 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
301 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
302 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
304 texture->pixmap = pixmap;
305 texture->color = color;
306 texture->width = 8;
307 texture->height = 8;
308 } else if (strcasecmp(type, "vgradient") == 0
309 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
310 XColor color;
311 RColor color1, color2;
312 RImage *image;
313 Pixmap pixmap;
314 int gtype;
315 int iwidth, iheight;
317 GETSTRORGOTO(val, tmp, 1, error);
319 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
320 wwarning("could not parse color %s in texture %s", tmp, text);
321 goto error;
324 color1.red = color.red >> 8;
325 color1.green = color.green >> 8;
326 color1.blue = color.blue >> 8;
328 GETSTRORGOTO(val, tmp, 2, error);
330 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
331 wwarning("could not parse color %s in texture %s", tmp, text);
332 goto error;
335 color2.red = color.red >> 8;
336 color2.green = color.green >> 8;
337 color2.blue = color.blue >> 8;
339 switch (type[0]) {
340 case 'h':
341 case 'H':
342 gtype = RHorizontalGradient;
343 iwidth = scrWidth;
344 iheight = 32;
345 break;
346 case 'V':
347 case 'v':
348 gtype = RVerticalGradient;
349 iwidth = 32;
350 iheight = scrHeight;
351 break;
352 default:
353 gtype = RDiagonalGradient;
354 iwidth = scrWidth;
355 iheight = scrHeight;
356 break;
359 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
361 if (!image) {
362 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
363 goto error;
366 if (!RConvertImage(rc, image, &pixmap)) {
367 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
368 RReleaseImage(image);
369 goto error;
372 texture->width = image->width;
373 texture->height = image->height;
374 RReleaseImage(image);
376 texture->pixmap = pixmap;
377 } else if (strcasecmp(type, "mvgradient") == 0
378 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
379 XColor color;
380 RColor **colors;
381 RImage *image;
382 Pixmap pixmap;
383 int i, j;
384 int gtype;
385 int iwidth, iheight;
387 colors = malloc(sizeof(RColor *) * (count - 1));
388 if (!colors) {
389 wwarning("out of memory while parsing texture");
390 goto error;
392 memset(colors, 0, sizeof(RColor *) * (count - 1));
394 for (i = 2; i < count; i++) {
395 val = WMGetFromPLArray(texarray, i);
396 if (!WMIsPLString(val)) {
397 wwarning("could not parse texture %s", text);
399 for (j = 0; colors[j] != NULL; j++)
400 wfree(colors[j]);
401 wfree(colors);
402 goto error;
404 tmp = WMGetFromPLString(val);
406 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
407 wwarning("could not parse color %s in texture %s", tmp, text);
409 for (j = 0; colors[j] != NULL; j++)
410 wfree(colors[j]);
411 wfree(colors);
412 goto error;
414 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
415 wwarning("out of memory while parsing texture");
417 for (j = 0; colors[j] != NULL; j++)
418 wfree(colors[j]);
419 wfree(colors);
420 goto error;
423 colors[i - 2]->red = color.red >> 8;
424 colors[i - 2]->green = color.green >> 8;
425 colors[i - 2]->blue = color.blue >> 8;
428 switch (type[1]) {
429 case 'h':
430 case 'H':
431 gtype = RHorizontalGradient;
432 iwidth = scrWidth;
433 iheight = 32;
434 break;
435 case 'V':
436 case 'v':
437 gtype = RVerticalGradient;
438 iwidth = 32;
439 iheight = scrHeight;
440 break;
441 default:
442 gtype = RDiagonalGradient;
443 iwidth = scrWidth;
444 iheight = scrHeight;
445 break;
448 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
450 for (j = 0; colors[j] != NULL; j++)
451 wfree(colors[j]);
452 wfree(colors);
454 if (!image) {
455 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
456 goto error;
459 if (!RConvertImage(rc, image, &pixmap)) {
460 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
461 RReleaseImage(image);
462 goto error;
465 texture->width = image->width;
466 texture->height = image->height;
467 RReleaseImage(image);
469 texture->pixmap = pixmap;
470 } else if (strcasecmp(type, "cpixmap") == 0
471 || strcasecmp(type, "spixmap") == 0
472 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
473 XColor color;
474 Pixmap pixmap = None;
475 RImage *image = NULL;
476 int iwidth, iheight;
477 RColor rcolor;
479 GETSTRORGOTO(val, tmp, 1, error);
481 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
482 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
485 if (!pixmap) {
486 image = loadImage(rc, tmp);
487 if (!image) {
488 goto error;
490 iwidth = image->width;
491 iheight = image->height;
494 GETSTRORGOTO(val, tmp, 2, error);
496 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
497 wwarning("could not parse color %s in texture %s\n", tmp, text);
498 RReleaseImage(image);
499 goto error;
501 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
502 rcolor.red = color.red >> 8;
503 rcolor.green = color.green >> 8;
504 rcolor.blue = color.blue >> 8;
505 RGetClosestXColor(rc, &rcolor, &color);
506 } else {
507 rcolor.red = 0;
508 rcolor.green = 0;
509 rcolor.blue = 0;
511 /* for images with a transparent color */
512 if (image->data[3]) {
513 RCombineImageWithColor(image, &rcolor);
516 switch (toupper(type[0])) {
517 case 'T':
518 texture->width = iwidth;
519 texture->height = iheight;
520 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
521 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
522 RReleaseImage(image);
523 goto error;
525 if (image)
526 RReleaseImage(image);
528 texture->pixmap = pixmap;
529 texture->color = color;
530 break;
531 case 'S':
532 case 'M':
533 case 'C':
535 Pixmap tpixmap =
536 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
537 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
538 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
540 texture->pixmap = tpixmap;
541 texture->color = color;
542 texture->width = scrWidth;
543 texture->height = scrHeight;
545 #ifdef XINERAMA
546 if (xineInfo.count && ! xineStretch) {
547 int i;
548 for (i = 0; i < xineInfo.count; ++i) {
549 applyImage(rc, texture, image, type[0],
550 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
551 xineInfo.screens[i].size.width,
552 xineInfo.screens[i].size.height);
554 } else {
555 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
557 #else /* !XINERAMA */
558 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
559 #endif /* !XINERAMA */
560 RReleaseImage(image);
562 break;
564 } else if (strcasecmp(type, "thgradient") == 0
565 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
566 XColor color;
567 RColor color1, color2;
568 RImage *image;
569 RImage *gradient;
570 RImage *tiled;
571 Pixmap pixmap;
572 int opaq;
573 char *file;
574 int gtype;
575 int twidth, theight;
577 GETSTRORGOTO(val, file, 1, error);
579 GETSTRORGOTO(val, tmp, 2, error);
581 opaq = atoi(tmp);
583 GETSTRORGOTO(val, tmp, 3, error);
585 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
586 wwarning("could not parse color %s in texture %s", tmp, text);
587 goto error;
590 color1.red = color.red >> 8;
591 color1.green = color.green >> 8;
592 color1.blue = color.blue >> 8;
594 GETSTRORGOTO(val, tmp, 4, error);
596 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
597 wwarning("could not parse color %s in texture %s", tmp, text);
598 goto error;
601 color2.red = color.red >> 8;
602 color2.green = color.green >> 8;
603 color2.blue = color.blue >> 8;
605 image = loadImage(rc, file);
606 if (!image) {
607 goto error;
610 switch (type[1]) {
611 case 'h':
612 case 'H':
613 gtype = RHorizontalGradient;
614 twidth = scrWidth;
615 theight = image->height > scrHeight ? scrHeight : image->height;
616 break;
617 case 'V':
618 case 'v':
619 gtype = RVerticalGradient;
620 twidth = image->width > scrWidth ? scrWidth : image->width;
621 theight = scrHeight;
622 break;
623 default:
624 gtype = RDiagonalGradient;
625 twidth = scrWidth;
626 theight = scrHeight;
627 break;
629 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
631 if (!gradient) {
632 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
633 RReleaseImage(gradient);
634 RReleaseImage(image);
635 goto error;
638 tiled = RMakeTiledImage(image, twidth, theight);
639 if (!tiled) {
640 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
641 RReleaseImage(gradient);
642 RReleaseImage(image);
643 goto error;
645 RReleaseImage(image);
647 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
648 RReleaseImage(gradient);
650 if (!RConvertImage(rc, tiled, &pixmap)) {
651 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
652 RReleaseImage(tiled);
653 goto error;
655 texture->width = tiled->width;
656 texture->height = tiled->height;
658 RReleaseImage(tiled);
660 texture->pixmap = pixmap;
661 } else if (strcasecmp(type, "function") == 0) {
662 /* Leave this in to handle the unlikely case of
663 * someone actually having function textures configured */
664 wwarning("function texture support has been removed");
665 goto error;
666 } else {
667 wwarning("invalid texture type %s", text);
668 goto error;
671 texture->spec = wstrdup(text);
673 return texture;
675 error:
676 if (texture)
677 wfree(texture);
678 if (texarray)
679 WMReleasePropList(texarray);
681 return NULL;
684 void freeTexture(BackgroundTexture * texture)
686 if (texture->solid) {
687 unsigned long pixel[1];
689 pixel[0] = texture->color.pixel;
690 /* dont free black/white pixels */
691 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
692 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
693 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
695 if (texture->pixmap) {
696 XFreePixmap(dpy, texture->pixmap);
698 wfree(texture->spec);
699 wfree(texture);
702 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
704 BackgroundTexture *newTexture = NULL;
705 int i;
707 /* unset the texture */
708 if (!texture) {
709 if (textures[workspace] != NULL) {
710 textures[workspace]->refcount--;
712 if (textures[workspace]->refcount == 0)
713 freeTexture(textures[workspace]);
715 textures[workspace] = NULL;
716 return;
719 if (textures[workspace]
720 && strcasecmp(textures[workspace]->spec, texture) == 0) {
721 /* texture did not change */
722 return;
725 /* check if the same texture is already created */
726 for (i = 0; i < *maxTextures; i++) {
727 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
728 newTexture = textures[i];
729 break;
733 if (!newTexture) {
734 /* create the texture */
735 newTexture = parseTexture(rc, texture);
737 if (!newTexture)
738 return;
740 if (textures[workspace] != NULL) {
742 textures[workspace]->refcount--;
744 if (textures[workspace]->refcount == 0)
745 freeTexture(textures[workspace]);
748 newTexture->refcount++;
749 textures[workspace] = newTexture;
751 if (*maxTextures < workspace)
752 *maxTextures = workspace;
755 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
757 Display *tmpDpy;
758 Pixmap copyP;
760 /* must open a new display or the RetainPermanent will
761 * leave stuff allocated in RContext unallocated after exit */
762 tmpDpy = XOpenDisplay(display);
763 if (!tmpDpy) {
764 wwarning("could not open display to update background image information");
766 return None;
767 } else {
768 XSync(dpy, False);
770 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
771 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
772 XSync(tmpDpy, False);
774 XSetCloseDownMode(tmpDpy, RetainPermanent);
775 XCloseDisplay(tmpDpy);
778 return copyP;
781 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
783 return 0;
786 void setPixmapProperty(Pixmap pixmap)
788 static Atom prop = 0;
789 Atom type;
790 int format;
791 unsigned long length, after;
792 unsigned char *data;
793 int mode;
795 if (!prop) {
796 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
799 XGrabServer(dpy);
801 /* Clear out the old pixmap */
802 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
803 &type, &format, &length, &after, &data);
805 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
806 XSetErrorHandler(dummyErrorHandler);
807 XKillClient(dpy, *((Pixmap *) data));
808 XSync(dpy, False);
809 XSetErrorHandler(NULL);
810 mode = PropModeReplace;
811 } else {
812 mode = PropModeAppend;
814 if (pixmap)
815 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
816 else
817 XDeleteProperty(dpy, root, prop);
819 XUngrabServer(dpy);
820 XFlush(dpy);
823 void changeTexture(BackgroundTexture * texture)
825 if (!texture) {
826 return;
829 if (texture->solid) {
830 XSetWindowBackground(dpy, root, texture->color.pixel);
831 } else {
832 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
834 XClearWindow(dpy, root);
836 XSync(dpy, False);
839 Pixmap pixmap;
841 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
843 setPixmapProperty(pixmap);
847 int readmsg(int fd, char *buffer, int size)
849 int count;
851 count = 0;
852 while (size > 0) {
853 count = read(fd, buffer, size);
854 if (count < 0)
855 return -1;
856 size -= count;
857 buffer += count;
858 *buffer = 0;
861 return size;
865 * Message Format:
866 * sizeSntexture_spec - sets the texture for workspace n
867 * sizeCn - change background texture to the one for workspace n
868 * sizePpath - set the pixmap search path
870 * n is 4 bytes
871 * size = 4 bytes for length of the message data
873 void helperLoop(RContext * rc)
875 BackgroundTexture *textures[WORKSPACE_COUNT];
876 int maxTextures = 0;
877 char buffer[2048], buf[8];
878 int size;
879 int errcount = 4;
881 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
883 while (1) {
884 int workspace;
886 /* get length of message */
887 if (readmsg(0, buffer, 4) < 0) {
888 wsyserror("error reading message from Window Maker");
889 errcount--;
890 if (errcount == 0) {
891 wfatal("quitting");
892 exit(1);
894 continue;
896 memcpy(buf, buffer, 4);
897 buf[4] = 0;
898 size = atoi(buf);
900 /* get message */
901 if (readmsg(0, buffer, size) < 0) {
902 wsyserror("error reading message from Window Maker");
903 errcount--;
904 if (errcount == 0) {
905 wfatal("quitting");
906 exit(1);
908 continue;
910 #ifdef DEBUG
911 printf("RECEIVED %s\n", buffer);
912 #endif
913 if (buffer[0] != 'P' && buffer[0] != 'K') {
914 memcpy(buf, &buffer[1], 4);
915 buf[4] = 0;
916 workspace = atoi(buf);
917 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
918 wwarning("received message with invalid workspace number %i\n", workspace);
919 continue;
923 switch (buffer[0]) {
924 case 'S':
925 #ifdef DEBUG
926 printf("set texture %s\n", &buffer[5]);
927 #endif
928 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
929 break;
931 case 'C':
932 #ifdef DEBUG
933 printf("change texture %i\n", workspace);
934 #endif
935 if (!textures[workspace]) {
936 changeTexture(textures[0]);
937 } else {
938 changeTexture(textures[workspace]);
940 break;
942 case 'P':
943 #ifdef DEBUG
944 printf("change pixmappath %s\n", &buffer[1]);
945 #endif
946 if (PixmapPath)
947 wfree(PixmapPath);
948 PixmapPath = wstrdup(&buffer[1]);
949 break;
951 case 'U':
952 #ifdef DEBUG
953 printf("unset workspace %i\n", workspace);
954 #endif
955 setupTexture(rc, textures, &maxTextures, workspace, NULL);
956 break;
958 case 'K':
959 #ifdef DEBUG
960 printf("exit command\n");
961 #endif
962 exit(0);
964 default:
965 wwarning("unknown message received");
966 break;
971 void updateDomain(char *domain, char *key, char *texture)
973 char *program = "wdwrite";
975 /* here is a mem leak */
976 system(wstrconcat("wdwrite ",
977 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
979 execlp(program, program, domain, key, texture, NULL);
980 wwarning("warning could not run \"%s\"", program);
983 static WMPropList *getValueForKey(char *domain, char *keyName)
985 char *path;
986 WMPropList *key, *val, *d;
988 key = WMCreatePLString(keyName);
990 /* try to find PixmapPath in user defaults */
991 path = wdefaultspathfordomain(domain);
992 d = WMReadPropListFromFile(path);
993 if (!d) {
994 wwarning("could not open domain file %s", path);
996 wfree(path);
998 if (d && !WMIsPLDictionary(d)) {
999 WMReleasePropList(d);
1000 d = NULL;
1002 if (d) {
1003 val = WMGetFromPLDictionary(d, key);
1004 } else {
1005 val = NULL;
1007 /* try to find PixmapPath in global defaults */
1008 if (!val) {
1009 path = wglobaldefaultspathfordomain(domain);
1010 if (!path) {
1011 wwarning("could not locate file for domain %s", domain);
1012 d = NULL;
1013 } else {
1014 d = WMReadPropListFromFile(path);
1015 wfree(path);
1018 if (d && !WMIsPLDictionary(d)) {
1019 WMReleasePropList(d);
1020 d = NULL;
1022 if (d) {
1023 val = WMGetFromPLDictionary(d, key);
1025 } else {
1026 val = NULL;
1030 if (val)
1031 WMRetainPropList(val);
1033 WMReleasePropList(key);
1034 if (d)
1035 WMReleasePropList(d);
1037 return val;
1040 char *getPixmapPath(char *domain)
1042 WMPropList *val;
1043 char *ptr, *data;
1044 int len, i, count;
1046 val = getValueForKey(domain, "PixmapPath");
1048 if (!val || !WMIsPLArray(val)) {
1049 if (val)
1050 WMReleasePropList(val);
1051 return wstrdup("");
1054 count = WMGetPropListItemCount(val);
1055 len = 0;
1056 for (i = 0; i < count; i++) {
1057 WMPropList *v;
1059 v = WMGetFromPLArray(val, i);
1060 if (!v || !WMIsPLString(v)) {
1061 continue;
1063 len += strlen(WMGetFromPLString(v)) + 1;
1066 ptr = data = wmalloc(len + 1);
1067 *ptr = 0;
1069 for (i = 0; i < count; i++) {
1070 WMPropList *v;
1072 v = WMGetFromPLArray(val, i);
1073 if (!v || !WMIsPLString(v)) {
1074 continue;
1076 strcpy(ptr, WMGetFromPLString(v));
1078 ptr += strlen(WMGetFromPLString(v));
1079 *ptr = ':';
1080 ptr++;
1082 if (i > 0)
1083 ptr--;
1084 *(ptr--) = 0;
1086 WMReleasePropList(val);
1088 return data;
1091 char *getFullPixmapPath(char *file)
1093 char *tmp;
1095 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1096 int bsize = 512;
1097 char *path = wmalloc(bsize);
1099 while (!getcwd(path, bsize)) {
1100 bsize += bsize / 2;
1101 path = wrealloc(path, bsize);
1104 tmp = wstrconcat(path, "/");
1105 wfree(path);
1106 path = wstrconcat(tmp, file);
1107 wfree(tmp);
1109 return path;
1112 /* the file is in the PixmapPath */
1113 wfree(tmp);
1115 return wstrdup(file);
1118 void wAbort()
1120 wfatal("aborting");
1121 exit(1);
1124 void print_help()
1126 printf("Usage: %s [options] [image]\n", __progname);
1127 puts("Sets the workspace background to the specified image or a texture and");
1128 puts("optionally update Window Maker configuration");
1129 puts("");
1130 puts(" -display display to use");
1131 puts(" -d, --dither dither image");
1132 puts(" -m, --match match colors");
1133 puts(" -S, --smooth smooth scaled image");
1134 #ifdef XINERAMA
1135 puts(" -X, --xinerama stretch image across Xinerama heads");
1136 #endif
1137 puts(" -b, --back-color <color> background color");
1138 puts(" -t, --tile tile image");
1139 puts(" -e, --center center image");
1140 puts(" -s, --scale scale image (default)");
1141 puts(" -a, --maxscale scale image and keep aspect ratio");
1142 puts(" -u, --update-wmaker update WindowMaker domain database");
1143 puts(" -D, --update-domain <domain> update <domain> database");
1144 puts(" -c, --colors <cpc> colors per channel to use");
1145 puts(" -p, --parse <texture> proplist style texture specification");
1146 puts(" -w, --workspace <workspace> update background for the specified workspace");
1147 puts(" -v, --version show version of wmsetbg and exit");
1148 puts(" -h, --help show this help and exit");
1151 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1153 WMPropList *array, *val;
1154 char *value;
1155 int j;
1157 val = WMCreatePropListFromDescription(texture);
1158 if (!val) {
1159 wwarning("could not parse texture %s", texture);
1160 return;
1163 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1165 if (!array) {
1166 array = WMCreatePLArray(NULL, NULL);
1169 j = WMGetPropListItemCount(array);
1170 if (workspace >= j) {
1171 WMPropList *empty;
1173 empty = WMCreatePLArray(NULL, NULL);
1175 while (j++ < workspace - 1) {
1176 WMAddToPLArray(array, empty);
1178 WMAddToPLArray(array, val);
1179 } else {
1180 WMDeleteFromPLArray(array, workspace);
1181 WMInsertInPLArray(array, workspace, val);
1184 value = WMGetPropListDescription(array, False);
1185 updateDomain(domain, "WorkspaceSpecificBack", value);
1188 int main(int argc, char **argv)
1190 int i;
1191 int helperMode = 0;
1192 RContext *rc;
1193 RContextAttributes rattr;
1194 char *style = "spixmap";
1195 char *back_color = "gray20";
1196 char *image_name = NULL;
1197 char *domain = "WindowMaker";
1198 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1199 char *texture = NULL;
1200 int workspace = -1;
1202 signal(SIGINT, SIG_DFL);
1203 signal(SIGTERM, SIG_DFL);
1204 signal(SIGQUIT, SIG_DFL);
1205 signal(SIGSEGV, SIG_DFL);
1206 signal(SIGBUS, SIG_DFL);
1207 signal(SIGFPE, SIG_DFL);
1208 signal(SIGABRT, SIG_DFL);
1209 signal(SIGHUP, SIG_DFL);
1210 signal(SIGPIPE, SIG_DFL);
1211 signal(SIGCHLD, SIG_DFL);
1213 WMInitializeApplication("wmsetbg", &argc, argv);
1215 for (i = 1; i < argc; i++) {
1216 if (strcmp(argv[i], "-helper") == 0) {
1217 helperMode = 1;
1218 } else if (strcmp(argv[i], "-display") == 0) {
1219 i++;
1220 if (i >= argc) {
1221 wfatal("too few arguments for %s\n", argv[i - 1]);
1222 exit(1);
1224 display = argv[i];
1225 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1226 style = "spixmap";
1227 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1228 style = "tpixmap";
1229 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1230 style = "cpixmap";
1231 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1232 style = "mpixmap";
1233 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1234 render_mode = RDitheredRendering;
1235 obey_user++;
1236 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1237 render_mode = RBestMatchRendering;
1238 obey_user++;
1239 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1240 smooth = True;
1241 #ifdef XINERAMA
1242 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1243 xineStretch = True;
1244 #endif
1245 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1246 update++;
1247 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1248 update++;
1249 i++;
1250 if (i >= argc) {
1251 wfatal("too few arguments for %s\n", argv[i - 1]);
1252 exit(1);
1254 domain = wstrdup(argv[i]);
1255 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1256 i++;
1257 if (i >= argc) {
1258 wfatal("too few arguments for %s\n", argv[i - 1]);
1259 exit(1);
1261 if (sscanf(argv[i], "%i", &cpc) != 1) {
1262 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1263 exit(1);
1265 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1266 i++;
1267 if (i >= argc) {
1268 wfatal("too few arguments for %s\n", argv[i - 1]);
1269 exit(1);
1271 back_color = argv[i];
1272 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1273 i++;
1274 if (i >= argc) {
1275 wfatal("too few arguments for %s\n", argv[i - 1]);
1276 exit(1);
1278 texture = argv[i];
1279 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1280 i++;
1281 if (i >= argc) {
1282 wfatal("too few arguments for %s\n", argv[i - 1]);
1283 exit(1);
1285 if (sscanf(argv[i], "%i", &workspace) != 1) {
1286 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1287 exit(1);
1289 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1290 printf(PROG_VERSION);
1291 exit(0);
1292 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1293 print_help();
1294 exit(0);
1295 } else if (argv[i][0] != '-') {
1296 image_name = argv[i];
1297 } else {
1298 printf("%s: invalid argument '%s'\n", __progname, argv[i]);
1299 printf("Try '%s --help' for more information\n", __progname);
1300 exit(1);
1303 if (!image_name && !texture && !helperMode) {
1304 printf("%s: you must specify a image file name or a texture\n", __progname);
1305 printf("Try '%s --help' for more information\n", __progname);
1306 exit(1);
1309 PixmapPath = getPixmapPath(domain);
1310 if (!smooth) {
1311 WMPropList *val;
1312 /* carlos, don't remove this */
1313 #if 0 /* some problem with Alpha... TODO: check if its right */
1314 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1315 #else
1316 val = getValueForKey(domain, "SmoothWorkspaceBack");
1317 #endif
1319 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1320 smooth = True;
1323 dpy = XOpenDisplay(display);
1324 if (!dpy) {
1325 wfatal("could not open display");
1326 exit(1);
1328 #if 0
1329 XSynchronize(dpy, 1);
1330 #endif
1332 root = DefaultRootWindow(dpy);
1334 scr = DefaultScreen(dpy);
1336 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1337 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1338 scrX = scrY = 0;
1340 initXinerama();
1342 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1343 render_mode = RDitheredRendering;
1345 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1346 rattr.render_mode = render_mode;
1347 rattr.colors_per_channel = cpc;
1348 rattr.standard_colormap_mode = RCreateStdColormap;
1350 rc = RCreateContext(dpy, scr, &rattr);
1352 if (!rc) {
1353 rattr.standard_colormap_mode = RIgnoreStdColormap;
1354 rc = RCreateContext(dpy, scr, &rattr);
1357 if (!rc) {
1358 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1359 exit(1);
1362 if (helperMode) {
1363 /* lower priority, so that it wont use all the CPU */
1364 nice(15);
1366 helperLoop(rc);
1367 } else {
1368 BackgroundTexture *tex;
1369 char buffer[4098];
1371 if (!texture) {
1372 char *image_path = getFullPixmapPath(image_name);
1374 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1375 wfree(image_path);
1376 texture = (char *)buffer;
1379 if (update && workspace < 0) {
1380 updateDomain(domain, "WorkspaceBack", texture);
1383 tex = parseTexture(rc, texture);
1384 if (!tex)
1385 exit(1);
1387 if (workspace < 0)
1388 changeTexture(tex);
1389 else {
1390 /* always update domain */
1391 changeTextureForWorkspace(domain, texture, workspace);
1395 return 0;