~ctwm/ctwm/trunk

479.1.25 by Matthew Fuller
Forgot to add image.c
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>
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
12
#include <unistd.h>
479.1.25 by Matthew Fuller
Forgot to add image.c
13
524.1.5 by Matthew Fuller
Pull list.h out of screen.h and #include it in the places that need
14
#include "list.h"
479.1.25 by Matthew Fuller
Forgot to add image.c
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
 */
480.1.17 by Matthew Fuller
const-ify all these image names we pass through this process.
36
Image *
37
GetImage(const char *name, ColorPair cp)
479.1.25 by Matthew Fuller
Forgot to add image.c
38
{
39
	name_list **list;
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
40
#define GIFNLEN 256
41
	char fullname[GIFNLEN];
479.1.25 by Matthew Fuller
Forgot to add image.c
42
	Image *image;
43
44
	if(name == NULL) {
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
45
		return NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
46
	}
644.2.19 by Matthew Fuller
Add conditions in a few places that get called a lot during config
47
	if(dpy == NULL) {
48
		// May happen in special cases like --cfgchk with no $DISPLAY
49
		return NULL;
50
	}
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
51
	image = NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
52
53
	list = &Scr->ImageCache;
604.1.4 by Matthew Fuller
Add braces around this if to fit our style.
54
	if(0) {
479.1.25 by Matthew Fuller
Forgot to add image.c
55
		/* dummy */ ;
604.1.4 by Matthew Fuller
Add braces around this if to fit our style.
56
	}
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
57
	else if((name [0] == '@') || (strncmp(name, "xpm:", 4) == 0)) {
479.1.25 by Matthew Fuller
Forgot to add image.c
58
#ifdef XPM
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
59
		snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
479.1.25 by Matthew Fuller
Forgot to add image.c
60
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
61
		if((image = LookInNameList(*list, fullname)) == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
62
			int startn = (name [0] == '@') ? 1 : 4;
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
63
			if((image = GetXpmImage(name + startn, cp)) != NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
64
				AddToList(list, fullname, image);
65
			}
66
		}
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
67
#else
68
		fprintf(stderr, "XPM support disabled, ignoring image %s\n", name);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
69
		return NULL;
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
70
#endif
479.1.25 by Matthew Fuller
Forgot to add image.c
71
	}
72
	else if(strncmp(name, "jpeg:", 5) == 0) {
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
73
#ifdef JPEG
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
74
		if((image = LookInNameList(*list, name)) == NULL) {
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
75
			if((image = GetJpegImage(&name [5])) != NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
76
				AddToList(list, name, image);
77
			}
78
		}
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
79
#else
80
		fprintf(stderr, "JPEG support disabled, ignoring image %s\n", name);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
81
		return NULL;
480.1.25 by Matthew Fuller
Move the #ifdef's for XPM/JPEG support inside the conditional blocks,
82
#endif
479.1.25 by Matthew Fuller
Forgot to add image.c
83
	}
84
	else if((strncmp(name, "xwd:", 4) == 0) || (name [0] == '|')) {
85
		int startn = (name [0] == '|') ? 0 : 4;
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
86
		if((image = LookInNameList(*list, name)) == NULL) {
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
87
			if((image = GetXwdImage(&name [startn], cp)) != NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
88
				AddToList(list, name, image);
89
			}
90
		}
91
	}
92
	else if(strncmp(name, ":xpm:", 5) == 0) {
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
93
		snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
94
		if((image = LookInNameList(*list, fullname)) == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
95
			image = get_builtin_scalable_pixmap(name, cp);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
96
			if(image == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
97
				/* g_b_s_p() already warned */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
98
				return NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
99
			}
100
			AddToList(list, fullname, image);
101
		}
102
	}
103
	else if(strncmp(name, "%xpm:", 5) == 0) {
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
104
		snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
105
		if((image = LookInNameList(*list, fullname)) == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
106
			image = get_builtin_animated_pixmap(name, cp);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
107
			if(image == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
108
				/* g_b_a_p() already warned */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
109
				return NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
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
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
119
		snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
120
		if((image = LookInNameList(*list, fullname)) == NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
121
			pm = get_builtin_plain_pixmap(name, &width, &height);
122
			if(pm == None) {
123
				/* g_b_p_p() already warned */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
124
				return NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
125
			}
480.1.19 by Matthew Fuller
Add an AllocImage() to ensure new Image's are initialized properly,
126
			image = AllocImage();
479.1.25 by Matthew Fuller
Forgot to add image.c
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 {
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
142
		snprintf(fullname, GIFNLEN, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
518.1.8 by Matthew Fuller
LookInNameList() returns void * nowadays, so there's no need to cast
143
		if((image = LookInNameList(*list, fullname)) == NULL) {
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
144
			if((image = GetBitmapImage(name, cp)) != NULL) {
479.1.25 by Matthew Fuller
Forgot to add image.c
145
				AddToList(list, fullname, image);
146
			}
147
		}
148
	}
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
149
	return image;
480.1.21 by Matthew Fuller
Add a constant for the space we put in fullname, and use snprintf()
150
#undef GIFNLEN
479.1.25 by Matthew Fuller
Forgot to add image.c
151
}
152
153
154
/*
480.1.19 by Matthew Fuller
Add an AllocImage() to ensure new Image's are initialized properly,
155
 * Creation/cleanup of Image structs
479.1.25 by Matthew Fuller
Forgot to add image.c
156
 */
480.1.19 by Matthew Fuller
Add an AllocImage() to ensure new Image's are initialized properly,
157
Image *
158
AllocImage(void)
159
{
160
	return calloc(1, sizeof(Image));
161
}
162
479.1.25 by Matthew Fuller
Forgot to add image.c
163
void
164
FreeImage(Image *image)
165
{
166
	Image *im, *im2;
167
168
	im = image;
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
169
	while(im != NULL) {
480.1.18 by Matthew Fuller
Make FreeImage() robust against image lists that loop back on
170
		/* Cleanup sub-bits */
479.1.25 by Matthew Fuller
Forgot to add image.c
171
		if(im->pixmap) {
172
			XFreePixmap(dpy, im->pixmap);
173
		}
174
		if(im->mask) {
175
			XFreePixmap(dpy, im->mask);
176
		}
480.1.18 by Matthew Fuller
Make FreeImage() robust against image lists that loop back on
177
178
		/* Cleanup self */
479.1.25 by Matthew Fuller
Forgot to add image.c
179
		im2 = im->next;
501.1.2 by Matthew Fuller
Explicitly NULL this ->next for extra safety against winding up in a
180
		im->next = NULL;
479.1.25 by Matthew Fuller
Forgot to add image.c
181
		free(im);
480.1.18 by Matthew Fuller
Make FreeImage() robust against image lists that loop back on
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
		}
479.1.25 by Matthew Fuller
Forgot to add image.c
192
		im = im2;
193
	}
194
}
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
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
 */
207
char *
480.1.17 by Matthew Fuller
const-ify all these image names we pass through this process.
208
ExpandPixmapPath(const char *name)
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
209
{
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
210
	char *ret;
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
211
212
	ret = NULL;
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
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;
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
234
		char *p = Scr->PixmapDirectory;
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
235
236
		/* PixmapDirectory is a colon-separated list */
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
237
		while((colon = strchr(p, ':'))) {
238
			*colon = '\0';
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
239
			asprintf(&ret, "%s/%s", p, name);
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
240
			*colon = ':';
241
			if(!access(ret, R_OK)) {
242
				return (ret);
243
			}
244
			free(ret);
245
			p = colon + 1;
246
		}
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
247
248
		asprintf(&ret, "%s/%s", p, name);
249
		if(!access(ret, R_OK)) {
250
			return (ret);
251
		}
252
		free(ret);
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
253
	}
480.1.6 by Matthew Fuller
Various cleanups on ExpandPixmapPath().
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);
480.1.3 by Matthew Fuller
ExpandPixmapPath is only used in the image funcs, so move it to
261
}
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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
 */
276
Image *
277
get_image_anim_cp(const char *name,
480.1.17 by Matthew Fuller
const-ify all these image names we pass through this process.
278
                  ColorPair cp, Image * (*imgloader)(const char *, ColorPair))
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
287
		return NULL;
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
288
	}
289
	if(stmp[1] == '\0') {
290
		fprintf(stderr, "%s(): nothing after %% in '%s'\n", __func__, name);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
291
		return NULL;
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
292
	}
293
	stmp = NULL;
294
295
	/*
296
	 * For animated requests, we load a series of files, replacing the %
297
	 * with numbers in series.
298
	 */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
299
	tail = head = NULL;
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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
480.1.12 by Matthew Fuller
make indent
312
		if(snprintf(path, ANIM_PATHLEN, "%s%d%s", pref, i,
313
		                suff) >= (ANIM_PATHLEN - 1)) {
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
314
			fprintf(stderr, "%s(): generated filename for '%s' #%d longer than %d.\n",
315
			        __func__, name, i, ANIM_PATHLEN);
316
			FreeImage(head);
480.1.23 by Matthew Fuller
Cleanup pref in this error branch.
317
			free(pref);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
318
			return NULL;
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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);
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
327
		if(tmp == NULL) {
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
328
			break;
329
		}
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
330
		tmp->next = NULL;
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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
		 */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
336
		if(head == NULL) {
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
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 */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
347
	if(tail != NULL) {
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
348
		tail->next = head;
349
	}
350
351
	/* Warn if we got nothing */
501.1.1 by Matthew Fuller
Use NULL instead of None for all these places dealing with Image *'s.
352
	if(head == NULL) {
480.1.11 by Matthew Fuller
Take the magic in LoadXpmImage() for putting together animations, and
353
		fprintf(stderr, "Cannot find any image frames for '%s'\n", name);
354
	}
355
356
	return head;
357
}