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