10bbfda8aSnia/* 20bbfda8aSnia * Image handling functions 30bbfda8aSnia * 40bbfda8aSnia * This provides some general and hub stuff. Details of different image 50bbfda8aSnia * type functions, and generation of builtins, go in their own files. 60bbfda8aSnia */ 70bbfda8aSnia 80bbfda8aSnia#include "ctwm.h" 90bbfda8aSnia 100bbfda8aSnia#include <stdio.h> 110bbfda8aSnia#include <string.h> 120bbfda8aSnia#include <unistd.h> 130bbfda8aSnia 140bbfda8aSnia#include "list.h" 150bbfda8aSnia#include "screen.h" 160bbfda8aSnia 170bbfda8aSnia#include "image.h" 180bbfda8aSnia#include "image_bitmap.h" 190bbfda8aSnia#include "image_bitmap_builtin.h" 200bbfda8aSnia#include "image_xwd.h" 210bbfda8aSnia#ifdef JPEG 220bbfda8aSnia#include "image_jpeg.h" 230bbfda8aSnia#endif 240bbfda8aSnia#if defined (XPM) 250bbfda8aSnia#include "image_xpm.h" 260bbfda8aSnia#endif 270bbfda8aSnia 280bbfda8aSnia/* Flag (maybe should be retired */ 290bbfda8aSniabool reportfilenotfound = false; 300bbfda8aSniaColormap AlternateCmap = None; 310bbfda8aSnia 320bbfda8aSnia 330bbfda8aSnia/* 340bbfda8aSnia * Find (load/generate) an image by name 350bbfda8aSnia */ 360bbfda8aSniaImage * 370bbfda8aSniaGetImage(const char *name, ColorPair cp) 380bbfda8aSnia{ 390bbfda8aSnia name_list **list; 400bbfda8aSnia#define GIFNLEN 256 410bbfda8aSnia char fullname[GIFNLEN]; 420bbfda8aSnia Image *image; 430bbfda8aSnia 440bbfda8aSnia if(name == NULL) { 450bbfda8aSnia return NULL; 460bbfda8aSnia } 47b18c2d1eSnia if(dpy == NULL) { 48b18c2d1eSnia // May happen in special cases like --cfgchk with no $DISPLAY 49b18c2d1eSnia return NULL; 50b18c2d1eSnia } 510bbfda8aSnia image = NULL; 520bbfda8aSnia 530bbfda8aSnia list = &Scr->ImageCache; 540bbfda8aSnia if(0) { 550bbfda8aSnia /* dummy */ ; 560bbfda8aSnia } 570bbfda8aSnia else if((name [0] == '@') || (strncmp(name, "xpm:", 4) == 0)) { 580bbfda8aSnia#ifdef XPM 590bbfda8aSnia snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 600bbfda8aSnia 610bbfda8aSnia if((image = LookInNameList(*list, fullname)) == NULL) { 620bbfda8aSnia int startn = (name [0] == '@') ? 1 : 4; 630bbfda8aSnia if((image = GetXpmImage(name + startn, cp)) != NULL) { 640bbfda8aSnia AddToList(list, fullname, image); 650bbfda8aSnia } 660bbfda8aSnia } 670bbfda8aSnia#else 680bbfda8aSnia fprintf(stderr, "XPM support disabled, ignoring image %s\n", name); 690bbfda8aSnia return NULL; 700bbfda8aSnia#endif 710bbfda8aSnia } 720bbfda8aSnia else if(strncmp(name, "jpeg:", 5) == 0) { 730bbfda8aSnia#ifdef JPEG 740bbfda8aSnia if((image = LookInNameList(*list, name)) == NULL) { 750bbfda8aSnia if((image = GetJpegImage(&name [5])) != NULL) { 760bbfda8aSnia AddToList(list, name, image); 770bbfda8aSnia } 780bbfda8aSnia } 790bbfda8aSnia#else 800bbfda8aSnia fprintf(stderr, "JPEG support disabled, ignoring image %s\n", name); 810bbfda8aSnia return NULL; 820bbfda8aSnia#endif 830bbfda8aSnia } 840bbfda8aSnia else if((strncmp(name, "xwd:", 4) == 0) || (name [0] == '|')) { 850bbfda8aSnia int startn = (name [0] == '|') ? 0 : 4; 860bbfda8aSnia if((image = LookInNameList(*list, name)) == NULL) { 870bbfda8aSnia if((image = GetXwdImage(&name [startn], cp)) != NULL) { 880bbfda8aSnia AddToList(list, name, image); 890bbfda8aSnia } 900bbfda8aSnia } 910bbfda8aSnia } 920bbfda8aSnia else if(strncmp(name, ":xpm:", 5) == 0) { 930bbfda8aSnia snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 940bbfda8aSnia if((image = LookInNameList(*list, fullname)) == NULL) { 950bbfda8aSnia image = get_builtin_scalable_pixmap(name, cp); 960bbfda8aSnia if(image == NULL) { 970bbfda8aSnia /* g_b_s_p() already warned */ 980bbfda8aSnia return NULL; 990bbfda8aSnia } 1000bbfda8aSnia AddToList(list, fullname, image); 1010bbfda8aSnia } 1020bbfda8aSnia } 1030bbfda8aSnia else if(strncmp(name, "%xpm:", 5) == 0) { 1040bbfda8aSnia snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 1050bbfda8aSnia if((image = LookInNameList(*list, fullname)) == NULL) { 1060bbfda8aSnia image = get_builtin_animated_pixmap(name, cp); 1070bbfda8aSnia if(image == NULL) { 1080bbfda8aSnia /* g_b_a_p() already warned */ 1090bbfda8aSnia return NULL; 1100bbfda8aSnia } 1110bbfda8aSnia AddToList(list, fullname, image); 1120bbfda8aSnia } 1130bbfda8aSnia } 1140bbfda8aSnia else if(name [0] == ':') { 1150bbfda8aSnia unsigned int width, height; 1160bbfda8aSnia Pixmap pm = 0; 1170bbfda8aSnia XGCValues gcvalues; 1180bbfda8aSnia 1190bbfda8aSnia snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 1200bbfda8aSnia if((image = LookInNameList(*list, fullname)) == NULL) { 1210bbfda8aSnia pm = get_builtin_plain_pixmap(name, &width, &height); 1220bbfda8aSnia if(pm == None) { 1230bbfda8aSnia /* g_b_p_p() already warned */ 1240bbfda8aSnia return NULL; 1250bbfda8aSnia } 1260bbfda8aSnia image = AllocImage(); 1270bbfda8aSnia image->pixmap = XCreatePixmap(dpy, Scr->Root, width, height, Scr->d_depth); 1280bbfda8aSnia if(Scr->rootGC == (GC) 0) { 1290bbfda8aSnia Scr->rootGC = XCreateGC(dpy, Scr->Root, 0, &gcvalues); 1300bbfda8aSnia } 1310bbfda8aSnia gcvalues.background = cp.back; 1320bbfda8aSnia gcvalues.foreground = cp.fore; 1330bbfda8aSnia XChangeGC(dpy, Scr->rootGC, GCForeground | GCBackground, &gcvalues); 1340bbfda8aSnia XCopyPlane(dpy, pm, image->pixmap, Scr->rootGC, 0, 0, width, height, 0, 0, 1350bbfda8aSnia (unsigned long) 1); 1360bbfda8aSnia image->width = width; 1370bbfda8aSnia image->height = height; 1380bbfda8aSnia AddToList(list, fullname, image); 1390bbfda8aSnia } 1400bbfda8aSnia } 1410bbfda8aSnia else { 1420bbfda8aSnia snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 1430bbfda8aSnia if((image = LookInNameList(*list, fullname)) == NULL) { 1440bbfda8aSnia if((image = GetBitmapImage(name, cp)) != NULL) { 1450bbfda8aSnia AddToList(list, fullname, image); 1460bbfda8aSnia } 1470bbfda8aSnia } 1480bbfda8aSnia } 1490bbfda8aSnia return image; 1500bbfda8aSnia#undef GIFNLEN 1510bbfda8aSnia} 1520bbfda8aSnia 1530bbfda8aSnia 1540bbfda8aSnia/* 1550bbfda8aSnia * Creation/cleanup of Image structs 1560bbfda8aSnia */ 1570bbfda8aSniaImage * 1580bbfda8aSniaAllocImage(void) 1590bbfda8aSnia{ 1600bbfda8aSnia return calloc(1, sizeof(Image)); 1610bbfda8aSnia} 1620bbfda8aSnia 1630bbfda8aSniavoid 1640bbfda8aSniaFreeImage(Image *image) 1650bbfda8aSnia{ 1660bbfda8aSnia Image *im, *im2; 1670bbfda8aSnia 1680bbfda8aSnia im = image; 1690bbfda8aSnia while(im != NULL) { 1700bbfda8aSnia /* Cleanup sub-bits */ 1710bbfda8aSnia if(im->pixmap) { 1720bbfda8aSnia XFreePixmap(dpy, im->pixmap); 1730bbfda8aSnia } 1740bbfda8aSnia if(im->mask) { 1750bbfda8aSnia XFreePixmap(dpy, im->mask); 1760bbfda8aSnia } 1770bbfda8aSnia 1780bbfda8aSnia /* Cleanup self */ 1790bbfda8aSnia im2 = im->next; 1800bbfda8aSnia im->next = NULL; 1810bbfda8aSnia free(im); 1820bbfda8aSnia 1830bbfda8aSnia /* 1840bbfda8aSnia * Loop back around, unless we hit the original. e.g., 1850bbfda8aSnia * "foo%.xpm" animations load the images into a closed loop, so 1860bbfda8aSnia * FreeImage() would do Very Bad Things running around the track 1870bbfda8aSnia * until it segfaults or the like. 1880bbfda8aSnia */ 1890bbfda8aSnia if(im2 == image) { 1900bbfda8aSnia break; 1910bbfda8aSnia } 1920bbfda8aSnia im = im2; 1930bbfda8aSnia } 1940bbfda8aSnia} 1950bbfda8aSnia 1960bbfda8aSnia 1970bbfda8aSnia 1980bbfda8aSnia/* 1990bbfda8aSnia * Utils for image* 2000bbfda8aSnia */ 2010bbfda8aSnia 2020bbfda8aSnia/* 2030bbfda8aSnia * Expand out the real pathname for an image. Turn ~ into $HOME if 2040bbfda8aSnia * it's there, and look under the entries in PixmapDirectory if the 2050bbfda8aSnia * result isn't a full path. 2060bbfda8aSnia */ 2070bbfda8aSniachar * 2080bbfda8aSniaExpandPixmapPath(const char *name) 2090bbfda8aSnia{ 2100bbfda8aSnia char *ret; 2110bbfda8aSnia 2120bbfda8aSnia ret = NULL; 2130bbfda8aSnia 2140bbfda8aSnia /* If it starts with '~/', replace it with our homedir */ 2150bbfda8aSnia if(name[0] == '~' && name[1] == '/') { 2160bbfda8aSnia asprintf(&ret, "%s/%s", Home, name + 2); 2170bbfda8aSnia return ret; 2180bbfda8aSnia } 2190bbfda8aSnia 2200bbfda8aSnia /* 2210bbfda8aSnia * If it starts with /, it's an absolute path, so just pass it 2220bbfda8aSnia * through. 2230bbfda8aSnia */ 2240bbfda8aSnia if(name[0] == '/') { 2250bbfda8aSnia return strdup(name); 2260bbfda8aSnia } 2270bbfda8aSnia 2280bbfda8aSnia /* 2290bbfda8aSnia * If we got here, it's some sort of relative path (or a bare 2300bbfda8aSnia * filename), so search for it under PixmapDirectory if we have it. 2310bbfda8aSnia */ 2320bbfda8aSnia if(Scr->PixmapDirectory) { 2330bbfda8aSnia char *colon; 2340bbfda8aSnia char *p = Scr->PixmapDirectory; 2350bbfda8aSnia 2360bbfda8aSnia /* PixmapDirectory is a colon-separated list */ 2370bbfda8aSnia while((colon = strchr(p, ':'))) { 2380bbfda8aSnia *colon = '\0'; 2390bbfda8aSnia asprintf(&ret, "%s/%s", p, name); 2400bbfda8aSnia *colon = ':'; 2410bbfda8aSnia if(!access(ret, R_OK)) { 2420bbfda8aSnia return (ret); 2430bbfda8aSnia } 2440bbfda8aSnia free(ret); 2450bbfda8aSnia p = colon + 1; 2460bbfda8aSnia } 2470bbfda8aSnia 2480bbfda8aSnia asprintf(&ret, "%s/%s", p, name); 2490bbfda8aSnia if(!access(ret, R_OK)) { 2500bbfda8aSnia return (ret); 2510bbfda8aSnia } 2520bbfda8aSnia free(ret); 2530bbfda8aSnia } 2540bbfda8aSnia 2550bbfda8aSnia 2560bbfda8aSnia /* 2570bbfda8aSnia * If we get here, we have no idea. For simplicity and consistency 2580bbfda8aSnia * for our callers, just return what we were given. 2590bbfda8aSnia */ 2600bbfda8aSnia return strdup(name); 2610bbfda8aSnia} 2620bbfda8aSnia 2630bbfda8aSnia 2640bbfda8aSnia/* 2650bbfda8aSnia * Generalized loader for animations. 2660bbfda8aSnia * 2670bbfda8aSnia * These are specified with a '%' in the filename, which is replaced by a 2680bbfda8aSnia * series of numbers. So e.g. 2690bbfda8aSnia * 2700bbfda8aSnia * "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] 2710bbfda8aSnia * 2720bbfda8aSnia * These then turn into a looped-linked-list of Image's. We support 2730bbfda8aSnia * these for all types of images, so write it up into a central handler 2740bbfda8aSnia * once to centralize the logic. 2750bbfda8aSnia */ 2760bbfda8aSniaImage * 2770bbfda8aSniaget_image_anim_cp(const char *name, 2780bbfda8aSnia ColorPair cp, Image * (*imgloader)(const char *, ColorPair)) 2790bbfda8aSnia{ 2800bbfda8aSnia Image *head, *tail; 2810bbfda8aSnia char *pref, *suff, *stmp; 2820bbfda8aSnia int i; 2830bbfda8aSnia 2840bbfda8aSnia /* This shouldn't get called for non-animations */ 2850bbfda8aSnia if((stmp = strchr(name, '%')) == NULL) { 2860bbfda8aSnia fprintf(stderr, "%s() called for non-animation '%s'\n", __func__, name); 2870bbfda8aSnia return NULL; 2880bbfda8aSnia } 2890bbfda8aSnia if(stmp[1] == '\0') { 2900bbfda8aSnia fprintf(stderr, "%s(): nothing after %% in '%s'\n", __func__, name); 2910bbfda8aSnia return NULL; 2920bbfda8aSnia } 2930bbfda8aSnia stmp = NULL; 2940bbfda8aSnia 2950bbfda8aSnia /* 2960bbfda8aSnia * For animated requests, we load a series of files, replacing the % 2970bbfda8aSnia * with numbers in series. 2980bbfda8aSnia */ 2990bbfda8aSnia tail = head = NULL; 3000bbfda8aSnia 3010bbfda8aSnia /* Working copy of the filename split to before/after the % */ 3020bbfda8aSnia pref = strdup(name); 3030bbfda8aSnia suff = strchr(pref, '%'); 3040bbfda8aSnia *suff++ = '\0'; 3050bbfda8aSnia 3060bbfda8aSnia /* "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] */ 3070bbfda8aSnia for(i = 1 ; ; i++) { 3080bbfda8aSnia#define ANIM_PATHLEN 256 3090bbfda8aSnia char path[ANIM_PATHLEN]; 3100bbfda8aSnia Image *tmp; 3110bbfda8aSnia 3120bbfda8aSnia if(snprintf(path, ANIM_PATHLEN, "%s%d%s", pref, i, 3130bbfda8aSnia suff) >= (ANIM_PATHLEN - 1)) { 3140bbfda8aSnia fprintf(stderr, "%s(): generated filename for '%s' #%d longer than %d.\n", 3150bbfda8aSnia __func__, name, i, ANIM_PATHLEN); 3160bbfda8aSnia FreeImage(head); 3170bbfda8aSnia free(pref); 3180bbfda8aSnia return NULL; 3190bbfda8aSnia } 3200bbfda8aSnia#undef ANIM_PATHLEN 3210bbfda8aSnia 3220bbfda8aSnia /* 3230bbfda8aSnia * Load this image, and set ->next so it's explicitly the 3240bbfda8aSnia * [current] tail of the list. 3250bbfda8aSnia */ 3260bbfda8aSnia tmp = imgloader(path, cp); 3270bbfda8aSnia if(tmp == NULL) { 3280bbfda8aSnia break; 3290bbfda8aSnia } 3300bbfda8aSnia tmp->next = NULL; 3310bbfda8aSnia 3320bbfda8aSnia /* 3330bbfda8aSnia * If it's the first, it's the head (image) we return, as well as 3340bbfda8aSnia * our current tail marker (s). Else, append to that tail. 3350bbfda8aSnia */ 3360bbfda8aSnia if(head == NULL) { 3370bbfda8aSnia tail = head = tmp; 3380bbfda8aSnia } 3390bbfda8aSnia else { 3400bbfda8aSnia tail->next = tmp; 3410bbfda8aSnia tail = tmp; 3420bbfda8aSnia } 3430bbfda8aSnia } 3440bbfda8aSnia free(pref); 3450bbfda8aSnia 3460bbfda8aSnia /* Set the tail to loop back to the head */ 3470bbfda8aSnia if(tail != NULL) { 3480bbfda8aSnia tail->next = head; 3490bbfda8aSnia } 3500bbfda8aSnia 3510bbfda8aSnia /* Warn if we got nothing */ 3520bbfda8aSnia if(head == NULL) { 3530bbfda8aSnia fprintf(stderr, "Cannot find any image frames for '%s'\n", name); 3540bbfda8aSnia } 3550bbfda8aSnia 3560bbfda8aSnia return head; 3570bbfda8aSnia} 358