Annotation of micropolis/src/tk/tkbind.c, revision 1.1

1.1     ! root        1: /* 
        !             2:  * tkBind.c --
        !             3:  *
        !             4:  *     This file provides procedures that associate Tcl commands
        !             5:  *     with X events or sequences of X events.
        !             6:  *
        !             7:  * Copyright 1989-1991 Regents of the University of California
        !             8:  * Permission to use, copy, modify, and distribute this
        !             9:  * software and its documentation for any purpose and without
        !            10:  * fee is hereby granted, provided that the above copyright
        !            11:  * notice appear in all copies.  The University of California
        !            12:  * makes no representations about the suitability of this
        !            13:  * software for any purpose.  It is provided "as is" without
        !            14:  * express or implied warranty.
        !            15:  */
        !            16: 
        !            17: #ifndef lint
        !            18: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkBind.c,v 1.48 92/08/10 16:55:24 ouster Exp $ SPRITE (Berkeley)";
        !            19: #endif /* not lint */
        !            20: 
        !            21: #include "tkconfig.h"
        !            22: #include "tkint.h"
        !            23: 
        !            24: /*
        !            25:  * The structure below represents a binding table.  A binding table
        !            26:  * represents a domain in which event bindings may occur.  It includes
        !            27:  * a space of objects relative to which events occur (usually windows,
        !            28:  * but not always), a history of recent events in the domain, and
        !            29:  * a set of mappings that associate particular Tcl commands with sequences
        !            30:  * of events in the domain.  Multiple binding tables may exist at once,
        !            31:  * either because there are multiple applications open, or because there
        !            32:  * are multiple domains within an application with separate event
        !            33:  * bindings for each (for example, each canvas widget has a separate
        !            34:  * binding table for associating events with the items in the canvas).
        !            35:  */
        !            36: 
        !            37: #define EVENT_BUFFER_SIZE 10
        !            38: typedef struct BindingTable {
        !            39:     XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
        !            40:                                         * (higher indices are for more recent
        !            41:                                         * events). */
        !            42:     int detailRing[EVENT_BUFFER_SIZE]; /* "Detail" information (keySym or
        !            43:                                         * button or 0) for each entry in
        !            44:                                         * eventRing. */
        !            45:     int curEvent;                      /* Index in eventRing of most recent
        !            46:                                         * event.  Newer events have higher
        !            47:                                         * indices. */
        !            48:     Tcl_HashTable patternTable;                /* Used to map from an event to a list
        !            49:                                         * of patterns that may match that
        !            50:                                         * event.  Keys are PatternTableKey
        !            51:                                         * structs, values are (PatSeq *). */
        !            52:     Tcl_HashTable objectTable;         /* Used to map from an object to a list
        !            53:                                         * of patterns associated with that
        !            54:                                         * object.  Keys are ClientData,
        !            55:                                         * values are (PatSeq *). */
        !            56:     Tcl_Interp *interp;                        /* Interpreter in which commands are
        !            57:                                         * executed. */
        !            58: } BindingTable;
        !            59: 
        !            60: /*
        !            61:  * Structures of the following form are used as keys in the patternTable
        !            62:  * for a binding table:
        !            63:  */
        !            64: 
        !            65: typedef struct PatternTableKey {
        !            66:     ClientData object;         /* Identifies object (or class of objects)
        !            67:                                 * relative to which event occurred.  For
        !            68:                                 * example, in the widget binding table for
        !            69:                                 * an application this is the path name of
        !            70:                                 * a widget, or a widget class, or "all". */
        !            71:     int type;                  /* Type of event (from X). */
        !            72:     int detail;                        /* Additional information, such as
        !            73:                                 * keysym or button, or 0 if nothing
        !            74:                                 * additional.*/
        !            75: } PatternTableKey;
        !            76: 
        !            77: /*
        !            78:  * The following structure defines a pattern, which is matched
        !            79:  * against X events as part of the process of converting X events
        !            80:  * into Tcl commands.
        !            81:  */
        !            82: 
        !            83: typedef struct Pattern {
        !            84:     int eventType;             /* Type of X event, e.g. ButtonPress. */
        !            85:     int needMods;              /* Mask of modifiers that must be
        !            86:                                 * present (0 means no modifiers are
        !            87:                                 * required). */
        !            88:     int hateMods;              /* Mask of modifiers that must not be
        !            89:                                 * present (0 means any modifiers are
        !            90:                                 * OK). */
        !            91:     int detail;                        /* Additional information that must
        !            92:                                 * match event.  Normally this is 0,
        !            93:                                 * meaning no additional information
        !            94:                                 * must match.  For KeyPress and
        !            95:                                 * KeyRelease events, a keySym may
        !            96:                                 * be specified to select a
        !            97:                                 * particular keystroke (0 means any
        !            98:                                 * keystrokes).  For button events,
        !            99:                                 * specifies a particular button (0
        !           100:                                 * means any buttons are OK). */
        !           101: } Pattern;
        !           102: 
        !           103: /*
        !           104:  * The structure below defines a pattern sequence, which consists
        !           105:  * of one or more patterns.  In order to trigger, a pattern
        !           106:  * sequence must match the most recent X events (first pattern
        !           107:  * to most recent event, next pattern to next event, and so on).
        !           108:  */
        !           109: 
        !           110: typedef struct PatSeq {
        !           111:     int numPats;               /* Number of patterns in sequence
        !           112:                                 * (usually 1). */
        !           113:     char *command;             /* Command to invoke when this
        !           114:                                 * pattern sequence matches (malloc-ed). */
        !           115:     int flags;                 /* Miscellaneous flag values;  see
        !           116:                                 * below for definitions. */
        !           117:     struct PatSeq *nextSeqPtr;
        !           118:                                /* Next in list of all pattern
        !           119:                                 * sequences that have the same
        !           120:                                 * initial pattern.  NULL means
        !           121:                                 * end of list. */
        !           122:     Tcl_HashEntry *hPtr;       /* Pointer to hash table entry for
        !           123:                                 * the initial pattern.  This is the
        !           124:                                 * head of the list of which nextSeqPtr
        !           125:                                 * forms a part. */
        !           126:     ClientData object;         /* Identifies object with which event is
        !           127:                                 * associated (e.g. window). */
        !           128:     struct PatSeq *nextObjPtr;
        !           129:                                /* Next in list of all pattern
        !           130:                                 * sequences for the same object
        !           131:                                 * (NULL for end of list).  Needed to
        !           132:                                 * implement Tk_DeleteAllBindings. */
        !           133:     Pattern pats[1];           /* Array of "numPats" patterns.  Only
        !           134:                                 * one element is declared here but
        !           135:                                 * in actuality enough space will be
        !           136:                                 * allocated for "numPats" patterns.
        !           137:                                 * To match, pats[0] must match event
        !           138:                                 * n, pats[1] must match event n-1,
        !           139:                                 * etc. */
        !           140: } PatSeq;
        !           141: 
        !           142: /*
        !           143:  * Flag values for PatSeq structures:
        !           144:  *
        !           145:  * PAT_NEARBY          1 means that all of the events matching
        !           146:  *                     this sequence must occur with nearby X
        !           147:  *                     and Y mouse coordinates and close in time.
        !           148:  *                     This is typically used to restrict multiple
        !           149:  *                     button presses.
        !           150:  * PAT_PERCENTS                1 means that the command for this pattern
        !           151:  *                     requires percent substitution.  0 means there
        !           152:  *                     are no percents in the command.
        !           153:  */
        !           154: 
        !           155: #define PAT_NEARBY             1
        !           156: #define PAT_PERCENTS           2
        !           157: 
        !           158: /*
        !           159:  * Constants that define how close together two events must be
        !           160:  * in milliseconds or pixels to meet the PAT_NEARBY constraint:
        !           161:  */
        !           162: 
        !           163: #define NEARBY_PIXELS          5
        !           164: #define NEARBY_MS              500
        !           165: 
        !           166: /*
        !           167:  * The data structure and hash table below are used to map from
        !           168:  * textual keysym names to keysym numbers.  This structure is
        !           169:  * present here because the corresponding X procedures are
        !           170:  * ridiculously slow.
        !           171:  */
        !           172: 
        !           173: typedef struct {
        !           174:     char *name;                                /* Name of keysym. */
        !           175:     KeySym value;                      /* Numeric identifier for keysym. */
        !           176: } KeySymInfo;
        !           177: KeySymInfo keyArray[] = {
        !           178: #ifndef lint
        !           179: #include "ks_names.h"
        !           180: #endif
        !           181:     (char *) NULL, 0
        !           182: };
        !           183: static Tcl_HashTable keySymTable;      /* Hashed form of above structure. */
        !           184: 
        !           185: static int initialized = 0;
        !           186: 
        !           187: /*
        !           188:  * A hash table is kept to map from the string names of event
        !           189:  * modifiers to information about those modifiers.  The structure
        !           190:  * for storing this information, and the hash table built at
        !           191:  * initialization time, are defined below.
        !           192:  */
        !           193: 
        !           194: typedef struct {
        !           195:     char *name;                        /* Name of modifier. */
        !           196:     int mask;                  /* Button/modifier mask value,                                                   * such as Button1Mask. */
        !           197:     int flags;                 /* Various flags;  see below for
        !           198:                                 * definitions. */
        !           199: } ModInfo;
        !           200: 
        !           201: /*
        !           202:  * Flags for ModInfo structures:
        !           203:  *
        !           204:  * DOUBLE -            Non-zero means duplicate this event,
        !           205:  *                     e.g. for double-clicks.
        !           206:  * TRIPLE -            Non-zero means triplicate this event,
        !           207:  *                     e.g. for triple-clicks.
        !           208:  * ANY -               Non-zero means that this event allows
        !           209:  *                     any unspecified modifiers.
        !           210:  */
        !           211: 
        !           212: #define DOUBLE         1
        !           213: #define TRIPLE         2
        !           214: #define ANY            4
        !           215: 
        !           216: static ModInfo modArray[] = {
        !           217:     "Control",         ControlMask,    0,
        !           218:     "Shift",           ShiftMask,      0,
        !           219:     "Lock",            LockMask,       0,
        !           220:     "B1",              Button1Mask,    0,
        !           221:     "Button1",         Button1Mask,    0,
        !           222:     "B2",              Button2Mask,    0,
        !           223:     "Button2",         Button2Mask,    0,
        !           224:     "B3",              Button3Mask,    0,
        !           225:     "Button3",         Button3Mask,    0,
        !           226:     "B4",              Button4Mask,    0,
        !           227:     "Button4",         Button4Mask,    0,
        !           228:     "B5",              Button5Mask,    0,
        !           229:     "Button5",         Button5Mask,    0,
        !           230:     "Mod1",            Mod1Mask,       0,
        !           231:     "M1",              Mod1Mask,       0,
        !           232:     "Meta",            Mod1Mask,       0,
        !           233:     "M",               Mod1Mask,       0,
        !           234:     "Mod2",            Mod2Mask,       0,
        !           235:     "M2",              Mod2Mask,       0,
        !           236:     "Alt",             Mod2Mask,       0,
        !           237:     "Mod3",            Mod3Mask,       0,
        !           238:     "M3",              Mod3Mask,       0,
        !           239:     "Mod4",            Mod4Mask,       0,
        !           240:     "M4",              Mod4Mask,       0,
        !           241:     "Mod5",            Mod5Mask,       0,
        !           242:     "M5",              Mod5Mask,       0,
        !           243:     "Double",          0,              DOUBLE,
        !           244:     "Triple",          0,              TRIPLE,
        !           245:     "Any",             0,              ANY,
        !           246:     NULL,              0,              0};
        !           247: static Tcl_HashTable modTable;
        !           248: 
        !           249: /*
        !           250:  * This module also keeps a hash table mapping from event names
        !           251:  * to information about those events.  The structure, an array
        !           252:  * to use to initialize the hash table, and the hash table are
        !           253:  * all defined below.
        !           254:  */
        !           255: 
        !           256: typedef struct {
        !           257:     char *name;                        /* Name of event. */
        !           258:     int type;                  /* Event type for X, such as
        !           259:                                 * ButtonPress. */
        !           260:     int eventMask;             /* Mask bits (for XSelectInput)
        !           261:                                 * for this event type. */
        !           262: } EventInfo;
        !           263: 
        !           264: /*
        !           265:  * Note:  some of the masks below are an OR-ed combination of
        !           266:  * several masks.  This is necessary because X doesn't report
        !           267:  * up events unless you also ask for down events.  Also, X
        !           268:  * doesn't report button state in motion events unless you've
        !           269:  * asked about button events.
        !           270:  */
        !           271: 
        !           272: static EventInfo eventArray[] = {
        !           273:     "Motion",          MotionNotify,
        !           274:            ButtonPressMask|PointerMotionMask,
        !           275:     "Button",          ButtonPress,            ButtonPressMask,
        !           276:     "ButtonPress",     ButtonPress,            ButtonPressMask,
        !           277:     "ButtonRelease",   ButtonRelease,
        !           278:            ButtonPressMask|ButtonReleaseMask,
        !           279:     "Colormap",                ColormapNotify,         ColormapChangeMask,
        !           280:     "Enter",           EnterNotify,            EnterWindowMask,
        !           281:     "Leave",           LeaveNotify,            LeaveWindowMask,
        !           282:     "Expose",          Expose,                 ExposureMask,
        !           283:     "FocusIn",         FocusIn,                FocusChangeMask,
        !           284:     "FocusOut",                FocusOut,               FocusChangeMask,
        !           285:     "Keymap",          KeymapNotify,           KeymapStateMask,
        !           286:     "Key",             KeyPress,               KeyPressMask,
        !           287:     "KeyPress",                KeyPress,               KeyPressMask,
        !           288:     "KeyRelease",      KeyRelease,
        !           289:            KeyPressMask|KeyReleaseMask,
        !           290:     "Property",                PropertyNotify,         PropertyChangeMask,
        !           291:     "ResizeRequest",   ResizeRequest,          ResizeRedirectMask,
        !           292:     "Circulate",       CirculateNotify,        StructureNotifyMask,
        !           293:     "Configure",       ConfigureNotify,        StructureNotifyMask,
        !           294:     "Destroy",         DestroyNotify,          StructureNotifyMask,
        !           295:     "Gravity",         GravityNotify,          StructureNotifyMask,
        !           296:     "Map",             MapNotify,              StructureNotifyMask,
        !           297:     "Reparent",                ReparentNotify,         StructureNotifyMask,
        !           298:     "Unmap",           UnmapNotify,            StructureNotifyMask,
        !           299:     "Visibility",      VisibilityNotify,       VisibilityChangeMask,
        !           300:     "CirculateRequest",        CirculateRequest,       SubstructureRedirectMask,
        !           301:     "ConfigureRequest",        ConfigureRequest,       SubstructureRedirectMask,
        !           302:     "MapRequest",      MapRequest,             SubstructureRedirectMask,
        !           303:     (char *) NULL,     0,                      0};
        !           304: static Tcl_HashTable eventTable;
        !           305: 
        !           306: /*
        !           307:  * The defines and table below are used to classify events into
        !           308:  * various groups.  The reason for this is that logically identical
        !           309:  * fields (e.g. "state") appear at different places in different
        !           310:  * types of events.  The classification masks can be used to figure
        !           311:  * out quickly where to extract information from events.
        !           312:  */
        !           313: 
        !           314: #define KEY_BUTTON_MOTION      0x1
        !           315: #define CROSSING               0x2
        !           316: #define FOCUS                  0x4
        !           317: #define EXPOSE                 0x8
        !           318: #define VISIBILITY             0x10
        !           319: #define CREATE                 0x20
        !           320: #define MAP                    0x40
        !           321: #define REPARENT               0x80
        !           322: #define CONFIG                 0x100
        !           323: #define CONFIG_REQ             0x200
        !           324: #define RESIZE_REQ             0x400
        !           325: #define GRAVITY                        0x800
        !           326: #define PROP                   0x0100
        !           327: #define SEL_CLEAR              0x2000
        !           328: #define SEL_REQ                        0x4000
        !           329: #define SEL_NOTIFY             0x8000
        !           330: #define COLORMAP               0x10000
        !           331: #define MAPPING                        0x20000
        !           332: 
        !           333: static int flagArray[LASTEvent] = {
        !           334:    /* Not used */              0,
        !           335:    /* Not used */              0,
        !           336:    /* KeyPress */              KEY_BUTTON_MOTION,
        !           337:    /* KeyRelease */            KEY_BUTTON_MOTION,
        !           338:    /* ButtonPress */           KEY_BUTTON_MOTION,
        !           339:    /* ButtonRelease */         KEY_BUTTON_MOTION,
        !           340:    /* MotionNotify */          KEY_BUTTON_MOTION,
        !           341:    /* EnterNotify */           CROSSING,
        !           342:    /* LeaveNotify */           CROSSING,
        !           343:    /* FocusIn */               FOCUS,
        !           344:    /* FocusOut */              FOCUS,
        !           345:    /* KeymapNotify */          0,
        !           346:    /* Expose */                        EXPOSE,
        !           347:    /* GraphicsExpose */                EXPOSE,
        !           348:    /* NoExpose */              0,
        !           349:    /* VisibilityNotify */      VISIBILITY,
        !           350:    /* CreateNotify */          CREATE,
        !           351:    /* DestroyNotify */         0,
        !           352:    /* UnmapNotify */           0,
        !           353:    /* MapNotify */             MAP,
        !           354:    /* MapRequest */            0,
        !           355:    /* ReparentNotify */                REPARENT,
        !           356:    /* ConfigureNotify */       CONFIG,
        !           357:    /* ConfigureRequest */      CONFIG_REQ,
        !           358:    /* GravityNotify */         0,
        !           359:    /* ResizeRequest */         RESIZE_REQ,
        !           360:    /* CirculateNotify */       0,
        !           361:    /* CirculateRequest */      0,
        !           362:    /* PropertyNotify */                PROP,
        !           363:    /* SelectionClear */                SEL_CLEAR,
        !           364:    /* SelectionRequest */      SEL_REQ,
        !           365:    /* SelectionNotify */       SEL_NOTIFY,
        !           366:    /* ColormapNotify */                COLORMAP,
        !           367:    /* ClientMessage */         0,
        !           368:    /* MappingNotify */         MAPPING
        !           369: };
        !           370: 
        !           371: /*
        !           372:  * Forward declarations for procedures defined later in this
        !           373:  * file:
        !           374:  */
        !           375: 
        !           376: static char *          ExpandPercents _ANSI_ARGS_((char *before,
        !           377:                            XEvent *eventPtr, KeySym keySym, char *after,
        !           378:                            int afterSize));
        !           379: static PatSeq *                FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
        !           380:                            BindingTable *bindPtr, ClientData object,
        !           381:                            char *eventString, int create,
        !           382:                            unsigned long *maskPtr));
        !           383: static char *          GetField _ANSI_ARGS_((char *p, char *copy, int size));
        !           384: static KeySym          GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
        !           385:                            XEvent *eventPtr));
        !           386: static PatSeq *                MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr,
        !           387:                            PatSeq *psPtr));
        !           388: 
        !           389: /*
        !           390:  *--------------------------------------------------------------
        !           391:  *
        !           392:  * Tk_CreateBindingTable --
        !           393:  *
        !           394:  *     Set up a new domain in which event bindings may be created.
        !           395:  *
        !           396:  * Results:
        !           397:  *     The return value is a token for the new table, which must
        !           398:  *     be passed to procedures like Tk_CreatBinding.
        !           399:  *
        !           400:  * Side effects:
        !           401:  *     Memory is allocated for the new table.
        !           402:  *
        !           403:  *--------------------------------------------------------------
        !           404:  */
        !           405: 
        !           406: Tk_BindingTable
        !           407: Tk_CreateBindingTable(interp)
        !           408:     Tcl_Interp *interp;                /* Interpreter to associate with the binding
        !           409:                                 * table:  commands are executed in this
        !           410:                                 * interpreter. */
        !           411: {
        !           412:     register BindingTable *bindPtr;
        !           413:     int i;
        !           414: 
        !           415:     /*
        !           416:      * If this is the first time a binding table has been created,
        !           417:      * initialize the global data structures.
        !           418:      */
        !           419: 
        !           420:     if (!initialized) {
        !           421:        register KeySymInfo *kPtr;
        !           422:        register Tcl_HashEntry *hPtr;
        !           423:        register ModInfo *modPtr;
        !           424:        register EventInfo *eiPtr;
        !           425:        int dummy;
        !           426: 
        !           427:        initialized = 1;
        !           428:     
        !           429:        Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
        !           430:        for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
        !           431:            hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
        !           432:            Tcl_SetHashValue(hPtr, kPtr->value);
        !           433:        }
        !           434:     
        !           435:        Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
        !           436:        for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
        !           437:            hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
        !           438:            Tcl_SetHashValue(hPtr, modPtr);
        !           439:        }
        !           440:     
        !           441:        Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
        !           442:        for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
        !           443:            hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
        !           444:            Tcl_SetHashValue(hPtr, eiPtr);
        !           445:        }
        !           446:     }
        !           447: 
        !           448:     /*
        !           449:      * Create and initialize a new binding table.
        !           450:      */
        !           451: 
        !           452:     bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
        !           453:     for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
        !           454:        bindPtr->eventRing[i].type = -1;
        !           455:     }
        !           456:     bindPtr->curEvent = 0;
        !           457:     Tcl_InitHashTable(&bindPtr->patternTable,
        !           458:            sizeof(PatternTableKey)/sizeof(int));
        !           459:     Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
        !           460:     bindPtr->interp = interp;
        !           461:     return (Tk_BindingTable) bindPtr;
        !           462: }
        !           463: 
        !           464: /*
        !           465:  *--------------------------------------------------------------
        !           466:  *
        !           467:  * Tk_DeleteBindingTable --
        !           468:  *
        !           469:  *     Destroy a binding table and free up all its memory.
        !           470:  *     The caller should not use bindingTable again after
        !           471:  *     this procedure returns.
        !           472:  *
        !           473:  * Results:
        !           474:  *     None.
        !           475:  *
        !           476:  * Side effects:
        !           477:  *     Memory is freed.
        !           478:  *
        !           479:  *--------------------------------------------------------------
        !           480:  */
        !           481: 
        !           482: void
        !           483: Tk_DeleteBindingTable(bindingTable)
        !           484:     Tk_BindingTable bindingTable;      /* Token for the binding table to
        !           485:                                         * destroy. */
        !           486: {
        !           487:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           488:     PatSeq *psPtr, *nextPtr;
        !           489:     Tcl_HashEntry *hPtr;
        !           490:     Tcl_HashSearch search;
        !           491: 
        !           492:     /*
        !           493:      * Find and delete all of the patterns associated with the binding
        !           494:      * table.
        !           495:      */
        !           496: 
        !           497:     for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
        !           498:            hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
        !           499:        for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
        !           500:                psPtr != NULL; psPtr = nextPtr) {
        !           501:            nextPtr = psPtr->nextSeqPtr;
        !           502:            Tk_EventuallyFree((ClientData) psPtr->command,
        !           503:                    (Tk_FreeProc *) free);
        !           504:            ckfree((char *) psPtr);
        !           505:        }
        !           506:     }
        !           507: 
        !           508:     /*
        !           509:      * Clean up the rest of the information associated with the
        !           510:      * binding table.
        !           511:      */
        !           512: 
        !           513:     Tcl_DeleteHashTable(&bindPtr->patternTable);
        !           514:     Tcl_DeleteHashTable(&bindPtr->objectTable);
        !           515:     ckfree((char *) bindPtr);
        !           516: }
        !           517: 
        !           518: /*
        !           519:  *--------------------------------------------------------------
        !           520:  *
        !           521:  * Tk_CreateBinding --
        !           522:  *
        !           523:  *     Add a binding to a binding table, so that future calls to
        !           524:  *     Tk_BindEvent may execute the command in the binding.
        !           525:  *
        !           526:  * Results:
        !           527:  *     The return value is 0 if an error occurred while setting
        !           528:  *     up the binding.  In this case, an error message will be
        !           529:  *     left in interp->result.  If all went well then the return
        !           530:  *     value is a mask of the event types that must be made
        !           531:  *     available to Tk_BindEvent in order to properly detect when
        !           532:  *     this binding triggers.  This value can be used to determine
        !           533:  *     what events to select for in a window, for example.
        !           534:  *
        !           535:  * Side effects:
        !           536:  *     The new binding may cause future calls to Tk_BindEvent to
        !           537:  *     behave differently than they did previously.
        !           538:  *
        !           539:  *--------------------------------------------------------------
        !           540:  */
        !           541: 
        !           542: unsigned long
        !           543: Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
        !           544:     Tcl_Interp *interp;                        /* Used for error reporting. */
        !           545:     Tk_BindingTable bindingTable;      /* Table in which to create binding. */
        !           546:     ClientData object;                 /* Token for object with which binding
        !           547:                                         * is associated. */
        !           548:     char *eventString;                 /* String describing event sequence
        !           549:                                         * that triggers binding. */
        !           550:     char *command;                     /* Contains Tcl command to execute
        !           551:                                         * when binding triggers. */
        !           552:     int append;                                /* 0 means replace any existing
        !           553:                                         * binding for eventString;  1 means
        !           554:                                         * append to that binding. */
        !           555: {
        !           556:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           557:     register PatSeq *psPtr;
        !           558:     unsigned long eventMask;
        !           559: 
        !           560:     psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask);
        !           561:     if (psPtr == NULL) {
        !           562:        return 0;
        !           563:     }
        !           564:     if (append && (psPtr->command != NULL)) {
        !           565:        int length;
        !           566:        char *new;
        !           567: 
        !           568:        length = strlen(psPtr->command) + strlen(command) + 3;
        !           569:        new = (char *) ckalloc((unsigned) length);
        !           570:        sprintf(new, "%s; %s", psPtr->command, command);
        !           571:        Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
        !           572:        psPtr->command = new;
        !           573:     } else {
        !           574:        if (psPtr->command != NULL) {
        !           575:            Tk_EventuallyFree((ClientData) psPtr->command,
        !           576:                    (Tk_FreeProc *) free);
        !           577:        }
        !           578:        psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
        !           579:        strcpy(psPtr->command, command);
        !           580:     }
        !           581: 
        !           582:     /*
        !           583:      * See if the command contains percents and thereby requires
        !           584:      * percent substitution.
        !           585:      */
        !           586: 
        !           587:     if (strchr(psPtr->command, '%') != NULL) {
        !           588:        psPtr->flags |= PAT_PERCENTS;
        !           589:     }
        !           590:     return eventMask;
        !           591: }
        !           592: 
        !           593: /*
        !           594:  *--------------------------------------------------------------
        !           595:  *
        !           596:  * Tk_DeleteBinding --
        !           597:  *
        !           598:  *     Remove an event binding from a binding table.
        !           599:  *
        !           600:  * Results:
        !           601:  *     The result is a standard Tcl return value.  If an error
        !           602:  *     occurs then interp->result will contain an error message.
        !           603:  *
        !           604:  * Side effects:
        !           605:  *     The binding given by object and eventString is removed
        !           606:  *     from bindingTable.
        !           607:  *
        !           608:  *--------------------------------------------------------------
        !           609:  */
        !           610: 
        !           611: int
        !           612: Tk_DeleteBinding(interp, bindingTable, object, eventString)
        !           613:     Tcl_Interp *interp;                        /* Used for error reporting. */
        !           614:     Tk_BindingTable bindingTable;      /* Table in which to delete binding. */
        !           615:     ClientData object;                 /* Token for object with which binding
        !           616:                                         * is associated. */
        !           617:     char *eventString;                 /* String describing event sequence
        !           618:                                         * that triggers binding. */
        !           619: {
        !           620:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           621:     register PatSeq *psPtr, *prevPtr;
        !           622:     unsigned long eventMask;
        !           623:     Tcl_HashEntry *hPtr;
        !           624: 
        !           625:     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
        !           626:     if (psPtr == NULL) {
        !           627:        Tcl_ResetResult(interp);
        !           628:        return TCL_OK;
        !           629:     }
        !           630: 
        !           631:     /*
        !           632:      * Unlink the binding from the list for its object, then from the
        !           633:      * list for its pattern.
        !           634:      */
        !           635: 
        !           636:     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
        !           637:     if (hPtr == NULL) {
        !           638:        panic("Tk_DeleteBinding couldn't find object table entry");
        !           639:     }
        !           640:     prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
        !           641:     if (prevPtr == psPtr) {
        !           642:        Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
        !           643:     } else {
        !           644:        for ( ; ; prevPtr = prevPtr->nextObjPtr) {
        !           645:            if (prevPtr == NULL) {
        !           646:                panic("Tk_DeleteBinding couldn't find on object list");
        !           647:            }
        !           648:            if (prevPtr->nextObjPtr == psPtr) {
        !           649:                prevPtr->nextObjPtr = psPtr->nextObjPtr;
        !           650:                break;
        !           651:            }
        !           652:        }
        !           653:     }
        !           654:     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
        !           655:     if (prevPtr == psPtr) {
        !           656:        if (psPtr->nextSeqPtr == NULL) {
        !           657:            Tcl_DeleteHashEntry(psPtr->hPtr);
        !           658:        } else {
        !           659:            Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
        !           660:        }
        !           661:     } else {
        !           662:        for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
        !           663:            if (prevPtr == NULL) {
        !           664:                panic("Tk_DeleteBinding couldn't find on hash chain");
        !           665:            }
        !           666:            if (prevPtr->nextSeqPtr == psPtr) {
        !           667:                prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
        !           668:                break;
        !           669:            }
        !           670:        }
        !           671:     }
        !           672:     Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
        !           673:     ckfree((char *) psPtr);
        !           674:     return TCL_OK;
        !           675: }
        !           676: 
        !           677: /*
        !           678:  *--------------------------------------------------------------
        !           679:  *
        !           680:  * Tk_GetBinding --
        !           681:  *
        !           682:  *     Return the command associated with a given event string.
        !           683:  *
        !           684:  * Results:
        !           685:  *     The return value is a pointer to the command string
        !           686:  *     associated with eventString for object in the domain
        !           687:  *     given by bindingTable.  If there is no binding for
        !           688:  *     eventString, or if eventString is improperly formed,
        !           689:  *     then NULL is returned and an error message is left in
        !           690:  *     interp->result.  The return value is semi-static:  it
        !           691:  *     will persist until the binding is changed or deleted.
        !           692:  *
        !           693:  * Side effects:
        !           694:  *     None.
        !           695:  *
        !           696:  *--------------------------------------------------------------
        !           697:  */
        !           698: 
        !           699: char *
        !           700: Tk_GetBinding(interp, bindingTable, object, eventString)
        !           701:     Tcl_Interp *interp;                        /* Interpreter for error reporting. */
        !           702:     Tk_BindingTable bindingTable;      /* Table in which to look for
        !           703:                                         * binding. */
        !           704:     ClientData object;                 /* Token for object with which binding
        !           705:                                         * is associated. */
        !           706:     char *eventString;                 /* String describing event sequence
        !           707:                                         * that triggers binding. */
        !           708: {
        !           709:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           710:     register PatSeq *psPtr;
        !           711:     unsigned long eventMask;
        !           712: 
        !           713:     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
        !           714:     if (psPtr == NULL) {
        !           715:        return NULL;
        !           716:     }
        !           717:     return psPtr->command;
        !           718: }
        !           719: 
        !           720: /*
        !           721:  *--------------------------------------------------------------
        !           722:  *
        !           723:  * Tk_GetAllBindings --
        !           724:  *
        !           725:  *     Return a list of event strings for all the bindings
        !           726:  *     associated with a given object.
        !           727:  *
        !           728:  * Results:
        !           729:  *     There is no return value.  Interp->result is modified to
        !           730:  *     hold a Tcl list with one entry for each binding associated
        !           731:  *     with object in bindingTable.  Each entry in the list
        !           732:  *     contains the event string associated with one binding.
        !           733:  *
        !           734:  * Side effects:
        !           735:  *     None.
        !           736:  *
        !           737:  *--------------------------------------------------------------
        !           738:  */
        !           739: 
        !           740: void
        !           741: Tk_GetAllBindings(interp, bindingTable, object)
        !           742:     Tcl_Interp *interp;                        /* Interpreter for error reporting. */
        !           743:     Tk_BindingTable bindingTable;      /* Table in which to look for
        !           744:                                         * bindings. */
        !           745:     ClientData object;                 /* Token for object. */
        !           746: 
        !           747: {
        !           748:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           749:     register PatSeq *psPtr;
        !           750:     register Pattern *patPtr;
        !           751:     Tcl_HashEntry *hPtr;
        !           752:     char string[200*EVENT_BUFFER_SIZE];
        !           753:     register char *p;
        !           754:     int patsLeft, needMods;
        !           755:     register ModInfo *modPtr;
        !           756: 
        !           757:     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
        !           758:     if (hPtr == NULL) {
        !           759:        return;
        !           760:     }
        !           761:     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
        !           762:            psPtr = psPtr->nextObjPtr) {
        !           763: 
        !           764:        p = string;
        !           765: 
        !           766:        /*
        !           767:         * For each binding, output information about each of the
        !           768:         * patterns in its sequence.  The order of the patterns in
        !           769:         * the sequence is backwards from the order in which they
        !           770:         * must be output.
        !           771:         */
        !           772: 
        !           773:        for (patsLeft = psPtr->numPats,
        !           774:                patPtr = &psPtr->pats[psPtr->numPats - 1];
        !           775:                patsLeft > 0; patsLeft--, patPtr--) {
        !           776: 
        !           777:            /*
        !           778:             * Check for simple case of an ASCII character.
        !           779:             */
        !           780: 
        !           781:            if ((patPtr->eventType == KeyPress)
        !           782:                    && (patPtr->needMods == 0)
        !           783:                    && (patPtr->hateMods == ~ShiftMask)
        !           784:                    && isascii(patPtr->detail) && isprint(patPtr->detail)
        !           785:                    && (patPtr->detail != '<')
        !           786:                    && (patPtr->detail != ' ')) {
        !           787: 
        !           788:                *p = patPtr->detail;
        !           789:                p++;
        !           790:                continue;
        !           791:            }
        !           792: 
        !           793:            /*
        !           794:             * It's a more general event specification.  First check
        !           795:             * for "Double" or "Triple", then "Any", then modifiers,
        !           796:             * the event type, then keysym or button detail.
        !           797:             */
        !           798: 
        !           799:            *p = '<';
        !           800:            p++;
        !           801:            if ((patsLeft > 1) && (memcmp((char *) patPtr,
        !           802:                    (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
        !           803:                patsLeft--;
        !           804:                patPtr--;
        !           805:                if ((patsLeft > 1) && (memcmp((char *) patPtr,
        !           806:                        (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
        !           807:                    patsLeft--;
        !           808:                    patPtr--;
        !           809:                    strcpy(p, "Triple-");
        !           810:                } else {
        !           811:                    strcpy(p, "Double-");
        !           812:                }
        !           813:                p += strlen(p);
        !           814:            }
        !           815: 
        !           816:            if (patPtr->hateMods == 0) {
        !           817:                strcpy(p, "Any-");
        !           818:                p += strlen(p);
        !           819:            }
        !           820: 
        !           821:            for (needMods = patPtr->needMods, modPtr = modArray;
        !           822:                    needMods != 0; modPtr++) {
        !           823:                if (modPtr->mask & needMods) {
        !           824:                    needMods &= ~modPtr->mask;
        !           825:                    strcpy(p, modPtr->name);
        !           826:                    p += strlen(p);
        !           827:                    *p = '-';
        !           828:                    p++;
        !           829:                }
        !           830:            }
        !           831: 
        !           832:            if ((patPtr->eventType != KeyPress)
        !           833:                    || (patPtr->detail == 0)) {
        !           834:                register EventInfo *eiPtr;
        !           835: 
        !           836:                for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
        !           837:                    if (eiPtr->type == patPtr->eventType) {
        !           838:                        strcpy(p, eiPtr->name);
        !           839:                        p += strlen(p);
        !           840:                        if (patPtr->detail != 0) {
        !           841:                            *p = '-';
        !           842:                            p++;
        !           843:                        }
        !           844:                        break;
        !           845:                    }
        !           846:                }
        !           847:            }
        !           848: 
        !           849:            if (patPtr->detail != 0) {
        !           850:                if ((patPtr->eventType == KeyPress)
        !           851:                        || (patPtr->eventType == KeyRelease)) {
        !           852:                    register KeySymInfo *kPtr;
        !           853: 
        !           854:                    for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
        !           855:                        if (patPtr->detail == (int) kPtr->value) {
        !           856:                            sprintf(p, "%.100s",  kPtr->name);
        !           857:                            p += strlen(p);
        !           858:                            break;
        !           859:                        }
        !           860:                    }
        !           861:                } else {
        !           862:                    sprintf(p, "%d", patPtr->detail);
        !           863:                    p += strlen(p);
        !           864:                }
        !           865:            }
        !           866:            *p = '>';
        !           867:            p++;
        !           868:        }
        !           869:        *p = 0;
        !           870:        if ((p - string) >= sizeof(string)) {
        !           871:            panic("Tk_GetAllBindings overflowed buffer");
        !           872:        }
        !           873:        Tcl_AppendElement(interp, string, 0);
        !           874:     }
        !           875: }
        !           876: 
        !           877: /*
        !           878:  *--------------------------------------------------------------
        !           879:  *
        !           880:  * Tk_DeleteAllBindings --
        !           881:  *
        !           882:  *     Remove all bindings associated with a given object in a
        !           883:  *     given binding table.
        !           884:  *
        !           885:  * Results:
        !           886:  *     All bindings associated with object are removed from
        !           887:  *     bindingTable.
        !           888:  *
        !           889:  * Side effects:
        !           890:  *     None.
        !           891:  *
        !           892:  *--------------------------------------------------------------
        !           893:  */
        !           894: 
        !           895: void
        !           896: Tk_DeleteAllBindings(bindingTable, object)
        !           897:     Tk_BindingTable bindingTable;      /* Table in which to delete
        !           898:                                         * bindings. */
        !           899:     ClientData object;                 /* Token for object. */
        !           900: {
        !           901:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           902:     register PatSeq *psPtr, *prevPtr;
        !           903:     PatSeq *nextPtr;
        !           904:     Tcl_HashEntry *hPtr;
        !           905: 
        !           906:     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
        !           907:     if (hPtr == NULL) {
        !           908:        return;
        !           909:     }
        !           910:     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
        !           911:            psPtr = nextPtr) {
        !           912:        nextPtr  = psPtr->nextObjPtr;
        !           913: 
        !           914:        /*
        !           915:         * Be sure to remove each binding from its hash chain in the
        !           916:         * pattern table.  If this is the last pattern in the chain,
        !           917:         * then delete the hash entry too.
        !           918:         */
        !           919: 
        !           920:        prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
        !           921:        if (prevPtr == psPtr) {
        !           922:            if (psPtr->nextSeqPtr == NULL) {
        !           923:                Tcl_DeleteHashEntry(psPtr->hPtr);
        !           924:            } else {
        !           925:                Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
        !           926:            }
        !           927:        } else {
        !           928:            for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
        !           929:                if (prevPtr == NULL) {
        !           930:                    panic("Tk_DeleteAllBindings couldn't find on hash chain");
        !           931:                }
        !           932:                if (prevPtr->nextSeqPtr == psPtr) {
        !           933:                    prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
        !           934:                    break;
        !           935:                }
        !           936:            }
        !           937:        }
        !           938:        Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
        !           939:        ckfree((char *) psPtr);
        !           940:     }
        !           941:     Tcl_DeleteHashEntry(hPtr);
        !           942: }
        !           943: 
        !           944: /*
        !           945:  *--------------------------------------------------------------
        !           946:  *
        !           947:  * Tk_BindEvent --
        !           948:  *
        !           949:  *     This procedure is invoked to process an X event.  The
        !           950:  *     event is added to those recorded for the binding table.
        !           951:  *     Then each of the objects at *objectPtr is checked in
        !           952:  *     order to see if it has a binding that matches the recent
        !           953:  *     events.  If so, that binding is invoked and the rest of
        !           954:  *     objects are skipped.
        !           955:  *
        !           956:  * Results:
        !           957:  *     None.
        !           958:  *
        !           959:  * Side effects:
        !           960:  *     Depends on the command associated with the matching
        !           961:  *     binding.
        !           962:  *
        !           963:  *--------------------------------------------------------------
        !           964:  */
        !           965: 
        !           966: void
        !           967: Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
        !           968:     Tk_BindingTable bindingTable;      /* Table in which to look for
        !           969:                                         * bindings. */
        !           970:     XEvent *eventPtr;                  /* What actually happened. */
        !           971:     Tk_Window tkwin;                   /* Window on display where event
        !           972:                                         * occurred (needed in order to
        !           973:                                         * locate display information). */
        !           974:     int numObjects;                    /* Number of objects at *objectPtr. */
        !           975:     ClientData *objectPtr;             /* Array of one or more objects
        !           976:                                         * to check for a matching binding. */
        !           977: {
        !           978:     BindingTable *bindPtr = (BindingTable *) bindingTable;
        !           979:     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
        !           980:     XEvent *ringPtr;
        !           981:     PatSeq *matchPtr;
        !           982:     PatternTableKey key;
        !           983:     Tcl_HashEntry *hPtr;
        !           984:     int detail;
        !           985: 
        !           986:     /*
        !           987:      * Add the new event to the ring of saved events for the
        !           988:      * binding table.  Consecutive MotionNotify events get combined:
        !           989:      * if both the new event and the previous event are MotionNotify,
        !           990:      * then put the new event *on top* of the previous event.
        !           991:      */
        !           992: 
        !           993:     if ((eventPtr->type != MotionNotify)
        !           994:            || (bindPtr->eventRing[bindPtr->curEvent].type != MotionNotify)) {
        !           995:        bindPtr->curEvent++;
        !           996:        if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
        !           997:            bindPtr->curEvent = 0;
        !           998:        }
        !           999:     }
        !          1000:     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
        !          1001:     memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
        !          1002:     detail = 0;
        !          1003:     bindPtr->detailRing[bindPtr->curEvent] = 0;
        !          1004:     if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
        !          1005:        detail = (int) GetKeySym(dispPtr, ringPtr);
        !          1006:        if (detail == NoSymbol) {
        !          1007:            detail = 0;
        !          1008:        }
        !          1009:     } else if ((ringPtr->type == ButtonPress)
        !          1010:            || (ringPtr->type == ButtonRelease)) {
        !          1011:        detail = ringPtr->xbutton.button;
        !          1012:     }
        !          1013:     bindPtr->detailRing[bindPtr->curEvent] = detail;
        !          1014: 
        !          1015:     /*
        !          1016:      * Loop over all the objects, matching the new event against
        !          1017:      * each in turn.
        !          1018:      */
        !          1019: 
        !          1020:     for ( ; numObjects > 0; numObjects--, objectPtr++) {
        !          1021: 
        !          1022:        /*
        !          1023:         * Match the new event against those recorded in the
        !          1024:         * pattern table, saving the longest matching pattern.
        !          1025:         * For events with details (button and key events) first
        !          1026:         * look for a binding for the specific key or button.
        !          1027:         * If none is found, then look for a binding for all
        !          1028:         * keys or buttons (detail of 0).
        !          1029:         */
        !          1030:     
        !          1031:        matchPtr = NULL;
        !          1032:        key.object = *objectPtr;
        !          1033:        key.type = ringPtr->type;
        !          1034:        key.detail = detail;
        !          1035:        hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
        !          1036:        if (hPtr != NULL) {
        !          1037:            matchPtr = MatchPatterns(bindPtr,
        !          1038:                    (PatSeq *) Tcl_GetHashValue(hPtr));
        !          1039:        }
        !          1040:        if ((detail != 0) && (matchPtr == NULL)) {
        !          1041:            key.detail = 0;
        !          1042:            hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
        !          1043:            if (hPtr != NULL) {
        !          1044:                matchPtr = MatchPatterns(bindPtr,
        !          1045:                        (PatSeq *) Tcl_GetHashValue(hPtr));
        !          1046:            }
        !          1047:        }
        !          1048:     
        !          1049:        if (matchPtr != NULL) {
        !          1050:     
        !          1051:            /*
        !          1052:             * %-substitution can increase the length of the command.
        !          1053:             * This code handles three cases:  (a) no substitution;
        !          1054:             * (b) substitution results in short command (use space
        !          1055:             * on stack); and (c) substitution results in long
        !          1056:             * command (malloc it).
        !          1057:             */
        !          1058:     
        !          1059: #define STATIC_SPACE 200
        !          1060:            char shortSpace[STATIC_SPACE];
        !          1061:            int result;
        !          1062: 
        !          1063:            if (matchPtr->flags & PAT_PERCENTS) {
        !          1064:                char *p;
        !          1065:                p = ExpandPercents(matchPtr->command, eventPtr,
        !          1066:                        (KeySym) detail, shortSpace, STATIC_SPACE);
        !          1067:                result = Tcl_GlobalEval(bindPtr->interp, p);
        !          1068:                if (p != shortSpace) {
        !          1069:                    ckfree(p);
        !          1070:                }
        !          1071:            } else {
        !          1072:                /*
        !          1073:                 * The code below is tricky in order allow the binding to
        !          1074:                 * be modified or deleted as part of the command that the
        !          1075:                 * binding invokes.  Must make sure that the actual command
        !          1076:                 * string isn't freed until the command completes, and must
        !          1077:                 * copy the address of this string into a local variable
        !          1078:                 * in case it's modified by the command.
        !          1079:                 */
        !          1080: 
        !          1081:                char *cmd = matchPtr->command;
        !          1082: 
        !          1083:                Tk_Preserve((ClientData) cmd);
        !          1084:                result = Tcl_GlobalEval(bindPtr->interp, cmd);
        !          1085:                Tk_Release((ClientData) cmd);
        !          1086:            }
        !          1087:            if (result != TCL_OK) {
        !          1088:                Tcl_AddErrorInfo(bindPtr->interp,
        !          1089:                        "\n    (command bound to event)");
        !          1090:                TkBindError(bindPtr->interp);
        !          1091:            }
        !          1092:            return;
        !          1093:        }
        !          1094:     }
        !          1095: }
        !          1096: 
        !          1097: /*
        !          1098:  *----------------------------------------------------------------------
        !          1099:  *
        !          1100:  * FindSequence --
        !          1101:  *
        !          1102:  *     Find the entry in a binding table that corresponds to a
        !          1103:  *     particular pattern string, and return a pointer to that
        !          1104:  *     entry.
        !          1105:  *
        !          1106:  * Results:
        !          1107:  *     The return value is normally a pointer to the PatSeq
        !          1108:  *     in patternTable that corresponds to eventString.  If an error
        !          1109:  *     was found while parsing eventString, or if "create" is 0 and
        !          1110:  *     no pattern sequence previously existed, then NULL is returned
        !          1111:  *     and interp->result contains a message describing the problem.
        !          1112:  *     If no pattern sequence previously existed for eventString, then
        !          1113:  *     a new one is created with a NULL command field.  In a successful
        !          1114:  *     return, *maskPtr is filled in with a mask of the event types
        !          1115:  *     on which the pattern sequence depends.
        !          1116:  *
        !          1117:  * Side effects:
        !          1118:  *     A new pattern sequence may be created.
        !          1119:  *
        !          1120:  *----------------------------------------------------------------------
        !          1121:  */
        !          1122: 
        !          1123: static PatSeq *
        !          1124: FindSequence(interp, bindPtr, object, eventString, create, maskPtr)
        !          1125:     Tcl_Interp *interp;                /* Interpreter to use for error
        !          1126:                                 * reporting. */
        !          1127:     BindingTable *bindPtr;     /* Table to use for lookup. */
        !          1128:     ClientData object;         /* Token for object(s) with which binding
        !          1129:                                 * is associated. */
        !          1130:     char *eventString;         /* String description of pattern to
        !          1131:                                 * match on.  See user documentation
        !          1132:                                 * for details. */
        !          1133:     int create;                        /* 0 means don't create the entry if
        !          1134:                                 * it doesn't already exist.   Non-zero
        !          1135:                                 * means create. */
        !          1136:     unsigned long *maskPtr;    /* *maskPtr is filled in with the event
        !          1137:                                 * types on which this pattern sequence
        !          1138:                                 * depends. */
        !          1139: 
        !          1140: {
        !          1141:     Pattern pats[EVENT_BUFFER_SIZE];
        !          1142:     int numPats;
        !          1143:     register char *p;
        !          1144:     register Pattern *patPtr;
        !          1145:     register PatSeq *psPtr;
        !          1146:     register Tcl_HashEntry *hPtr;
        !          1147: #define FIELD_SIZE 20
        !          1148:     char field[FIELD_SIZE];
        !          1149:     int flags, any, count, new, sequenceSize;
        !          1150:     unsigned long eventMask;
        !          1151:     PatternTableKey key;
        !          1152: 
        !          1153:     /*
        !          1154:      *-------------------------------------------------------------
        !          1155:      * Step 1: parse the pattern string to produce an array
        !          1156:      * of Patterns.  The array is generated backwards, so
        !          1157:      * that the lowest-indexed pattern corresponds to the last
        !          1158:      * event that must occur.
        !          1159:      *-------------------------------------------------------------
        !          1160:      */
        !          1161: 
        !          1162:     p = eventString;
        !          1163:     flags = 0;
        !          1164:     eventMask = 0;
        !          1165:     for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
        !          1166:            numPats < EVENT_BUFFER_SIZE;
        !          1167:            numPats++, patPtr--) {
        !          1168:        patPtr->eventType = -1;
        !          1169:        patPtr->needMods = 0;
        !          1170:        patPtr->hateMods = ~0;
        !          1171:        patPtr->detail = 0;
        !          1172:        while (isspace(*p)) {
        !          1173:            p++;
        !          1174:        }
        !          1175:        if (*p == '\0') {
        !          1176:            break;
        !          1177:        }
        !          1178: 
        !          1179:        /*
        !          1180:         * Handle simple ASCII characters.  Note:  the shift
        !          1181:         * modifier is ignored in this case (it's really part
        !          1182:         * of the character, rather than a "modifier").
        !          1183:         */
        !          1184: 
        !          1185:        if (*p != '<') {
        !          1186:            char string[2];
        !          1187: 
        !          1188:            patPtr->eventType = KeyPress;
        !          1189:            eventMask |= KeyPressMask;
        !          1190:            string[0] = *p;
        !          1191:            string[1] = 0;
        !          1192:            hPtr = Tcl_FindHashEntry(&keySymTable, string);
        !          1193:            if (hPtr != NULL) {
        !          1194:                patPtr->detail = (int) Tcl_GetHashValue(hPtr);
        !          1195:            } else {
        !          1196:                if (isprint(*p)) {
        !          1197:                    patPtr->detail = *p;
        !          1198:                } else {
        !          1199:                    sprintf(interp->result,
        !          1200:                            "bad ASCII character 0x%x", *p);
        !          1201:                    return NULL;
        !          1202:                }
        !          1203:            }
        !          1204:            patPtr->hateMods = ~ShiftMask;
        !          1205:            p++;
        !          1206:            continue;
        !          1207:        }
        !          1208: 
        !          1209:        /*
        !          1210:         * A fancier event description.  Must consist of
        !          1211:         * 1. open angle bracket.
        !          1212:         * 2. any number of modifiers, each followed by spaces
        !          1213:         *    or dashes.
        !          1214:         * 3. an optional event name.
        !          1215:         * 4. an option button or keysym name.  Either this or
        !          1216:         *    item 3 *must* be present;  if both are present
        !          1217:         *    then they are separated by spaces or dashes.
        !          1218:         * 5. a close angle bracket.
        !          1219:         */
        !          1220: 
        !          1221:        any = 0;
        !          1222:        count = 1;
        !          1223:        p++;
        !          1224:        while (1) {
        !          1225:            register ModInfo *modPtr;
        !          1226:            p = GetField(p, field, FIELD_SIZE);
        !          1227:            hPtr = Tcl_FindHashEntry(&modTable, field);
        !          1228:            if (hPtr == NULL) {
        !          1229:                break;
        !          1230:            }
        !          1231:            modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
        !          1232:            patPtr->needMods |= modPtr->mask;
        !          1233:            if (modPtr->flags & (DOUBLE|TRIPLE)) {
        !          1234:                flags |= PAT_NEARBY;
        !          1235:                if (modPtr->flags & DOUBLE) {
        !          1236:                    count = 2;
        !          1237:                } else {
        !          1238:                    count = 3;
        !          1239:                }
        !          1240:            }
        !          1241:            if (modPtr->flags & ANY) {
        !          1242:                any = 1;
        !          1243:            }
        !          1244:            while ((*p == '-') || isspace(*p)) {
        !          1245:                p++;
        !          1246:            }
        !          1247:        }
        !          1248:        if (any) {
        !          1249:            patPtr->hateMods = 0;
        !          1250:        } else {
        !          1251:            patPtr->hateMods = ~patPtr->needMods;
        !          1252:        }
        !          1253:        hPtr = Tcl_FindHashEntry(&eventTable, field);
        !          1254:        if (hPtr != NULL) {
        !          1255:            register EventInfo *eiPtr;
        !          1256:            eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
        !          1257:            patPtr->eventType = eiPtr->type;
        !          1258:            eventMask |= eiPtr->eventMask;
        !          1259:            while ((*p == '-') || isspace(*p)) {
        !          1260:                p++;
        !          1261:            }
        !          1262:            p = GetField(p, field, FIELD_SIZE);
        !          1263:        }
        !          1264:        if (*field != '\0') {
        !          1265:            if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
        !          1266:                static int masks[] = {~0, ~Button1Mask, ~Button2Mask,
        !          1267:                        ~Button3Mask, ~Button4Mask, ~Button5Mask};
        !          1268: 
        !          1269:                if (patPtr->eventType == -1) {
        !          1270:                    patPtr->eventType = ButtonPress;
        !          1271:                    eventMask |= ButtonPressMask;
        !          1272:                } else if ((patPtr->eventType == KeyPress)
        !          1273:                        || (patPtr->eventType == KeyRelease)) {
        !          1274:                    goto getKeysym;
        !          1275:                } else if ((patPtr->eventType != ButtonPress)
        !          1276:                        && (patPtr->eventType != ButtonRelease)) {
        !          1277:                    Tcl_AppendResult(interp, "specified button \"", field,
        !          1278:                            "\" for non-button event", (char *) NULL);
        !          1279:                    return NULL;
        !          1280:                }
        !          1281:                patPtr->detail = (*field - '0');
        !          1282: 
        !          1283:                /*
        !          1284:                 * Ignore this button as a modifier:  its state is already
        !          1285:                 * fixed.
        !          1286:                 */
        !          1287: 
        !          1288:                patPtr->needMods &= masks[patPtr->detail];
        !          1289:                patPtr->hateMods &= masks[patPtr->detail];
        !          1290:            } else {
        !          1291:                getKeysym:
        !          1292:                hPtr = Tcl_FindHashEntry(&keySymTable, (char *) field);
        !          1293:                if (hPtr == NULL) {
        !          1294:                    Tcl_AppendResult(interp, "bad event type or keysym \"",
        !          1295:                            field, "\"", (char *) NULL);
        !          1296:                    return NULL;
        !          1297:                }
        !          1298:                if (patPtr->eventType == -1) {
        !          1299:                    patPtr->eventType = KeyPress;
        !          1300:                    eventMask |= KeyPressMask;
        !          1301:                } else if ((patPtr->eventType != KeyPress)
        !          1302:                        && (patPtr->eventType != KeyRelease)) {
        !          1303:                    Tcl_AppendResult(interp, "specified keysym \"", field,
        !          1304:                            "\" for non-key event", (char *) NULL);
        !          1305:                    return NULL;
        !          1306:                }
        !          1307:                patPtr->detail = (int) Tcl_GetHashValue(hPtr);
        !          1308: 
        !          1309:                /*
        !          1310:                 * Don't get upset about the shift modifier with keys:
        !          1311:                 * if the key doesn't permit the shift modifier then
        !          1312:                 * that will already be factored in when translating
        !          1313:                 * from keycode to keysym in Tk_BindEvent.  If the keysym
        !          1314:                 * has both a shifted and unshifted form, we want to allow
        !          1315:                 * the shifted form to be specified explicitly, though.
        !          1316:                 */
        !          1317: 
        !          1318:                patPtr->hateMods &= ~ShiftMask;
        !          1319:            }
        !          1320:        } else if (patPtr->eventType == -1) {
        !          1321:            interp->result = "no event type or button # or keysym";
        !          1322:            return NULL;
        !          1323:        }
        !          1324:        while ((*p == '-') || isspace(*p)) {
        !          1325:            p++;
        !          1326:        }
        !          1327:        if (*p != '>') {
        !          1328:            interp->result = "missing \">\" in binding";
        !          1329:            return NULL;
        !          1330:        }
        !          1331:        p++;
        !          1332: 
        !          1333:        /*
        !          1334:         * Replicate events for DOUBLE and TRIPLE.
        !          1335:         */
        !          1336: 
        !          1337:        if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
        !          1338:            patPtr[-1] = patPtr[0];
        !          1339:            patPtr--;
        !          1340:            numPats++;
        !          1341:            if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
        !          1342:                patPtr[-1] = patPtr[0];
        !          1343:                patPtr--;
        !          1344:                numPats++;
        !          1345:            }
        !          1346:        }
        !          1347:     }
        !          1348: 
        !          1349:     /*
        !          1350:      *-------------------------------------------------------------
        !          1351:      * Step 2: find the sequence in the binding table if it exists,
        !          1352:      * and add a new sequence to the table if it doesn't.
        !          1353:      *-------------------------------------------------------------
        !          1354:      */
        !          1355: 
        !          1356:     if (numPats == 0) {
        !          1357:        interp->result = "no events specified in binding";
        !          1358:        return NULL;
        !          1359:     }
        !          1360:     patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
        !          1361:     key.object = object;
        !          1362:     key.type = patPtr->eventType;
        !          1363:     key.detail = patPtr->detail;
        !          1364:     hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
        !          1365:     sequenceSize = numPats*sizeof(Pattern);
        !          1366:     if (!new) {
        !          1367:        for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
        !          1368:                psPtr = psPtr->nextSeqPtr) {
        !          1369:            if ((numPats == psPtr->numPats)
        !          1370:                    && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
        !          1371:                    && (memcmp((char *) patPtr, (char *) psPtr->pats,
        !          1372:                    sequenceSize) == 0)) {
        !          1373:                *maskPtr = eventMask; /*don't forget to pass back the mask*/
        !          1374:                goto done;
        !          1375:            }
        !          1376:        }
        !          1377:     }
        !          1378:     if (!create) {
        !          1379:        if (new) {
        !          1380:            Tcl_DeleteHashEntry(hPtr);
        !          1381:        }
        !          1382:        Tcl_AppendResult(interp, "no binding exists for \"",
        !          1383:                eventString, "\"", (char *) NULL);
        !          1384:        return NULL;
        !          1385:     }
        !          1386:     psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
        !          1387:            + (numPats-1)*sizeof(Pattern)));
        !          1388:     psPtr->numPats = numPats;
        !          1389:     psPtr->command = NULL;
        !          1390:     psPtr->flags = flags;
        !          1391:     psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
        !          1392:     psPtr->hPtr = hPtr;
        !          1393:     Tcl_SetHashValue(hPtr, psPtr);
        !          1394: 
        !          1395:     /*
        !          1396:      * Link the pattern into the list associated with the object.
        !          1397:      */
        !          1398: 
        !          1399:     psPtr->object = object;
        !          1400:     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
        !          1401:     if (new) {
        !          1402:        psPtr->nextObjPtr = NULL;
        !          1403:     } else {
        !          1404:        psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
        !          1405:     }
        !          1406:     Tcl_SetHashValue(hPtr, psPtr);
        !          1407: 
        !          1408:     memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
        !          1409: 
        !          1410:     done:
        !          1411:     *maskPtr = eventMask;
        !          1412:     return psPtr;
        !          1413: }
        !          1414: 
        !          1415: /*
        !          1416:  *----------------------------------------------------------------------
        !          1417:  *
        !          1418:  * GetField --
        !          1419:  *
        !          1420:  *     Used to parse pattern descriptions.  Copies up to
        !          1421:  *     size characters from p to copy, stopping at end of
        !          1422:  *     string, space, "-", ">", or whenever size is
        !          1423:  *     exceeded.
        !          1424:  *
        !          1425:  * Results:
        !          1426:  *     The return value is a pointer to the character just
        !          1427:  *     after the last one copied (usually "-" or space or
        !          1428:  *     ">", but could be anything if size was exceeded).
        !          1429:  *     Also places NULL-terminated string (up to size
        !          1430:  *     character, including NULL), at copy.
        !          1431:  *
        !          1432:  * Side effects:
        !          1433:  *     None.
        !          1434:  *
        !          1435:  *----------------------------------------------------------------------
        !          1436:  */
        !          1437: 
        !          1438: static char *
        !          1439: GetField(p, copy, size)
        !          1440:     register char *p;          /* Pointer to part of pattern. */
        !          1441:     register char *copy;       /* Place to copy field. */
        !          1442:     int size;                  /* Maximum number of characters to
        !          1443:                                 * copy. */
        !          1444: {
        !          1445:     while ((*p != '\0') && !isspace(*p) && (*p != '>')
        !          1446:            && (*p != '-') && (size > 1)) {
        !          1447:        *copy = *p;
        !          1448:        p++;
        !          1449:        copy++;
        !          1450:        size--;
        !          1451:     }
        !          1452:     *copy = '\0';
        !          1453:     return p;
        !          1454: }
        !          1455: 
        !          1456: /*
        !          1457:  *----------------------------------------------------------------------
        !          1458:  *
        !          1459:  * GetKeySym --
        !          1460:  *
        !          1461:  *     Given an X KeyPress or KeyRelease event, map the
        !          1462:  *     keycode in the event into a KeySym.
        !          1463:  *
        !          1464:  * Results:
        !          1465:  *     The return value is the KeySym corresponding to
        !          1466:  *     eventPtr, or NoSymbol if no matching Keysym could be
        !          1467:  *     found.
        !          1468:  *
        !          1469:  * Side effects:
        !          1470:  *     In the first call for a given display, keycode-to-
        !          1471:  *     KeySym maps get loaded.
        !          1472:  *
        !          1473:  *----------------------------------------------------------------------
        !          1474:  */
        !          1475: 
        !          1476: static KeySym
        !          1477: GetKeySym(dispPtr, eventPtr)
        !          1478:     register TkDisplay *dispPtr;       /* Display in which to
        !          1479:                                         * map keycode. */
        !          1480:     register XEvent *eventPtr;         /* Description of X event. */
        !          1481: {
        !          1482:     KeySym *symPtr;
        !          1483:     KeySym sym;
        !          1484: 
        !          1485:     /*
        !          1486:      * Read the key mapping information from the server if
        !          1487:      * we don't have it already.
        !          1488:      */
        !          1489: 
        !          1490:     if (dispPtr->symsPerCode == 0) {
        !          1491:        Display *dpy = dispPtr->display;
        !          1492: 
        !          1493: #ifdef IS_LINUX
        !          1494:        XDisplayKeycodes(dpy, &dispPtr->firstKeycode, &dispPtr->lastKeycode);
        !          1495: #else
        !          1496:        dispPtr->firstKeycode = 
        !          1497:          dpy->min_keycode;
        !          1498:        dispPtr->lastKeycode = 
        !          1499:          dpy->max_keycode;
        !          1500: #endif
        !          1501:        dispPtr->keySyms = XGetKeyboardMapping(dpy,
        !          1502:                dispPtr->firstKeycode, dispPtr->lastKeycode + 1
        !          1503:                - dispPtr->firstKeycode, &dispPtr->symsPerCode);
        !          1504:     }
        !          1505: 
        !          1506:     /*
        !          1507:      * Compute the lower-case KeySym for this keycode.  May
        !          1508:      * have to convert an upper-case KeySym to a lower-case
        !          1509:      * one if the list only has a single element.
        !          1510:      */
        !          1511: 
        !          1512:     if ((eventPtr->xkey.keycode < dispPtr->firstKeycode)
        !          1513:            || (eventPtr->xkey.keycode > dispPtr->lastKeycode)) {
        !          1514:        return NoSymbol;
        !          1515:     }
        !          1516:     symPtr = &dispPtr->keySyms[(eventPtr->xkey.keycode
        !          1517:            - dispPtr->firstKeycode) * dispPtr->symsPerCode];
        !          1518:     sym = *symPtr;
        !          1519:     if ((dispPtr->symsPerCode == 1) || (symPtr[1] == NoSymbol)) {
        !          1520:        if ((sym >= XK_A) && (sym <= XK_Z)) {
        !          1521:            sym += (XK_a - XK_A);
        !          1522:        } else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) {
        !          1523:            sym += (XK_agrave - XK_Agrave);
        !          1524:        } else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) {
        !          1525:            sym += (XK_oslash - XK_Ooblique);
        !          1526:        }
        !          1527:     }
        !          1528: 
        !          1529:     /*
        !          1530:      * See whether the key is shifted or caps-locked.  If so,
        !          1531:      * use an upper-case equivalent if provided, or compute
        !          1532:      * one (for caps-lock, just compute upper-case: don't
        !          1533:      * use shifted KeySym since that would shift non-alphabetic
        !          1534:      * keys).
        !          1535:      */
        !          1536: 
        !          1537:     if (eventPtr->xkey.state & ShiftMask) {
        !          1538:        if ((dispPtr->symsPerCode > 1) && (symPtr[1] != NoSymbol)) {
        !          1539:            return symPtr[1];
        !          1540:        }
        !          1541:        shiftToUpper:
        !          1542:        if ((sym >= XK_a) && (sym <= XK_z)) {
        !          1543:            sym += (XK_A - XK_a);
        !          1544:        } else if ((sym >= XK_agrave) && (sym <= XK_adiaeresis)) {
        !          1545:            sym += (XK_Agrave - XK_agrave);
        !          1546:        } else if ((sym >= XK_oslash) && (sym <= XK_thorn)) {
        !          1547:            sym += (XK_Ooblique - XK_oslash);
        !          1548:        }
        !          1549:        return sym;
        !          1550:     }
        !          1551:     if (eventPtr->xkey.state & LockMask) {
        !          1552:        goto shiftToUpper;
        !          1553:     }
        !          1554:     return sym;
        !          1555: }
        !          1556: 
        !          1557: /*
        !          1558:  *----------------------------------------------------------------------
        !          1559:  *
        !          1560:  * MatchPatterns --
        !          1561:  *
        !          1562:  *     Given a list of pattern sequences and a list of
        !          1563:  *     recent events, return a pattern sequence that matches
        !          1564:  *     the event list.
        !          1565:  *
        !          1566:  * Results:
        !          1567:  *     The return value is NULL if no pattern matches the
        !          1568:  *     recent events from bindPtr.  If one or more patterns
        !          1569:  *     matches, then the longest (or most specific) matching
        !          1570:  *     pattern is returned.
        !          1571:  *
        !          1572:  * Side effects:
        !          1573:  *     None.
        !          1574:  *
        !          1575:  *----------------------------------------------------------------------
        !          1576:  */
        !          1577: 
        !          1578: static PatSeq *
        !          1579: MatchPatterns(bindPtr, psPtr)
        !          1580:     BindingTable *bindPtr;     /* Information about binding table, such
        !          1581:                                 * as ring of recent events. */
        !          1582:     register PatSeq *psPtr;    /* List of pattern sequences. */
        !          1583: {
        !          1584:     register PatSeq *bestPtr = NULL;
        !          1585: 
        !          1586:     /*
        !          1587:      * Iterate over all the pattern sequences.
        !          1588:      */
        !          1589: 
        !          1590:     for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
        !          1591:        register XEvent *eventPtr;
        !          1592:        register Pattern *patPtr;
        !          1593:        Window window;
        !          1594:        int *detailPtr;
        !          1595:        int patCount, ringCount, flags, state;
        !          1596: 
        !          1597:        /*
        !          1598:         * Iterate over all the patterns in a sequence to be
        !          1599:         * sure that they all match.
        !          1600:         */
        !          1601: 
        !          1602:        eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
        !          1603:        detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
        !          1604:        window = eventPtr->xany.window;
        !          1605:        patPtr = psPtr->pats;
        !          1606:        patCount = psPtr->numPats;
        !          1607:        ringCount = EVENT_BUFFER_SIZE;
        !          1608:        while (patCount > 0) {
        !          1609:            if (ringCount <= 0) {
        !          1610:                goto nextSequence;
        !          1611:            }
        !          1612:            if (eventPtr->xany.window != window) {
        !          1613:                goto nextSequence;
        !          1614:            }
        !          1615:            if (eventPtr->xany.type != patPtr->eventType) {
        !          1616:                /*
        !          1617:                 * If the event is a mouse motion, button release,
        !          1618:                 * or key release event, and it didn't match
        !          1619:                 * the pattern, then just skip the event and try
        !          1620:                 * the next event against the same pattern.
        !          1621:                 */
        !          1622: 
        !          1623:                if ((eventPtr->xany.type == MotionNotify)
        !          1624:                        || (eventPtr->xany.type == ButtonRelease)
        !          1625:                        || (eventPtr->xany.type == KeyRelease)
        !          1626:                        || (eventPtr->xany.type == NoExpose)
        !          1627:                        || (eventPtr->xany.type == GraphicsExpose)) {
        !          1628:                    goto nextEvent;
        !          1629:                }
        !          1630:                goto nextSequence;
        !          1631:            }
        !          1632: 
        !          1633:            flags = flagArray[eventPtr->type];
        !          1634:            if (flags & KEY_BUTTON_MOTION) {
        !          1635:                state = eventPtr->xkey.state;
        !          1636:            } else if (flags & CROSSING) {
        !          1637:                state = eventPtr->xcrossing.state;
        !          1638:            } else {
        !          1639:                state = 0;
        !          1640:            }
        !          1641:            if ((state & patPtr->needMods)
        !          1642:                    != patPtr->needMods) {
        !          1643:                goto nextSequence;
        !          1644:            }
        !          1645:            if ((state & patPtr->hateMods) != 0) {
        !          1646:                goto nextSequence;
        !          1647:            }
        !          1648:            if ((patPtr->detail != 0)
        !          1649:                    && (patPtr->detail != *detailPtr)) {
        !          1650:                goto nextSequence;
        !          1651:            }
        !          1652:            if (psPtr->flags & PAT_NEARBY) {
        !          1653:                register XEvent *firstPtr;
        !          1654: 
        !          1655:                firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
        !          1656:                if ((firstPtr->xkey.x_root
        !          1657:                            < (eventPtr->xkey.x_root - NEARBY_PIXELS))
        !          1658:                        || (firstPtr->xkey.x_root
        !          1659:                            > (eventPtr->xkey.x_root + NEARBY_PIXELS))
        !          1660:                        || (firstPtr->xkey.y_root
        !          1661:                            < (eventPtr->xkey.y_root - NEARBY_PIXELS))
        !          1662:                        || (firstPtr->xkey.y_root
        !          1663:                            > (eventPtr->xkey.y_root + NEARBY_PIXELS))
        !          1664:                        || (firstPtr->xkey.time
        !          1665:                            > (eventPtr->xkey.time + NEARBY_MS))) {
        !          1666:                    goto nextSequence;
        !          1667:                }
        !          1668:            }
        !          1669:            patPtr++;
        !          1670:            patCount--;
        !          1671:            nextEvent:
        !          1672:            if (eventPtr == bindPtr->eventRing) {
        !          1673:                eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
        !          1674:                detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
        !          1675:            } else {
        !          1676:                eventPtr--;
        !          1677:                detailPtr--;
        !          1678:            }
        !          1679:            ringCount--;
        !          1680:        }
        !          1681: 
        !          1682:        /*
        !          1683:         * This sequence matches.  If we've already got another match,
        !          1684:         * pick whichever is most specific.  Detail is most important,
        !          1685:         * then needMods, then hateMods.
        !          1686:         */
        !          1687: 
        !          1688:        if (bestPtr != NULL) {
        !          1689:            register Pattern *patPtr2;
        !          1690:            int i;
        !          1691: 
        !          1692:            if (psPtr->numPats != bestPtr->numPats) {
        !          1693:                if (bestPtr->numPats > psPtr->numPats) {
        !          1694:                    goto nextSequence;
        !          1695:                } else {
        !          1696:                    goto newBest;
        !          1697:                }
        !          1698:            }
        !          1699:            for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
        !          1700:                    i < psPtr->numPats; i++,patPtr++, patPtr2++) {
        !          1701:                if (patPtr->detail != patPtr2->detail) {
        !          1702:                    if (patPtr->detail == 0) {
        !          1703:                        goto nextSequence;
        !          1704:                    } else {
        !          1705:                        goto newBest;
        !          1706:                    }
        !          1707:                }
        !          1708:                if (patPtr->needMods != patPtr2->needMods) {
        !          1709:                    if ((patPtr->needMods & patPtr2->needMods)
        !          1710:                        == patPtr->needMods) {
        !          1711:                    goto nextSequence;
        !          1712:                    } else {
        !          1713:                        goto newBest;
        !          1714:                    }
        !          1715:                }
        !          1716:                if (patPtr->hateMods != patPtr2->hateMods) {
        !          1717:                    if ((patPtr->hateMods & patPtr2->hateMods)
        !          1718:                        == patPtr2->hateMods) {
        !          1719:                        goto newBest;
        !          1720:                    } else {
        !          1721:                        goto nextSequence;
        !          1722:                    }
        !          1723:                }
        !          1724:            }
        !          1725:            goto nextSequence;  /* Tie goes to newest pattern. */
        !          1726:        }
        !          1727:        newBest:
        !          1728:        bestPtr = psPtr;
        !          1729: 
        !          1730:        nextSequence: continue;
        !          1731:     }
        !          1732:     return bestPtr;
        !          1733: }
        !          1734: 
        !          1735: /*
        !          1736:  *--------------------------------------------------------------
        !          1737:  *
        !          1738:  * ExpandPercents --
        !          1739:  *
        !          1740:  *     Given a command and an event, produce a new command
        !          1741:  *     by replacing % constructs in the original command
        !          1742:  *     with information from the X event.
        !          1743:  *
        !          1744:  * Results:
        !          1745:  *     The return result is a pointer to the new %-substituted
        !          1746:  *     command.  If the command fits in the space at after, then
        !          1747:  *     the return value is after.  If the command is too large
        !          1748:  *     to fit at after, then the return value is a pointer to
        !          1749:  *     a malloc-ed buffer holding the command;  in this case it
        !          1750:  *     is the caller's responsibility to free up the buffer when
        !          1751:  *     finished with it.
        !          1752:  *
        !          1753:  * Side effects:
        !          1754:  *     None.
        !          1755:  *
        !          1756:  *--------------------------------------------------------------
        !          1757:  */
        !          1758: 
        !          1759: static char *
        !          1760: ExpandPercents(before, eventPtr, keySym, after, afterSize)
        !          1761:     register char *before;     /* Command containing percent
        !          1762:                                 * expressions to be replaced. */
        !          1763:     register XEvent *eventPtr; /* X event containing information
        !          1764:                                 * to be used in % replacements. */
        !          1765:     KeySym keySym;             /* KeySym: only relevant for
        !          1766:                                 * KeyPress and KeyRelease events). */
        !          1767:     char *after;               /* Place to generate new expanded
        !          1768:                                 * command.  Must contain at least
        !          1769:                                 * "afterSize" bytes of space. */
        !          1770:     int afterSize;             /* Number of bytes of space available at
        !          1771:                                 * after. */
        !          1772: {
        !          1773:     register char *buffer;     /* Pointer to buffer currently being used
        !          1774:                                 * as destination. */
        !          1775:     register char *dst;                /* Pointer to next place to store character
        !          1776:                                 * in substituted string. */
        !          1777:     int spaceLeft;             /* Indicates how many more non-null bytes
        !          1778:                                 * may be stored at *dst before space
        !          1779:                                 * runs out. */
        !          1780:     int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
        !          1781:                                 * list element. */
        !          1782:     int number, flags;
        !          1783: #define NUM_SIZE 40
        !          1784:     register char *string;
        !          1785:     char numStorage[NUM_SIZE+1];
        !          1786: 
        !          1787:     if (eventPtr->type < LASTEvent) {
        !          1788:        flags = flagArray[eventPtr->type];
        !          1789:     } else {
        !          1790:        flags = 0;
        !          1791:     }
        !          1792:     dst = buffer = after;
        !          1793:     spaceLeft = afterSize - 1;
        !          1794:     while (*before != 0) {
        !          1795:        if (*before != '%') {
        !          1796: 
        !          1797:            /*
        !          1798:             * Expand the destination string if necessary.
        !          1799:             */
        !          1800: 
        !          1801:            if (spaceLeft <= 0) {
        !          1802:                char *newSpace;
        !          1803: 
        !          1804:                newSpace = (char *) ckalloc((unsigned) (2*afterSize));
        !          1805:                memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
        !          1806:                afterSize *= 2;
        !          1807:                dst = newSpace + (dst - buffer);
        !          1808:                if (buffer != after) {
        !          1809:                    ckfree(buffer);
        !          1810:                }
        !          1811:                buffer = newSpace;
        !          1812:                spaceLeft = afterSize - (dst-buffer) - 1;
        !          1813:            }
        !          1814:            *dst = *before;
        !          1815:            dst++;
        !          1816:            before++;
        !          1817:            spaceLeft--;
        !          1818:            continue;
        !          1819:        }
        !          1820: 
        !          1821:        number = 0;
        !          1822:        string = "??";
        !          1823:        switch (before[1]) {
        !          1824:            case '#':
        !          1825:                number = eventPtr->xany.serial;
        !          1826:                goto doNumber;
        !          1827:            case 'a':
        !          1828:                number = (int) eventPtr->xconfigure.above;
        !          1829:                goto doNumber;
        !          1830:            case 'b':
        !          1831:                number = eventPtr->xbutton.button;
        !          1832:                goto doNumber;
        !          1833:            case 'c':
        !          1834:                if (flags & EXPOSE) {
        !          1835:                    number = eventPtr->xexpose.count;
        !          1836:                } else if (flags & MAPPING) {
        !          1837:                    number = eventPtr->xmapping.count;
        !          1838:                }
        !          1839:                goto doNumber;
        !          1840:            case 'd':
        !          1841:                if (flags & (CROSSING|FOCUS)) {
        !          1842:                    switch (eventPtr->xcrossing.detail) {
        !          1843:                        case NotifyAncestor:
        !          1844:                            string = "NotifyAncestor";
        !          1845:                            break;
        !          1846:                        case NotifyVirtual:
        !          1847:                            string = "NotifyVirtual";
        !          1848:                            break;
        !          1849:                        case NotifyInferior:
        !          1850:                            string = "NotifyInferior";
        !          1851:                            break;
        !          1852:                        case NotifyNonlinear:
        !          1853:                            string = "NotifyNonlinear";
        !          1854:                            break;
        !          1855:                        case NotifyNonlinearVirtual:
        !          1856:                            string = "NotifyNonlinearVirtual";
        !          1857:                            break;
        !          1858:                        case NotifyPointer:
        !          1859:                            string = "NotifyPointer";
        !          1860:                            break;
        !          1861:                        case NotifyPointerRoot:
        !          1862:                            string = "NotifyPointerRoot";
        !          1863:                            break;
        !          1864:                        case NotifyDetailNone:
        !          1865:                            string = "NotifyDetailNone";
        !          1866:                            break;
        !          1867:                    }
        !          1868:                } else if (flags & CONFIG_REQ) {
        !          1869:                    switch (eventPtr->xconfigurerequest.detail) {
        !          1870:                        case Above:
        !          1871:                            string = "Above";
        !          1872:                            break;
        !          1873:                        case Below:
        !          1874:                            string = "Below";
        !          1875:                            break;
        !          1876:                        case TopIf:
        !          1877:                            string = "TopIf";
        !          1878:                            break;
        !          1879:                        case BottomIf:
        !          1880:                            string = "BottomIf";
        !          1881:                            break;
        !          1882:                        case Opposite:
        !          1883:                            string = "Opposite";
        !          1884:                            break;
        !          1885:                    }
        !          1886:                }
        !          1887:                goto doString;
        !          1888:            case 'f':
        !          1889:                number = eventPtr->xcrossing.focus;
        !          1890:                goto doNumber;
        !          1891:            case 'h':
        !          1892:                if (flags & EXPOSE) {
        !          1893:                    number = eventPtr->xexpose.height;
        !          1894:                } else if (flags & (CONFIG|CONFIG_REQ)) {
        !          1895:                    number = eventPtr->xconfigure.height;
        !          1896:                } else if (flags & RESIZE_REQ) {
        !          1897:                    number = eventPtr->xresizerequest.height;
        !          1898:                }
        !          1899:                goto doNumber;
        !          1900:            case 'k':
        !          1901:                number = eventPtr->xkey.keycode;
        !          1902:                goto doNumber;
        !          1903:            case 'm':
        !          1904:                if (flags & CROSSING) {
        !          1905:                    number = eventPtr->xcrossing.mode;
        !          1906:                } else if (flags & FOCUS) {
        !          1907:                    number = eventPtr->xfocus.mode;
        !          1908:                }
        !          1909:                switch (number) {
        !          1910:                    case NotifyNormal:
        !          1911:                        string = "NotifyNormal";
        !          1912:                        break;
        !          1913:                    case NotifyGrab:
        !          1914:                        string = "NotifyGrab";
        !          1915:                        break;
        !          1916:                    case NotifyUngrab:
        !          1917:                        string = "NotifyUngrab";
        !          1918:                        break;
        !          1919:                    case NotifyWhileGrabbed:
        !          1920:                        string = "NotifyWhileGrabbed";
        !          1921:                        break;
        !          1922:                }
        !          1923:                goto doString;
        !          1924:            case 'o':
        !          1925:                if (flags & CREATE) {
        !          1926:                    number = eventPtr->xcreatewindow.override_redirect;
        !          1927:                } else if (flags & MAP) {
        !          1928:                    number = eventPtr->xmap.override_redirect;
        !          1929:                } else if (flags & REPARENT) {
        !          1930:                    number = eventPtr->xreparent.override_redirect;
        !          1931:                } else if (flags & CONFIG) {
        !          1932:                    number = eventPtr->xconfigure.override_redirect;
        !          1933:                }
        !          1934:                goto doNumber;
        !          1935:            case 'p':
        !          1936:                switch (eventPtr->xcirculate.place) {
        !          1937:                    case PlaceOnTop:
        !          1938:                        string = "PlaceOnTop";
        !          1939:                        break;
        !          1940:                    case PlaceOnBottom:
        !          1941:                        string = "PlaceOnBottom";
        !          1942:                        break;
        !          1943:                }
        !          1944:                goto doString;
        !          1945:            case 's':
        !          1946:                if (flags & KEY_BUTTON_MOTION) {
        !          1947:                    number = eventPtr->xkey.state;
        !          1948:                } else if (flags & CROSSING) {
        !          1949:                    number = eventPtr->xcrossing.state;
        !          1950:                } else if (flags & VISIBILITY) {
        !          1951:                    switch (eventPtr->xvisibility.state) {
        !          1952:                        case VisibilityUnobscured:
        !          1953:                            string = "VisibilityUnobscured";
        !          1954:                            break;
        !          1955:                        case VisibilityPartiallyObscured:
        !          1956:                            string = "VisibilityPartiallyObscured";
        !          1957:                            break;
        !          1958:                        case VisibilityFullyObscured:
        !          1959:                            string = "VisibilityFullyObscured";
        !          1960:                            break;
        !          1961:                    }
        !          1962:                    goto doString;
        !          1963:                }
        !          1964:                goto doNumber;
        !          1965:            case 't':
        !          1966:                if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
        !          1967:                    number = (int) eventPtr->xkey.time;
        !          1968:                } else if (flags & SEL_REQ) {
        !          1969:                    number = (int) eventPtr->xselectionrequest.time;
        !          1970:                } else if (flags & SEL_NOTIFY) {
        !          1971:                    number = (int) eventPtr->xselection.time;
        !          1972:                }
        !          1973:                goto doNumber;
        !          1974:            case 'v':
        !          1975:                number = eventPtr->xconfigurerequest.value_mask;
        !          1976:                goto doNumber;
        !          1977:            case 'w':
        !          1978:                if (flags & EXPOSE) {
        !          1979:                    number = eventPtr->xexpose.width;
        !          1980:                } else if (flags & (CONFIG|CONFIG_REQ)) {
        !          1981:                    number = eventPtr->xconfigure.width;
        !          1982:                } else if (flags & RESIZE_REQ) {
        !          1983:                    number = eventPtr->xresizerequest.width;
        !          1984:                }
        !          1985:                goto doNumber;
        !          1986:            case 'x':
        !          1987:                if (flags & KEY_BUTTON_MOTION) {
        !          1988:                    number = eventPtr->xkey.x;
        !          1989:                } else if (flags & EXPOSE) {
        !          1990:                    number = eventPtr->xexpose.x;
        !          1991:                } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
        !          1992:                    number = eventPtr->xcreatewindow.x;
        !          1993:                } else if (flags & REPARENT) {
        !          1994:                    number = eventPtr->xreparent.x;
        !          1995:                } else if (flags & CROSSING) {
        !          1996:                    number = eventPtr->xcrossing.x;
        !          1997:                }
        !          1998:                goto doNumber;
        !          1999:            case 'y':
        !          2000:                if (flags & KEY_BUTTON_MOTION) {
        !          2001:                    number = eventPtr->xkey.y;
        !          2002:                } else if (flags & EXPOSE) {
        !          2003:                    number = eventPtr->xexpose.y;
        !          2004:                } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
        !          2005:                    number = eventPtr->xcreatewindow.y;
        !          2006:                } else if (flags & REPARENT) {
        !          2007:                    number = eventPtr->xreparent.y;
        !          2008:                } else if (flags & CROSSING) {
        !          2009:                    number = eventPtr->xcrossing.y;
        !          2010: 
        !          2011:                }
        !          2012:                goto doNumber;
        !          2013:            case 'A':
        !          2014:                if ((eventPtr->type == KeyPress)
        !          2015:                        || (eventPtr->type == KeyRelease)) {
        !          2016:                    int numChars;
        !          2017: 
        !          2018:                    numChars = XLookupString(&eventPtr->xkey, numStorage,
        !          2019:                            NUM_SIZE, (KeySym *) NULL,
        !          2020:                            (XComposeStatus *) NULL);
        !          2021:                    numStorage[numChars] = '\0';
        !          2022:                    string = numStorage;
        !          2023:                }
        !          2024:                goto doString;
        !          2025:            case 'B':
        !          2026:                number = eventPtr->xcreatewindow.border_width;
        !          2027:                goto doNumber;
        !          2028:            case 'D':
        !          2029:                number = (int) eventPtr->xany.display;
        !          2030:                goto doNumber;
        !          2031:            case 'E':
        !          2032:                number = (int) eventPtr->xany.send_event;
        !          2033:                goto doNumber;
        !          2034:            case 'K':
        !          2035:                if ((eventPtr->type == KeyPress)
        !          2036:                        || (eventPtr->type == KeyRelease)) {
        !          2037:                    register KeySymInfo *kPtr;
        !          2038: 
        !          2039:                    for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
        !          2040:                        if (kPtr->value == keySym) {
        !          2041:                            string = kPtr->name;
        !          2042:                            break;
        !          2043:                        }
        !          2044:                    }
        !          2045:                }
        !          2046:                goto doString;
        !          2047:            case 'N':
        !          2048:                number = (int) keySym;
        !          2049:                goto doNumber;
        !          2050:            case 'R':
        !          2051:                number = (int) eventPtr->xkey.root;
        !          2052:                goto doNumber;
        !          2053:            case 'S':
        !          2054:                number = (int) eventPtr->xkey.subwindow;
        !          2055:                goto doNumber;
        !          2056:            case 'T':
        !          2057:                number = eventPtr->type;
        !          2058:                goto doNumber;
        !          2059:            case 'W': {
        !          2060:                TkWindow *winPtr;
        !          2061: 
        !          2062:                if (XFindContext(eventPtr->xany.display, eventPtr->xany.window,
        !          2063:                        tkWindowContext, (void *) &winPtr) == 0) {
        !          2064:                    string = winPtr->pathName;
        !          2065:                } else {
        !          2066:                    string = "??";
        !          2067:                }
        !          2068:                goto doString;
        !          2069:            }
        !          2070:            case 'X':
        !          2071:                number = eventPtr->xkey.x_root;
        !          2072:                goto doNumber;
        !          2073:            case 'Y':
        !          2074:                number = eventPtr->xkey.y_root;
        !          2075:                goto doNumber;
        !          2076:            default:
        !          2077:                numStorage[0] = before[1];
        !          2078:                numStorage[1] = '\0';
        !          2079:                string = numStorage;
        !          2080:                goto doString;
        !          2081:        }
        !          2082: 
        !          2083:        doNumber:
        !          2084:        sprintf(numStorage, "%d", number);
        !          2085:        string = numStorage;
        !          2086: 
        !          2087:        doString:
        !          2088:        spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
        !          2089:        if (spaceNeeded >= spaceLeft) {
        !          2090:            char *newSpace;
        !          2091: 
        !          2092:            newSpace = (char *) ckalloc((unsigned)
        !          2093:                    (afterSize + spaceNeeded + 50));
        !          2094:            memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
        !          2095:            afterSize += spaceNeeded + 50;
        !          2096:            dst = newSpace + (dst - buffer);
        !          2097:            if (buffer != after) {
        !          2098:                ckfree(buffer);
        !          2099:            }
        !          2100:            buffer = newSpace;
        !          2101:            spaceLeft = afterSize - (dst-buffer) - 1;
        !          2102:        }
        !          2103:        spaceNeeded = Tcl_ConvertElement(string, dst,
        !          2104:                cvtFlags | TCL_DONT_USE_BRACES);
        !          2105:        dst += spaceNeeded;
        !          2106:        spaceLeft -= spaceNeeded;
        !          2107:        before += 2;
        !          2108:     }
        !          2109:     *dst = '\0';
        !          2110:     return buffer;
        !          2111: }
        !          2112: 
        !          2113: /*
        !          2114:  *----------------------------------------------------------------------
        !          2115:  *
        !          2116:  * TkBindError --
        !          2117:  *
        !          2118:  *     This procedure is invoked to handle errors that occur in Tcl
        !          2119:  *     commands that are invoked in "background" (e.g. from event or
        !          2120:  *     timer bindings).
        !          2121:  *
        !          2122:  * Results:
        !          2123:  *     None.
        !          2124:  *
        !          2125:  * Side effects:
        !          2126:  *     The command "tkerror" is invoked to process the error, passing
        !          2127:  *     it the error message.  If that fails, then an error message
        !          2128:  *     is output on stderr.
        !          2129:  *
        !          2130:  *----------------------------------------------------------------------
        !          2131:  */
        !          2132: 
        !          2133: void
        !          2134: TkBindError(interp)
        !          2135:     Tcl_Interp *interp;                /* Interpreter in which an error has
        !          2136:                                 * occurred. */
        !          2137: {
        !          2138:     char *argv[2];
        !          2139:     char *command;
        !          2140:     char *error;
        !          2141:     char *errorInfo, *tmp;
        !          2142:     int result;
        !          2143: 
        !          2144:     error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1));
        !          2145:     strcpy(error, interp->result);
        !          2146:     tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
        !          2147:     if (tmp == NULL) {
        !          2148:        errorInfo = error;
        !          2149:     } else {
        !          2150:        errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1));
        !          2151:        strcpy(errorInfo, tmp);
        !          2152:     }
        !          2153:     argv[0] = "tkerror";
        !          2154:     argv[1] = error;
        !          2155:     command = Tcl_Merge(2, argv);
        !          2156:     result = Tcl_GlobalEval(interp, command);
        !          2157:     if (result != TCL_OK) {
        !          2158:        if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
        !          2159:            fprintf(stderr, "%s\n", errorInfo);
        !          2160:        } else {
        !          2161:            fprintf(stderr, "tkerror failed to handle background error.\n");
        !          2162:            fprintf(stderr, "    Original error: %s\n", error);
        !          2163:            fprintf(stderr, "    Error in tkerror: %s\n", interp->result);
        !          2164:        }
        !          2165:     }
        !          2166:     Tcl_ResetResult(interp);
        !          2167:     ckfree(command);
        !          2168:     ckfree(error);
        !          2169:     if (errorInfo != error) {
        !          2170:        ckfree(errorInfo);
        !          2171:     }
        !          2172: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.