Annotation of micropolis/src/tk/tkbind.c, revision 1.1.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.