1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-18 20:10:29 +01:00
Files
wmaker/util/wmsetbg.c
1998-11-27 12:26:46 +00:00

510 lines
14 KiB
C

/* wmsetbg.c- sets root window background image
*
* WindowMaker window manager
*
* Copyright (c) 1998 Dan Pascu
* Copyright (c) 1998 Alfredo K. Kojima
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <string.h>
#include <wraster.h>
#include <pwd.h>
#include <sys/types.h>
#include "../src/wconfig.h"
#include <proplist.h>
#ifdef DEBUG
#include <sys/time.h>
#include <time.h>
#endif
#define WTP_TILE 1
#define WTP_SCALE 2
#define WTP_CENTER 3
char *ProgName;
#define fuckin_crap
#ifdef fuckin_crap
/* Alfredo please take a look at this function. I don't like the way
* it sends the XKillClient. Should it interfere this way with the rest?
* This was added by the patch to allow the transparent background for Eterm.
* Also for this purpose Eterm have a program named Esetroot.
* People wanting that feature can use that instead of wmsetbg. Why do we
* need to patch wmsetbg to do this?
* In case you want to keep it, please also check the way it does
* the PropModeAppend. -Dan */
static void
setPixmapProperty(Pixmap pixmap, Display *dpy, Window root)
{
Atom prop, type;
int format;
unsigned long length, after;
unsigned char *data;
/* This will locate the property, creating it if it doesn't exist */
prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
if (prop == None)
return;
/* Clear out the old pixmap */
XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
&type, &format, &length, &after, &data);
/* I think this is OK -Alfredo */
if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
XKillClient(dpy, *((Pixmap *)data));
}
XDeleteProperty(dpy, root, prop);
/* I don't understand this one. The atom is *always* created in
the previous XInternAtom() -Alfredo */
prop = XInternAtom(dpy, "_XROOTPMAP_ID", True);
if (prop == None)
return;
/* Now add the new one. We use PropModeAppend because PropModeReplace
doesn't seem to work if there isn't already a property there. */
/* Must be something wrong with this code.
* Anyways, better do a XGrabServer() between the XDeleteProperty() and
* this XChangeProperty() if things will be this way. -Alfredo */
XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeAppend,
(unsigned char *) &pixmap, 1);
XFlush(dpy);
/* Potential resource leak. Must check the rest of the
* program for other resources that rely on XCloseDisplay() to
* free them. -Alfredo */
XSetCloseDownMode(dpy, RetainPermanent);
XFlush(dpy);
}
#endif
void*
wmalloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (!ptr) {
perror(ProgName);
exit(1);
}
return ptr;
}
char*
gethomedir()
{
char *home = getenv("HOME");
struct passwd *user;
if (home)
return home;
user = getpwuid(getuid());
if (!user) {
perror(ProgName);
return "/";
}
if (!user->pw_dir) {
return "/";
} else {
return user->pw_dir;
}
}
void wAbort()
{
exit(1);
}
void
print_help()
{
printf("usage: %s [-options] image\n", ProgName);
puts("options:");
puts(" -d dither image");
puts(" -m match colors");
puts(" -b <color> background color");
puts(" -t tile image");
puts(" -e center image");
puts(" -s scale image (default)");
puts(" -u update WindowMaker domain database");
puts(" -D <domain> update <domain> database");
puts(" -c <cpc> colors per channel to use");
}
char*
defaultsPathForDomain(char *domain)
{
char path[1024];
char *gspath, *tmp;
gspath = getenv("GNUSTEP_USER_ROOT");
if (gspath) {
strcpy(path, gspath);
strcat(path, "/");
} else {
strcpy(path, gethomedir());
strcat(path, "/GNUstep/");
}
strcat(path, DEFAULTS_DIR);
strcat(path, "/");
strcat(path, domain);
tmp = wmalloc(strlen(path)+2);
strcpy(tmp, path);
return tmp;
}
char *wstrdup(char *str)
{
return strcpy(wmalloc(strlen(str)+1), str);
}
/* Returns an array of pointers to the pixmap paths, doing ~ expansion */
static char**
getPixmapPath(char *domain)
{
char **ret;
char *path;
proplist_t prop, pixmap_path, key, value;
int count, i;
path = defaultsPathForDomain(domain);
if (!path)
return NULL;
prop = PLGetProplistWithPath(path);
if (!prop || !PLIsDictionary(prop))
return NULL;
key = PLMakeString("PixmapPath");
pixmap_path = PLGetDictionaryEntry(prop, key);
PLRelease(key);
if (!pixmap_path || !PLIsArray(pixmap_path))
return NULL;
count = PLGetNumberOfElements(pixmap_path);
if (count < 1)
return NULL;
ret = wmalloc(sizeof(char*)*(count+1));
for (i=0; i<count; i++) {
value = PLGetArrayElement(pixmap_path, i);
if (!value || !PLIsString(value))
break;
ret[i] = wstrdup(PLGetString(value));
if (ret[i][0]=='~' && ret[i][1]=='/') {
/* home is statically allocated. Don't free it */
char *fullpath, *home=gethomedir();
fullpath = wmalloc(strlen(home)+strlen(ret[i]));
strcpy(fullpath, home);
strcat(fullpath, &ret[i][1]);
free(ret[i]);
ret[i] = fullpath;
}
}
ret[i] = NULL;
return ret;
}
int
main(int argc, char **argv)
{
Display *dpy;
Window root_win;
RContextAttributes rattr;
int screen_number, default_depth, i, style = WTP_SCALE;
int scr_width, scr_height;
RContext *rcontext;
RImage *image, *tmp;
Pixmap secretBuffer = None;
Pixmap pixmap;
XColor xcolor;
char *back_color = "black";
char *image_name = NULL;
char *domain = "WindowMaker";
char *program = "wdwrite";
int update=0, cpc=4, render_mode=RM_MATCH, obey_user=0;
#ifdef DEBUG
double t1, t2, total, t;
struct timeval timev;
#endif
ProgName = strrchr(argv[0],'/');
if (!ProgName)
ProgName = argv[0];
else
ProgName++;
if (argc>1) {
for (i=1; i<argc; i++) {
if (strcmp(argv[i], "-s")==0) {
style = WTP_SCALE;
} else if (strcmp(argv[i], "-t")==0) {
style = WTP_TILE;
} else if (strcmp(argv[i], "-e")==0) {
style = WTP_CENTER;
} else if (strcmp(argv[i], "-d")==0) {
render_mode = RM_DITHER;
obey_user++;
} else if (strcmp(argv[i], "-m")==0) {
render_mode = RM_MATCH;
obey_user++;
} else if (strcmp(argv[i], "-u")==0) {
update++;
} else if (strcmp(argv[i], "-D")==0) {
update++;
i++;
if (i>=argc) {
fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
exit(0);
}
domain = wstrdup(argv[i]);
} else if (strcmp(argv[i], "-c")==0) {
i++;
if (i>=argc) {
fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
exit(0);
}
if (sscanf(argv[i], "%i", &cpc)!=1) {
fprintf(stderr, "bad value for colors per channel: \"%s\"\n", argv[i]);
exit(0);
}
} else if (strcmp(argv[i], "-b")==0) {
i++;
if (i>=argc) {
fprintf(stderr, "too few arguments for %s\n", argv[i-1]);
exit(0);
}
back_color = argv[i];
} else if (strcmp(argv[i], "-x")==0) {
/* secret option:renders the pixmap in the supplied drawable */
i++;
if (i>=argc ||
sscanf(argv[i], "%x", (unsigned*)&secretBuffer)!=1) {
print_help();
exit(1);
}
} else if (argv[i][0] != '-') {
image_name = argv[i];
} else {
print_help();
exit(1);
}
}
}
if (image_name == NULL) {
print_help();
exit(1);
}
if (update) {
char *value = wmalloc(sizeof(image_name) + 30);
char *tmp=image_name, **paths;
int i;
/* should we read PixmapPath from the same file as we write into ? */
paths = getPixmapPath("WindowMaker");
if (paths) {
for(i=0; paths[i]!=NULL; i++) {
if ((tmp = strstr(image_name, paths[i])) != NULL &&
tmp == image_name) {
tmp += strlen(paths[i]);
while(*tmp=='/') tmp++;
break;
}
}
}
if (!tmp)
tmp = image_name;
if (style == WTP_TILE)
strcpy(value, "(tpixmap, \"");
else if (style == WTP_SCALE)
strcpy(value, "(spixmap, \"");
else
strcpy(value, "(cpixmap, \"");
strcat(value, tmp);
strcat(value, "\", \"");
strcat(value, back_color);
strcat(value, "\")");
execlp(program, program, domain, "WorkspaceBack", value, NULL);
printf("%s: warning could not run \"%s\"\n", ProgName, program);
/* Do not exit. At least try to put the image in the background */
/* Won't this waste CPU for nothing? We're going to be called again,
* anyways. -Alfredo */
/* If it fails to update the WindowMaker domain with "wdwrite" we
* won't be called again, because Window Maker will not notice any
* change. If it reaches this point, this means it failed.
* On success it will never get here. -Dan */
/*exit(0);*/
}
dpy = XOpenDisplay("");
if (!dpy) {
puts("Could not open display!");
exit(1);
}
#ifdef DEBUG
XSynchronize(dpy, True);
#endif
screen_number = DefaultScreen(dpy);
root_win = RootWindow(dpy, screen_number);
default_depth = DefaultDepth(dpy, screen_number);
scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
if (!XParseColor(dpy, DefaultColormap(dpy, screen_number), back_color,
&xcolor)) {
printf("invalid color %s\n", back_color);
exit(1);
}
if (!obey_user && default_depth <= 8)
render_mode = RM_DITHER;
rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_DefaultVisual;
rattr.render_mode = render_mode;
rattr.colors_per_channel = cpc;
rcontext = RCreateContext(dpy, screen_number, &rattr);
if (!rcontext) {
printf("could not initialize graphics library context: %s\n",
RMessageForError(RErrorCode));
exit(1);
}
#ifdef DEBUG
gettimeofday(&timev, NULL);
t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
t = t1;
#endif
image = RLoadImage(rcontext, image_name, 0);
#ifdef DEBUG
gettimeofday(&timev, NULL);
t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
total = t2 - t1;
printf("load image in %f sec\n", total);
#endif
if (!image) {
printf("could not load image %s:%s\n", image_name, RMessageForError(RErrorCode));
exit(1);
}
#ifdef DEBUG
gettimeofday(&timev, NULL);
t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
#endif
if (style == WTP_SCALE) {
tmp = RScaleImage(image, scr_width, scr_height);
if (!tmp) {
printf("could not scale image: %s\n", image_name);
exit(1);
}
RDestroyImage(image);
image = tmp;
} else if (style==WTP_CENTER && (image->width!=scr_width
|| image->height!=scr_height)) {
RColor color;
color.red = xcolor.red>>8;
color.green = xcolor.green>>8;
color.blue = xcolor.blue>>8;
color.alpha = 255;
tmp = RMakeCenteredImage(image, scr_width, scr_height, &color);
if (!tmp) {
printf("could not create centered image: %s\n", image_name);
exit(1);
}
RDestroyImage(image);
image = tmp;
}
#ifdef DEBUG
gettimeofday(&timev, NULL);
t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
total = t2 - t1;
printf("scale image in %f sec\n", total);
gettimeofday(&timev, NULL);
t1 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
#endif
RConvertImage(rcontext, image, &pixmap);
#ifdef DEBUG
gettimeofday(&timev, NULL);
t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
total = t2 - t1;
printf("convert image to pixmap in %f sec\n", total);
total = t2 - t;
printf("total image proccessing in %f sec\n", total);
#endif
RDestroyImage(image);
if (secretBuffer==None) {
#ifdef fuckin_crap
setPixmapProperty(pixmap, dpy, root_win);
#endif
XSetWindowBackgroundPixmap(dpy, root_win, pixmap);
XClearWindow(dpy, root_win);
} else {
XCopyArea(dpy, pixmap, secretBuffer, DefaultGC(dpy, screen_number),
0, 0, scr_width, scr_height, 0, 0);
}
XSync(dpy, False);
XCloseDisplay(dpy);
#ifdef DEBUG
gettimeofday(&timev, NULL);
t2 = (double)timev.tv_sec + (((double)timev.tv_usec)/1000000);
total = t2 - t;
printf("total proccessing time: %f sec\n", total);
#endif
if (secretBuffer)
exit(123);
else
exit(0);
}