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