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 }