Window Maker 0.20.3
[wmaker-crm.git] / util / wmsetbg.c
blob1504d5f2dfc118d9badd9459a5a5ba187264f8b9
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 #define fuckin_crap
53 #ifdef fuckin_crap
54 /* Alfredo please take a look at this function. I don't like the way
55 * it sends the XKillClient. Should it interfere this way with the rest?
56 * This was added by the patch to allow the transparent background for Eterm.
57 * Also for this purpose Eterm have a program named Esetroot.
58 * People wanting that feature can use that instead of wmsetbg. Why do we
59 * need to patch wmsetbg to do this?
60 * In case you want to keep it, please also check the way it does
61 * the PropModeAppend. -Dan */
62 static void
63 setPixmapProperty(Pixmap pixmap, Display *dpy, Window root)
65 Atom prop, type;
66 int format;
67 unsigned long length, after;
68 unsigned char *data;
70 /* This will locate the property, creating it if it doesn't exist */
71 prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
73 if (prop == None)
74 return;
76 /* Clear out the old pixmap */
77 XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
78 &type, &format, &length, &after, &data);
80 /* I think this is OK -Alfredo */
81 if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
82 XKillClient(dpy, *((Pixmap *)data));
84 XDeleteProperty(dpy, root, prop);
86 /* I don't understand this one. The atom is *always* created in
87 the previous XInternAtom() -Alfredo */
88 prop = XInternAtom(dpy, "_XROOTPMAP_ID", True);
89 if (prop == None)
90 return;
92 /* Now add the new one. We use PropModeAppend because PropModeReplace
93 doesn't seem to work if there isn't already a property there. */
94 /* Must be something wrong with this code.
95 * Anyways, better do a XGrabServer() between the XDeleteProperty() and
96 * this XChangeProperty() if things will be this way. -Alfredo */
97 XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeAppend,
98 (unsigned char *) &pixmap, 1);
100 XFlush(dpy);
101 /* Potential resource leak. Must check the rest of the
102 * program for other resources that rely on XCloseDisplay() to
103 * free them. -Alfredo */
104 XSetCloseDownMode(dpy, RetainPermanent);
105 XFlush(dpy);
107 #endif
109 void*
110 wmalloc(size_t size)
112 void *ptr;
113 ptr = malloc(size);
114 if (!ptr) {
115 perror(ProgName);
116 exit(1);
118 return ptr;
121 char*
122 gethomedir()
124 char *home = getenv("HOME");
125 struct passwd *user;
127 if (home)
128 return home;
130 user = getpwuid(getuid());
131 if (!user) {
132 perror(ProgName);
133 return "/";
135 if (!user->pw_dir) {
136 return "/";
137 } else {
138 return user->pw_dir;
144 void wAbort()
146 exit(1);
150 void
151 print_help()
153 printf("usage: %s [-options] image\n", ProgName);
154 puts("options:");
155 puts(" -d dither image");
156 puts(" -m match colors");
157 puts(" -b <color> background color");
158 puts(" -t tile image");
159 puts(" -e center image");
160 puts(" -s scale image (default)");
161 puts(" -u update WindowMaker domain database");
162 puts(" -D <domain> update <domain> database");
163 puts(" -c <cpc> colors per channel to use");
167 char*
168 defaultsPathForDomain(char *domain)
170 char path[1024];
171 char *gspath, *tmp;
173 gspath = getenv("GNUSTEP_USER_ROOT");
174 if (gspath) {
175 strcpy(path, gspath);
176 strcat(path, "/");
177 } else {
178 strcpy(path, gethomedir());
179 strcat(path, "/GNUstep/");
181 strcat(path, DEFAULTS_DIR);
182 strcat(path, "/");
183 strcat(path, domain);
185 tmp = wmalloc(strlen(path)+2);
186 strcpy(tmp, path);
188 return tmp;
192 char *wstrdup(char *str)
194 return strcpy(wmalloc(strlen(str)+1), str);
198 /* Returns an array of pointers to the pixmap paths, doing ~ expansion */
199 static char**
200 getPixmapPath(char *domain)
202 char **ret;
203 char *path;
204 proplist_t prop, pixmap_path, key, value;
205 int count, i;
207 path = defaultsPathForDomain(domain);
208 if (!path)
209 return NULL;
211 prop = PLGetProplistWithPath(path);
212 if (!prop || !PLIsDictionary(prop))
213 return NULL;
215 key = PLMakeString("PixmapPath");
216 pixmap_path = PLGetDictionaryEntry(prop, key);
217 PLRelease(key);
218 if (!pixmap_path || !PLIsArray(pixmap_path))
219 return NULL;
221 count = PLGetNumberOfElements(pixmap_path);
222 if (count < 1)
223 return NULL;
225 ret = wmalloc(sizeof(char*)*(count+1));
227 for (i=0; i<count; i++) {
228 value = PLGetArrayElement(pixmap_path, i);
229 if (!value || !PLIsString(value))
230 break;
231 ret[i] = wstrdup(PLGetString(value));
232 if (ret[i][0]=='~' && ret[i][1]=='/') {
233 /* home is statically allocated. Don't free it */
234 char *fullpath, *home=gethomedir();
236 fullpath = wmalloc(strlen(home)+strlen(ret[i]));
237 strcpy(fullpath, home);
238 strcat(fullpath, &ret[i][1]);
239 free(ret[i]);
240 ret[i] = fullpath;
243 ret[i] = NULL;
244 return ret;
254 main(int argc, char **argv)
256 Display *dpy;
257 Window root_win;
258 RContextAttributes rattr;
259 int screen_number, default_depth, i, style = WTP_SCALE;
260 int scr_width, scr_height;
261 RContext *rcontext;
262 RImage *image, *tmp;
263 Pixmap secretBuffer = None;
264 Pixmap pixmap;
265 XColor xcolor;
266 char *back_color = "black";
267 char *image_name = NULL;
268 char *domain = "WindowMaker";
269 char *program = "wdwrite";
270 int update=0, cpc=4, render_mode=RM_MATCH, obey_user=0;
271 #ifdef DEBUG
272 double t1, t2, total, t;
273 struct timeval timev;
274 #endif
277 ProgName = strrchr(argv[0],'/');
278 if (!ProgName)
279 ProgName = argv[0];
280 else
281 ProgName++;
283 if (argc>1) {
284 for (i=1; i<argc; i++) {
285 if (strcmp(argv[i], "-s")==0) {
286 style = WTP_SCALE;
287 } else if (strcmp(argv[i], "-t")==0) {
288 style = WTP_TILE;
289 } else if (strcmp(argv[i], "-e")==0) {
290 style = WTP_CENTER;
291 } else if (strcmp(argv[i], "-d")==0) {
292 render_mode = RM_DITHER;
293 obey_user++;
294 } else if (strcmp(argv[i], "-m")==0) {
295 render_mode = RM_MATCH;
296 obey_user++;
297 } else if (strcmp(argv[i], "-u")==0) {
298 update++;
299 } else if (strcmp(argv[i], "-D")==0) {
300 update++;
301 i++;
302 if (i>=argc) {
303 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
304 exit(0);
306 domain = wstrdup(argv[i]);
307 } else if (strcmp(argv[i], "-c")==0) {
308 i++;
309 if (i>=argc) {
310 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
311 exit(0);
313 if (sscanf(argv[i], "%i", &cpc)!=1) {
314 fprintf(stderr, "bad value for colors per channel: \"%s\"\n", argv[i]);
315 exit(0);
317 } else if (strcmp(argv[i], "-b")==0) {
318 i++;
319 if (i>=argc) {
320 fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
321 exit(0);
323 back_color = argv[i];
324 } else if (strcmp(argv[i], "-x")==0) {
325 /* secret option:renders the pixmap in the supplied drawable */
326 i++;
327 if (i>=argc ||
328 sscanf(argv[i], "%x", (unsigned*)&secretBuffer)!=1) {
329 print_help();
330 exit(1);
332 } else if (argv[i][0] != '-') {
333 image_name = argv[i];
334 } else {
335 print_help();
336 exit(1);
341 if (image_name == NULL) {
342 print_help();
343 exit(1);
345 if (update) {
346 char *value = wmalloc(sizeof(image_name) + 30);
347 char *tmp=image_name, **paths;
348 int i;
350 /* should we read PixmapPath from the same file as we write into ? */
351 paths = getPixmapPath("WindowMaker");
352 if (paths) {
353 for(i=0; paths[i]!=NULL; i++) {
354 if ((tmp = strstr(image_name, paths[i])) != NULL &&
355 tmp == image_name) {
356 tmp += strlen(paths[i]);
357 while(*tmp=='/') tmp++;
358 break;
363 if (!tmp)
364 tmp = image_name;
366 if (style == WTP_TILE)
367 strcpy(value, "(tpixmap, \"");
368 else if (style == WTP_SCALE)
369 strcpy(value, "(spixmap, \"");
370 else
371 strcpy(value, "(cpixmap, \"");
372 strcat(value, tmp);
373 strcat(value, "\", \"");
374 strcat(value, back_color);
375 strcat(value, "\")");
376 execlp(program, program, domain, "WorkspaceBack", value, NULL);
377 printf("%s: warning could not run \"%s\"\n", ProgName, program);
378 /* Do not exit. At least try to put the image in the background */
379 /* Won't this waste CPU for nothing? We're going to be called again,
380 * anyways. -Alfredo */
381 /* If it fails to update the WindowMaker domain with "wdwrite" we
382 * won't be called again, because Window Maker will not notice any
383 * change. If it reaches this point, this means it failed.
384 * On success it will never get here. -Dan */
385 /*exit(0);*/
388 dpy = XOpenDisplay("");
389 if (!dpy) {
390 puts("Could not open display!");
391 exit(1);
393 #ifdef DEBUG
394 XSynchronize(dpy, True);
395 #endif
396 screen_number = DefaultScreen(dpy);
397 root_win = RootWindow(dpy, screen_number);
398 default_depth = DefaultDepth(dpy, screen_number);
399 scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
400 scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
402 if (!XParseColor(dpy, DefaultColormap(dpy, screen_number), back_color,
403 &xcolor)) {
404 printf("invalid color %s\n", back_color);
405 exit(1);
408 if (!obey_user && default_depth <= 8)
409 render_mode = RM_DITHER;
411 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
412 rattr.render_mode = render_mode;
413 rattr.colors_per_channel = cpc;
415 rcontext = RCreateContext(dpy, screen_number, &rattr);
416 if (!rcontext) {
417 printf("could not initialize graphics library context: %s\n",
418 RMessageForError(RErrorCode));
419 exit(1);
422 #ifdef DEBUG
423 gettimeofday(&timev, NULL);
424 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
425 t = t1;
426 #endif
427 image = RLoadImage(rcontext, image_name, 0);
428 #ifdef DEBUG
429 gettimeofday(&timev, NULL);
430 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
431 total = t2 - t1;
432 printf("load image in %f sec\n", total);
433 #endif
435 if (!image) {
436 printf("could not load image %s:%s\n", image_name, RMessageForError(RErrorCode));
437 exit(1);
440 #ifdef DEBUG
441 gettimeofday(&timev, NULL);
442 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
443 #endif
444 if (style == WTP_SCALE) {
445 tmp = RScaleImage(image, scr_width, scr_height);
446 if (!tmp) {
447 printf("could not scale image: %s\n", image_name);
448 exit(1);
450 RDestroyImage(image);
451 image = tmp;
452 } else if (style==WTP_CENTER && (image->width!=scr_width
453 || image->height!=scr_height)) {
454 RColor color;
456 color.red = xcolor.red>>8;
457 color.green = xcolor.green>>8;
458 color.blue = xcolor.blue>>8;
459 color.alpha = 255;
460 tmp = RMakeCenteredImage(image, scr_width, scr_height, &color);
461 if (!tmp) {
462 printf("could not create centered image: %s\n", image_name);
463 exit(1);
465 RDestroyImage(image);
466 image = tmp;
468 #ifdef DEBUG
469 gettimeofday(&timev, NULL);
470 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
471 total = t2 - t1;
472 printf("scale image in %f sec\n", total);
474 gettimeofday(&timev, NULL);
475 t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
476 #endif
477 RConvertImage(rcontext, image, &pixmap);
478 #ifdef DEBUG
479 gettimeofday(&timev, NULL);
480 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
481 total = t2 - t1;
482 printf("convert image to pixmap in %f sec\n", total);
483 total = t2 - t;
484 printf("total image proccessing in %f sec\n", total);
485 #endif
486 RDestroyImage(image);
487 if (secretBuffer==None) {
488 #ifdef fuckin_crap
489 setPixmapProperty(pixmap, dpy, root_win);
490 #endif
491 XSetWindowBackgroundPixmap(dpy, root_win, pixmap);
492 XClearWindow(dpy, root_win);
493 } else {
494 XCopyArea(dpy, pixmap, secretBuffer, DefaultGC(dpy, screen_number),
495 0, 0, scr_width, scr_height, 0, 0);
497 XSync(dpy, False);
498 XCloseDisplay(dpy);
499 #ifdef DEBUG
500 gettimeofday(&timev, NULL);
501 t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
502 total = t2 - t;
503 printf("total proccessing time: %f sec\n", total);
504 #endif
505 if (secretBuffer)
506 exit(123);
507 else
508 exit(0);