Updating to version 0.20.2
[wmaker-crm.git] / util / wmsetbg.c
blob1eeaebafa4be0104a456114b449988bb0a1d307a
1 /* wmsetbg.c- sets root window background image
3 * WindowMaker window manager
4 *
5 * Copyright (c) 1998 Dan Pascu
6 * Copyright (c) 1998 Alfredo K. Kojima
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30 #include <string.h>
31 #include <wraster.h>
32 #include <pwd.h>
33 #include <sys/types.h>
35 #include "../src/wconfig.h"
37 #include <proplist.h>
40 #ifdef DEBUG
41 #include <sys/time.h>
42 #include <time.h>
43 #endif
45 #define WTP_TILE 1
46 #define WTP_SCALE 2
47 #define WTP_CENTER 3
49 char *ProgName;
51 #ifdef at_one_step_from_the_trash
52 /* Alfredo please take a look at this function. I don't like the way
53 * it sends the XKillClient. Should it interfere this way with the rest?
54 * This was added by the patch to allow the transparent background for Eterm.
55 * Also for this purpose Eterm have a program named Esetroot.
56 * People wanting that feature can use that instead of wmsetbg. Why do we
57 * need to patch wmsetbg to do this?
58 * In case you want to keep it, please also check the way it does
59 * the PropModeAppend. -Dan */
60 static void
61 setPixmapProperty(Pixmap pixmap, Display *dpy, Window root)
63 Atom prop, type;
64 int format;
65 unsigned long length, after;
66 unsigned char *data;
68 /* This will locate the property, creating it if it doesn't exist */
69 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
71 if (prop == None)
72 return;
74 /* Clear out the old pixmap */
75 XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
76 &type, &format, &length, &after, &data);
78 /* I think this is OK -Alfredo */
79 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
80 XKillClient(dpy, *((Pixmap *)data));
82 XDeleteProperty(dpy, root, prop);
84 /* I don't understand this one. The atom is *always* created in
85 the previous XInternAtom() -Alfredo */
86 prop = XInternAtom(dpy, "_XROOTPMAP_ID", True);
87 if (prop == None)
88 return;
90 /* Now add the new one. We use PropModeAppend because PropModeReplace
91 doesn't seem to work if there isn't already a property there. */
92 /* Must be something wrong with this code.
93 * Anyways, better do a XGrabServer() between the XDeleteProperty() and
94 * this XChangeProperty() if things will be this way. -Alfredo */
95 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeAppend,
96 (unsigned char *) &pixmap, 1);
98 XFlush(dpy);
99 /* Potential resource leak. Must check the rest of the
100 * program for other resources that rely on XCloseDisplay() to
101 * free them. -Alfredo */
102 XSetCloseDownMode(dpy, RetainPermanent);
103 XFlush(dpy);
105 #endif
107 void*
108 wmalloc(size_t size)
110 void *ptr;
111 ptr = malloc(size);
112 if (!ptr) {
113 perror(ProgName);
114 exit(1);
116 return ptr;
119 char*
120 gethomedir()
122 char *home = getenv("HOME");
123 struct passwd *user;
125 if (home)
126 return home;
128 user = getpwuid(getuid());
129 if (!user) {
130 perror(ProgName);
131 return "/";
133 if (!user->pw_dir) {
134 return "/";
135 } else {
136 return user->pw_dir;
142 void wAbort()
144 exit(1);
148 void
149 print_help()
151 printf("usage: %s [-options] image\n", ProgName);
152 puts("options:");
153 puts(" -d dither image");
154 puts(" -m match colors");
155 puts(" -b <color> background color");
156 puts(" -t tile image");
157 puts(" -e center image");
158 puts(" -s scale image (default)");
159 puts(" -u update WindowMaker domain database");
160 puts(" -D <domain> update <domain> database");
161 puts(" -c <cpc> colors per channel to use");
165 char*
166 defaultsPathForDomain(char *domain)
168 char path[1024];
169 char *gspath, *tmp;
171 gspath = getenv("GNUSTEP_USER_ROOT");
172 if (gspath) {
173 strcpy(path, gspath);
174 strcat(path, "/");
175 } else {
176 strcpy(path, gethomedir());
177 strcat(path, "/GNUstep/");
179 strcat(path, DEFAULTS_DIR);
180 strcat(path, "/");
181 strcat(path, domain);
183 tmp = wmalloc(strlen(path)+2);
184 strcpy(tmp, path);
186 return tmp;
190 char *wstrdup(char *str)
192 return strcpy(wmalloc(strlen(str)+1), str);
196 /* Returns an array of pointers to the pixmap paths, doing ~ expansion */
197 static char**
198 getPixmapPath(char *domain)
200 char **ret;
201 char *path;
202 proplist_t prop, pixmap_path, key, value;
203 int count, i;
205 path = defaultsPathForDomain(domain);
206 if (!path)
207 return NULL;
209 prop = PLGetProplistWithPath(path);
210 if (!prop || !PLIsDictionary(prop))
211 return NULL;
213 key = PLMakeString("PixmapPath");
214 pixmap_path = PLGetDictionaryEntry(prop, key);
215 PLRelease(key);
216 if (!pixmap_path || !PLIsArray(pixmap_path))
217 return NULL;
219 count = PLGetNumberOfElements(pixmap_path);
220 if (count < 1)
221 return NULL;
223 ret = wmalloc(sizeof(char*)*(count+1));
225 for (i=0; i<count; i++) {
226 value = PLGetArrayElement(pixmap_path, i);
227 if (!value || !PLIsString(value))
228 break;
229 ret[i] = wstrdup(PLGetString(value));
230 if (ret[i][0]=='~' && ret[i][1]=='/') {
231 /* home is statically allocated. Don't free it */
232 char *fullpath, *home=gethomedir();
234 fullpath = wmalloc(strlen(home)+strlen(ret[i]));
235 strcpy(fullpath, home);
236 strcat(fullpath, &ret[i][1]);
237 free(ret[i]);
238 ret[i] = fullpath;
241 ret[i] = NULL;
242 return ret;
252 main(int argc, char **argv)
254 Display *dpy;
255 Window root_win;
256 RContextAttributes rattr;
257 int screen_number, default_depth, i, style = WTP_SCALE;
258 int scr_width, scr_height;
259 RContext *rcontext;
260 RImage *image, *tmp;
261 Pixmap secretBuffer = None;
262 Pixmap pixmap;
263 XColor xcolor;
264 char *back_color = "black";
265 char *image_name = NULL;
266 char *domain = "WindowMaker";
267 char *program = "wdwrite";
268 int update=0, cpc=4, render_mode=RM_MATCH, obey_user=0;
269 #ifdef DEBUG
270 double t1, t2, total, t;
271 struct timeval timev;
272 #endif
275 ProgName = strrchr(argv[0],'/');
276 if (!ProgName)
277 ProgName = argv[0];
278 else
279 ProgName++;
281 if (argc>1) {
282 for (i=1; i<argc; i++) {
283 if (strcmp(argv[i], "-s")==0) {
284 style = WTP_SCALE;
285 } else if (strcmp(argv[i], "-t")==0) {
286 style = WTP_TILE;
287 } else if (strcmp(argv[i], "-e")==0) {
288 style = WTP_CENTER;
289 } else if (strcmp(argv[i], "-d")==0) {
290 render_mode = RM_DITHER;
291 obey_user++;
292 } else if (strcmp(argv[i], "-m")==0) {
293 render_mode = RM_MATCH;
294 obey_user++;
295 } else if (strcmp(argv[i], "-u")==0) {
296 update++;
297 } else if (strcmp(argv[i], "-D")==0) {
298 update++;
299 i++;
300 if (i>=argc) {
301 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
302 exit(0);
304 domain = wstrdup(argv[i]);
305 } else if (strcmp(argv[i], "-c")==0) {
306 i++;
307 if (i>=argc) {
308 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
309 exit(0);
311 if (sscanf(argv[i], "%i", &cpc)!=1) {
312 fprintf(stderr, "bad value for colors per channel: \"%s\"\n", argv[i]);
313 exit(0);
315 } else if (strcmp(argv[i], "-b")==0) {
316 i++;
317 if (i>=argc) {
318 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
319 exit(0);
321 back_color = argv[i];
322 } else if (strcmp(argv[i], "-x")==0) {
323 /* secret option:renders the pixmap in the supplied drawable */
324 i++;
325 if (i>=argc ||
326 sscanf(argv[i], "%x", (unsigned*)&secretBuffer)!=1) {
327 print_help();
328 exit(1);
330 } else if (argv[i][0] != '-') {
331 image_name = argv[i];
332 } else {
333 print_help();
334 exit(1);
339 if (image_name == NULL) {
340 print_help();
341 exit(1);
343 if (update) {
344 char *value = wmalloc(sizeof(image_name) + 30);
345 char *tmp=image_name, **paths;
346 int i;
348 /* should we read PixmapPath from the same file as we write into ? */
349 paths = getPixmapPath("WindowMaker");
350 if (paths) {
351 for(i=0; paths[i]!=NULL; i++) {
352 if ((tmp = strstr(image_name, paths[i])) != NULL &&
353 tmp == image_name) {
354 tmp += strlen(paths[i]);
355 while(*tmp=='/') tmp++;
356 break;
361 if (!tmp)
362 tmp = image_name;
364 if (style == WTP_TILE)
365 strcpy(value, "(tpixmap, \"");
366 else if (style == WTP_SCALE)
367 strcpy(value, "(spixmap, \"");
368 else
369 strcpy(value, "(cpixmap, \"");
370 strcat(value, tmp);
371 strcat(value, "\", \"");
372 strcat(value, back_color);
373 strcat(value, "\")");
374 execlp(program, program, domain, "WorkspaceBack", value, NULL);
375 printf("%s: warning could not run \"%s\"\n", ProgName, program);
376 /* Do not exit. At least try to put the image in the background */
377 /* Won't this waste CPU for nothing? We're going to be called again,
378 * anyways. -Alfredo */
379 /* If it fails to update the WindowMaker domain with "wdwrite" we
380 * won't be called again, because Window Maker will not notice any
381 * change. If it reaches this point, this means it failed.
382 * On success it will never get here. -Dan */
383 /*exit(0);*/
386 dpy = XOpenDisplay("");
387 if (!dpy) {
388 puts("Could not open display!");
389 exit(1);
391 #ifdef DEBUG
392 XSynchronize(dpy, True);
393 #endif
394 screen_number = DefaultScreen(dpy);
395 root_win = RootWindow(dpy, screen_number);
396 default_depth = DefaultDepth(dpy, screen_number);
397 scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
398 scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
400 if (!XParseColor(dpy, DefaultColormap(dpy, screen_number), back_color,
401 &xcolor)) {
402 printf("invalid color %s\n", back_color);
403 exit(1);
406 if (!obey_user && default_depth <= 8)
407 render_mode = RM_DITHER;
409 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
410 rattr.render_mode = render_mode;
411 rattr.colors_per_channel = cpc;
413 rcontext = RCreateContext(dpy, screen_number, &rattr);
414 if (!rcontext) {
415 printf("could not initialize graphics library context: %s\n",
416 RMessageForError(RErrorCode));
417 exit(1);
420 #ifdef DEBUG
421 gettimeofday(&timev, NULL);
422 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
423 t = t1;
424 #endif
425 image = RLoadImage(rcontext, image_name, 0);
426 #ifdef DEBUG
427 gettimeofday(&timev, NULL);
428 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
429 total = t2 - t1;
430 printf("load image in %f sec\n", total);
431 #endif
433 if (!image) {
434 printf("could not load image %s:%s\n", image_name, RMessageForError(RErrorCode));
435 exit(1);
438 #ifdef DEBUG
439 gettimeofday(&timev, NULL);
440 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
441 #endif
442 if (style == WTP_SCALE) {
443 tmp = RScaleImage(image, scr_width, scr_height);
444 if (!tmp) {
445 printf("could not scale image: %s\n", image_name);
446 exit(1);
448 RDestroyImage(image);
449 image = tmp;
450 } else if (style==WTP_CENTER && (image->width!=scr_width
451 || image->height!=scr_height)) {
452 RColor color;
454 color.red = xcolor.red>>8;
455 color.green = xcolor.green>>8;
456 color.blue = xcolor.blue>>8;
457 color.alpha = 255;
458 tmp = RMakeCenteredImage(image, scr_width, scr_height, &color);
459 if (!tmp) {
460 printf("could not create centered image: %s\n", image_name);
461 exit(1);
463 RDestroyImage(image);
464 image = tmp;
466 #ifdef DEBUG
467 gettimeofday(&timev, NULL);
468 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
469 total = t2 - t1;
470 printf("scale image in %f sec\n", total);
472 gettimeofday(&timev, NULL);
473 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
474 #endif
475 RConvertImage(rcontext, image, &pixmap);
476 #ifdef DEBUG
477 gettimeofday(&timev, NULL);
478 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
479 total = t2 - t1;
480 printf("convert image to pixmap in %f sec\n", total);
481 total = t2 - t;
482 printf("total image proccessing in %f sec\n", total);
483 #endif
484 RDestroyImage(image);
485 if (secretBuffer==None) {
486 #ifdef at_one_step_from_the_trash
487 setPixmapProperty(pixmap, dpy, root_win);
488 #endif
489 XSetWindowBackgroundPixmap(dpy, root_win, pixmap);
490 XClearWindow(dpy, root_win);
491 } else {
492 XCopyArea(dpy, pixmap, secretBuffer, DefaultGC(dpy, screen_number),
493 0, 0, scr_width, scr_height, 0, 0);
495 XSync(dpy, False);
496 XCloseDisplay(dpy);
497 #ifdef DEBUG
498 gettimeofday(&timev, NULL);
499 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
500 total = t2 - t;
501 printf("total proccessing time: %f sec\n", total);
502 #endif
503 if (secretBuffer)
504 exit(123);
505 else
506 exit(0);