Support Xinerama in wmsetbg.
[wmaker-crm.git] / util / wmsetbg.c
blob96e14b5455e12c15d8dbfa821080e88b1f92e45a
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 #ifdef HAVE_DLFCN_H
51 #include <dlfcn.h>
52 #endif
54 #include "../src/wconfig.h"
56 #ifndef GLOBAL_DEFAULTS_SUBDIR
57 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
58 #endif
60 #include <WINGs/WINGs.h>
61 #include <wraster.h>
63 typedef struct {
64 WMRect *screens;
65 int count; /* screen count, 0 = inactive */
66 } WXineramaInfo;
68 #define PROG_VERSION "wmsetbg (Window Maker) 2.8"
70 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
72 Display *dpy;
73 char *display = "";
74 Window root;
75 int scr;
76 int scrWidth;
77 int scrHeight;
78 int scrX, scrY;
80 WXineramaInfo xineInfo;
82 Bool smooth = False;
83 #ifdef XINERAMA
84 Bool xineStretch = False;
85 #endif
87 Pixmap CurrentPixmap = None;
88 char *PixmapPath = NULL;
90 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
92 typedef struct BackgroundTexture {
93 int refcount;
95 int solid;
97 char *spec;
99 XColor color;
100 Pixmap pixmap; /* for all textures, including solid */
101 int width; /* size of the pixmap */
102 int height;
103 } BackgroundTexture;
105 void initXinerama()
107 xineInfo.screens = NULL;
108 xineInfo.count = 0;
109 #ifdef XINERAMA
110 # ifdef SOLARIS_XINERAMA
111 if (XineramaGetState(dpy, scr)) {
112 XRectangle head[MAXFRAMEBUFFERS];
113 unsigned char hints[MAXFRAMEBUFFERS];
114 int i;
116 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
118 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
120 for (i = 0; i < xineInfo.count; i++) {
121 xineInfo.screens[i].pos.x = head[i].x;
122 xineInfo.screens[i].pos.y = head[i].y;
123 xineInfo.screens[i].size.width = head[i].width;
124 xineInfo.screens[i].size.height = head[i].height;
128 # else /* !SOLARIS_XINERAMA */
129 if (XineramaIsActive(dpy)) {
130 XineramaScreenInfo *xine_screens;
131 int i;
133 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
135 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
137 for (i = 0; i < xineInfo.count; i++) {
138 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
139 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
140 xineInfo.screens[i].size.width = xine_screens[i].width;
141 xineInfo.screens[i].size.height = xine_screens[i].height;
143 XFree(xine_screens);
145 # endif /* !SOLARIS_XINERAMA */
146 #endif /* XINERAMA */
149 RImage *loadImage(RContext * rc, char *file)
151 char *path;
152 RImage *image;
154 if (access(file, F_OK) != 0) {
155 path = wfindfile(PixmapPath, file);
156 if (!path) {
157 wwarning("%s:could not find image file used in texture", file);
158 return NULL;
160 } else {
161 path = wstrdup(file);
164 image = RLoadImage(rc, path, 0);
165 if (!image) {
166 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
168 wfree(path);
170 return image;
173 static void
174 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
175 int x, int y, int width, int height)
177 int w, h;
178 Bool fimage = False;
180 switch (toupper(type)) {
181 case 'S':
182 case 'M':
183 if (toupper(type) == 'S') {
184 w = width;
185 h = height;
186 } else {
187 if (image->width * height > image->height * width) {
188 w = width;
189 h = (width * image->height) / image->width;
190 } else {
191 w = (height * image->width) / image->height;
192 h = height;
196 if (w != image->width || h != image->height) {
197 RImage *simage;
199 if (smooth) {
200 simage = RSmoothScaleImage(image, w, h);
201 } else {
202 simage = RScaleImage(image, w, h);
205 if (!simage) {
206 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
207 return;
209 fimage = True;
210 image = simage;
213 /* fall through */
214 case 'C':
216 Pixmap pixmap;
218 if (!RConvertImage(rc, image, &pixmap)) {
219 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
220 return;
223 if (image->width != width || image->height != height) {
224 int sx, sy, w, h;
226 if (image->height < height) {
227 h = image->height;
228 y += (height - h) / 2;
229 sy = 0;
230 } else {
231 sy = (image->height - height) / 2;
232 h = height;
234 if (image->width < width) {
235 w = image->width;
236 x += (width - w) / 2;
237 sx = 0;
238 } else {
239 sx = (image->width - width) / 2;
240 w = width;
243 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
244 } else
245 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
246 x, y);
248 XFreePixmap(dpy, pixmap);
249 if (fimage) {
250 RReleaseImage(image);
253 break;
257 BackgroundTexture *parseTexture(RContext * rc, char *text)
259 BackgroundTexture *texture = NULL;
260 WMPropList *texarray;
261 WMPropList *val;
262 int count;
263 char *tmp;
264 char *type;
266 #define GETSTRORGOTO(val, str, i, label) \
267 val = WMGetFromPLArray(texarray, i);\
268 if (!WMIsPLString(val)) {\
269 wwarning("could not parse texture %s", text);\
270 goto label;\
272 str = WMGetFromPLString(val)
274 texarray = WMCreatePropListFromDescription(text);
275 if (!texarray || !WMIsPLArray(texarray)
276 || (count = WMGetPropListItemCount(texarray)) < 2) {
278 wwarning("could not parse texture %s", text);
279 if (texarray)
280 WMReleasePropList(texarray);
281 return NULL;
284 texture = wmalloc(sizeof(BackgroundTexture));
285 memset(texture, 0, sizeof(BackgroundTexture));
287 GETSTRORGOTO(val, type, 0, error);
289 if (strcasecmp(type, "solid") == 0) {
290 XColor color;
291 Pixmap pixmap;
293 texture->solid = 1;
295 GETSTRORGOTO(val, tmp, 1, error);
297 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
298 wwarning("could not parse color %s in texture %s", tmp, text);
299 goto error;
301 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
303 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
304 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
305 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
307 texture->pixmap = pixmap;
308 texture->color = color;
309 texture->width = 8;
310 texture->height = 8;
311 } else if (strcasecmp(type, "vgradient") == 0
312 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
313 XColor color;
314 RColor color1, color2;
315 RImage *image;
316 Pixmap pixmap;
317 int gtype;
318 int iwidth, iheight;
320 GETSTRORGOTO(val, tmp, 1, error);
322 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
323 wwarning("could not parse color %s in texture %s", tmp, text);
324 goto error;
327 color1.red = color.red >> 8;
328 color1.green = color.green >> 8;
329 color1.blue = color.blue >> 8;
331 GETSTRORGOTO(val, tmp, 2, error);
333 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
334 wwarning("could not parse color %s in texture %s", tmp, text);
335 goto error;
338 color2.red = color.red >> 8;
339 color2.green = color.green >> 8;
340 color2.blue = color.blue >> 8;
342 switch (type[0]) {
343 case 'h':
344 case 'H':
345 gtype = RHorizontalGradient;
346 iwidth = scrWidth;
347 iheight = 32;
348 break;
349 case 'V':
350 case 'v':
351 gtype = RVerticalGradient;
352 iwidth = 32;
353 iheight = scrHeight;
354 break;
355 default:
356 gtype = RDiagonalGradient;
357 iwidth = scrWidth;
358 iheight = scrHeight;
359 break;
362 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
364 if (!image) {
365 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
366 goto error;
369 if (!RConvertImage(rc, image, &pixmap)) {
370 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
371 RReleaseImage(image);
372 goto error;
375 texture->width = image->width;
376 texture->height = image->height;
377 RReleaseImage(image);
379 texture->pixmap = pixmap;
380 } else if (strcasecmp(type, "mvgradient") == 0
381 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
382 XColor color;
383 RColor **colors;
384 RImage *image;
385 Pixmap pixmap;
386 int i, j;
387 int gtype;
388 int iwidth, iheight;
390 colors = malloc(sizeof(RColor *) * (count - 1));
391 if (!colors) {
392 wwarning("out of memory while parsing texture");
393 goto error;
395 memset(colors, 0, sizeof(RColor *) * (count - 1));
397 for (i = 2; i < count; i++) {
398 val = WMGetFromPLArray(texarray, i);
399 if (!WMIsPLString(val)) {
400 wwarning("could not parse texture %s", text);
402 for (j = 0; colors[j] != NULL; j++)
403 wfree(colors[j]);
404 wfree(colors);
405 goto error;
407 tmp = WMGetFromPLString(val);
409 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
410 wwarning("could not parse color %s in texture %s", tmp, text);
412 for (j = 0; colors[j] != NULL; j++)
413 wfree(colors[j]);
414 wfree(colors);
415 goto error;
417 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
418 wwarning("out of memory while parsing texture");
420 for (j = 0; colors[j] != NULL; j++)
421 wfree(colors[j]);
422 wfree(colors);
423 goto error;
426 colors[i - 2]->red = color.red >> 8;
427 colors[i - 2]->green = color.green >> 8;
428 colors[i - 2]->blue = color.blue >> 8;
431 switch (type[1]) {
432 case 'h':
433 case 'H':
434 gtype = RHorizontalGradient;
435 iwidth = scrWidth;
436 iheight = 32;
437 break;
438 case 'V':
439 case 'v':
440 gtype = RVerticalGradient;
441 iwidth = 32;
442 iheight = scrHeight;
443 break;
444 default:
445 gtype = RDiagonalGradient;
446 iwidth = scrWidth;
447 iheight = scrHeight;
448 break;
451 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
453 for (j = 0; colors[j] != NULL; j++)
454 wfree(colors[j]);
455 wfree(colors);
457 if (!image) {
458 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
459 goto error;
462 if (!RConvertImage(rc, image, &pixmap)) {
463 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
464 RReleaseImage(image);
465 goto error;
468 texture->width = image->width;
469 texture->height = image->height;
470 RReleaseImage(image);
472 texture->pixmap = pixmap;
473 } else if (strcasecmp(type, "cpixmap") == 0
474 || strcasecmp(type, "spixmap") == 0
475 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
476 XColor color;
477 Pixmap pixmap = None;
478 RImage *image = NULL;
479 int iwidth, iheight;
480 RColor rcolor;
482 GETSTRORGOTO(val, tmp, 1, error);
484 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
485 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
488 if (!pixmap) {
489 image = loadImage(rc, tmp);
490 if (!image) {
491 goto error;
493 iwidth = image->width;
494 iheight = image->height;
497 GETSTRORGOTO(val, tmp, 2, error);
499 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
500 wwarning("could not parse color %s in texture %s\n", tmp, text);
501 RReleaseImage(image);
502 goto error;
504 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
505 rcolor.red = color.red >> 8;
506 rcolor.green = color.green >> 8;
507 rcolor.blue = color.blue >> 8;
508 RGetClosestXColor(rc, &rcolor, &color);
509 } else {
510 rcolor.red = 0;
511 rcolor.green = 0;
512 rcolor.blue = 0;
514 /* for images with a transparent color */
515 if (image->data[3]) {
516 RCombineImageWithColor(image, &rcolor);
519 switch (toupper(type[0])) {
520 case 'T':
521 texture->width = iwidth;
522 texture->height = iheight;
523 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
524 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
525 RReleaseImage(image);
526 goto error;
528 if (image)
529 RReleaseImage(image);
531 texture->pixmap = pixmap;
532 texture->color = color;
533 break;
534 case 'S':
535 case 'M':
536 case 'C':
538 Pixmap tpixmap =
539 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
540 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
541 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
543 texture->pixmap = tpixmap;
544 texture->color = color;
545 texture->width = scrWidth;
546 texture->height = scrHeight;
548 #ifdef XINERAMA
549 if (xineInfo.count && ! xineStretch) {
550 int i;
551 for (i = 0; i < xineInfo.count; ++i) {
552 applyImage(rc, texture, image, type[0],
553 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
554 xineInfo.screens[i].size.width,
555 xineInfo.screens[i].size.height);
557 } else {
558 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
560 #else /* !XINERAMA */
561 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
562 #endif /* !XINERAMA */
563 RReleaseImage(image);
565 break;
567 } else if (strcasecmp(type, "thgradient") == 0
568 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
569 XColor color;
570 RColor color1, color2;
571 RImage *image;
572 RImage *gradient;
573 RImage *tiled;
574 Pixmap pixmap;
575 int opaq;
576 char *file;
577 int gtype;
578 int twidth, theight;
580 GETSTRORGOTO(val, file, 1, error);
582 GETSTRORGOTO(val, tmp, 2, error);
584 opaq = atoi(tmp);
586 GETSTRORGOTO(val, tmp, 3, error);
588 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
589 wwarning("could not parse color %s in texture %s", tmp, text);
590 goto error;
593 color1.red = color.red >> 8;
594 color1.green = color.green >> 8;
595 color1.blue = color.blue >> 8;
597 GETSTRORGOTO(val, tmp, 4, error);
599 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
600 wwarning("could not parse color %s in texture %s", tmp, text);
601 goto error;
604 color2.red = color.red >> 8;
605 color2.green = color.green >> 8;
606 color2.blue = color.blue >> 8;
608 image = loadImage(rc, file);
609 if (!image) {
610 goto error;
613 switch (type[1]) {
614 case 'h':
615 case 'H':
616 gtype = RHorizontalGradient;
617 twidth = scrWidth;
618 theight = image->height > scrHeight ? scrHeight : image->height;
619 break;
620 case 'V':
621 case 'v':
622 gtype = RVerticalGradient;
623 twidth = image->width > scrWidth ? scrWidth : image->width;
624 theight = scrHeight;
625 break;
626 default:
627 gtype = RDiagonalGradient;
628 twidth = scrWidth;
629 theight = scrHeight;
630 break;
632 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
634 if (!gradient) {
635 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
636 RReleaseImage(gradient);
637 RReleaseImage(image);
638 goto error;
641 tiled = RMakeTiledImage(image, twidth, theight);
642 if (!tiled) {
643 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
644 RReleaseImage(gradient);
645 RReleaseImage(image);
646 goto error;
648 RReleaseImage(image);
650 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
651 RReleaseImage(gradient);
653 if (!RConvertImage(rc, tiled, &pixmap)) {
654 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
655 RReleaseImage(tiled);
656 goto error;
658 texture->width = tiled->width;
659 texture->height = tiled->height;
661 RReleaseImage(tiled);
663 texture->pixmap = pixmap;
664 } else if (strcasecmp(type, "function") == 0) {
665 #ifdef HAVE_DLFCN_H
666 void (*initFunc) (Display *, Colormap);
667 RImage *(*mainFunc) (int, char **, int, int, int);
668 Pixmap pixmap;
669 RImage *image = 0;
670 int success = 0;
671 char *lib, *func, **argv = 0;
672 void *handle = 0;
673 int i, argc;
675 if (count < 3)
676 goto function_cleanup;
678 /* get the library name */
679 GETSTRORGOTO(val, lib, 1, function_cleanup);
681 /* get the function name */
682 GETSTRORGOTO(val, func, 2, function_cleanup);
684 argc = count - 2;
685 argv = (char **)wmalloc(argc * sizeof(char *));
687 /* get the parameters */
688 argv[0] = func;
689 for (i = 0; i < argc - 1; i++) {
690 GETSTRORGOTO(val, tmp, 3 + i, function_cleanup);
691 argv[i + 1] = wstrdup(tmp);
694 handle = dlopen(lib, RTLD_LAZY);
695 if (!handle) {
696 wwarning("could not find library %s", lib);
697 goto function_cleanup;
700 initFunc = dlsym(handle, "initWindowMaker");
701 if (!initFunc) {
702 wwarning("could not initialize library %s", lib);
703 goto function_cleanup;
705 initFunc(dpy, DefaultColormap(dpy, scr));
707 mainFunc = dlsym(handle, func);
708 if (!mainFunc) {
709 wwarning("could not find function %s::%s", lib, func);
710 goto function_cleanup;
712 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
714 if (!RConvertImage(rc, image, &pixmap)) {
715 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
716 goto function_cleanup;
718 texture->width = scrWidth;
719 texture->height = scrHeight;
720 texture->pixmap = pixmap;
721 success = 1;
723 function_cleanup:
724 if (argv) {
725 int i;
726 for (i = 0; i < argc; i++) {
727 wfree(argv[i]);
730 if (handle) {
731 dlclose(handle);
733 if (image) {
734 RReleaseImage(image);
736 if (!success) {
737 goto error;
739 #else
740 wwarning("function textures not supported");
741 goto error;
742 #endif
743 } else {
744 wwarning("invalid texture type %s", text);
745 goto error;
748 texture->spec = wstrdup(text);
750 return texture;
752 error:
753 if (texture)
754 wfree(texture);
755 if (texarray)
756 WMReleasePropList(texarray);
758 return NULL;
761 void freeTexture(BackgroundTexture * texture)
763 if (texture->solid) {
764 unsigned long pixel[1];
766 pixel[0] = texture->color.pixel;
767 /* dont free black/white pixels */
768 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
769 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
770 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
772 if (texture->pixmap) {
773 XFreePixmap(dpy, texture->pixmap);
775 wfree(texture->spec);
776 wfree(texture);
779 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
781 BackgroundTexture *newTexture = NULL;
782 int i;
784 /* unset the texture */
785 if (!texture) {
786 if (textures[workspace] != NULL) {
787 textures[workspace]->refcount--;
789 if (textures[workspace]->refcount == 0)
790 freeTexture(textures[workspace]);
792 textures[workspace] = NULL;
793 return;
796 if (textures[workspace]
797 && strcasecmp(textures[workspace]->spec, texture) == 0) {
798 /* texture did not change */
799 return;
802 /* check if the same texture is already created */
803 for (i = 0; i < *maxTextures; i++) {
804 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
805 newTexture = textures[i];
806 break;
810 if (!newTexture) {
811 /* create the texture */
812 newTexture = parseTexture(rc, texture);
814 if (!newTexture)
815 return;
817 if (textures[workspace] != NULL) {
819 textures[workspace]->refcount--;
821 if (textures[workspace]->refcount == 0)
822 freeTexture(textures[workspace]);
825 newTexture->refcount++;
826 textures[workspace] = newTexture;
828 if (*maxTextures < workspace)
829 *maxTextures = workspace;
832 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
834 Display *tmpDpy;
835 Pixmap copyP;
837 /* must open a new display or the RetainPermanent will
838 * leave stuff allocated in RContext unallocated after exit */
839 tmpDpy = XOpenDisplay(display);
840 if (!tmpDpy) {
841 wwarning("could not open display to update background image information");
843 return None;
844 } else {
845 XSync(dpy, False);
847 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
848 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
849 XSync(tmpDpy, False);
851 XSetCloseDownMode(tmpDpy, RetainPermanent);
852 XCloseDisplay(tmpDpy);
855 return copyP;
858 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
860 return 0;
863 void setPixmapProperty(Pixmap pixmap)
865 static Atom prop = 0;
866 Atom type;
867 int format;
868 unsigned long length, after;
869 unsigned char *data;
870 int mode;
872 if (!prop) {
873 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
876 XGrabServer(dpy);
878 /* Clear out the old pixmap */
879 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
880 &type, &format, &length, &after, &data);
882 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
883 XSetErrorHandler(dummyErrorHandler);
884 XKillClient(dpy, *((Pixmap *) data));
885 XSync(dpy, False);
886 XSetErrorHandler(NULL);
887 mode = PropModeReplace;
888 } else {
889 mode = PropModeAppend;
891 if (pixmap)
892 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
893 else
894 XDeleteProperty(dpy, root, prop);
896 XUngrabServer(dpy);
897 XFlush(dpy);
900 void changeTexture(BackgroundTexture * texture)
902 if (!texture) {
903 return;
906 if (texture->solid) {
907 XSetWindowBackground(dpy, root, texture->color.pixel);
908 } else {
909 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
911 XClearWindow(dpy, root);
913 XSync(dpy, False);
916 Pixmap pixmap;
918 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
920 setPixmapProperty(pixmap);
924 int readmsg(int fd, char *buffer, int size)
926 int count;
928 count = 0;
929 while (size > 0) {
930 count = read(fd, buffer, size);
931 if (count < 0)
932 return -1;
933 size -= count;
934 buffer += count;
935 *buffer = 0;
938 return size;
942 * Message Format:
943 * sizeSntexture_spec - sets the texture for workspace n
944 * sizeCn - change background texture to the one for workspace n
945 * sizePpath - set the pixmap search path
947 * n is 4 bytes
948 * size = 4 bytes for length of the message data
950 void helperLoop(RContext * rc)
952 BackgroundTexture *textures[WORKSPACE_COUNT];
953 int maxTextures = 0;
954 char buffer[2048], buf[8];
955 int size;
956 int errcount = 4;
958 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
960 while (1) {
961 int workspace;
963 /* get length of message */
964 if (readmsg(0, buffer, 4) < 0) {
965 wsyserror("error reading message from Window Maker");
966 errcount--;
967 if (errcount == 0) {
968 wfatal("quitting");
969 exit(1);
971 continue;
973 memcpy(buf, buffer, 4);
974 buf[4] = 0;
975 size = atoi(buf);
977 /* get message */
978 if (readmsg(0, buffer, size) < 0) {
979 wsyserror("error reading message from Window Maker");
980 errcount--;
981 if (errcount == 0) {
982 wfatal("quitting");
983 exit(1);
985 continue;
987 #ifdef DEBUG
988 printf("RECEIVED %s\n", buffer);
989 #endif
990 if (buffer[0] != 'P' && buffer[0] != 'K') {
991 memcpy(buf, &buffer[1], 4);
992 buf[4] = 0;
993 workspace = atoi(buf);
994 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
995 wwarning("received message with invalid workspace number %i\n", workspace);
996 continue;
1000 switch (buffer[0]) {
1001 case 'S':
1002 #ifdef DEBUG
1003 printf("set texture %s\n", &buffer[5]);
1004 #endif
1005 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
1006 break;
1008 case 'C':
1009 #ifdef DEBUG
1010 printf("change texture %i\n", workspace);
1011 #endif
1012 if (!textures[workspace]) {
1013 changeTexture(textures[0]);
1014 } else {
1015 changeTexture(textures[workspace]);
1017 break;
1019 case 'P':
1020 #ifdef DEBUG
1021 printf("change pixmappath %s\n", &buffer[1]);
1022 #endif
1023 if (PixmapPath)
1024 wfree(PixmapPath);
1025 PixmapPath = wstrdup(&buffer[1]);
1026 break;
1028 case 'U':
1029 #ifdef DEBUG
1030 printf("unset workspace %i\n", workspace);
1031 #endif
1032 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1033 break;
1035 case 'K':
1036 #ifdef DEBUG
1037 printf("exit command\n");
1038 #endif
1039 exit(0);
1041 default:
1042 wwarning("unknown message received");
1043 break;
1048 void updateDomain(char *domain, char *key, char *texture)
1050 char *program = "wdwrite";
1052 /* here is a mem leak */
1053 system(wstrconcat("wdwrite ",
1054 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
1056 execlp(program, program, domain, key, texture, NULL);
1057 wwarning("warning could not run \"%s\"", program);
1060 char *globalDefaultsPathForDomain(char *domain)
1062 char path[1024];
1064 sprintf(path, "%s/%s/%s", SYSCONFDIR, GLOBAL_DEFAULTS_SUBDIR, domain);
1066 return wstrdup(path);
1069 static WMPropList *getValueForKey(char *domain, char *keyName)
1071 char *path;
1072 WMPropList *key, *val, *d;
1074 key = WMCreatePLString(keyName);
1076 /* try to find PixmapPath in user defaults */
1077 path = wdefaultspathfordomain(domain);
1078 d = WMReadPropListFromFile(path);
1079 if (!d) {
1080 wwarning("could not open domain file %s", path);
1082 wfree(path);
1084 if (d && !WMIsPLDictionary(d)) {
1085 WMReleasePropList(d);
1086 d = NULL;
1088 if (d) {
1089 val = WMGetFromPLDictionary(d, key);
1090 } else {
1091 val = NULL;
1093 /* try to find PixmapPath in global defaults */
1094 if (!val) {
1095 path = globalDefaultsPathForDomain(domain);
1096 if (!path) {
1097 wwarning("could not locate file for domain %s", domain);
1098 d = NULL;
1099 } else {
1100 d = WMReadPropListFromFile(path);
1101 wfree(path);
1104 if (d && !WMIsPLDictionary(d)) {
1105 WMReleasePropList(d);
1106 d = NULL;
1108 if (d) {
1109 val = WMGetFromPLDictionary(d, key);
1111 } else {
1112 val = NULL;
1116 if (val)
1117 WMRetainPropList(val);
1119 WMReleasePropList(key);
1120 if (d)
1121 WMReleasePropList(d);
1123 return val;
1126 char *getPixmapPath(char *domain)
1128 WMPropList *val;
1129 char *ptr, *data;
1130 int len, i, count;
1132 val = getValueForKey(domain, "PixmapPath");
1134 if (!val || !WMIsPLArray(val)) {
1135 if (val)
1136 WMReleasePropList(val);
1137 return wstrdup("");
1140 count = WMGetPropListItemCount(val);
1141 len = 0;
1142 for (i = 0; i < count; i++) {
1143 WMPropList *v;
1145 v = WMGetFromPLArray(val, i);
1146 if (!v || !WMIsPLString(v)) {
1147 continue;
1149 len += strlen(WMGetFromPLString(v)) + 1;
1152 ptr = data = wmalloc(len + 1);
1153 *ptr = 0;
1155 for (i = 0; i < count; i++) {
1156 WMPropList *v;
1158 v = WMGetFromPLArray(val, i);
1159 if (!v || !WMIsPLString(v)) {
1160 continue;
1162 strcpy(ptr, WMGetFromPLString(v));
1164 ptr += strlen(WMGetFromPLString(v));
1165 *ptr = ':';
1166 ptr++;
1168 if (i > 0)
1169 ptr--;
1170 *(ptr--) = 0;
1172 WMReleasePropList(val);
1174 return data;
1177 char *getFullPixmapPath(char *file)
1179 char *tmp;
1181 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1182 int bsize = 512;
1183 char *path = wmalloc(bsize);
1185 while (!getcwd(path, bsize)) {
1186 bsize += bsize / 2;
1187 path = wrealloc(path, bsize);
1190 tmp = wstrconcat(path, "/");
1191 wfree(path);
1192 path = wstrconcat(tmp, file);
1193 wfree(tmp);
1195 return path;
1198 /* the file is in the PixmapPath */
1199 wfree(tmp);
1201 return wstrdup(file);
1204 void wAbort()
1206 wfatal("aborting");
1207 exit(1);
1210 void print_help(char *ProgName)
1212 printf("Usage: %s [options] [image]\n", ProgName);
1213 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1214 puts("");
1215 #define P(m) puts(m)
1216 P(" -display display to use");
1217 P(" -d, --dither dither image");
1218 P(" -m, --match match colors");
1219 P(" -S, --smooth smooth scaled image");
1220 #ifdef XINERAMA
1221 P(" -X, --xinerama stretch image across Xinerama heads");
1222 #endif
1223 P(" -b, --back-color <color> background color");
1224 P(" -t, --tile tile image");
1225 P(" -e, --center center image");
1226 P(" -s, --scale scale image (default)");
1227 P(" -a, --maxscale scale image and keep aspect ratio");
1228 P(" -u, --update-wmaker update WindowMaker domain database");
1229 P(" -D, --update-domain <domain> update <domain> database");
1230 P(" -c, --colors <cpc> colors per channel to use");
1231 P(" -p, --parse <texture> proplist style texture specification");
1232 P(" -w, --workspace <workspace> update background for the specified workspace");
1233 P(" --version show version of wmsetbg and exit");
1234 P(" --help show this help and exit");
1235 #undef P
1238 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1240 WMPropList *array, *val;
1241 char *value;
1242 int j;
1244 val = WMCreatePropListFromDescription(texture);
1245 if (!val) {
1246 wwarning("could not parse texture %s", texture);
1247 return;
1250 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1252 if (!array) {
1253 array = WMCreatePLArray(NULL, NULL);
1256 j = WMGetPropListItemCount(array);
1257 if (workspace >= j) {
1258 WMPropList *empty;
1260 empty = WMCreatePLArray(NULL, NULL);
1262 while (j++ < workspace - 1) {
1263 WMAddToPLArray(array, empty);
1265 WMAddToPLArray(array, val);
1266 } else {
1267 WMDeleteFromPLArray(array, workspace);
1268 WMInsertInPLArray(array, workspace, val);
1271 value = WMGetPropListDescription(array, False);
1272 updateDomain(domain, "WorkspaceSpecificBack", value);
1275 int main(int argc, char **argv)
1277 int i;
1278 int helperMode = 0;
1279 RContext *rc;
1280 RContextAttributes rattr;
1281 char *style = "spixmap";
1282 char *back_color = "gray20";
1283 char *image_name = NULL;
1284 char *domain = "WindowMaker";
1285 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1286 char *texture = NULL;
1287 int workspace = -1;
1289 signal(SIGINT, SIG_DFL);
1290 signal(SIGTERM, SIG_DFL);
1291 signal(SIGQUIT, SIG_DFL);
1292 signal(SIGSEGV, SIG_DFL);
1293 signal(SIGBUS, SIG_DFL);
1294 signal(SIGFPE, SIG_DFL);
1295 signal(SIGABRT, SIG_DFL);
1296 signal(SIGHUP, SIG_DFL);
1297 signal(SIGPIPE, SIG_DFL);
1298 signal(SIGCHLD, SIG_DFL);
1300 WMInitializeApplication("wmsetbg", &argc, argv);
1302 for (i = 1; i < argc; i++) {
1303 if (strcmp(argv[i], "-helper") == 0) {
1304 helperMode = 1;
1305 } else if (strcmp(argv[i], "-display") == 0) {
1306 i++;
1307 if (i >= argc) {
1308 wfatal("too few arguments for %s\n", argv[i - 1]);
1309 exit(1);
1311 display = argv[i];
1312 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1313 style = "spixmap";
1314 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1315 style = "tpixmap";
1316 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1317 style = "cpixmap";
1318 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1319 style = "mpixmap";
1320 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1321 render_mode = RDitheredRendering;
1322 obey_user++;
1323 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1324 render_mode = RBestMatchRendering;
1325 obey_user++;
1326 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1327 smooth = True;
1328 #ifdef XINERAMA
1329 } else if (strcmp(argv[i], "-X") == 0 || strcmp(argv[i], "--xinerama") == 0) {
1330 xineStretch = True;
1331 #endif
1332 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1333 update++;
1334 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1335 update++;
1336 i++;
1337 if (i >= argc) {
1338 wfatal("too few arguments for %s\n", argv[i - 1]);
1339 exit(1);
1341 domain = wstrdup(argv[i]);
1342 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1343 i++;
1344 if (i >= argc) {
1345 wfatal("too few arguments for %s\n", argv[i - 1]);
1346 exit(1);
1348 if (sscanf(argv[i], "%i", &cpc) != 1) {
1349 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1350 exit(1);
1352 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1353 i++;
1354 if (i >= argc) {
1355 wfatal("too few arguments for %s\n", argv[i - 1]);
1356 exit(1);
1358 back_color = argv[i];
1359 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1360 i++;
1361 if (i >= argc) {
1362 wfatal("too few arguments for %s\n", argv[i - 1]);
1363 exit(1);
1365 texture = argv[i];
1366 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1367 i++;
1368 if (i >= argc) {
1369 wfatal("too few arguments for %s\n", argv[i - 1]);
1370 exit(1);
1372 if (sscanf(argv[i], "%i", &workspace) != 1) {
1373 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1374 exit(1);
1376 } else if (strcmp(argv[i], "--version") == 0) {
1378 printf(PROG_VERSION);
1379 exit(0);
1381 } else if (strcmp(argv[i], "--help") == 0) {
1382 print_help(argv[0]);
1383 exit(0);
1384 } else if (argv[i][0] != '-') {
1385 image_name = argv[i];
1386 } else {
1387 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1388 printf("Try '%s --help' for more information\n", argv[0]);
1389 exit(1);
1392 if (!image_name && !texture && !helperMode) {
1393 printf("%s: you must specify a image file name or a texture\n", argv[0]);
1394 printf("Try '%s --help' for more information\n", argv[0]);
1395 exit(1);
1398 PixmapPath = getPixmapPath(domain);
1399 if (!smooth) {
1400 WMPropList *val;
1401 #if 0 /* some problem with Alpha... TODO: check if its right */
1402 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1403 #else
1404 val = getValueForKey(domain, "SmoothWorkspaceBack");
1405 #endif
1407 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1408 smooth = True;
1411 dpy = XOpenDisplay(display);
1412 if (!dpy) {
1413 wfatal("could not open display");
1414 exit(1);
1416 #if 0
1417 XSynchronize(dpy, 1);
1418 #endif
1420 root = DefaultRootWindow(dpy);
1422 scr = DefaultScreen(dpy);
1424 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1425 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1426 scrX = scrY = 0;
1428 initXinerama();
1430 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1431 render_mode = RDitheredRendering;
1433 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1434 rattr.render_mode = render_mode;
1435 rattr.colors_per_channel = cpc;
1436 rattr.standard_colormap_mode = RCreateStdColormap;
1438 rc = RCreateContext(dpy, scr, &rattr);
1440 if (!rc) {
1441 rattr.standard_colormap_mode = RIgnoreStdColormap;
1442 rc = RCreateContext(dpy, scr, &rattr);
1445 if (!rc) {
1446 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1447 exit(1);
1450 if (helperMode) {
1451 /* lower priority, so that it wont use all the CPU */
1452 nice(15);
1454 helperLoop(rc);
1455 } else {
1456 BackgroundTexture *tex;
1457 char buffer[4098];
1459 if (!texture) {
1460 char *image_path = getFullPixmapPath(image_name);
1462 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1463 wfree(image_path);
1464 texture = (char *)buffer;
1467 if (update && workspace < 0) {
1468 updateDomain(domain, "WorkspaceBack", texture);
1471 tex = parseTexture(rc, texture);
1472 if (!tex)
1473 exit(1);
1475 if (workspace < 0)
1476 changeTexture(tex);
1477 else {
1478 /* always update domain */
1479 changeTextureForWorkspace(domain, texture, workspace);
1483 return 0;