Change to the linux kernel coding style
[wmaker-crm.git] / util / wmsetbg.c
1 /* wmsetbg.c- sets root window background image and also works as
2 * workspace background setting helper for wmaker
3 *
4 * WindowMaker window manager
5 *
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
7 * Copyright (c) 1998-2003 Dan Pascu
8 *
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.
13 *
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.
18 *
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.
23 */
24
25 /*
26 * TODO: rewrite, too dirty
27 */
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>
39
40 #include "../src/config.h"
41
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
49
50 #ifdef HAVE_DLFCN_H
51 #include <dlfcn.h>
52 #endif
53
54 #include "../src/wconfig.h"
55
56 #include <WINGs/WINGs.h>
57 #include <wraster.h>
58
59 typedef struct {
60 WMRect *screens;
61 int count; /* screen count, 0 = inactive */
62 } WXineramaInfo;
63
64 #define PROG_VERSION "wmsetbg (Window Maker) 2.8"
65
66 #define WORKSPACE_COUNT (MAX_WORKSPACES+1)
67
68 Display *dpy;
69 char *display = "";
70 Window root;
71 int scr;
72 int scrWidth;
73 int scrHeight;
74 int scrX, scrY;
75
76 WXineramaInfo xineInfo;
77
78 Bool smooth = False;
79
80 Pixmap CurrentPixmap = None;
81 char *PixmapPath = NULL;
82
83 extern Pixmap LoadJPEG(RContext * rc, char *file_name, int *width, int *height);
84
85 typedef struct BackgroundTexture {
86 int refcount;
87
88 int solid;
89
90 char *spec;
91
92 XColor color;
93 Pixmap pixmap; /* for all textures, including solid */
94 int width; /* size of the pixmap */
95 int height;
96 } BackgroundTexture;
97
98 void initXinerama()
99 {
100 xineInfo.screens = NULL;
101 xineInfo.count = 0;
102 #ifdef XINERAMA
103 # ifdef SOLARIS_XINERAMA
104 if (XineramaGetState(dpy, scr)) {
105 XRectangle head[MAXFRAMEBUFFERS];
106 unsigned char hints[MAXFRAMEBUFFERS];
107 int i;
108
109 if (XineramaGetInfo(dpy, scr, head, hints, &xineInfo.count)) {
110
111 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
112
113 for (i = 0; i < xineInfo.count; i++) {
114 xineInfo.screens[i].pos.x = head[i].x;
115 xineInfo.screens[i].pos.y = head[i].y;
116 xineInfo.screens[i].size.width = head[i].width;
117 xineInfo.screens[i].size.height = head[i].height;
118 }
119 }
120 }
121 # else /* !SOLARIS_XINERAMA */
122 if (XineramaIsActive(dpy)) {
123 XineramaScreenInfo *xine_screens;
124 int i;
125
126 xine_screens = XineramaQueryScreens(dpy, &xineInfo.count);
127
128 xineInfo.screens = wmalloc(sizeof(WMRect) * (xineInfo.count + 1));
129
130 for (i = 0; i < xineInfo.count; i++) {
131 xineInfo.screens[i].pos.x = xine_screens[i].x_org;
132 xineInfo.screens[i].pos.y = xine_screens[i].y_org;
133 xineInfo.screens[i].size.width = xine_screens[i].width;
134 xineInfo.screens[i].size.height = xine_screens[i].height;
135 }
136 XFree(xine_screens);
137 }
138 # endif /* !SOLARIS_XINERAMA */
139 #endif /* XINERAMA */
140 }
141
142 RImage *loadImage(RContext * rc, char *file)
143 {
144 char *path;
145 RImage *image;
146
147 if (access(file, F_OK) != 0) {
148 path = wfindfile(PixmapPath, file);
149 if (!path) {
150 wwarning("%s:could not find image file used in texture", file);
151 return NULL;
152 }
153 } else {
154 path = wstrdup(file);
155 }
156
157 image = RLoadImage(rc, path, 0);
158 if (!image) {
159 wwarning("%s:could not load image file used in texture:%s", path, RMessageForError(RErrorCode));
160 }
161 wfree(path);
162
163 return image;
164 }
165
166 static void
167 applyImage(RContext * rc, BackgroundTexture * texture, RImage * image, char type,
168 int x, int y, int width, int height)
169 {
170 int w, h;
171 Bool fimage = False;
172
173 switch (toupper(type)) {
174 case 'S':
175 case 'M':
176 if (toupper(type) == 'S') {
177 w = width;
178 h = height;
179 } else {
180 if (image->width * height > image->height * width) {
181 w = width;
182 h = (width * image->height) / image->width;
183 } else {
184 w = (height * image->width) / image->height;
185 h = height;
186 }
187 }
188
189 if (w != image->width || h != image->height) {
190 RImage *simage;
191
192 if (smooth) {
193 simage = RSmoothScaleImage(image, w, h);
194 } else {
195 simage = RScaleImage(image, w, h);
196 }
197
198 if (!simage) {
199 wwarning("could not scale image:%s", RMessageForError(RErrorCode));
200 return;
201 }
202 fimage = True;
203 image = simage;
204 }
205
206 /* fall through */
207 case 'C':
208 {
209 Pixmap pixmap;
210
211 if (!RConvertImage(rc, image, &pixmap)) {
212 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
213 return;
214 }
215
216 if (image->width != width || image->height != height) {
217 int sx, sy, w, h;
218
219 if (image->height < height) {
220 h = image->height;
221 y += (height - h) / 2;
222 sy = 0;
223 } else {
224 sy = (image->height - height) / 2;
225 h = height;
226 }
227 if (image->width < width) {
228 w = image->width;
229 x += (width - w) / 2;
230 sx = 0;
231 } else {
232 sx = (image->width - width) / 2;
233 w = width;
234 }
235
236 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), sx, sy, w, h, x, y);
237 } else
238 XCopyArea(dpy, pixmap, texture->pixmap, DefaultGC(dpy, scr), 0, 0, width, height,
239 x, y);
240
241 XFreePixmap(dpy, pixmap);
242 if (fimage) {
243 RReleaseImage(image);
244 }
245 }
246 break;
247 }
248 }
249
250 BackgroundTexture *parseTexture(RContext * rc, char *text)
251 {
252 BackgroundTexture *texture = NULL;
253 WMPropList *texarray;
254 WMPropList *val;
255 int count;
256 char *tmp;
257 char *type;
258
259 #define GETSTRORGOTO(val, str, i, label) \
260 val = WMGetFromPLArray(texarray, i);\
261 if (!WMIsPLString(val)) {\
262 wwarning("could not parse texture %s", text);\
263 goto label;\
264 }\
265 str = WMGetFromPLString(val)
266
267 texarray = WMCreatePropListFromDescription(text);
268 if (!texarray || !WMIsPLArray(texarray)
269 || (count = WMGetPropListItemCount(texarray)) < 2) {
270
271 wwarning("could not parse texture %s", text);
272 if (texarray)
273 WMReleasePropList(texarray);
274 return NULL;
275 }
276
277 texture = wmalloc(sizeof(BackgroundTexture));
278 memset(texture, 0, sizeof(BackgroundTexture));
279
280 GETSTRORGOTO(val, type, 0, error);
281
282 if (strcasecmp(type, "solid") == 0) {
283 XColor color;
284 Pixmap pixmap;
285
286 texture->solid = 1;
287
288 GETSTRORGOTO(val, tmp, 1, error);
289
290 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
291 wwarning("could not parse color %s in texture %s", tmp, text);
292 goto error;
293 }
294 XAllocColor(dpy, DefaultColormap(dpy, scr), &color);
295
296 pixmap = XCreatePixmap(dpy, root, 8, 8, DefaultDepth(dpy, scr));
297 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
298 XFillRectangle(dpy, pixmap, DefaultGC(dpy, scr), 0, 0, 8, 8);
299
300 texture->pixmap = pixmap;
301 texture->color = color;
302 texture->width = 8;
303 texture->height = 8;
304 } else if (strcasecmp(type, "vgradient") == 0
305 || strcasecmp(type, "dgradient") == 0 || strcasecmp(type, "hgradient") == 0) {
306 XColor color;
307 RColor color1, color2;
308 RImage *image;
309 Pixmap pixmap;
310 int gtype;
311 int iwidth, iheight;
312
313 GETSTRORGOTO(val, tmp, 1, error);
314
315 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
316 wwarning("could not parse color %s in texture %s", tmp, text);
317 goto error;
318 }
319
320 color1.red = color.red >> 8;
321 color1.green = color.green >> 8;
322 color1.blue = color.blue >> 8;
323
324 GETSTRORGOTO(val, tmp, 2, error);
325
326 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
327 wwarning("could not parse color %s in texture %s", tmp, text);
328 goto error;
329 }
330
331 color2.red = color.red >> 8;
332 color2.green = color.green >> 8;
333 color2.blue = color.blue >> 8;
334
335 switch (type[0]) {
336 case 'h':
337 case 'H':
338 gtype = RHorizontalGradient;
339 iwidth = scrWidth;
340 iheight = 32;
341 break;
342 case 'V':
343 case 'v':
344 gtype = RVerticalGradient;
345 iwidth = 32;
346 iheight = scrHeight;
347 break;
348 default:
349 gtype = RDiagonalGradient;
350 iwidth = scrWidth;
351 iheight = scrHeight;
352 break;
353 }
354
355 image = RRenderGradient(iwidth, iheight, &color1, &color2, gtype);
356
357 if (!image) {
358 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
359 goto error;
360 }
361
362 if (!RConvertImage(rc, image, &pixmap)) {
363 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
364 RReleaseImage(image);
365 goto error;
366 }
367
368 texture->width = image->width;
369 texture->height = image->height;
370 RReleaseImage(image);
371
372 texture->pixmap = pixmap;
373 } else if (strcasecmp(type, "mvgradient") == 0
374 || strcasecmp(type, "mdgradient") == 0 || strcasecmp(type, "mhgradient") == 0) {
375 XColor color;
376 RColor **colors;
377 RImage *image;
378 Pixmap pixmap;
379 int i, j;
380 int gtype;
381 int iwidth, iheight;
382
383 colors = malloc(sizeof(RColor *) * (count - 1));
384 if (!colors) {
385 wwarning("out of memory while parsing texture");
386 goto error;
387 }
388 memset(colors, 0, sizeof(RColor *) * (count - 1));
389
390 for (i = 2; i < count; i++) {
391 val = WMGetFromPLArray(texarray, i);
392 if (!WMIsPLString(val)) {
393 wwarning("could not parse texture %s", text);
394
395 for (j = 0; colors[j] != NULL; j++)
396 wfree(colors[j]);
397 wfree(colors);
398 goto error;
399 }
400 tmp = WMGetFromPLString(val);
401
402 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
403 wwarning("could not parse color %s in texture %s", tmp, text);
404
405 for (j = 0; colors[j] != NULL; j++)
406 wfree(colors[j]);
407 wfree(colors);
408 goto error;
409 }
410 if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
411 wwarning("out of memory while parsing texture");
412
413 for (j = 0; colors[j] != NULL; j++)
414 wfree(colors[j]);
415 wfree(colors);
416 goto error;
417 }
418
419 colors[i - 2]->red = color.red >> 8;
420 colors[i - 2]->green = color.green >> 8;
421 colors[i - 2]->blue = color.blue >> 8;
422 }
423
424 switch (type[1]) {
425 case 'h':
426 case 'H':
427 gtype = RHorizontalGradient;
428 iwidth = scrWidth;
429 iheight = 32;
430 break;
431 case 'V':
432 case 'v':
433 gtype = RVerticalGradient;
434 iwidth = 32;
435 iheight = scrHeight;
436 break;
437 default:
438 gtype = RDiagonalGradient;
439 iwidth = scrWidth;
440 iheight = scrHeight;
441 break;
442 }
443
444 image = RRenderMultiGradient(iwidth, iheight, colors, gtype);
445
446 for (j = 0; colors[j] != NULL; j++)
447 wfree(colors[j]);
448 wfree(colors);
449
450 if (!image) {
451 wwarning("could not render gradient texture:%s", RMessageForError(RErrorCode));
452 goto error;
453 }
454
455 if (!RConvertImage(rc, image, &pixmap)) {
456 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
457 RReleaseImage(image);
458 goto error;
459 }
460
461 texture->width = image->width;
462 texture->height = image->height;
463 RReleaseImage(image);
464
465 texture->pixmap = pixmap;
466 } else if (strcasecmp(type, "cpixmap") == 0
467 || strcasecmp(type, "spixmap") == 0
468 || strcasecmp(type, "mpixmap") == 0 || strcasecmp(type, "tpixmap") == 0) {
469 XColor color;
470 Pixmap pixmap = None;
471 RImage *image = NULL;
472 int iwidth, iheight;
473 RColor rcolor;
474
475 GETSTRORGOTO(val, tmp, 1, error);
476 /*
477 if (toupper(type[0]) == 'T' || toupper(type[0]) == 'C')
478 pixmap = LoadJPEG(rc, tmp, &iwidth, &iheight);
479 */
480
481 if (!pixmap) {
482 image = loadImage(rc, tmp);
483 if (!image) {
484 goto error;
485 }
486 iwidth = image->width;
487 iheight = image->height;
488 }
489
490 GETSTRORGOTO(val, tmp, 2, error);
491
492 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
493 wwarning("could not parse color %s in texture %s\n", tmp, text);
494 RReleaseImage(image);
495 goto error;
496 }
497 if (!XAllocColor(dpy, DefaultColormap(dpy, scr), &color)) {
498 rcolor.red = color.red >> 8;
499 rcolor.green = color.green >> 8;
500 rcolor.blue = color.blue >> 8;
501 RGetClosestXColor(rc, &rcolor, &color);
502 } else {
503 rcolor.red = 0;
504 rcolor.green = 0;
505 rcolor.blue = 0;
506 }
507 /* for images with a transparent color */
508 if (image->data[3]) {
509 RCombineImageWithColor(image, &rcolor);
510 }
511
512 switch (toupper(type[0])) {
513 case 'T':
514 texture->width = iwidth;
515 texture->height = iheight;
516 if (!pixmap && !RConvertImage(rc, image, &pixmap)) {
517 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
518 RReleaseImage(image);
519 goto error;
520 }
521 if (image)
522 RReleaseImage(image);
523
524 texture->pixmap = pixmap;
525 texture->color = color;
526 break;
527 case 'S':
528 case 'M':
529 case 'C':
530 {
531 Pixmap tpixmap =
532 XCreatePixmap(dpy, root, scrWidth, scrHeight, DefaultDepth(dpy, scr));
533 XSetForeground(dpy, DefaultGC(dpy, scr), color.pixel);
534 XFillRectangle(dpy, tpixmap, DefaultGC(dpy, scr), 0, 0, scrWidth, scrHeight);
535
536 texture->pixmap = tpixmap;
537 texture->color = color;
538 texture->width = scrWidth;
539 texture->height = scrHeight;
540
541 #ifdef XINERAMA
542 if (xineInfo.count) {
543 int i;
544 for (i = 0; i < xineInfo.count; ++i) {
545 applyImage(rc, texture, image, type[0],
546 xineInfo.screens[i].pos.x, xineInfo.screens[i].pos.y,
547 xineInfo.screens[i].size.width,
548 xineInfo.screens[i].size.height);
549 }
550 } else {
551 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
552 }
553 #else /* !XINERAMA */
554 applyImage(rc, texture, image, type[0], 0, 0, scrWidth, scrHeight);
555 #endif /* !XINERAMA */
556 RReleaseImage(image);
557 }
558 break;
559 }
560 } else if (strcasecmp(type, "thgradient") == 0
561 || strcasecmp(type, "tvgradient") == 0 || strcasecmp(type, "tdgradient") == 0) {
562 XColor color;
563 RColor color1, color2;
564 RImage *image;
565 RImage *gradient;
566 RImage *tiled;
567 Pixmap pixmap;
568 int opaq;
569 char *file;
570 int gtype;
571 int twidth, theight;
572
573 GETSTRORGOTO(val, file, 1, error);
574
575 GETSTRORGOTO(val, tmp, 2, error);
576
577 opaq = atoi(tmp);
578
579 GETSTRORGOTO(val, tmp, 3, error);
580
581 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
582 wwarning("could not parse color %s in texture %s", tmp, text);
583 goto error;
584 }
585
586 color1.red = color.red >> 8;
587 color1.green = color.green >> 8;
588 color1.blue = color.blue >> 8;
589
590 GETSTRORGOTO(val, tmp, 4, error);
591
592 if (!XParseColor(dpy, DefaultColormap(dpy, scr), tmp, &color)) {
593 wwarning("could not parse color %s in texture %s", tmp, text);
594 goto error;
595 }
596
597 color2.red = color.red >> 8;
598 color2.green = color.green >> 8;
599 color2.blue = color.blue >> 8;
600
601 image = loadImage(rc, file);
602 if (!image) {
603 goto error;
604 }
605
606 switch (type[1]) {
607 case 'h':
608 case 'H':
609 gtype = RHorizontalGradient;
610 twidth = scrWidth;
611 theight = image->height > scrHeight ? scrHeight : image->height;
612 break;
613 case 'V':
614 case 'v':
615 gtype = RVerticalGradient;
616 twidth = image->width > scrWidth ? scrWidth : image->width;
617 theight = scrHeight;
618 break;
619 default:
620 gtype = RDiagonalGradient;
621 twidth = scrWidth;
622 theight = scrHeight;
623 break;
624 }
625 gradient = RRenderGradient(twidth, theight, &color1, &color2, gtype);
626
627 if (!gradient) {
628 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
629 RReleaseImage(gradient);
630 RReleaseImage(image);
631 goto error;
632 }
633
634 tiled = RMakeTiledImage(image, twidth, theight);
635 if (!tiled) {
636 wwarning("could not render texture:%s", RMessageForError(RErrorCode));
637 RReleaseImage(gradient);
638 RReleaseImage(image);
639 goto error;
640 }
641 RReleaseImage(image);
642
643 RCombineImagesWithOpaqueness(tiled, gradient, opaq);
644 RReleaseImage(gradient);
645
646 if (!RConvertImage(rc, tiled, &pixmap)) {
647 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
648 RReleaseImage(tiled);
649 goto error;
650 }
651 texture->width = tiled->width;
652 texture->height = tiled->height;
653
654 RReleaseImage(tiled);
655
656 texture->pixmap = pixmap;
657 } else if (strcasecmp(type, "function") == 0) {
658 #ifdef HAVE_DLFCN_H
659 void (*initFunc) (Display *, Colormap);
660 RImage *(*mainFunc) (int, char **, int, int, int);
661 Pixmap pixmap;
662 RImage *image = 0;
663 int success = 0;
664 char *lib, *func, **argv = 0;
665 void *handle = 0;
666 int i, argc;
667
668 if (count < 3)
669 goto function_cleanup;
670
671 /* get the library name */
672 GETSTRORGOTO(val, lib, 1, function_cleanup);
673
674 /* get the function name */
675 GETSTRORGOTO(val, func, 2, function_cleanup);
676
677 argc = count - 2;
678 argv = (char **)wmalloc(argc * sizeof(char *));
679
680 /* get the parameters */
681 argv[0] = func;
682 for (i = 0; i < argc - 1; i++) {
683 GETSTRORGOTO(val, tmp, 3 + i, function_cleanup);
684 argv[i + 1] = wstrdup(tmp);
685 }
686
687 handle = dlopen(lib, RTLD_LAZY);
688 if (!handle) {
689 wwarning("could not find library %s", lib);
690 goto function_cleanup;
691 }
692
693 initFunc = dlsym(handle, "initWindowMaker");
694 if (!initFunc) {
695 wwarning("could not initialize library %s", lib);
696 goto function_cleanup;
697 }
698 initFunc(dpy, DefaultColormap(dpy, scr));
699
700 mainFunc = dlsym(handle, func);
701 if (!mainFunc) {
702 wwarning("could not find function %s::%s", lib, func);
703 goto function_cleanup;
704 }
705 image = mainFunc(argc, argv, scrWidth, scrHeight, 0);
706
707 if (!RConvertImage(rc, image, &pixmap)) {
708 wwarning("could not convert texture:%s", RMessageForError(RErrorCode));
709 goto function_cleanup;
710 }
711 texture->width = scrWidth;
712 texture->height = scrHeight;
713 texture->pixmap = pixmap;
714 success = 1;
715
716 function_cleanup:
717 if (argv) {
718 int i;
719 for (i = 0; i < argc; i++) {
720 wfree(argv[i]);
721 }
722 }
723 if (handle) {
724 dlclose(handle);
725 }
726 if (image) {
727 RReleaseImage(image);
728 }
729 if (!success) {
730 goto error;
731 }
732 #else
733 wwarning("function textures not supported");
734 goto error;
735 #endif
736 } else {
737 wwarning("invalid texture type %s", text);
738 goto error;
739 }
740
741 texture->spec = wstrdup(text);
742
743 return texture;
744
745 error:
746 if (texture)
747 wfree(texture);
748 if (texarray)
749 WMReleasePropList(texarray);
750
751 return NULL;
752 }
753
754 void freeTexture(BackgroundTexture * texture)
755 {
756 if (texture->solid) {
757 unsigned long pixel[1];
758
759 pixel[0] = texture->color.pixel;
760 /* dont free black/white pixels */
761 if (pixel[0] != BlackPixelOfScreen(DefaultScreenOfDisplay(dpy))
762 && pixel[0] != WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
763 XFreeColors(dpy, DefaultColormap(dpy, scr), pixel, 1, 0);
764 }
765 if (texture->pixmap) {
766 XFreePixmap(dpy, texture->pixmap);
767 }
768 wfree(texture->spec);
769 wfree(texture);
770 }
771
772 void setupTexture(RContext * rc, BackgroundTexture ** textures, int *maxTextures, int workspace, char *texture)
773 {
774 BackgroundTexture *newTexture = NULL;
775 int i;
776
777 /* unset the texture */
778 if (!texture) {
779 if (textures[workspace] != NULL) {
780 textures[workspace]->refcount--;
781
782 if (textures[workspace]->refcount == 0)
783 freeTexture(textures[workspace]);
784 }
785 textures[workspace] = NULL;
786 return;
787 }
788
789 if (textures[workspace]
790 && strcasecmp(textures[workspace]->spec, texture) == 0) {
791 /* texture did not change */
792 return;
793 }
794
795 /* check if the same texture is already created */
796 for (i = 0; i < *maxTextures; i++) {
797 if (textures[i] && strcasecmp(textures[i]->spec, texture) == 0) {
798 newTexture = textures[i];
799 break;
800 }
801 }
802
803 if (!newTexture) {
804 /* create the texture */
805 newTexture = parseTexture(rc, texture);
806 }
807 if (!newTexture)
808 return;
809
810 if (textures[workspace] != NULL) {
811
812 textures[workspace]->refcount--;
813
814 if (textures[workspace]->refcount == 0)
815 freeTexture(textures[workspace]);
816 }
817
818 newTexture->refcount++;
819 textures[workspace] = newTexture;
820
821 if (*maxTextures < workspace)
822 *maxTextures = workspace;
823 }
824
825 Pixmap duplicatePixmap(Pixmap pixmap, int width, int height)
826 {
827 Display *tmpDpy;
828 Pixmap copyP;
829
830 /* must open a new display or the RetainPermanent will
831 * leave stuff allocated in RContext unallocated after exit */
832 tmpDpy = XOpenDisplay(display);
833 if (!tmpDpy) {
834 wwarning("could not open display to update background image information");
835
836 return None;
837 } else {
838 XSync(dpy, False);
839
840 copyP = XCreatePixmap(tmpDpy, root, width, height, DefaultDepth(tmpDpy, scr));
841 XCopyArea(tmpDpy, pixmap, copyP, DefaultGC(tmpDpy, scr), 0, 0, width, height, 0, 0);
842 XSync(tmpDpy, False);
843
844 XSetCloseDownMode(tmpDpy, RetainPermanent);
845 XCloseDisplay(tmpDpy);
846 }
847
848 return copyP;
849 }
850
851 static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
852 {
853 return 0;
854 }
855
856 void setPixmapProperty(Pixmap pixmap)
857 {
858 static Atom prop = 0;
859 Atom type;
860 int format;
861 unsigned long length, after;
862 unsigned char *data;
863 int mode;
864
865 if (!prop) {
866 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
867 }
868
869 XGrabServer(dpy);
870
871 /* Clear out the old pixmap */
872 XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
873 &type, &format, &length, &after, &data);
874
875 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
876 XSetErrorHandler(dummyErrorHandler);
877 XKillClient(dpy, *((Pixmap *) data));
878 XSync(dpy, False);
879 XSetErrorHandler(NULL);
880 mode = PropModeReplace;
881 } else {
882 mode = PropModeAppend;
883 }
884 if (pixmap)
885 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
886 else
887 XDeleteProperty(dpy, root, prop);
888
889 XUngrabServer(dpy);
890 XFlush(dpy);
891 }
892
893 void changeTexture(BackgroundTexture * texture)
894 {
895 if (!texture) {
896 return;
897 }
898
899 if (texture->solid) {
900 XSetWindowBackground(dpy, root, texture->color.pixel);
901 } else {
902 XSetWindowBackgroundPixmap(dpy, root, texture->pixmap);
903 }
904 XClearWindow(dpy, root);
905
906 XSync(dpy, False);
907
908 {
909 Pixmap pixmap;
910
911 pixmap = duplicatePixmap(texture->pixmap, texture->width, texture->height);
912
913 setPixmapProperty(pixmap);
914 }
915 }
916
917 int readmsg(int fd, char *buffer, int size)
918 {
919 int count;
920
921 count = 0;
922 while (size > 0) {
923 count = read(fd, buffer, size);
924 if (count < 0)
925 return -1;
926 size -= count;
927 buffer += count;
928 *buffer = 0;
929 }
930
931 return size;
932 }
933
934 /*
935 * Message Format:
936 * sizeSntexture_spec - sets the texture for workspace n
937 * sizeCn - change background texture to the one for workspace n
938 * sizePpath - set the pixmap search path
939 *
940 * n is 4 bytes
941 * size = 4 bytes for length of the message data
942 */
943 void helperLoop(RContext * rc)
944 {
945 BackgroundTexture *textures[WORKSPACE_COUNT];
946 int maxTextures = 0;
947 char buffer[2048], buf[8];
948 int size;
949 int errcount = 4;
950
951 memset(textures, 0, WORKSPACE_COUNT * sizeof(BackgroundTexture *));
952
953 while (1) {
954 int workspace;
955
956 /* get length of message */
957 if (readmsg(0, buffer, 4) < 0) {
958 wsyserror("error reading message from Window Maker");
959 errcount--;
960 if (errcount == 0) {
961 wfatal("quitting");
962 exit(1);
963 }
964 continue;
965 }
966 memcpy(buf, buffer, 4);
967 buf[4] = 0;
968 size = atoi(buf);
969
970 /* get message */
971 if (readmsg(0, buffer, size) < 0) {
972 wsyserror("error reading message from Window Maker");
973 errcount--;
974 if (errcount == 0) {
975 wfatal("quitting");
976 exit(1);
977 }
978 continue;
979 }
980 #ifdef DEBUG
981 printf("RECEIVED %s\n", buffer);
982 #endif
983 if (buffer[0] != 'P' && buffer[0] != 'K') {
984 memcpy(buf, &buffer[1], 4);
985 buf[4] = 0;
986 workspace = atoi(buf);
987 if (workspace < 0 || workspace >= WORKSPACE_COUNT) {
988 wwarning("received message with invalid workspace number %i\n", workspace);
989 continue;
990 }
991 }
992
993 switch (buffer[0]) {
994 case 'S':
995 #ifdef DEBUG
996 printf("set texture %s\n", &buffer[5]);
997 #endif
998 setupTexture(rc, textures, &maxTextures, workspace, &buffer[5]);
999 break;
1000
1001 case 'C':
1002 #ifdef DEBUG
1003 printf("change texture %i\n", workspace);
1004 #endif
1005 if (!textures[workspace]) {
1006 changeTexture(textures[0]);
1007 } else {
1008 changeTexture(textures[workspace]);
1009 }
1010 break;
1011
1012 case 'P':
1013 #ifdef DEBUG
1014 printf("change pixmappath %s\n", &buffer[1]);
1015 #endif
1016 if (PixmapPath)
1017 wfree(PixmapPath);
1018 PixmapPath = wstrdup(&buffer[1]);
1019 break;
1020
1021 case 'U':
1022 #ifdef DEBUG
1023 printf("unset workspace %i\n", workspace);
1024 #endif
1025 setupTexture(rc, textures, &maxTextures, workspace, NULL);
1026 break;
1027
1028 case 'K':
1029 #ifdef DEBUG
1030 printf("exit command\n");
1031 #endif
1032 exit(0);
1033
1034 default:
1035 wwarning("unknown message received");
1036 break;
1037 }
1038 }
1039 }
1040
1041 void updateDomain(char *domain, char *key, char *texture)
1042 {
1043 char *program = "wdwrite";
1044
1045 /* here is a mem leak */
1046 system(wstrconcat("wdwrite ",
1047 wstrconcat(domain, smooth ? " SmoothWorkspaceBack YES" : " SmoothWorkspaceBack NO")));
1048
1049 execlp(program, program, domain, key, texture, NULL);
1050 wwarning("warning could not run \"%s\"", program);
1051 }
1052
1053 char *globalDefaultsPathForDomain(char *domain)
1054 {
1055 char path[1024];
1056
1057 sprintf(path, "%s/WindowMaker/%s", SYSCONFDIR, domain);
1058
1059 return wstrdup(path);
1060 }
1061
1062 static WMPropList *getValueForKey(char *domain, char *keyName)
1063 {
1064 char *path;
1065 WMPropList *key, *val, *d;
1066
1067 key = WMCreatePLString(keyName);
1068
1069 /* try to find PixmapPath in user defaults */
1070 path = wdefaultspathfordomain(domain);
1071 d = WMReadPropListFromFile(path);
1072 if (!d) {
1073 wwarning("could not open domain file %s", path);
1074 }
1075 wfree(path);
1076
1077 if (d && !WMIsPLDictionary(d)) {
1078 WMReleasePropList(d);
1079 d = NULL;
1080 }
1081 if (d) {
1082 val = WMGetFromPLDictionary(d, key);
1083 } else {
1084 val = NULL;
1085 }
1086 /* try to find PixmapPath in global defaults */
1087 if (!val) {
1088 path = globalDefaultsPathForDomain(domain);
1089 if (!path) {
1090 wwarning("could not locate file for domain %s", domain);
1091 d = NULL;
1092 } else {
1093 d = WMReadPropListFromFile(path);
1094 wfree(path);
1095 }
1096
1097 if (d && !WMIsPLDictionary(d)) {
1098 WMReleasePropList(d);
1099 d = NULL;
1100 }
1101 if (d) {
1102 val = WMGetFromPLDictionary(d, key);
1103
1104 } else {
1105 val = NULL;
1106 }
1107 }
1108
1109 if (val)
1110 WMRetainPropList(val);
1111
1112 WMReleasePropList(key);
1113 if (d)
1114 WMReleasePropList(d);
1115
1116 return val;
1117 }
1118
1119 char *getPixmapPath(char *domain)
1120 {
1121 WMPropList *val;
1122 char *ptr, *data;
1123 int len, i, count;
1124
1125 val = getValueForKey(domain, "PixmapPath");
1126
1127 if (!val || !WMIsPLArray(val)) {
1128 if (val)
1129 WMReleasePropList(val);
1130 return wstrdup("");
1131 }
1132
1133 count = WMGetPropListItemCount(val);
1134 len = 0;
1135 for (i = 0; i < count; i++) {
1136 WMPropList *v;
1137
1138 v = WMGetFromPLArray(val, i);
1139 if (!v || !WMIsPLString(v)) {
1140 continue;
1141 }
1142 len += strlen(WMGetFromPLString(v)) + 1;
1143 }
1144
1145 ptr = data = wmalloc(len + 1);
1146 *ptr = 0;
1147
1148 for (i = 0; i < count; i++) {
1149 WMPropList *v;
1150
1151 v = WMGetFromPLArray(val, i);
1152 if (!v || !WMIsPLString(v)) {
1153 continue;
1154 }
1155 strcpy(ptr, WMGetFromPLString(v));
1156
1157 ptr += strlen(WMGetFromPLString(v));
1158 *ptr = ':';
1159 ptr++;
1160 }
1161 if (i > 0)
1162 ptr--;
1163 *(ptr--) = 0;
1164
1165 WMReleasePropList(val);
1166
1167 return data;
1168 }
1169
1170 char *getFullPixmapPath(char *file)
1171 {
1172 char *tmp;
1173
1174 if (!PixmapPath || !(tmp = wfindfile(PixmapPath, file))) {
1175 int bsize = 512;
1176 char *path = wmalloc(bsize);
1177
1178 while (!getcwd(path, bsize)) {
1179 bsize += bsize / 2;
1180 path = wrealloc(path, bsize);
1181 }
1182
1183 tmp = wstrconcat(path, "/");
1184 wfree(path);
1185 path = wstrconcat(tmp, file);
1186 wfree(tmp);
1187
1188 return path;
1189 }
1190
1191 /* the file is in the PixmapPath */
1192 wfree(tmp);
1193
1194 return wstrdup(file);
1195 }
1196
1197 void wAbort()
1198 {
1199 wfatal("aborting");
1200 exit(1);
1201 }
1202
1203 void print_help(char *ProgName)
1204 {
1205 printf("Usage: %s [options] [image]\n", ProgName);
1206 puts("Sets the workspace background to the specified image or a texture and optionally update Window Maker configuration");
1207 puts("");
1208 #define P(m) puts(m)
1209 P(" -display display to use");
1210 P(" -d, --dither dither image");
1211 P(" -m, --match match colors");
1212 P(" -S, --smooth smooth scaled image");
1213 P(" -b, --back-color <color> background color");
1214 P(" -t, --tile tile image");
1215 P(" -e, --center center image");
1216 P(" -s, --scale scale image (default)");
1217 P(" -a, --maxscale scale image and keep aspect ratio");
1218 P(" -u, --update-wmaker update WindowMaker domain database");
1219 P(" -D, --update-domain <domain> update <domain> database");
1220 P(" -c, --colors <cpc> colors per channel to use");
1221 P(" -p, --parse <texture> proplist style texture specification");
1222 P(" -w, --workspace <workspace> update background for the specified workspace");
1223 P(" --version show version of wmsetbg and exit");
1224 P(" --help show this help and exit");
1225 #undef P
1226 }
1227
1228 void changeTextureForWorkspace(char *domain, char *texture, int workspace)
1229 {
1230 WMPropList *array, *val;
1231 char *value;
1232 int j;
1233
1234 val = WMCreatePropListFromDescription(texture);
1235 if (!val) {
1236 wwarning("could not parse texture %s", texture);
1237 return;
1238 }
1239
1240 array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
1241
1242 if (!array) {
1243 array = WMCreatePLArray(NULL, NULL);
1244 }
1245
1246 j = WMGetPropListItemCount(array);
1247 if (workspace >= j) {
1248 WMPropList *empty;
1249
1250 empty = WMCreatePLArray(NULL, NULL);
1251
1252 while (j++ < workspace - 1) {
1253 WMAddToPLArray(array, empty);
1254 }
1255 WMAddToPLArray(array, val);
1256 } else {
1257 WMDeleteFromPLArray(array, workspace);
1258 WMInsertInPLArray(array, workspace, val);
1259 }
1260
1261 value = WMGetPropListDescription(array, False);
1262 updateDomain(domain, "WorkspaceSpecificBack", value);
1263 }
1264
1265 int main(int argc, char **argv)
1266 {
1267 int i;
1268 int helperMode = 0;
1269 RContext *rc;
1270 RContextAttributes rattr;
1271 char *style = "spixmap";
1272 char *back_color = "gray20";
1273 char *image_name = NULL;
1274 char *domain = "WindowMaker";
1275 int update = 0, cpc = 4, render_mode = RDitheredRendering, obey_user = 0;
1276 char *texture = NULL;
1277 int workspace = -1;
1278
1279 signal(SIGINT, SIG_DFL);
1280 signal(SIGTERM, SIG_DFL);
1281 signal(SIGQUIT, SIG_DFL);
1282 signal(SIGSEGV, SIG_DFL);
1283 signal(SIGBUS, SIG_DFL);
1284 signal(SIGFPE, SIG_DFL);
1285 signal(SIGABRT, SIG_DFL);
1286 signal(SIGHUP, SIG_DFL);
1287 signal(SIGPIPE, SIG_DFL);
1288 signal(SIGCHLD, SIG_DFL);
1289
1290 WMInitializeApplication("wmsetbg", &argc, argv);
1291
1292 for (i = 1; i < argc; i++) {
1293 if (strcmp(argv[i], "-helper") == 0) {
1294 helperMode = 1;
1295 } else if (strcmp(argv[i], "-display") == 0) {
1296 i++;
1297 if (i >= argc) {
1298 wfatal("too few arguments for %s\n", argv[i - 1]);
1299 exit(1);
1300 }
1301 display = argv[i];
1302 } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--scale") == 0) {
1303 style = "spixmap";
1304 } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--tile") == 0) {
1305 style = "tpixmap";
1306 } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--center") == 0) {
1307 style = "cpixmap";
1308 } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--maxscale") == 0) {
1309 style = "mpixmap";
1310 } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dither") == 0) {
1311 render_mode = RDitheredRendering;
1312 obey_user++;
1313 } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--match") == 0) {
1314 render_mode = RBestMatchRendering;
1315 obey_user++;
1316 } else if (strcmp(argv[i], "-S") == 0 || strcmp(argv[i], "--smooth") == 0) {
1317 smooth = True;
1318 } else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--update-wmaker") == 0) {
1319 update++;
1320 } else if (strcmp(argv[i], "-D") == 0 || strcmp(argv[i], "--update-domain") == 0) {
1321 update++;
1322 i++;
1323 if (i >= argc) {
1324 wfatal("too few arguments for %s\n", argv[i - 1]);
1325 exit(1);
1326 }
1327 domain = wstrdup(argv[i]);
1328 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--colors") == 0) {
1329 i++;
1330 if (i >= argc) {
1331 wfatal("too few arguments for %s\n", argv[i - 1]);
1332 exit(1);
1333 }
1334 if (sscanf(argv[i], "%i", &cpc) != 1) {
1335 wfatal("bad value for colors per channel: \"%s\"\n", argv[i]);
1336 exit(1);
1337 }
1338 } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--back-color") == 0) {
1339 i++;
1340 if (i >= argc) {
1341 wfatal("too few arguments for %s\n", argv[i - 1]);
1342 exit(1);
1343 }
1344 back_color = argv[i];
1345 } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parse") == 0) {
1346 i++;
1347 if (i >= argc) {
1348 wfatal("too few arguments for %s\n", argv[i - 1]);
1349 exit(1);
1350 }
1351 texture = argv[i];
1352 } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--workspace") == 0) {
1353 i++;
1354 if (i >= argc) {
1355 wfatal("too few arguments for %s\n", argv[i - 1]);
1356 exit(1);
1357 }
1358 if (sscanf(argv[i], "%i", &workspace) != 1) {
1359 wfatal("bad value for workspace number: \"%s\"", argv[i]);
1360 exit(1);
1361 }
1362 } else if (strcmp(argv[i], "--version") == 0) {
1363
1364 printf(PROG_VERSION);
1365 exit(0);
1366
1367 } else if (strcmp(argv[i], "--help") == 0) {
1368 print_help(argv[0]);
1369 exit(0);
1370 } else if (argv[i][0] != '-') {
1371 image_name = argv[i];
1372 } else {
1373 printf("%s: invalid argument '%s'\n", argv[0], argv[i]);
1374 printf("Try '%s --help' for more information\n", argv[0]);
1375 exit(1);
1376 }
1377 }
1378 if (!image_name && !texture && !helperMode) {
1379 printf("%s: you must specify a image file name or a texture\n", argv[0]);
1380 printf("Try '%s --help' for more information\n", argv[0]);
1381 exit(1);
1382 }
1383
1384 PixmapPath = getPixmapPath(domain);
1385 if (!smooth) {
1386 WMPropList *val;
1387 #if 0 /* some problem with Alpha... TODO: check if its right */
1388 val = WMGetFromPLDictionary(domain, WMCreatePLString("SmoothWorkspaceBack"));
1389 #else
1390 val = getValueForKey(domain, "SmoothWorkspaceBack");
1391 #endif
1392
1393 if (val && WMIsPLString(val) && strcasecmp(WMGetFromPLString(val), "YES") == 0)
1394 smooth = True;
1395 }
1396
1397 dpy = XOpenDisplay(display);
1398 if (!dpy) {
1399 wfatal("could not open display");
1400 exit(1);
1401 }
1402 #if 0
1403 XSynchronize(dpy, 1);
1404 #endif
1405
1406 root = DefaultRootWindow(dpy);
1407
1408 scr = DefaultScreen(dpy);
1409
1410 scrWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
1411 scrHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
1412 scrX = scrY = 0;
1413
1414 initXinerama();
1415
1416 if (!obey_user && DefaultDepth(dpy, scr) <= 8)
1417 render_mode = RDitheredRendering;
1418
1419 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap | RC_DefaultVisual;
1420 rattr.render_mode = render_mode;
1421 rattr.colors_per_channel = cpc;
1422 rattr.standard_colormap_mode = RCreateStdColormap;
1423
1424 rc = RCreateContext(dpy, scr, &rattr);
1425
1426 if (!rc) {
1427 rattr.standard_colormap_mode = RIgnoreStdColormap;
1428 rc = RCreateContext(dpy, scr, &rattr);
1429 }
1430
1431 if (!rc) {
1432 wfatal("could not initialize wrlib: %s", RMessageForError(RErrorCode));
1433 exit(1);
1434 }
1435
1436 if (helperMode) {
1437 /* lower priority, so that it wont use all the CPU */
1438 nice(15);
1439
1440 helperLoop(rc);
1441 } else {
1442 BackgroundTexture *tex;
1443 char buffer[4098];
1444
1445 if (!texture) {
1446 char *image_path = getFullPixmapPath(image_name);
1447
1448 sprintf(buffer, "(%s, \"%s\", %s)", style, image_path, back_color);
1449 wfree(image_path);
1450 texture = (char *)buffer;
1451 }
1452
1453 if (update && workspace < 0) {
1454 updateDomain(domain, "WorkspaceBack", texture);
1455 }
1456
1457 tex = parseTexture(rc, texture);
1458 if (!tex)
1459 exit(1);
1460
1461 if (workspace < 0)
1462 changeTexture(tex);
1463 else {
1464 /* always update domain */
1465 changeTextureForWorkspace(domain, texture, workspace);
1466 }
1467 }
1468
1469 return 0;
1470 }