image.c revision 0bbfda8a
1/* 2 * Image handling functions 3 * 4 * This provides some general and hub stuff. Details of different image 5 * type functions, and generation of builtins, go in their own files. 6 */ 7 8#include "ctwm.h" 9 10#include <stdio.h> 11#include <string.h> 12#include <unistd.h> 13 14#include "list.h" 15#include "screen.h" 16 17#include "image.h" 18#include "image_bitmap.h" 19#include "image_bitmap_builtin.h" 20#include "image_xwd.h" 21#ifdef JPEG 22#include "image_jpeg.h" 23#endif 24#if defined (XPM) 25#include "image_xpm.h" 26#endif 27 28/* Flag (maybe should be retired */ 29bool reportfilenotfound = false; 30Colormap AlternateCmap = None; 31 32 33/* 34 * Find (load/generate) an image by name 35 */ 36Image * 37GetImage(const char *name, ColorPair cp) 38{ 39 name_list **list; 40#define GIFNLEN 256 41 char fullname[GIFNLEN]; 42 Image *image; 43 44 if(name == NULL) { 45 return NULL; 46 } 47 image = NULL; 48 49 list = &Scr->ImageCache; 50 if(0) { 51 /* dummy */ ; 52 } 53 else if((name [0] == '@') || (strncmp(name, "xpm:", 4) == 0)) { 54#ifdef XPM 55 snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 56 57 if((image = LookInNameList(*list, fullname)) == NULL) { 58 int startn = (name [0] == '@') ? 1 : 4; 59 if((image = GetXpmImage(name + startn, cp)) != NULL) { 60 AddToList(list, fullname, image); 61 } 62 } 63#else 64 fprintf(stderr, "XPM support disabled, ignoring image %s\n", name); 65 return NULL; 66#endif 67 } 68 else if(strncmp(name, "jpeg:", 5) == 0) { 69#ifdef JPEG 70 if((image = LookInNameList(*list, name)) == NULL) { 71 if((image = GetJpegImage(&name [5])) != NULL) { 72 AddToList(list, name, image); 73 } 74 } 75#else 76 fprintf(stderr, "JPEG support disabled, ignoring image %s\n", name); 77 return NULL; 78#endif 79 } 80 else if((strncmp(name, "xwd:", 4) == 0) || (name [0] == '|')) { 81 int startn = (name [0] == '|') ? 0 : 4; 82 if((image = LookInNameList(*list, name)) == NULL) { 83 if((image = GetXwdImage(&name [startn], cp)) != NULL) { 84 AddToList(list, name, image); 85 } 86 } 87 } 88 else if(strncmp(name, ":xpm:", 5) == 0) { 89 snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 90 if((image = LookInNameList(*list, fullname)) == NULL) { 91 image = get_builtin_scalable_pixmap(name, cp); 92 if(image == NULL) { 93 /* g_b_s_p() already warned */ 94 return NULL; 95 } 96 AddToList(list, fullname, image); 97 } 98 } 99 else if(strncmp(name, "%xpm:", 5) == 0) { 100 snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 101 if((image = LookInNameList(*list, fullname)) == NULL) { 102 image = get_builtin_animated_pixmap(name, cp); 103 if(image == NULL) { 104 /* g_b_a_p() already warned */ 105 return NULL; 106 } 107 AddToList(list, fullname, image); 108 } 109 } 110 else if(name [0] == ':') { 111 unsigned int width, height; 112 Pixmap pm = 0; 113 XGCValues gcvalues; 114 115 snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 116 if((image = LookInNameList(*list, fullname)) == NULL) { 117 pm = get_builtin_plain_pixmap(name, &width, &height); 118 if(pm == None) { 119 /* g_b_p_p() already warned */ 120 return NULL; 121 } 122 image = AllocImage(); 123 image->pixmap = XCreatePixmap(dpy, Scr->Root, width, height, Scr->d_depth); 124 if(Scr->rootGC == (GC) 0) { 125 Scr->rootGC = XCreateGC(dpy, Scr->Root, 0, &gcvalues); 126 } 127 gcvalues.background = cp.back; 128 gcvalues.foreground = cp.fore; 129 XChangeGC(dpy, Scr->rootGC, GCForeground | GCBackground, &gcvalues); 130 XCopyPlane(dpy, pm, image->pixmap, Scr->rootGC, 0, 0, width, height, 0, 0, 131 (unsigned long) 1); 132 image->width = width; 133 image->height = height; 134 AddToList(list, fullname, image); 135 } 136 } 137 else { 138 snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back); 139 if((image = LookInNameList(*list, fullname)) == NULL) { 140 if((image = GetBitmapImage(name, cp)) != NULL) { 141 AddToList(list, fullname, image); 142 } 143 } 144 } 145 return image; 146#undef GIFNLEN 147} 148 149 150/* 151 * Creation/cleanup of Image structs 152 */ 153Image * 154AllocImage(void) 155{ 156 return calloc(1, sizeof(Image)); 157} 158 159void 160FreeImage(Image *image) 161{ 162 Image *im, *im2; 163 164 im = image; 165 while(im != NULL) { 166 /* Cleanup sub-bits */ 167 if(im->pixmap) { 168 XFreePixmap(dpy, im->pixmap); 169 } 170 if(im->mask) { 171 XFreePixmap(dpy, im->mask); 172 } 173 174 /* Cleanup self */ 175 im2 = im->next; 176 im->next = NULL; 177 free(im); 178 179 /* 180 * Loop back around, unless we hit the original. e.g., 181 * "foo%.xpm" animations load the images into a closed loop, so 182 * FreeImage() would do Very Bad Things running around the track 183 * until it segfaults or the like. 184 */ 185 if(im2 == image) { 186 break; 187 } 188 im = im2; 189 } 190} 191 192 193 194/* 195 * Utils for image* 196 */ 197 198/* 199 * Expand out the real pathname for an image. Turn ~ into $HOME if 200 * it's there, and look under the entries in PixmapDirectory if the 201 * result isn't a full path. 202 */ 203char * 204ExpandPixmapPath(const char *name) 205{ 206 char *ret; 207 208 ret = NULL; 209 210 /* If it starts with '~/', replace it with our homedir */ 211 if(name[0] == '~' && name[1] == '/') { 212 asprintf(&ret, "%s/%s", Home, name + 2); 213 return ret; 214 } 215 216 /* 217 * If it starts with /, it's an absolute path, so just pass it 218 * through. 219 */ 220 if(name[0] == '/') { 221 return strdup(name); 222 } 223 224 /* 225 * If we got here, it's some sort of relative path (or a bare 226 * filename), so search for it under PixmapDirectory if we have it. 227 */ 228 if(Scr->PixmapDirectory) { 229 char *colon; 230 char *p = Scr->PixmapDirectory; 231 232 /* PixmapDirectory is a colon-separated list */ 233 while((colon = strchr(p, ':'))) { 234 *colon = '\0'; 235 asprintf(&ret, "%s/%s", p, name); 236 *colon = ':'; 237 if(!access(ret, R_OK)) { 238 return (ret); 239 } 240 free(ret); 241 p = colon + 1; 242 } 243 244 asprintf(&ret, "%s/%s", p, name); 245 if(!access(ret, R_OK)) { 246 return (ret); 247 } 248 free(ret); 249 } 250 251 252 /* 253 * If we get here, we have no idea. For simplicity and consistency 254 * for our callers, just return what we were given. 255 */ 256 return strdup(name); 257} 258 259 260/* 261 * Generalized loader for animations. 262 * 263 * These are specified with a '%' in the filename, which is replaced by a 264 * series of numbers. So e.g. 265 * 266 * "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] 267 * 268 * These then turn into a looped-linked-list of Image's. We support 269 * these for all types of images, so write it up into a central handler 270 * once to centralize the logic. 271 */ 272Image * 273get_image_anim_cp(const char *name, 274 ColorPair cp, Image * (*imgloader)(const char *, ColorPair)) 275{ 276 Image *head, *tail; 277 char *pref, *suff, *stmp; 278 int i; 279 280 /* This shouldn't get called for non-animations */ 281 if((stmp = strchr(name, '%')) == NULL) { 282 fprintf(stderr, "%s() called for non-animation '%s'\n", __func__, name); 283 return NULL; 284 } 285 if(stmp[1] == '\0') { 286 fprintf(stderr, "%s(): nothing after %% in '%s'\n", __func__, name); 287 return NULL; 288 } 289 stmp = NULL; 290 291 /* 292 * For animated requests, we load a series of files, replacing the % 293 * with numbers in series. 294 */ 295 tail = head = NULL; 296 297 /* Working copy of the filename split to before/after the % */ 298 pref = strdup(name); 299 suff = strchr(pref, '%'); 300 *suff++ = '\0'; 301 302 /* "foo%.xpm" -> [ "foo1.xpm", "foo2.xpm", ...] */ 303 for(i = 1 ; ; i++) { 304#define ANIM_PATHLEN 256 305 char path[ANIM_PATHLEN]; 306 Image *tmp; 307 308 if(snprintf(path, ANIM_PATHLEN, "%s%d%s", pref, i, 309 suff) >= (ANIM_PATHLEN - 1)) { 310 fprintf(stderr, "%s(): generated filename for '%s' #%d longer than %d.\n", 311 __func__, name, i, ANIM_PATHLEN); 312 FreeImage(head); 313 free(pref); 314 return NULL; 315 } 316#undef ANIM_PATHLEN 317 318 /* 319 * Load this image, and set ->next so it's explicitly the 320 * [current] tail of the list. 321 */ 322 tmp = imgloader(path, cp); 323 if(tmp == NULL) { 324 break; 325 } 326 tmp->next = NULL; 327 328 /* 329 * If it's the first, it's the head (image) we return, as well as 330 * our current tail marker (s). Else, append to that tail. 331 */ 332 if(head == NULL) { 333 tail = head = tmp; 334 } 335 else { 336 tail->next = tmp; 337 tail = tmp; 338 } 339 } 340 free(pref); 341 342 /* Set the tail to loop back to the head */ 343 if(tail != NULL) { 344 tail->next = head; 345 } 346 347 /* Warn if we got nothing */ 348 if(head == NULL) { 349 fprintf(stderr, "Cannot find any image frames for '%s'\n", name); 350 } 351 352 return head; 353} 354