1 /*
2  * tkImgPmap.c --
3  *
4  *	This file implements images of type "pixmap" for Tk.
5  *
6  * Copyright (c) 1996, Expert Interface Technologies
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  */
12 
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include "pixmapInt.h"
17 #include "tkimg.h"
18 
19 #ifndef PACKAGE_TCLNAME
20 #define PACKAGE_TCLNAME "img::pixmap"
21 #endif
22 
23 #if defined(__WIN32__) && !defined (__GNUC__)
24 #define strncasecmp _strnicmp
25 #endif
26 
27 #ifndef MAC_TCL
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #endif
31 
32 #define UCHAR(c) ((unsigned char) (c))
33 
34 /*
35  * Prototypes for procedures used only locally in this file:
36  */
37 
38 static int TkimgXpmCreate(Tcl_Interp *interp,
39 	const char *name, int argc, Tcl_Obj *objv[],
40 	const Tk_ImageType *typePtr, Tk_ImageMaster master,
41 	ClientData *clientDataPtr);
42 static ClientData TkimgXpmGet(Tk_Window tkwin,
43 	ClientData clientData);
44 static void TkimgXpmDisplay(ClientData clientData,
45 	Display *display, Drawable drawable,
46 	int imageX, int imageY, int width, int height,
47 	int drawableX, int drawableY);
48 static void TkimgXpmFree(ClientData clientData,
49 	Display *display);
50 static void TkimgXpmDelete(ClientData clientData);
51 static int TkimgXpmCmd(ClientData clientData,
52 	Tcl_Interp *interp, int argc, const char **argv);
53 static void TkimgXpmCmdDeletedProc(
54 	ClientData clientData);
55 static void TkimgXpmConfigureInstance(
56 	PixmapInstance *instancePtr);
57 static int TkimgXpmConfigureMaster(
58 	PixmapMaster *masterPtr, int argc, const char **argv,
59 	int flags);
60 static int TkimgXpmGetData(Tcl_Interp *interp,
61 	PixmapMaster *masterPtr);
62 static const char **TkimgXpmGetDataFromFile(Tcl_Interp *interp,
63 	char *string, int *numLines_return);
64 static const char **TkimgXpmGetDataFromString(Tcl_Interp *interp,
65 	char *string, int *numLines_return);
66 static void TkimgXpmGetPixmapFromData(
67 	Tcl_Interp *interp,
68 	PixmapMaster *masterPtr,
69 	PixmapInstance *instancePtr);
70 static char *GetType(char *colorDefn,
71 	int *type_ret);
72 static char *GetColor(char *colorDefn,
73 	char *colorName, int *type_ret);
74 
75 /*
76  * Information used for parsing configuration specs:
77  */
78 
79 static Tk_ConfigSpec configSpecs[] = {
80     {TK_CONFIG_STRING, "-data", (char *) NULL, (char *) NULL,
81 	(char *) NULL, offsetof(PixmapMaster, dataString), TK_CONFIG_NULL_OK},
82     {TK_CONFIG_STRING, "-file", (char *) NULL, (char *) NULL,
83 	(char *) NULL, offsetof(PixmapMaster, fileString), TK_CONFIG_NULL_OK},
84     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
85 	(char *) NULL, 0, 0}
86 };
87 
88 Tk_ImageType imgPixmapImageType = {
89     "pixmap",				/* name */
90     (Tk_ImageCreateProc *) TkimgXpmCreate,/* createProc */
91     TkimgXpmGet,				/* getProc */
92     TkimgXpmDisplay,			/* displayProc */
93     TkimgXpmFree,				/* freeProc */
94     TkimgXpmDelete,			/* deleteProc */
95 #ifdef TK_CONFIG_OBJS
96     (Tk_ImagePostscriptProc *) NULL,	/* postscriptProc */
97 #endif
98     (Tk_ImageType *) NULL		/* nextPtr */
99 };
100 
101 /*
102  *----------------------------------------------------------------------
103  *
104  * TkimgXpmCreate --
105  *
106  *	This procedure is called by the Tk image code to create "pixmap"
107  *	images.
108  *
109  * Results:
110  *	A standard Tcl result.
111  *
112  * Side effects:
113  *	The data structure for a new image is allocated.
114  *
115  *----------------------------------------------------------------------
116  */
117 
118 static int
TkimgXpmCreate(Tcl_Interp * interp,const char * name,int argc,Tcl_Obj * objv[],const Tk_ImageType * typePtr,Tk_ImageMaster master,ClientData * clientDataPtr)119 TkimgXpmCreate(
120     Tcl_Interp *interp,		/* Interpreter for application containing
121 				 * image. */
122     const char *name,			/* Name to use for image. */
123     int argc,			/* Number of arguments. */
124     Tcl_Obj *objv[],		/* Argument strings for options (doesn't
125 				 * include image name or type). */
126     const Tk_ImageType *typePtr,/* Pointer to our type record (not used). */
127     Tk_ImageMaster master,	/* Token for image, to be used by us in
128 				 * later callbacks. */
129     ClientData *clientDataPtr	/* Store manager's token for image here;
130 				 * it will be returned in later callbacks. */
131 ) {
132     PixmapMaster *masterPtr;
133     int i;
134     char *argvbuf[10];
135     const char **args = (const char **) argvbuf;
136 
137     /*
138      * Convert the objc/objv arguments into string equivalent.
139      */
140     if (argc > 10) {
141 	args = (const char **) ckalloc(argc * sizeof(char *));
142     }
143     for (i = 0; i < argc; i++) {
144 	args[i] = tkimg_GetStringFromObj2(objv[i], NULL);
145     }
146 
147     masterPtr = (PixmapMaster *) ckalloc(sizeof(PixmapMaster));
148     masterPtr->tkMaster = master;
149     masterPtr->interp = interp;
150     masterPtr->imageCmd = Tcl_CreateCommand(interp, name, TkimgXpmCmd,
151 	    (ClientData) masterPtr, TkimgXpmCmdDeletedProc);
152 
153     masterPtr->fileString = NULL;
154     masterPtr->dataString = NULL;
155     masterPtr->data = NULL;
156     masterPtr->isDataAlloced = 0;
157     masterPtr->instancePtr = NULL;
158 
159     if (TkimgXpmConfigureMaster(masterPtr, argc, args, 0) != TCL_OK) {
160 	TkimgXpmDelete((ClientData) masterPtr);
161 	if (args != ((const char **) argvbuf)) {
162 	    ckfree((char *) args);
163 	}
164 	return TCL_ERROR;
165     }
166     *clientDataPtr = (ClientData) masterPtr;
167     if (args != ((const char **) argvbuf)) {
168 	ckfree((char *) args);
169     }
170     return TCL_OK;
171 }
172 
173 /*
174  *----------------------------------------------------------------------
175  *
176  * TkimgXpmConfigureMaster --
177  *
178  *	This procedure is called when a pixmap image is created or
179  *	reconfigured.  It process configuration options and resets
180  *	any instances of the image.
181  *
182  * Results:
183  *	A standard Tcl return value.  If TCL_ERROR is returned then
184  *	an error message is left in masterPtr->interp->result.
185  *
186  * Side effects:
187  *	Existing instances of the image will be redisplayed to match
188  *	the new configuration options.
189  *
190  *	If any error occurs, the state of *masterPtr is restored to
191  *	previous state.
192  *
193  *----------------------------------------------------------------------
194  */
195 
196 static int
TkimgXpmConfigureMaster(PixmapMaster * masterPtr,int argc,const char ** argv,int flags)197 TkimgXpmConfigureMaster(
198     PixmapMaster *masterPtr,	/* Pointer to data structure describing
199 				 * overall pixmap image to (reconfigure). */
200     int argc,			/* Number of entries in argv. */
201     const char **argv,		/* Pairs of configuration options for image. */
202     int flags			/* Flags to pass to Tk_ConfigureWidget,
203 				 * such as TK_CONFIG_ARGV_ONLY. */
204 ) {
205     PixmapInstance *instancePtr;
206     char * oldData, * oldFile;
207 
208     oldData = masterPtr->dataString;
209     oldFile = masterPtr->fileString;
210 
211     if (Tk_ConfigureWidget(masterPtr->interp, Tk_MainWindow(masterPtr->interp),
212 	    configSpecs, argc, argv, (char *) masterPtr, flags)
213 	    != TCL_OK) {
214 	return TCL_ERROR;
215     }
216 
217     if (masterPtr->dataString != NULL ||
218 	masterPtr->fileString != NULL) {
219 	if (TkimgXpmGetData(masterPtr->interp, masterPtr) != TCL_OK) {
220 	    goto error;
221 	}
222     } else {
223 	Tcl_AppendResult(masterPtr->interp,
224 	    "must specify one of -data or -file", NULL);
225 	goto error;
226     }
227 
228     /*
229      * Cycle through all of the instances of this image, regenerating
230      * the information for each instance.  Then force the image to be
231      * redisplayed everywhere that it is used.
232      */
233     for (instancePtr = masterPtr->instancePtr; instancePtr != NULL;
234 	instancePtr = instancePtr->nextPtr) {
235 	TkimgXpmConfigureInstance(instancePtr);
236     }
237 
238     if (masterPtr->data) {
239 	Tk_ImageChanged(masterPtr->tkMaster, 0, 0,
240 	    masterPtr->size[0], masterPtr->size[1],
241 	    masterPtr->size[0], masterPtr->size[1]);
242     } else {
243 	Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0);
244     }
245 
246     return TCL_OK;
247 
248   error:
249     /* Restore it to the original (possible valid) mode */
250     if (masterPtr->dataString && masterPtr->dataString != oldData) {
251 	ckfree(masterPtr->dataString);
252     }
253     if (masterPtr->fileString && masterPtr->fileString != oldFile) {
254 	ckfree(masterPtr->fileString);
255     }
256     masterPtr->dataString = oldData;
257     masterPtr->fileString = oldFile;
258     return TCL_ERROR;
259 }
260 
261 /*
262  *----------------------------------------------------------------------
263  *
264  * TkimgXpmGetData --
265  *
266  *	Given a file name or ASCII string, this procedure parses the
267  *	file or string contents to produce binary data for a pixmap.
268  *
269  * Results:
270  *	If the pixmap description was parsed successfully then the data
271  *	is read into an array of strings. This array will later be used
272  *	to create X Pixmaps for each instance.
273  *
274  * Side effects:
275  *	The masterPtr->data array is allocated when successful. Contents of
276  *	*masterPtr is changed only when successful.
277  *----------------------------------------------------------------------
278  */
279 
280 static int
TkimgXpmGetData(Tcl_Interp * interp,PixmapMaster * masterPtr)281 TkimgXpmGetData(
282     Tcl_Interp *interp,			/* For reporting errors. */
283     PixmapMaster *masterPtr
284 ) {
285     const char ** data = NULL;
286     int  isAllocated = 0;		/* do we need to free "data"? */
287     int listArgc;
288     const char ** listArgv = NULL;
289     int numLines;
290     int size[2];
291     int cpp;
292     int ncolors;
293     int code = TCL_OK;
294 
295     if (masterPtr->fileString != NULL) {
296 	if (Tcl_IsSafe(interp)) {
297 	    Tcl_AppendResult(interp, "can't get image from a file in a",
298 		    " safe interpreter", (char *) NULL);
299 	    return TCL_ERROR;
300 	}
301 	data = TkimgXpmGetDataFromFile(interp, masterPtr->fileString, &numLines);
302 	isAllocated = 1;
303     }
304     else if (masterPtr->dataString != NULL) {
305 	data = TkimgXpmGetDataFromString(interp,masterPtr->dataString,&numLines);
306 	isAllocated = 1;
307     }
308     else {
309 	/* Should have been enforced by TkimgXpmConfigureMaster() */
310 	Tcl_Panic("TkimgXpmGetData(): -data and -file are all NULL");
311     }
312 
313     if (data == NULL) {
314 	/* nothing has been allocated yet. Don't need to goto done */
315 	return TCL_ERROR;
316     }
317 
318     /* Parse the first line of the data and get info about this pixmap */
319     if (Tcl_SplitList(interp, data[0], &listArgc, &listArgv) != TCL_OK) {
320 	code = TCL_ERROR; goto done;
321     }
322 
323     if (listArgc < 4) {	/* file format error */
324 	code = TCL_ERROR; goto done;
325     }
326 
327     if (Tcl_GetInt(interp, listArgv[0], &size[0]) != TCL_OK) {
328 	code = TCL_ERROR; goto done;
329     }
330     if (Tcl_GetInt(interp, listArgv[1], &size[1]) != TCL_OK) {
331 	code = TCL_ERROR; goto done;
332     }
333     if (Tcl_GetInt(interp, listArgv[2], &ncolors) != TCL_OK) {
334 	code = TCL_ERROR; goto done;
335     }
336     if (Tcl_GetInt(interp, listArgv[3], &cpp) != TCL_OK) {
337 	code = TCL_ERROR; goto done;
338     }
339 
340     if (isAllocated) {
341 	if (numLines != size[1] + ncolors + 1) {
342 	    /* the number of lines read from the file/data
343 	     * is not the same as specified in the data
344 	     */
345 	    code = TCL_ERROR; goto done;
346 	}
347     }
348 
349   done:
350     if (code == TCL_OK) {
351 	if (masterPtr->isDataAlloced && masterPtr->data) {
352 	    ckfree((char*)masterPtr->data);
353 	}
354 	masterPtr->isDataAlloced = isAllocated;
355 	masterPtr->data = (char **) data;
356 	masterPtr->size[0] = size[0];
357 	masterPtr->size[1] = size[1];
358 	masterPtr->cpp = cpp;
359 	masterPtr->ncolors = ncolors;
360     } else {
361 	if (isAllocated && data) {
362 	    ckfree((char*)data);
363 	}
364 
365 	Tcl_ResetResult(interp);
366 	Tcl_AppendResult(interp, "File format error", NULL);
367     }
368 
369     if (listArgv) {
370 	ckfree((char*)listArgv);
371     }
372 
373     return code;
374 }
375 
376 static const char **
TkimgXpmGetDataFromString(Tcl_Interp * interp,char * string,int * numLines_return)377 TkimgXpmGetDataFromString(
378     Tcl_Interp * interp,
379     char * string,
380     int * numLines_return
381 ) {
382     int quoted;
383     char * p, * list;
384     int numLines;
385     const char ** data;
386 
387     /* skip the leading blanks (leading blanks are not defined in the
388      * the XPM definition, but skipping them shouldn't hurt. Also, the ability
389      * to skip the leading blanks is good for using in-line XPM data in TCL
390      * scripts
391      */
392     while (isspace(UCHAR(*string))) {
393 	++ string;
394     }
395 
396     /* parse the header */
397     if (strncmp("/* XPM", string, 6) != 0) {
398 	goto error;
399     }
400 
401     /* strip the comments */
402     for (quoted = 0, p=string; *p;) {
403 	if (!quoted) {
404 	    if (*p == '"') {
405 		quoted = 1;
406 		++ p;
407 		continue;
408 	    }
409 
410 	    if (*p == '/' && *(p+1) == '*') {
411 		*p++ = ' ';
412 		*p++ = ' ';
413 		while (1) {
414 		    if (*p == 0) {
415 			break;
416 		    }
417 		    if (*p == '*' && *(p+1) == '/') {
418 			*p++ = ' ';
419 			*p++ = ' ';
420 			break;
421 		    }
422 		    *p++ = ' ';
423 		}
424 		continue;
425 	    }
426 	    ++ p;
427 	} else {
428 	    if (*p == '"') {
429 		quoted = 0;
430 	    }
431 	    ++ p;
432 	}
433     }
434 
435     /* Search for the opening brace */
436     for (p=string; *p;) {
437 	if (*p != '{') {
438 	    ++ p;
439 	} else {
440 	    ++p;
441 	    break;
442 	}
443     }
444 
445     /* Change the buffer in to a proper TCL list */
446     quoted = 0;
447     list = p;
448 
449     while (*p) {
450 	if (!quoted) {
451 	    if (*p == '"') {
452 		quoted = 1;
453 		++ p;
454 		continue;
455 	    }
456 
457 	    if (isspace(UCHAR(*p))) {
458 		*p = ' ';
459 	    }
460 	    else if (*p == ',') {
461 		*p = ' ';
462 	    }
463 	    else if (*p == '}') {
464 		*p = 0;
465 		break;
466 	    }
467 	    ++p;
468 	}
469 	else {
470 	    if (*p == '"') {
471 		quoted = 0;
472 	    }
473 	    ++ p;
474 	}
475     }
476 
477     /* The following code depends on the fact that Tcl_SplitList
478      * strips away double quoates inside a list: ie:
479      * if string == "\"1\" \"2\"" then
480      *		list[0] = "1"
481      *		list[1] = "2"
482      * and NOT
483      *
484      *		list[0] = "\"1\""
485      *		list[1] = "\"2\""
486      */
487     if (Tcl_SplitList(interp, list, &numLines, &data) != TCL_OK) {
488 	goto error;
489     } else {
490 	if (numLines == 0) {
491 	    /* error: empty data? */
492 	    if (data != NULL) {
493 		ckfree((char*)data);
494 		goto error;
495 	    }
496 	}
497 	* numLines_return = numLines;
498 	return data;
499     }
500 
501   error:
502     Tcl_AppendResult(interp, "File format error", NULL);
503     return (const char**) NULL;
504 }
505 
506 static const char **
TkimgXpmGetDataFromFile(Tcl_Interp * interp,char * fileName,int * numLines_return)507 TkimgXpmGetDataFromFile(
508     Tcl_Interp * interp,
509     char * fileName,
510     int * numLines_return
511 ) {
512     Tcl_Channel chan;
513     int size;
514     const char ** data = (const char **) NULL;
515     char *cmdBuffer = NULL;
516 
517     chan = tkimg_OpenFileChannel(interp, fileName, 0);
518     if (!chan) {
519 	return (const char **) NULL;
520     }
521 
522     size = Tcl_Seek(chan, 0, SEEK_END);
523     if (size > 0) {
524 	Tcl_Seek(chan, 0, SEEK_SET);
525 	cmdBuffer = (char *) ckalloc(size+1);
526 	size = Tcl_Read(chan, cmdBuffer, size);
527     }
528     if (Tcl_Close(interp, chan) != TCL_OK) {
529 	goto error;
530     }
531     if (size < 0) {
532 	Tcl_AppendResult(interp, fileName, ": ",
533 		Tcl_PosixError(interp), (char *)NULL);
534 	goto error;
535     }
536     cmdBuffer[size] = 0;
537 
538     data = TkimgXpmGetDataFromString(interp, cmdBuffer, numLines_return);
539     error:
540     if (cmdBuffer) {
541 	ckfree(cmdBuffer);
542     }
543     return data;
544 }
545 
546 
547 static char *
GetType(char * colorDefn,int * type_ret)548 GetType(
549     char * colorDefn,
550     int  * type_ret
551 ) {
552     char * p = colorDefn;
553 
554     /* skip white spaces */
555     while (*p && isspace(UCHAR(*p))) {
556 	p ++;
557     }
558 
559     /* parse the type */
560     if (p[0] != '\0' && p[0] == 'm' &&
561 	p[1] != '\0' && isspace(UCHAR(p[1]))) {
562 	*type_ret = XPM_MONO;
563 	p += 2;
564     }
565     else if (p[0] != '\0' && p[0] == 'g' &&
566 	     p[1] != '\0' && p[1] == '4' &&
567 	     p[2] != '\0' && isspace(UCHAR(p[2]))) {
568 	*type_ret = XPM_GRAY_4;
569 	p += 3;
570     }
571     else if (p[0] != '\0' && p[0] == 'g' &&
572 	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
573 	*type_ret = XPM_GRAY;
574 	p += 2;
575     }
576     else if (p[0] != '\0' && p[0] == 'c' &&
577 	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
578 	*type_ret = XPM_COLOR;
579 	p += 2;
580     }
581     else if (p[0] != '\0' && p[0] == 's' &&
582 	     p[1] != '\0' && isspace(UCHAR(p[1]))) {
583 	*type_ret = XPM_SYMBOLIC;
584 	p += 2;
585     }
586     else {
587 	*type_ret = XPM_UNKNOWN;
588 	return NULL;
589     }
590 
591     return p;
592 }
593 
594 /*
595  * colorName is guaranteed to be big enough
596  */
597 
598 static char *
GetColor(char * colorDefn,char * colorName,int * type_ret)599 GetColor(
600     char * colorDefn,
601     char * colorName,		/* if found, name is copied to this array */
602     int  * type_ret
603 ) {
604     int type;
605     char * p;
606 
607     if (!colorDefn) {
608 	return NULL;
609     }
610 
611     if ((colorDefn = GetType(colorDefn, &type)) == NULL) {
612 	/* unknown type */
613 	return NULL;
614     }
615     else {
616 	*type_ret = type;
617     }
618 
619     /* skip white spaces */
620     while (*colorDefn && isspace(UCHAR(*colorDefn))) {
621 	colorDefn ++;
622     }
623 
624     p = colorName;
625 
626     while (1) {
627 	int dummy;
628 
629 	while (*colorDefn && !isspace(UCHAR(*colorDefn))) {
630 	    *p++ = *colorDefn++;
631 	}
632 
633 	if (!*colorDefn) {
634 	    break;
635 	}
636 
637 	if (GetType(colorDefn, &dummy) == NULL) {
638 	    /* the next string should also be considered as a part of a color
639 	     * name */
640 
641 	    while (*colorDefn && isspace(UCHAR(*colorDefn))) {
642 		*p++ = *colorDefn++;
643 	    }
644 	} else {
645 	    break;
646 	}
647 	if (!*colorDefn) {
648 	    break;
649 	}
650     }
651 
652     /* Mark the end of the colorName */
653     *p = '\0';
654 
655     return colorDefn;
656 }
657 
658 /*----------------------------------------------------------------------
659  * TkimgXpmGetPixmapFromData --
660  *
661  *	Creates a pixmap for an image instance.
662  *----------------------------------------------------------------------
663  */
664 
665 static void
TkimgXpmGetPixmapFromData(Tcl_Interp * interp,PixmapMaster * masterPtr,PixmapInstance * instancePtr)666 TkimgXpmGetPixmapFromData(
667     Tcl_Interp * interp,
668     PixmapMaster *masterPtr,
669     PixmapInstance *instancePtr
670 ) {
671     XImage * image = NULL, * mask = NULL;
672     int depth, i, j, k, lOffset, isTransp = 0, isMono;
673     ColorStruct * colors;
674 
675     depth = Tk_Depth(instancePtr->tkwin);
676 
677     switch ((Tk_Visual(instancePtr->tkwin))->class) {
678       case StaticGray:
679       case GrayScale:
680 	isMono = 1;
681 	break;
682       default:
683 	isMono = 0;
684     }
685 
686     TkimgXpmAllocTmpBuffer(masterPtr, instancePtr, &image, &mask);
687 
688     /*
689      * Parse the colors
690      */
691     lOffset = 1;
692     colors = (ColorStruct*)ckalloc(sizeof(ColorStruct)*masterPtr->ncolors);
693 
694     /*
695      * Initialize the color structures
696      */
697     for (i=0; i<masterPtr->ncolors; i++) {
698 	colors[i].colorPtr = NULL;
699 	if (masterPtr->cpp == 1) {
700 	    colors[i].c = 0;
701 	} else {
702 	    colors[i].cstring = (char*)ckalloc(masterPtr->cpp);
703 	    colors[i].cstring[0] = 0;
704 	}
705     }
706 
707     for (i=0; i<masterPtr->ncolors; i++) {
708 	char * colorDefn;		/* the color definition line */
709 	char * colorName;		/* temp place to hold the color name
710 					 * defined for one type of visual */
711 	char * useName;			/* the color name used for this
712 					 * color. If there are many names
713 					 * defined, choose the name that is
714 					 * "best" for the target visual
715 					 */
716 	int found;
717 
718 	colorDefn = masterPtr->data[i+lOffset]+masterPtr->cpp;
719 	colorName = (char*)ckalloc(strlen(colorDefn));
720 	useName   = (char*)ckalloc(strlen(colorDefn));
721 	found     = 0;
722 
723 	while (colorDefn && *colorDefn) {
724 	    int type;
725 
726 	    if ((colorDefn=GetColor(colorDefn, colorName, &type)) == NULL) {
727 		break;
728 	    }
729 	    if (colorName[0] == '\0') {
730 		continue;
731 	    }
732 
733 	    switch (type) {
734 	      case XPM_MONO:
735 		if (isMono && depth == 1) {
736 		    strcpy(useName, colorName);
737 		    found = 1; goto gotcolor;
738 		}
739 		break;
740 	      case XPM_GRAY_4:
741 		if (isMono && depth == 4) {
742 		    strcpy(useName, colorName);
743 		    found = 1; goto gotcolor;
744 		}
745 		break;
746 	      case XPM_GRAY:
747 		if (isMono && depth > 4) {
748 		    strcpy(useName, colorName);
749 		    found = 1; goto gotcolor;
750 		}
751 		break;
752 	      case XPM_COLOR:
753 		if (!isMono) {
754 		    strcpy(useName, colorName);
755 		    found = 1; goto gotcolor;
756 		}
757 		break;
758 	    }
759 	    if (type != XPM_SYMBOLIC && type != XPM_UNKNOWN) {
760 		if (!found) {			/* use this color as default */
761 		    strcpy(useName, colorName);
762 		    found = 1;
763 		}
764 	    }
765 	}
766 
767       gotcolor:
768 	if (masterPtr->cpp == 1) {
769 	    colors[i].c = masterPtr->data[i+lOffset][0];
770 	} else {
771 	    strncpy(colors[i].cstring, masterPtr->data[i+lOffset],
772 		(size_t)masterPtr->cpp);
773 	}
774 
775 	if (found) {
776 	    if (strncasecmp(useName, "none", 5) != 0) {
777 		colors[i].colorPtr = Tk_GetColor(interp,
778 		    instancePtr->tkwin, Tk_GetUid(useName));
779 		if (colors[i].colorPtr == NULL) {
780 		    colors[i].colorPtr = Tk_GetColor(interp,
781 			instancePtr->tkwin, Tk_GetUid("black"));
782 		}
783 	    }
784 	} else {
785 	    colors[i].colorPtr = Tk_GetColor(interp,
786 		instancePtr->tkwin, Tk_GetUid("black"));
787 	}
788 
789 	ckfree(colorName);
790 	ckfree(useName);
791     }
792 
793     lOffset += masterPtr->ncolors;
794 
795     /*
796      * Parse the main body of the image
797      */
798     for (i=0; i<masterPtr->size[1]; i++) {
799 	char * p = masterPtr->data[i+lOffset];
800 
801 	for (j=0; j<masterPtr->size[0]; j++) {
802 	    if (masterPtr->cpp == 1) {
803 		for (k=0; k<masterPtr->ncolors; k++) {
804 		    if (*p == colors[k].c) {
805 			TkimgXpmSetPixel(instancePtr, image, mask, j, i,
806 			        colors[k].colorPtr, &isTransp);
807 			break;
808 		    }
809 		}
810 		if (*p) {
811 		    p++;
812 		}
813 	    } else {
814 		for (k=0; k<masterPtr->ncolors; k++) {
815 		    if (strncmp(p, colors[k].cstring,
816 			    (size_t)masterPtr->cpp) == 0) {
817 			TkimgXpmSetPixel(instancePtr, image, mask, j, i,
818 			        colors[k].colorPtr, &isTransp);
819 			break;
820 		    }
821 		}
822 		for (k=0; *p && k<masterPtr->cpp; k++) {
823 		    p++;
824 		}
825 	    }
826 	}
827     }
828 
829     instancePtr->colors = colors;
830 
831     TkimgXpmRealizePixmap(masterPtr, instancePtr, image, mask, isTransp);
832     TkimgXpmFreeTmpBuffer(masterPtr, instancePtr, image, mask);
833 }
834 
835 /*
836  *----------------------------------------------------------------------
837  *
838  * TkimgXpmConfigureInstance --
839  *
840  *	This procedure is called to create displaying information for
841  *	a pixmap image instance based on the configuration information
842  *	in the master.  It is invoked both when new instances are
843  *	created and when the master is reconfigured.
844  *
845  * Results:
846  *	None.
847  *
848  * Side effects:
849  *	Generates errors via Tk_BackgroundError if there are problems
850  *	in setting up the instance.
851  *
852  *----------------------------------------------------------------------
853  */
854 
855 static void
TkimgXpmConfigureInstance(PixmapInstance * instancePtr)856 TkimgXpmConfigureInstance(
857     PixmapInstance *instancePtr		/* Instance to reconfigure. */
858 ) {
859     PixmapMaster *masterPtr = instancePtr->masterPtr;
860 
861     if (instancePtr->pixmap != None) {
862 	Tk_FreePixmap(Tk_Display(instancePtr->tkwin), instancePtr->pixmap);
863     }
864     TkimgXpmFreeInstanceData(instancePtr, 0);
865 
866     if (instancePtr->colors != NULL) {
867 	int i;
868 	for (i=0; i<masterPtr->ncolors; i++) {
869 	    if (instancePtr->colors[i].colorPtr != NULL) {
870 		Tk_FreeColor(instancePtr->colors[i].colorPtr);
871 	    }
872 	    if (masterPtr->cpp != 1) {
873 		ckfree(instancePtr->colors[i].cstring);
874 	    }
875 	}
876 	ckfree((char*)instancePtr->colors);
877     }
878 
879     if (Tk_WindowId(instancePtr->tkwin) == None) {
880 	Tk_MakeWindowExist(instancePtr->tkwin);
881     }
882 
883     /*
884      * Assumption: masterPtr->data is always non NULL (enfored by
885      * TkimgXpmConfigureMaster()). Also, the data must be in a valid
886      * format (partially enforced by TkimgXpmConfigureMaster(), see comments
887      * inside that function).
888      */
889     TkimgXpmGetPixmapFromData(masterPtr->interp, masterPtr, instancePtr);
890 }
891 
892 /*
893  *--------------------------------------------------------------
894  *
895  * TkimgXpmCmd --
896  *
897  *	This procedure is invoked to process the Tcl command
898  *	that corresponds to an image managed by this module.
899  *	See the user documentation for details on what it does.
900  *
901  * Results:
902  *	A standard Tcl result.
903  *
904  * Side effects:
905  *	See the user documentation.
906  *
907  *--------------------------------------------------------------
908  */
909 
910 static int
TkimgXpmCmd(ClientData clientData,Tcl_Interp * interp,int argc,const char ** argv)911 TkimgXpmCmd(
912     ClientData clientData,	/* Information about button widget. */
913     Tcl_Interp *interp,		/* Current interpreter. */
914     int argc,			/* Number of arguments. */
915     const char **argv		/* Argument strings. */
916 ) {
917     PixmapMaster *masterPtr = (PixmapMaster *) clientData;
918     int c, code;
919     size_t length;
920 
921     if (argc < 2) {
922 	Tcl_AppendResult(interp, "wrong # args: should be \"",
923 		argv[0], " option ?arg arg ...?\"",
924 		(char *) NULL);
925 	return TCL_ERROR;
926     }
927     c = argv[1][0];
928     length = strlen(argv[1]);
929 
930     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
931 	    && (length >= 2)) {
932 	if (argc != 3) {
933 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
934 		    argv[0], " cget option\"",
935 		    (char *) NULL);
936 	    return TCL_ERROR;
937 	}
938 	return Tk_ConfigureValue(interp, Tk_MainWindow(interp), configSpecs,
939 		(char *) masterPtr, argv[2], 0);
940     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
941 	    && (length >= 2)) {
942 	if (argc == 2) {
943 	    code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp),
944 		    configSpecs, (char *) masterPtr, (char *) NULL, 0);
945 	} else if (argc == 3) {
946 	    code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp),
947 		    configSpecs, (char *) masterPtr, argv[2], 0);
948 	} else {
949 	    code = TkimgXpmConfigureMaster(masterPtr, argc-2, argv+2,
950 		    TK_CONFIG_ARGV_ONLY);
951 	}
952 	return code;
953     } else if ((c == 'r') && (strncmp(argv[1], "refcount", length) == 0)) {
954 	/*
955 	 * The "refcount" command is for debugging only
956 	 */
957 	PixmapInstance *instancePtr;
958 	int count = 0;
959 	char buff[30];
960 
961 	if (argc != 1) {
962 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
963 		    argv[0], "\"", (char *) NULL);
964 	    return TCL_ERROR;
965 	}
966 	for (instancePtr=masterPtr->instancePtr; instancePtr;
967 	     instancePtr = instancePtr->nextPtr) {
968 	    count += instancePtr->refCount;
969 	}
970 	sprintf(buff, "%d", count);
971 	Tcl_AppendResult(interp, buff, (char *) NULL);
972 	return TCL_OK;
973     } else {
974 	Tcl_AppendResult(interp, "bad option \"", argv[1],
975 	    "\": must be cget, configure or refcount", (char *) NULL);
976 	return TCL_ERROR;
977     }
978 }
979 
980 /*
981  *----------------------------------------------------------------------
982  *
983  * TkimgXpmGet --
984  *
985  *	This procedure is called for each use of a pixmap image in a
986  *	widget.
987  *
988  * Results:
989  *	The return value is a token for the instance, which is passed
990  *	back to us in calls to TkimgXpmDisplay and TkimgXpmFre.
991  *
992  * Side effects:
993  *	A data structure is set up for the instance (or, an existing
994  *	instance is re-used for the new one).
995  *
996  *----------------------------------------------------------------------
997  */
998 
999 static ClientData
TkimgXpmGet(Tk_Window tkwin,ClientData masterData)1000 TkimgXpmGet(
1001     Tk_Window tkwin,		/* Window in which the instance will be
1002 				 * used. */
1003     ClientData masterData	/* Pointer to our master structure for the
1004 				 * image. */
1005 ) {
1006     PixmapMaster *masterPtr = (PixmapMaster *) masterData;
1007     PixmapInstance *instancePtr;
1008 
1009     /*
1010      * See if there is already an instance for this window.  If so
1011      * then just re-use it.
1012      */
1013 
1014     for (instancePtr = masterPtr->instancePtr; instancePtr != NULL;
1015 	    instancePtr = instancePtr->nextPtr) {
1016 	if (instancePtr->tkwin == tkwin) {
1017 	    instancePtr->refCount++;
1018 	    return (ClientData) instancePtr;
1019 	}
1020     }
1021 
1022     /*
1023      * The image isn't already in use in this window.  Make a new
1024      * instance of the image.
1025      */
1026     instancePtr = (PixmapInstance *) ckalloc(sizeof(PixmapInstance));
1027     instancePtr->refCount = 1;
1028     instancePtr->masterPtr = masterPtr;
1029     instancePtr->tkwin = tkwin;
1030     instancePtr->pixmap = None;
1031     instancePtr->nextPtr = masterPtr->instancePtr;
1032     instancePtr->colors = NULL;
1033     masterPtr->instancePtr = instancePtr;
1034 
1035     TkimgInitPixmapInstance(masterPtr, instancePtr);
1036     TkimgXpmConfigureInstance(instancePtr);
1037 
1038     /*
1039      * If this is the first instance, must set the size of the image.
1040      */
1041     if (instancePtr->nextPtr == NULL) {
1042 	if (masterPtr->data) {
1043 	    Tk_ImageChanged(masterPtr->tkMaster, 0, 0,
1044 	        masterPtr->size[0], masterPtr->size[1],
1045 	        masterPtr->size[0], masterPtr->size[1]);
1046 	} else {
1047 	    Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0);
1048 	}
1049     }
1050 
1051     return (ClientData) instancePtr;
1052 }
1053 
1054 /*
1055  *----------------------------------------------------------------------
1056  *
1057  * TkimgXpmDisplay --
1058  *
1059  *	This procedure is invoked to draw a pixmap image.
1060  *
1061  * Results:
1062  *	None.
1063  *
1064  * Side effects:
1065  *	A portion of the image gets rendered in a pixmap or window.
1066  *
1067  *----------------------------------------------------------------------
1068  */
1069 
1070 static void
TkimgXpmDisplay(ClientData clientData,Display * display,Drawable drawable,int imageX,int imageY,int width,int height,int drawableX,int drawableY)1071 TkimgXpmDisplay(
1072     ClientData clientData,	/* Pointer to PixmapInstance structure for
1073 				 * for instance to be displayed. */
1074     Display *display,		/* Display on which to draw image. */
1075     Drawable drawable,		/* Pixmap or window in which to draw image. */
1076     int imageX, int imageY,	/* Upper-left corner of region within image
1077 				 * to draw. */
1078     int width, int height,	/* Dimensions of region within image to draw.*/
1079     int drawableX, int drawableY/* Coordinates within drawable that
1080 				 * correspond to imageX and imageY. */
1081 ) {
1082     TkimgpXpmDisplay(clientData, display, drawable, imageX, imageY, width,
1083 	height, drawableX, drawableY);
1084 }
1085 
1086 /*
1087  *----------------------------------------------------------------------
1088  *
1089  * TkimgXpmFree --
1090  *
1091  *	This procedure is called when a widget ceases to use a
1092  *	particular instance of an image.
1093  *
1094  * Results:
1095  *	None.
1096  *
1097  * Side effects:
1098  *	Internal data structures get cleaned up.
1099  *
1100  *----------------------------------------------------------------------
1101  */
1102 
1103 static void
TkimgXpmFree(ClientData clientData,Display * display)1104 TkimgXpmFree(
1105     ClientData clientData,	/* Pointer to PixmapInstance structure for
1106 				 * for instance to be displayed. */
1107     Display *display		/* Display containing window that used image.*/
1108 ) {
1109     PixmapInstance *instancePtr = (PixmapInstance *) clientData;
1110     PixmapInstance *prevPtr;
1111 
1112     instancePtr->refCount--;
1113     if (instancePtr->refCount > 0) {
1114 	return;
1115     }
1116 
1117     /*
1118      * There are no more uses of the image within this widget.  Free
1119      * the instance structure.
1120      */
1121     if (instancePtr->pixmap != None) {
1122 	Tk_FreePixmap(display, instancePtr->pixmap);
1123     }
1124     TkimgXpmFreeInstanceData(instancePtr, 1);
1125 
1126     if (instancePtr->colors != NULL) {
1127 	int i;
1128 	for (i=0; i<instancePtr->masterPtr->ncolors; i++) {
1129 	    if (instancePtr->colors[i].colorPtr != NULL) {
1130 		Tk_FreeColor(instancePtr->colors[i].colorPtr);
1131 	    }
1132 	    if (instancePtr->masterPtr->cpp != 1) {
1133 		ckfree(instancePtr->colors[i].cstring);
1134 	    }
1135 	}
1136 	ckfree((char*)instancePtr->colors);
1137     }
1138 
1139     if (instancePtr->masterPtr->instancePtr == instancePtr) {
1140 	instancePtr->masterPtr->instancePtr = instancePtr->nextPtr;
1141     } else {
1142 	for (prevPtr = instancePtr->masterPtr->instancePtr;
1143 		prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) {
1144 	    /* Empty loop body */
1145 	}
1146 	prevPtr->nextPtr = instancePtr->nextPtr;
1147     }
1148     ckfree((char *) instancePtr);
1149 }
1150 
1151 /*
1152  *----------------------------------------------------------------------
1153  *
1154  * TkimgXpmDelete --
1155  *
1156  *	This procedure is called by the image code to delete the
1157  *	master structure for an image.
1158  *
1159  * Results:
1160  *	None.
1161  *
1162  * Side effects:
1163  *	Resources associated with the image get freed.
1164  *
1165  *----------------------------------------------------------------------
1166  */
1167 
1168 static void
TkimgXpmDelete(ClientData masterData)1169 TkimgXpmDelete(
1170     ClientData masterData	/* Pointer to PixmapMaster structure for
1171 				 * image.  Must not have any more instances. */
1172 ) {
1173     PixmapMaster *masterPtr = (PixmapMaster *) masterData;
1174 
1175     if (masterPtr->instancePtr != NULL) {
1176 	Tcl_Panic("tried to delete pixmap image when instances still exist");
1177     }
1178     masterPtr->tkMaster = NULL;
1179     if (masterPtr->imageCmd != NULL) {
1180 	Tcl_DeleteCommand(masterPtr->interp,
1181 		Tcl_GetCommandName(masterPtr->interp, masterPtr->imageCmd));
1182     }
1183     if (masterPtr->isDataAlloced && masterPtr->data != NULL) {
1184 	ckfree((char*)masterPtr->data);
1185 	masterPtr->data = NULL;
1186     }
1187 
1188     Tk_FreeOptions(configSpecs, (char *) masterPtr, (Display *) NULL, 0);
1189     ckfree((char *) masterPtr);
1190 }
1191 
1192 /*
1193  *----------------------------------------------------------------------
1194  *
1195  * TkimgXpmCmdDeletedProc --
1196  *
1197  *	This procedure is invoked when the image command for an image
1198  *	is deleted.  It deletes the image.
1199  *
1200  * Results:
1201  *	None.
1202  *
1203  * Side effects:
1204  *	The image is deleted.
1205  *
1206  *----------------------------------------------------------------------
1207  */
1208 
1209 static void
TkimgXpmCmdDeletedProc(ClientData clientData)1210 TkimgXpmCmdDeletedProc(
1211     ClientData clientData	/* Pointer to PixmapMaster structure for
1212 				 * image. */
1213 ) {
1214     PixmapMaster *masterPtr = (PixmapMaster *) clientData;
1215 
1216     masterPtr->imageCmd = NULL;
1217     if (masterPtr->tkMaster != NULL) {
1218 	Tk_DeleteImage(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster));
1219     }
1220 }
1221 
1222 /*
1223  * Package management. Initialization of stub information.
1224  */
1225 
1226 /*
1227  *----------------------------------------------------------------------------
1228  *
1229  * Tkimgpixmap_Init --
1230  *
1231  *  Initialisation routine for loadable module
1232  *
1233  * Results:
1234  *  None.
1235  *
1236  * Side effects:
1237  *  Creates commands in the interpreter, loads package.
1238  *
1239  *----------------------------------------------------------------------------
1240  */
1241 
1242 int
Tkimgpixmap_Init(Tcl_Interp * interp)1243 Tkimgpixmap_Init(
1244       Tcl_Interp *interp	/* Interpreter to initialise. */
1245 ) {
1246     static int initialized = 0;
1247 
1248     if (Tcl_InitStubs(interp, "8.3", 0) == NULL) {
1249         return TCL_ERROR;
1250     }
1251     if (Tk_InitStubs(interp, "8.3", 0) == NULL) {
1252         return TCL_ERROR;
1253     }
1254     if (Tkimg_InitStubs(interp, TKIMG_VERSION, 0) == NULL) {
1255         return TCL_ERROR;
1256     }
1257 
1258 #ifndef TCL_MAC
1259     if (!initialized) {
1260 	Tk_CreateImageType(&imgPixmapImageType);
1261 	initialized = 1;
1262     }
1263 #endif
1264 
1265     /*
1266      * At last provide the package ...
1267      */
1268 
1269     if (Tcl_PkgProvide(interp, PACKAGE_TCLNAME, PACKAGE_VERSION) != TCL_OK) {
1270         return TCL_ERROR;
1271     }
1272     return TCL_OK;
1273 }
1274 
1275 /*
1276  *----------------------------------------------------------------------------
1277  *
1278  * Tkimgpixmap_SafeInit --
1279  *
1280  *  Initialisation routine for loadable module in a safe interpreter.
1281  *
1282  * Results:
1283  *  None.
1284  *
1285  * Side effects:
1286  *  Creates commands in the interpreter,
1287  *  loads xml package.
1288  *
1289  *----------------------------------------------------------------------------
1290  */
1291 
1292 int
Tkimgpixmap_SafeInit(Tcl_Interp * interp)1293 Tkimgpixmap_SafeInit(
1294       Tcl_Interp *interp	/* Interpreter to initialise. */
1295 ) {
1296     return Tkimgpixmap_Init (interp);
1297 }
1298