Initial revision
[wmaker-crm.git] / util / wmsetbg.c
blob3a3bf325960e24ae77904c7996c5e948bd159fa7
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.
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <X11/Xatom.h>
31 #include <string.h>
32 #include <wraster.h>
33 #include <pwd.h>
34 #include <sys/types.h>
36 #include "../src/wconfig.h"
38 #include <proplist.h>
41 #ifdef DEBUG
42 #include <sys/time.h>
43 #include <time.h>
44 #endif
46 #define WTP_TILE 1
47 #define WTP_SCALE 2
48 #define WTP_CENTER 3
50 char *ProgName;
53 /* Alfredo please take a look at this function. I don't like the way
54 * it sends the XKillClient. Should it interfere this way with the rest?
55 * This was added by the patch to allow the transparent background for Eterm.
56 * Also for this purpose Eterm have a program named Esetroot.
57 * People wanting that feature can use that instead of wmsetbg. Why do we
58 * need to patch wmsetbg to do this?
59 * In case you want to keep it, please also check the way it does
60 * the PropModeAppend. -Dan */
61 static void
62 setPixmapProperty(Pixmap pixmap, Display *dpy, Window root)
64 Atom prop, type;
65 int format;
66 unsigned long length, after;
67 unsigned char *data;
69 /* This will locate the property, creating it if it doesn't exist */
70 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
72 if (prop == None)
73 return;
75 /* Clear out the old pixmap */
76 XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
77 &type, &format, &length, &after, &data);
79 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
80 XKillClient(dpy, *((Pixmap *)data));
82 XDeleteProperty(dpy, root, prop);
84 prop = XInternAtom(dpy, "_XROOTPMAP_ID", True);
85 if (prop == None)
86 return;
88 /* Now add the new one. We use PropModeAppend because PropModeReplace
89 doesn't seem to work if there isn't already a property there. */
90 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeAppend,
91 (unsigned char *) &pixmap, 1);
93 XFlush(dpy);
94 XSetCloseDownMode(dpy, RetainPermanent);
95 XFlush(dpy);
99 void*
100 wmalloc(size_t size)
102 void *ptr;
103 ptr = malloc(size);
104 if (!ptr) {
105 perror(ProgName);
106 exit(1);
108 return ptr;
111 char*
112 gethomedir()
114 char *home = getenv("HOME");
115 struct passwd *user;
117 if (home)
118 return home;
120 user = getpwuid(getuid());
121 if (!user) {
122 perror(ProgName);
123 return "/";
125 if (!user->pw_dir) {
126 return "/";
127 } else {
128 return user->pw_dir;
134 void wAbort()
136 exit(1);
140 void
141 print_help()
143 printf("usage: %s [-options] image\n", ProgName);
144 puts("options:");
145 puts(" -d dither image");
146 puts(" -m match colors");
147 puts(" -b <color> background color");
148 puts(" -t tile image");
149 puts(" -e center image");
150 puts(" -s scale image (default)");
151 puts(" -u update WindowMaker domain database");
152 puts(" -D <domain> update <domain> database");
153 puts(" -c <cpc> colors per channel to use");
157 char*
158 defaultsPathForDomain(char *domain)
160 char path[1024];
161 char *gspath, *tmp;
163 gspath = getenv("GNUSTEP_USER_ROOT");
164 if (gspath) {
165 strcpy(path, gspath);
166 strcat(path, "/");
167 } else {
168 strcpy(path, gethomedir());
169 strcat(path, "/GNUstep/");
171 strcat(path, DEFAULTS_DIR);
172 strcat(path, "/");
173 strcat(path, domain);
175 tmp = wmalloc(strlen(path)+2);
176 strcpy(tmp, path);
178 return tmp;
182 char *wstrdup(char *str)
184 return strcpy(wmalloc(strlen(str)+1), str);
188 /* Returns an array of pointers to the pixmap paths, doing ~ expansion */
189 static char**
190 getPixmapPath(char *domain)
192 char **ret;
193 char *path;
194 proplist_t prop, pixmap_path, key, value;
195 int count, i;
197 path = defaultsPathForDomain(domain);
198 if (!path)
199 return NULL;
201 prop = PLGetProplistWithPath(path);
202 if (!prop || !PLIsDictionary(prop))
203 return NULL;
205 key = PLMakeString("PixmapPath");
206 pixmap_path = PLGetDictionaryEntry(prop, key);
207 PLRelease(key);
208 if (!pixmap_path || !PLIsArray(pixmap_path))
209 return NULL;
211 count = PLGetNumberOfElements(pixmap_path);
212 if (count < 1)
213 return NULL;
215 ret = wmalloc(sizeof(char*)*(count+1));
217 for (i=0; i<count; i++) {
218 value = PLGetArrayElement(pixmap_path, i);
219 if (!value || !PLIsString(value))
220 break;
221 ret[i] = wstrdup(PLGetString(value));
222 if (ret[i][0]=='~' && ret[i][1]=='/') {
223 /* home is statically allocated. Don't free it */
224 char *fullpath, *home=gethomedir();
226 fullpath = wmalloc(strlen(home)+strlen(ret[i]));
227 strcpy(fullpath, home);
228 strcat(fullpath, &ret[i][1]);
229 free(ret[i]);
230 ret[i] = fullpath;
233 ret[i] = NULL;
234 return ret;
239 main(int argc, char **argv)
241 Display *dpy;
242 Window root_win;
243 RContextAttributes rattr;
244 int screen_number, default_depth, i, style = WTP_SCALE;
245 int scr_width, scr_height;
246 RContext *rcontext;
247 RImage *image, *tmp;
248 Pixmap secretBuffer = None;
249 Pixmap pixmap;
250 XColor xcolor;
251 char *back_color = "black";
252 char *image_name = NULL;
253 char *domain = "WindowMaker";
254 char *program = "wdwrite";
255 int update=0, cpc=4, render_mode=RM_MATCH, obey_user=0;
256 #ifdef DEBUG
257 double t1, t2, total, t;
258 struct timeval timev;
259 #endif
262 ProgName = strrchr(argv[0],'/');
263 if (!ProgName)
264 ProgName = argv[0];
265 else
266 ProgName++;
268 if (argc>1) {
269 for (i=1; i<argc; i++) {
270 if (strcmp(argv[i], "-s")==0) {
271 style = WTP_SCALE;
272 } else if (strcmp(argv[i], "-t")==0) {
273 style = WTP_TILE;
274 } else if (strcmp(argv[i], "-e")==0) {
275 style = WTP_CENTER;
276 } else if (strcmp(argv[i], "-d")==0) {
277 render_mode = RM_DITHER;
278 obey_user++;
279 } else if (strcmp(argv[i], "-m")==0) {
280 render_mode = RM_MATCH;
281 obey_user++;
282 } else if (strcmp(argv[i], "-u")==0) {
283 update++;
284 } else if (strcmp(argv[i], "-D")==0) {
285 update++;
286 i++;
287 if (i>=argc) {
288 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
289 exit(0);
291 domain = wstrdup(argv[i]);
292 } else if (strcmp(argv[i], "-c")==0) {
293 i++;
294 if (i>=argc) {
295 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
296 exit(0);
298 if (sscanf(argv[i], "%i", &cpc)!=1) {
299 fprintf(stderr, "bad value for colors per channel: \"%s\"\n", argv[i]);
300 exit(0);
302 } else if (strcmp(argv[i], "-b")==0) {
303 i++;
304 if (i>=argc) {
305 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
306 exit(0);
308 back_color = argv[i];
309 } else if (strcmp(argv[i], "-x")==0) {
310 /* secret option:renders the pixmap in the supplied drawable */
311 i++;
312 if (i>=argc ||
313 sscanf(argv[i], "%x", (unsigned*)&secretBuffer)!=1) {
314 print_help();
315 exit(1);
317 } else if (argv[i][0] != '-') {
318 image_name = argv[i];
319 } else {
320 print_help();
321 exit(1);
326 if (image_name == NULL) {
327 print_help();
328 exit(1);
330 if (update) {
331 char *value = wmalloc(sizeof(image_name) + 30);
332 char *tmp=image_name, **paths;
333 int i;
335 /* should we read PixmapPath from the same file as we write into ? */
336 paths = getPixmapPath("WindowMaker");
337 if (paths) {
338 for(i=0; paths[i]!=NULL; i++) {
339 if ((tmp = strstr(image_name, paths[i])) != NULL &&
340 tmp == image_name) {
341 tmp += strlen(paths[i]);
342 while(*tmp=='/') tmp++;
343 break;
348 if (!tmp)
349 tmp = image_name;
351 if (style == WTP_TILE)
352 strcpy(value, "(tpixmap, \"");
353 else if (style == WTP_SCALE)
354 strcpy(value, "(spixmap, \"");
355 else
356 strcpy(value, "(cpixmap, \"");
357 strcat(value, tmp);
358 strcat(value, "\", \"");
359 strcat(value, back_color);
360 strcat(value, "\")");
361 execlp(program, program, domain, "WorkspaceBack", value, NULL);
362 printf("%s: warning could not run \"%s\"\n", ProgName, program);
363 /* Do not exit. At least try to put the image in the background */
364 /* Won't this waste CPU for nothing? We're going to be called again,
365 * anyways. -Alfredo */
366 /* If it fails to update the WindowMaker domain with "wdwrite" we
367 * won't be called again, because Window Maker will not notice any
368 * change. If it reaches this point, this means it failed.
369 * On success it will never get here. -Dan */
370 /*exit(0);*/
373 dpy = XOpenDisplay("");
374 if (!dpy) {
375 puts("Could not open display!");
376 exit(1);
378 #ifdef DEBUG
379 XSynchronize(dpy, True);
380 #endif
381 screen_number = DefaultScreen(dpy);
382 root_win = RootWindow(dpy, screen_number);
383 default_depth = DefaultDepth(dpy, screen_number);
384 scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
385 scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
387 if (!XParseColor(dpy, DefaultColormap(dpy, screen_number), back_color,
388 &xcolor)) {
389 printf("invalid color %s\n", back_color);
390 exit(1);
393 if (!obey_user && default_depth <= 8)
394 render_mode = RM_DITHER;
396 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
397 rattr.render_mode = render_mode;
398 rattr.colors_per_channel = cpc;
400 rcontext = RCreateContext(dpy, screen_number, &rattr);
401 if (!rcontext) {
402 printf("could not initialize graphics library context: %s\n",
403 RErrorString);
404 exit(1);
407 #ifdef DEBUG
408 gettimeofday(&timev, NULL);
409 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
410 t = t1;
411 #endif
412 image = RLoadImage(rcontext, image_name, 0);
413 #ifdef DEBUG
414 gettimeofday(&timev, NULL);
415 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
416 total = t2 - t1;
417 printf("load image in %f sec\n", total);
418 #endif
420 if (!image) {
421 printf("could not load image %s:%s\n", image_name, RErrorString);
422 exit(1);
425 #ifdef DEBUG
426 gettimeofday(&timev, NULL);
427 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
428 #endif
429 if (style == WTP_SCALE) {
430 tmp = RScaleImage(image, scr_width, scr_height);
431 if (!tmp) {
432 printf("could not scale image: %s\n", image_name);
433 exit(1);
435 RDestroyImage(image);
436 image = tmp;
437 } else if (style==WTP_CENTER && (image->width!=scr_width
438 || image->height!=scr_height)) {
439 RColor color;
441 color.red = xcolor.red>>8;
442 color.green = xcolor.green>>8;
443 color.blue = xcolor.blue>>8;
444 color.alpha = 255;
445 tmp = RMakeCenteredImage(image, scr_width, scr_height, &color);
446 if (!tmp) {
447 printf("could not create centered image: %s\n", image_name);
448 exit(1);
450 RDestroyImage(image);
451 image = tmp;
453 #ifdef DEBUG
454 gettimeofday(&timev, NULL);
455 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
456 total = t2 - t1;
457 printf("scale image in %f sec\n", total);
459 gettimeofday(&timev, NULL);
460 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
461 #endif
462 RConvertImage(rcontext, image, &pixmap);
463 #ifdef DEBUG
464 gettimeofday(&timev, NULL);
465 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
466 total = t2 - t1;
467 printf("convert image to pixmap in %f sec\n", total);
468 total = t2 - t;
469 printf("total image proccessing in %f sec\n", total);
470 #endif
471 RDestroyImage(image);
472 if (secretBuffer==None) {
473 setPixmapProperty(pixmap, dpy, root_win);
474 XSetWindowBackgroundPixmap(dpy, root_win, pixmap);
475 XClearWindow(dpy, root_win);
476 } else {
477 XCopyArea(dpy, pixmap, secretBuffer, DefaultGC(dpy, screen_number),
478 0, 0, scr_width, scr_height, 0, 0);
480 XSync(dpy, False);
481 XCloseDisplay(dpy);
482 #ifdef DEBUG
483 gettimeofday(&timev, NULL);
484 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
485 total = t2 - t;
486 printf("total proccessing time: %f sec\n", total);
487 #endif
488 if (secretBuffer)
489 exit(123);
490 else
491 exit(0);