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 */
29 bool reportfilenotfound = false;
30 Colormap AlternateCmap = None;
31
32
33 /*
34 * Find (load/generate) an image by name
35 */
36 Image *
GetImage(const char * name,ColorPair cp)37 GetImage(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 */
153 Image *
AllocImage(void)154 AllocImage(void)
155 {
156 return calloc(1, sizeof(Image));
157 }
158
159 void
FreeImage(Image * image)160 FreeImage(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 */
203 char *
ExpandPixmapPath(const char * name)204 ExpandPixmapPath(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 */
272 Image *
get_image_anim_cp(const char * name,ColorPair cp,Image * (* imgloader)(const char *,ColorPair))273 get_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