/* * Image handling functions * * This provides some general and hub stuff. Details of different image * type functions, and generation of builtins, go in their own files. */ #include "ctwm.h" #include #include #include #include "list.h" #include "screen.h" #include "image.h" #include "image_bitmap.h" #include "image_bitmap_builtin.h" #include "image_xwd.h" #ifdef JPEG #include "image_jpeg.h" #endif #if defined (XPM) #include "image_xpm.h" #endif /* Flag (maybe should be retired */ bool reportfilenotfound = false; Colormap AlternateCmap = None; /* * Find (load/generate) an image by name */ Image * GetImage(const char *name, ColorPair cp) { name_list **list; #define GIFNLEN 256 char fullname[GIFNLEN]; Image *image; if(name == NULL) { return NULL; } if(dpy == NULL) { // May happen in special cases like --cfgchk with no $DISPLAY return NULL; } image = NULL; list = &Scr->ImageCache; if(0) { /* dummy */ ; } else if((name [0] == '@') || (strncmp(name, "xpm:", 4) == 0)) { #ifdef XPM snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); if((image = LookInNameList(*list, fullname)) == NULL) { int startn = (name [0] == '@') ? 1 : 4; if((image = GetXpmImage(name + startn, cp)) != NULL) { AddToList(list, fullname, image); } } #else fprintf(stderr, "XPM support disabled, ignoring image %s\n", name); return NULL; #endif } else if(strncmp(name, "jpeg:", 5) == 0) { #ifdef JPEG if((image = LookInNameList(*list, name)) == NULL) { if((image = GetJpegImage(&name [5])) != NULL) { AddToList(list, name, image); } } #else fprintf(stderr, "JPEG support disabled, ignoring image %s\n", name); return NULL; #endif } else if((strncmp(name, "xwd:", 4) == 0) || (name [0] == '|')) { int startn = (name [0] == '|') ? 0 : 4; if((image = LookInNameList(*list, name)) == NULL) { if((image = GetXwdImage(&name [startn], cp)) != NULL) { AddToList(list, name, image); } } } else if(strncmp(name, ":xpm:", 5) == 0) { snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); if((image = LookInNameList(*list, fullname)) == NULL) { image = get_builtin_scalable_pixmap(name, cp); if(image == NULL) { /* g_b_s_p() already warned */ return NULL; } AddToList(list, fullname, image); } } else if(strncmp(name, "%xpm:", 5) == 0) { snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); if((image = LookInNameList(*list, fullname)) == NULL) { image = get_builtin_animated_pixmap(name, cp); if(image == NULL) { /* g_b_a_p() already warned */ return NULL; } AddToList(list, fullname, image); } } else if(name [0] == ':') { unsigned int width, height; Pixmap pm = 0; XGCValues gcvalues; snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); if((image = LookInNameList(*list, fullname)) == NULL) { pm = get_builtin_plain_pixmap(name, &width, &height); if(pm == None) { /* g_b_p_p() already warned */ return NULL; } image = AllocImage(); image->pixmap = XCreatePixmap(dpy, Scr->Root, width, height, Scr->d_depth); if(Scr->rootGC == (GC) 0) { Scr->rootGC = XCreateGC(dpy, Scr->Root, 0, &gcvalues); } gcvalues.background = cp.back; gcvalues.foreground = cp.fore; XChangeGC(dpy, Scr->rootGC, GCForeground | GCBackground, &gcvalues); XCopyPlane(dpy, pm, image->pixmap, Scr->rootGC, 0, 0, width, height, 0, 0, (unsigned long) 1); image->width = width; image->height = height; AddToList(list, fullname, image); } } else { snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); if((image = LookInNameList(*list, fullname)) == NULL) { if((image = GetBitmapImage(name, cp)) != NULL) { AddToList(list, fullname, image); } } } return image; #undef GIFNLEN } /* * Creation/cleanup of Image structs */ Image * AllocImage(void) { return calloc(1, sizeof(Image)); } void FreeImage(Image *image) { Image *im, *im2; im = image; while(im != NULL) { /* Cleanup sub-bits */ if(im->pixmap) { XFreePixmap(dpy, im->pixmap); } if(im->mask) { XFreePixmap(dpy, im->mask); } /* Cleanup self */ im2 = im->next; im->next = NULL; free(im); /* * Loop back around, unless we hit the original. e.g., * "foo%.xpm" animations load the images into a closed loop, so * FreeImage() would do Very Bad Things running around the track * until it segfaults or the like. */ if(im2 == image) { break; } im = im2; } } /* * Utils for image* */ /* * Expand out the real pathname for an image. Turn ~ into $HOME if * it's there, and look under the entries in PixmapDirectory if the * result isn't a full path. */ char * ExpandPixmapPath(const char *name) { char *ret; ret = NULL; /* If it starts with '~/', replace it with our homedir */ if(name[0] == '~' && name[1] == '/') { asprintf(&ret, "%s/%s", Home, name + 2); return ret; } /* * If it starts with /, it's an absolute path, so just pass it * through. */ if(name[0] == '/') { return strdup(name); } /* * If we got here, it's some sort of relative path (or a bare * filename), so search for it under PixmapDirectory if we have it. */ if(Scr->PixmapDirectory) { char *colon; char *p = Scr->PixmapDirectory; /* PixmapDirectory is a colon-separated list */ while((colon = strchr(p, ':'))) { *colon = '\0'; asprintf(&ret, "%s/%s", p, name); *colon = ':'; if(!access(ret, R_OK)) { return (ret); } free(ret); p = colon + 1; } asprintf(&ret, "%s/%s", p, name); if(!access(ret, R_OK)) { return (ret); } free(ret); } /* * If we get here, we have no idea. For simplicity and consistency * for our callers, just return what we were given. */ return strdup(name); } /* * Generalized loader for animations. * * These are specified with a '%' in the filename, which is replaced by a * series of numbers. So e.g. * * "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] * * These then turn into a looped-linked-list of Image's. We support * these for all types of images, so write it up into a central handler * once to centralize the logic. */ Image * get_image_anim_cp(const char *name, ColorPair cp, Image * (*imgloader)(const char *, ColorPair)) { Image *head, *tail; char *pref, *suff, *stmp; int i; /* This shouldn't get called for non-animations */ if((stmp = strchr(name, '%')) == NULL) { fprintf(stderr, "%s() called for non-animation '%s'\n", __func__, name); return NULL; } if(stmp[1] == '\0') { fprintf(stderr, "%s(): nothing after %% in '%s'\n", __func__, name); return NULL; } stmp = NULL; /* * For animated requests, we load a series of files, replacing the % * with numbers in series. */ tail = head = NULL; /* Working copy of the filename split to before/after the % */ pref = strdup(name); suff = strchr(pref, '%'); *suff++ = '\0'; /* "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] */ for(i = 1 ; ; i++) { #define ANIM_PATHLEN 256 char path[ANIM_PATHLEN]; Image *tmp; if(snprintf(path, ANIM_PATHLEN, "%s%d%s", pref, i, suff) >= (ANIM_PATHLEN - 1)) { fprintf(stderr, "%s(): generated filename for '%s' #%d longer than %d.\n", __func__, name, i, ANIM_PATHLEN); FreeImage(head); free(pref); return NULL; } #undef ANIM_PATHLEN /* * Load this image, and set ->next so it's explicitly the * [current] tail of the list. */ tmp = imgloader(path, cp); if(tmp == NULL) { break; } tmp->next = NULL; /* * If it's the first, it's the head (image) we return, as well as * our current tail marker (s). Else, append to that tail. */ if(head == NULL) { tail = head = tmp; } else { tail->next = tmp; tail = tmp; } } free(pref); /* Set the tail to loop back to the head */ if(tail != NULL) { tail->next = head; } /* Warn if we got nothing */ if(head == NULL) { fprintf(stderr, "Cannot find any image frames for '%s'\n", name); } return head; }