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