home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkBind.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  75KB  |  2,573 lines

  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 (c) 1989-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkBind.c 1.110 96/03/22 11:54:55
  14.  */
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * The structure below represents a binding table.  A binding table
  21.  * represents a domain in which event bindings may occur.  It includes
  22.  * a space of objects relative to which events occur (usually windows,
  23.  * but not always), a history of recent events in the domain, and
  24.  * a set of mappings that associate particular Tcl commands with sequences
  25.  * of events in the domain.  Multiple binding tables may exist at once,
  26.  * either because there are multiple applications open, or because there
  27.  * are multiple domains within an application with separate event
  28.  * bindings for each (for example, each canvas widget has a separate
  29.  * binding table for associating events with the items in the canvas).
  30.  *
  31.  * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
  32.  * below 30.  To see this, consider a triple mouse button click while
  33.  * the Shift key is down (and auto-repeating).  There may be as many
  34.  * as 3 auto-repeat events after each mouse button press or release
  35.  * (see the first large comment block within Tk_BindEvent for more on
  36.  * this), for a total of 20 events to cover the three button presses
  37.  * and two intervening releases.  If you reduce EVENT_BUFFER_SIZE too
  38.  * much, shift multi-clicks will be lost.
  39.  * 
  40.  */
  41.  
  42. #define EVENT_BUFFER_SIZE 30
  43. typedef struct BindingTable {
  44.     XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
  45.                      * (higher indices are for more recent
  46.                      * events). */
  47.     int detailRing[EVENT_BUFFER_SIZE];    /* "Detail" information (keySym or
  48.                      * button or 0) for each entry in
  49.                      * eventRing. */
  50.     int curEvent;            /* Index in eventRing of most recent
  51.                      * event.  Newer events have higher
  52.                      * indices. */
  53.     Tcl_HashTable patternTable;        /* Used to map from an event to a list
  54.                      * of patterns that may match that
  55.                      * event.  Keys are PatternTableKey
  56.                      * structs, values are (PatSeq *). */
  57.     Tcl_HashTable objectTable;        /* Used to map from an object to a list
  58.                      * of patterns associated with that
  59.                      * object.  Keys are ClientData,
  60.                      * values are (PatSeq *). */
  61.     Tcl_Interp *interp;            /* Interpreter in which commands are
  62.                      * executed. */
  63. } BindingTable;
  64.  
  65. /*
  66.  * Structures of the following form are used as keys in the patternTable
  67.  * for a binding table:
  68.  */
  69.  
  70. typedef struct PatternTableKey {
  71.     ClientData object;        /* Identifies object (or class of objects)
  72.                  * relative to which event occurred.  For
  73.                  * example, in the widget binding table for
  74.                  * an application this is the path name of
  75.                  * a widget, or a widget class, or "all". */
  76.     int type;            /* Type of event (from X). */
  77.     int detail;            /* Additional information, such as
  78.                  * keysym or button, or 0 if nothing
  79.                  * additional.*/
  80. } PatternTableKey;
  81.  
  82. /*
  83.  * The following structure defines a pattern, which is matched
  84.  * against X events as part of the process of converting X events
  85.  * into Tcl commands.
  86.  */
  87.  
  88. typedef struct Pattern {
  89.     int eventType;        /* Type of X event, e.g. ButtonPress. */
  90.     int needMods;        /* Mask of modifiers that must be
  91.                  * present (0 means no modifiers are
  92.                  * required). */
  93.     int detail;            /* Additional information that must
  94.                  * match event.  Normally this is 0,
  95.                  * meaning no additional information
  96.                  * must match.  For KeyPress and
  97.                  * KeyRelease events, a keySym may
  98.                  * be specified to select a
  99.                  * particular keystroke (0 means any
  100.                  * keystrokes).  For button events,
  101.                  * specifies a particular button (0
  102.                  * means any buttons are OK). */
  103. } Pattern;
  104.  
  105. /*
  106.  * The structure below defines a pattern sequence, which consists
  107.  * of one or more patterns.  In order to trigger, a pattern
  108.  * sequence must match the most recent X events (first pattern
  109.  * to most recent event, next pattern to next event, and so on).
  110.  */
  111.  
  112. typedef struct PatSeq {
  113.     int numPats;        /* Number of patterns in sequence
  114.                  * (usually 1). */
  115.     char *command;        /* Command to invoke when this
  116.                  * pattern sequence matches (malloc-ed). */
  117.     int flags;            /* Miscellaneous flag values;  see
  118.                  * below for definitions. */
  119.     struct PatSeq *nextSeqPtr;
  120.                 /* Next in list of all pattern
  121.                  * sequences that have the same
  122.                  * initial pattern.  NULL means
  123.                  * end of list. */
  124.     Tcl_HashEntry *hPtr;    /* Pointer to hash table entry for
  125.                  * the initial pattern.  This is the
  126.                  * head of the list of which nextSeqPtr
  127.                  * forms a part. */
  128.     ClientData object;        /* Identifies object with which event is
  129.                  * associated (e.g. window). */
  130.     struct PatSeq *nextObjPtr;
  131.                 /* Next in list of all pattern
  132.                  * sequences for the same object
  133.                  * (NULL for end of list).  Needed to
  134.                  * implement Tk_DeleteAllBindings. */
  135.     Pattern pats[1];        /* Array of "numPats" patterns.  Only
  136.                  * one element is declared here but
  137.                  * in actuality enough space will be
  138.                  * allocated for "numPats" patterns.
  139.                  * To match, pats[0] must match event
  140.                  * n, pats[1] must match event n-1,
  141.                  * etc. */
  142. } PatSeq;
  143.  
  144. /*
  145.  * Flag values for PatSeq structures:
  146.  *
  147.  * PAT_NEARBY        1 means that all of the events matching
  148.  *            this sequence must occur with nearby X
  149.  *            and Y mouse coordinates and close in time.
  150.  *            This is typically used to restrict multiple
  151.  *            button presses.
  152.  */
  153.  
  154. #define PAT_NEARBY        1
  155.  
  156. /*
  157.  * Constants that define how close together two events must be
  158.  * in milliseconds or pixels to meet the PAT_NEARBY constraint:
  159.  */
  160.  
  161. #define NEARBY_PIXELS        5
  162. #define NEARBY_MS        500
  163.  
  164. /*
  165.  * One of the following structures exists for each interpreter,
  166.  * associated with the key "tkBind".  This structure keeps track
  167.  * of the current display and screen in the interpreter, so that
  168.  * a script can be invoked whenever the display/screen changes
  169.  * (the script does things like point tkPriv at a display-specific
  170.  * structure).
  171.  */
  172.  
  173. typedef struct ScreenInfo {
  174.     TkDisplay *curDispPtr;    /* Display for last binding command invoked
  175.                  * in this application. */
  176.     int curScreenIndex;        /* Index of screen for last binding command. */
  177.     int bindingDepth;        /* Number of active instances of Tk_BindEvent
  178.                  * in this application. */
  179. } ScreenInfo;
  180.  
  181. /*
  182.  * In X11R4 and earlier versions, XStringToKeysym is ridiculously
  183.  * slow.  The data structure and hash table below, along with the
  184.  * code that uses them, implement a fast mapping from strings to
  185.  * keysyms.  In X11R5 and later releases XStringToKeysym is plenty
  186.  * fast so this stuff isn't needed.  The #define REDO_KEYSYM_LOOKUP
  187.  * is normally undefined, so that XStringToKeysym gets used.  It
  188.  * can be set in the Makefile to enable the use of the hash table
  189.  * below.
  190.  */
  191.  
  192. #ifdef REDO_KEYSYM_LOOKUP
  193. typedef struct {
  194.     char *name;                /* Name of keysym. */
  195.     KeySym value;            /* Numeric identifier for keysym. */
  196. } KeySymInfo;
  197. static KeySymInfo keyArray[] = {
  198. #ifndef lint
  199. #include "ks_names.h"
  200. #endif
  201.     {(char *) NULL, 0}
  202. };
  203. static Tcl_HashTable keySymTable;    /* keyArray hashed by keysym value. */
  204. static Tcl_HashTable nameTable;        /* keyArray hashed by keysym name. */
  205. #endif /* REDO_KEYSYM_LOOKUP */
  206.  
  207. static int initialized = 0;
  208.  
  209. /*
  210.  * A hash table is kept to map from the string names of event
  211.  * modifiers to information about those modifiers.  The structure
  212.  * for storing this information, and the hash table built at
  213.  * initialization time, are defined below.
  214.  */
  215.  
  216. typedef struct {
  217.     char *name;            /* Name of modifier. */
  218.     int mask;            /* Button/modifier mask value,                             * such as Button1Mask. */
  219.     int flags;            /* Various flags;  see below for
  220.                  * definitions. */
  221. } ModInfo;
  222.  
  223. /*
  224.  * Flags for ModInfo structures:
  225.  *
  226.  * DOUBLE -        Non-zero means duplicate this event,
  227.  *            e.g. for double-clicks.
  228.  * TRIPLE -        Non-zero means triplicate this event,
  229.  *            e.g. for triple-clicks.
  230.  */
  231.  
  232. #define DOUBLE        1
  233. #define TRIPLE        2
  234.  
  235. /*
  236.  * The following special modifier mask bits are defined, to indicate
  237.  * logical modifiers such as Meta and Alt that may float among the
  238.  * actual modifier bits.
  239.  */
  240.  
  241. #define META_MASK    (AnyModifier<<1)
  242. #define ALT_MASK    (AnyModifier<<2)
  243.  
  244. static ModInfo modArray[] = {
  245.     {"Control",        ControlMask,    0},
  246.     {"Shift",        ShiftMask,    0},
  247.     {"Lock",        LockMask,    0},
  248.     {"Meta",        META_MASK,    0},
  249.     {"M",        META_MASK,    0},
  250.     {"Alt",        ALT_MASK,    0},
  251.     {"B1",        Button1Mask,    0},
  252.     {"Button1",        Button1Mask,    0},
  253.     {"B2",        Button2Mask,    0},
  254.     {"Button2",        Button2Mask,    0},
  255.     {"B3",        Button3Mask,    0},
  256.     {"Button3",        Button3Mask,    0},
  257.     {"B4",        Button4Mask,    0},
  258.     {"Button4",        Button4Mask,    0},
  259.     {"B5",        Button5Mask,    0},
  260.     {"Button5",        Button5Mask,    0},
  261.     {"Mod1",        Mod1Mask,    0},
  262.     {"M1",        Mod1Mask,    0},
  263.     {"Command",        Mod1Mask,    0},
  264.     {"Mod2",        Mod2Mask,    0},
  265.     {"M2",        Mod2Mask,    0},
  266.     {"Option",        Mod2Mask,    0},
  267.     {"Mod3",        Mod3Mask,    0},
  268.     {"M3",        Mod3Mask,    0},
  269.     {"Mod4",        Mod4Mask,    0},
  270.     {"M4",        Mod4Mask,    0},
  271.     {"Mod5",        Mod5Mask,    0},
  272.     {"M5",        Mod5Mask,    0},
  273.     {"Double",        0,        DOUBLE},
  274.     {"Triple",        0,        TRIPLE},
  275.     {"Any",        0,        0},    /* Ignored: historical relic. */
  276.     {NULL,        0,        0}
  277. };
  278. static Tcl_HashTable modTable;
  279.  
  280. /*
  281.  * This module also keeps a hash table mapping from event names
  282.  * to information about those events.  The structure, an array
  283.  * to use to initialize the hash table, and the hash table are
  284.  * all defined below.
  285.  */
  286.  
  287. typedef struct {
  288.     char *name;            /* Name of event. */
  289.     int type;            /* Event type for X, such as
  290.                  * ButtonPress. */
  291.     int eventMask;        /* Mask bits (for XSelectInput)
  292.                  * for this event type. */
  293. } EventInfo;
  294.  
  295. /*
  296.  * Note:  some of the masks below are an OR-ed combination of
  297.  * several masks.  This is necessary because X doesn't report
  298.  * up events unless you also ask for down events.  Also, X
  299.  * doesn't report button state in motion events unless you've
  300.  * asked about button events.
  301.  */
  302.  
  303. static EventInfo eventArray[] = {
  304.     {"Motion",        MotionNotify,
  305.         ButtonPressMask|PointerMotionMask},
  306.     {"Button",        ButtonPress,        ButtonPressMask},
  307.     {"ButtonPress",    ButtonPress,        ButtonPressMask},
  308.     {"ButtonRelease",    ButtonRelease,
  309.         ButtonPressMask|ButtonReleaseMask},
  310.     {"Colormap",    ColormapNotify,        ColormapChangeMask},
  311.     {"Enter",        EnterNotify,        EnterWindowMask},
  312.     {"Leave",        LeaveNotify,        LeaveWindowMask},
  313.     {"Expose",        Expose,            ExposureMask},
  314.     {"FocusIn",        FocusIn,        FocusChangeMask},
  315.     {"FocusOut",    FocusOut,        FocusChangeMask},
  316.     {"Key",        KeyPress,        KeyPressMask},
  317.     {"KeyPress",    KeyPress,        KeyPressMask},
  318.     {"KeyRelease",    KeyRelease,
  319.         KeyPressMask|KeyReleaseMask},
  320.     {"Property",    PropertyNotify,        PropertyChangeMask},
  321.     {"Circulate",    CirculateNotify,    StructureNotifyMask},
  322.     {"Configure",    ConfigureNotify,    StructureNotifyMask},
  323.     {"Destroy",        DestroyNotify,        StructureNotifyMask},
  324.     {"Gravity",        GravityNotify,        StructureNotifyMask},
  325.     {"Map",        MapNotify,        StructureNotifyMask},
  326.     {"Reparent",    ReparentNotify,        StructureNotifyMask},
  327.     {"Unmap",        UnmapNotify,        StructureNotifyMask},
  328.     {"Visibility",    VisibilityNotify,    VisibilityChangeMask},
  329.     {"Activate",    ActivateNotify,        ActivateMask},
  330.     {"Deactivate",    DeactivateNotify,    ActivateMask},
  331.     {(char *) NULL,    0,            0}
  332. };
  333. static Tcl_HashTable eventTable;
  334.  
  335. /*
  336.  * The defines and table below are used to classify events into
  337.  * various groups.  The reason for this is that logically identical
  338.  * fields (e.g. "state") appear at different places in different
  339.  * types of events.  The classification masks can be used to figure
  340.  * out quickly where to extract information from events.
  341.  */
  342.  
  343. #define KEY_BUTTON_MOTION    0x1
  344. #define CROSSING        0x2
  345. #define FOCUS            0x4
  346. #define EXPOSE            0x8
  347. #define VISIBILITY        0x10
  348. #define CREATE            0x20
  349. #define MAP            0x40
  350. #define REPARENT        0x80
  351. #define CONFIG            0x100
  352. #define CONFIG_REQ        0x200
  353. #define RESIZE_REQ        0x400
  354. #define GRAVITY            0x800
  355. #define PROP            0x1000
  356. #define SEL_CLEAR        0x2000
  357. #define SEL_REQ            0x4000
  358. #define SEL_NOTIFY        0x8000
  359. #define COLORMAP        0x10000
  360. #define MAPPING            0x20000
  361. #define ACTIVATE        0x40000
  362.  
  363. static int flagArray[TK_LASTEVENT] = {
  364.    /* Not used */        0,
  365.    /* Not used */        0,
  366.    /* KeyPress */        KEY_BUTTON_MOTION,
  367.    /* KeyRelease */        KEY_BUTTON_MOTION,
  368.    /* ButtonPress */        KEY_BUTTON_MOTION,
  369.    /* ButtonRelease */        KEY_BUTTON_MOTION,
  370.    /* MotionNotify */        KEY_BUTTON_MOTION,
  371.    /* EnterNotify */        CROSSING,
  372.    /* LeaveNotify */        CROSSING,
  373.    /* FocusIn */        FOCUS,
  374.    /* FocusOut */        FOCUS,
  375.    /* KeymapNotify */        0,
  376.    /* Expose */            EXPOSE,
  377.    /* GraphicsExpose */        EXPOSE,
  378.    /* NoExpose */        0,
  379.    /* VisibilityNotify */    VISIBILITY,
  380.    /* CreateNotify */        CREATE,
  381.    /* DestroyNotify */        0,
  382.    /* UnmapNotify */        0,
  383.    /* MapNotify */        MAP,
  384.    /* MapRequest */        0,
  385.    /* ReparentNotify */        REPARENT,
  386.    /* ConfigureNotify */    CONFIG,
  387.    /* ConfigureRequest */    CONFIG_REQ,
  388.    /* GravityNotify */        0,
  389.    /* ResizeRequest */        RESIZE_REQ,
  390.    /* CirculateNotify */    0,
  391.    /* CirculateRequest */    0,
  392.    /* PropertyNotify */        PROP,
  393.    /* SelectionClear */        SEL_CLEAR,
  394.    /* SelectionRequest */    SEL_REQ,
  395.    /* SelectionNotify */    SEL_NOTIFY,
  396.    /* ColormapNotify */        COLORMAP,
  397.    /* ClientMessage */        0,
  398.    /* MappingNotify */        MAPPING,
  399.    /* Activate */        ACTIVATE,        
  400.    /* Deactivate */        ACTIVATE
  401. };
  402.  
  403. /*
  404.  * Prototypes for local procedures defined in this file:
  405.  */
  406.  
  407. static void        ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,
  408.                 char *dispName, int screenIndex));
  409. static void        ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,
  410.                 char *before, XEvent *eventPtr, KeySym keySym,
  411.                 Tcl_DString *dsPtr));
  412. static PatSeq *        FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
  413.                 BindingTable *bindPtr, ClientData object,
  414.                 char *eventString, int create,
  415.                 unsigned long *maskPtr));
  416. static void        FreeScreenInfo _ANSI_ARGS_((ClientData clientData,
  417.                 Tcl_Interp *interp));
  418. static char *        GetField _ANSI_ARGS_((char *p, char *copy, int size));
  419. static KeySym        GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
  420.                 XEvent *eventPtr));
  421. static void        InitKeymapInfo _ANSI_ARGS_((TkDisplay *dispPtr));
  422. static PatSeq *        MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,
  423.                 BindingTable *bindPtr, PatSeq *psPtr));
  424.  
  425. /*
  426.  *--------------------------------------------------------------
  427.  *
  428.  * Tk_CreateBindingTable --
  429.  *
  430.  *    Set up a new domain in which event bindings may be created.
  431.  *
  432.  * Results:
  433.  *    The return value is a token for the new table, which must
  434.  *    be passed to procedures like Tk_CreatBinding.
  435.  *
  436.  * Side effects:
  437.  *    Memory is allocated for the new table.
  438.  *
  439.  *--------------------------------------------------------------
  440.  */
  441.  
  442. Tk_BindingTable
  443. Tk_CreateBindingTable(interp)
  444.     Tcl_Interp *interp;        /* Interpreter to associate with the binding
  445.                  * table:  commands are executed in this
  446.                  * interpreter. */
  447. {
  448.     register BindingTable *bindPtr;
  449.     int i;
  450.  
  451.     /*
  452.      * If this is the first time a binding table has been created,
  453.      * initialize the global data structures.
  454.      */
  455.  
  456.     if (!initialized) {
  457.     register Tcl_HashEntry *hPtr;
  458.     register ModInfo *modPtr;
  459.     register EventInfo *eiPtr;
  460.     int dummy;
  461.  
  462. #ifdef REDO_KEYSYM_LOOKUP
  463.     register KeySymInfo *kPtr;
  464.  
  465.     Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
  466.     Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS);
  467.     for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
  468.         hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
  469.         Tcl_SetHashValue(hPtr, kPtr->value);
  470.         hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value,
  471.             &dummy);
  472.         Tcl_SetHashValue(hPtr, kPtr->name);
  473.     }
  474. #endif /* REDO_KEYSYM_LOOKUP */
  475.  
  476.     initialized = 1;
  477.     
  478.     Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
  479.     for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
  480.         hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
  481.         Tcl_SetHashValue(hPtr, modPtr);
  482.     }
  483.     
  484.     Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
  485.     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  486.         hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
  487.         Tcl_SetHashValue(hPtr, eiPtr);
  488.     }
  489.     }
  490.  
  491.     /*
  492.      * Create and initialize a new binding table.
  493.      */
  494.  
  495.     bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
  496.     for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
  497.     bindPtr->eventRing[i].type = -1;
  498.     }
  499.     bindPtr->curEvent = 0;
  500.     Tcl_InitHashTable(&bindPtr->patternTable,
  501.         sizeof(PatternTableKey)/sizeof(int));
  502.     Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
  503.     bindPtr->interp = interp;
  504.     return (Tk_BindingTable) bindPtr;
  505. }
  506.  
  507. /*
  508.  *--------------------------------------------------------------
  509.  *
  510.  * Tk_DeleteBindingTable --
  511.  *
  512.  *    Destroy a binding table and free up all its memory.
  513.  *    The caller should not use bindingTable again after
  514.  *    this procedure returns.
  515.  *
  516.  * Results:
  517.  *    None.
  518.  *
  519.  * Side effects:
  520.  *    Memory is freed.
  521.  *
  522.  *--------------------------------------------------------------
  523.  */
  524.  
  525. void
  526. Tk_DeleteBindingTable(bindingTable)
  527.     Tk_BindingTable bindingTable;    /* Token for the binding table to
  528.                      * destroy. */
  529. {
  530.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  531.     PatSeq *psPtr, *nextPtr;
  532.     Tcl_HashEntry *hPtr;
  533.     Tcl_HashSearch search;
  534.  
  535.     /*
  536.      * Find and delete all of the patterns associated with the binding
  537.      * table.
  538.      */
  539.  
  540.     for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
  541.         hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  542.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  543.         psPtr != NULL; psPtr = nextPtr) {
  544.         nextPtr = psPtr->nextSeqPtr;
  545.         ckfree((char *) psPtr->command);
  546.         ckfree((char *) psPtr);
  547.     }
  548.     }
  549.  
  550.     /*
  551.      * Clean up the rest of the information associated with the
  552.      * binding table.
  553.      */
  554.  
  555.     Tcl_DeleteHashTable(&bindPtr->patternTable);
  556.     Tcl_DeleteHashTable(&bindPtr->objectTable);
  557.     ckfree((char *) bindPtr);
  558. }
  559.  
  560. /*
  561.  *--------------------------------------------------------------
  562.  *
  563.  * Tk_CreateBinding --
  564.  *
  565.  *    Add a binding to a binding table, so that future calls to
  566.  *    Tk_BindEvent may execute the command in the binding.
  567.  *
  568.  * Results:
  569.  *    The return value is 0 if an error occurred while setting
  570.  *    up the binding.  In this case, an error message will be
  571.  *    left in interp->result.  If all went well then the return
  572.  *    value is a mask of the event types that must be made
  573.  *    available to Tk_BindEvent in order to properly detect when
  574.  *    this binding triggers.  This value can be used to determine
  575.  *    what events to select for in a window, for example.
  576.  *
  577.  * Side effects:
  578.  *    The new binding may cause future calls to Tk_BindEvent to
  579.  *    behave differently than they did previously.
  580.  *
  581.  *--------------------------------------------------------------
  582.  */
  583.  
  584. unsigned long
  585. Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
  586.     Tcl_Interp *interp;            /* Used for error reporting. */
  587.     Tk_BindingTable bindingTable;    /* Table in which to create binding. */
  588.     ClientData object;            /* Token for object with which binding
  589.                      * is associated. */
  590.     char *eventString;            /* String describing event sequence
  591.                      * that triggers binding. */
  592.     char *command;            /* Contains Tcl command to execute
  593.                      * when binding triggers. */
  594.     int append;                /* 0 means replace any existing
  595.                      * binding for eventString;  1 means
  596.                      * append to that binding. */
  597. {
  598.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  599.     register PatSeq *psPtr;
  600.     unsigned long eventMask;
  601.  
  602.     psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask);
  603.     if (psPtr == NULL) {
  604.     return 0;
  605.     }
  606.     if (append && (psPtr->command != NULL)) {
  607.     int length;
  608.     char *new;
  609.  
  610.     length = strlen(psPtr->command) + strlen(command) + 2;
  611.     new = (char *) ckalloc((unsigned) length);
  612.     sprintf(new, "%s\n%s", psPtr->command, command);
  613.     ckfree((char *) psPtr->command);
  614.     psPtr->command = new;
  615.     } else {
  616.     if (psPtr->command != NULL) {
  617.         ckfree((char *) psPtr->command);
  618.     }
  619.     psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
  620.     strcpy(psPtr->command, command);
  621.     }
  622.     return eventMask;
  623. }
  624.  
  625. /*
  626.  *--------------------------------------------------------------
  627.  *
  628.  * Tk_DeleteBinding --
  629.  *
  630.  *    Remove an event binding from a binding table.
  631.  *
  632.  * Results:
  633.  *    The result is a standard Tcl return value.  If an error
  634.  *    occurs then interp->result will contain an error message.
  635.  *
  636.  * Side effects:
  637.  *    The binding given by object and eventString is removed
  638.  *    from bindingTable.
  639.  *
  640.  *--------------------------------------------------------------
  641.  */
  642.  
  643. int
  644. Tk_DeleteBinding(interp, bindingTable, object, eventString)
  645.     Tcl_Interp *interp;            /* Used for error reporting. */
  646.     Tk_BindingTable bindingTable;    /* Table in which to delete binding. */
  647.     ClientData object;            /* Token for object with which binding
  648.                      * is associated. */
  649.     char *eventString;            /* String describing event sequence
  650.                      * that triggers binding. */
  651. {
  652.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  653.     register PatSeq *psPtr, *prevPtr;
  654.     unsigned long eventMask;
  655.     Tcl_HashEntry *hPtr;
  656.  
  657.     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
  658.     if (psPtr == NULL) {
  659.     Tcl_ResetResult(interp);
  660.     return TCL_OK;
  661.     }
  662.  
  663.     /*
  664.      * Unlink the binding from the list for its object, then from the
  665.      * list for its pattern.
  666.      */
  667.  
  668.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  669.     if (hPtr == NULL) {
  670.     panic("Tk_DeleteBinding couldn't find object table entry");
  671.     }
  672.     prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  673.     if (prevPtr == psPtr) {
  674.     Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
  675.     } else {
  676.     for ( ; ; prevPtr = prevPtr->nextObjPtr) {
  677.         if (prevPtr == NULL) {
  678.         panic("Tk_DeleteBinding couldn't find on object list");
  679.         }
  680.         if (prevPtr->nextObjPtr == psPtr) {
  681.         prevPtr->nextObjPtr = psPtr->nextObjPtr;
  682.         break;
  683.         }
  684.     }
  685.     }
  686.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  687.     if (prevPtr == psPtr) {
  688.     if (psPtr->nextSeqPtr == NULL) {
  689.         Tcl_DeleteHashEntry(psPtr->hPtr);
  690.     } else {
  691.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  692.     }
  693.     } else {
  694.     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  695.         if (prevPtr == NULL) {
  696.         panic("Tk_DeleteBinding couldn't find on hash chain");
  697.         }
  698.         if (prevPtr->nextSeqPtr == psPtr) {
  699.         prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  700.         break;
  701.         }
  702.     }
  703.     }
  704.     ckfree((char *) psPtr->command);
  705.     ckfree((char *) psPtr);
  706.     return TCL_OK;
  707. }
  708.  
  709. /*
  710.  *--------------------------------------------------------------
  711.  *
  712.  * Tk_GetBinding --
  713.  *
  714.  *    Return the command associated with a given event string.
  715.  *
  716.  * Results:
  717.  *    The return value is a pointer to the command string
  718.  *    associated with eventString for object in the domain
  719.  *    given by bindingTable.  If there is no binding for
  720.  *    eventString, or if eventString is improperly formed,
  721.  *    then NULL is returned and an error message is left in
  722.  *    interp->result.  The return value is semi-static:  it
  723.  *    will persist until the binding is changed or deleted.
  724.  *
  725.  * Side effects:
  726.  *    None.
  727.  *
  728.  *--------------------------------------------------------------
  729.  */
  730.  
  731. char *
  732. Tk_GetBinding(interp, bindingTable, object, eventString)
  733.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  734.     Tk_BindingTable bindingTable;    /* Table in which to look for
  735.                      * binding. */
  736.     ClientData object;            /* Token for object with which binding
  737.                      * is associated. */
  738.     char *eventString;            /* String describing event sequence
  739.                      * that triggers binding. */
  740. {
  741.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  742.     register PatSeq *psPtr;
  743.     unsigned long eventMask;
  744.  
  745.     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
  746.     if (psPtr == NULL) {
  747.     return NULL;
  748.     }
  749.     return psPtr->command;
  750. }
  751.  
  752. /*
  753.  *--------------------------------------------------------------
  754.  *
  755.  * Tk_GetAllBindings --
  756.  *
  757.  *    Return a list of event strings for all the bindings
  758.  *    associated with a given object.
  759.  *
  760.  * Results:
  761.  *    There is no return value.  Interp->result is modified to
  762.  *    hold a Tcl list with one entry for each binding associated
  763.  *    with object in bindingTable.  Each entry in the list
  764.  *    contains the event string associated with one binding.
  765.  *
  766.  * Side effects:
  767.  *    None.
  768.  *
  769.  *--------------------------------------------------------------
  770.  */
  771.  
  772. void
  773. Tk_GetAllBindings(interp, bindingTable, object)
  774.     Tcl_Interp *interp;            /* Interpreter returning result or
  775.                      * error. */
  776.     Tk_BindingTable bindingTable;    /* Table in which to look for
  777.                      * bindings. */
  778.     ClientData object;            /* Token for object. */
  779.  
  780. {
  781.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  782.     register PatSeq *psPtr;
  783.     register Pattern *patPtr;
  784.     Tcl_HashEntry *hPtr;
  785.     Tcl_DString ds;
  786.     char c, buffer[10];
  787.     int patsLeft, needMods;
  788.     register ModInfo *modPtr;
  789.     register EventInfo *eiPtr;
  790.  
  791.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  792.     if (hPtr == NULL) {
  793.     return;
  794.     }
  795.     Tcl_DStringInit(&ds);
  796.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  797.         psPtr = psPtr->nextObjPtr) {
  798.     Tcl_DStringSetLength(&ds, 0);
  799.  
  800.     /*
  801.      * For each binding, output information about each of the
  802.      * patterns in its sequence.  The order of the patterns in
  803.      * the sequence is backwards from the order in which they
  804.      * must be output.
  805.      */
  806.  
  807.     for (patsLeft = psPtr->numPats,
  808.         patPtr = &psPtr->pats[psPtr->numPats - 1];
  809.         patsLeft > 0; patsLeft--, patPtr--) {
  810.  
  811.         /*
  812.          * Check for simple case of an ASCII character.
  813.          */
  814.  
  815.         if ((patPtr->eventType == KeyPress)
  816.             && (patPtr->needMods == 0)
  817.             && (patPtr->detail < 128)
  818.             && isprint(UCHAR(patPtr->detail))
  819.             && (patPtr->detail != '<')
  820.             && (patPtr->detail != ' ')) {
  821.  
  822.         c = patPtr->detail;
  823.         Tcl_DStringAppend(&ds, &c, 1);
  824.         continue;
  825.         }
  826.  
  827.         /*
  828.          * It's a more general event specification.  First check
  829.          * for "Double" or "Triple", then modifiers, then event type,
  830.          * then keysym or button detail.
  831.          */
  832.  
  833.         Tcl_DStringAppend(&ds, "<", 1);
  834.         if ((patsLeft > 1) && (memcmp((char *) patPtr,
  835.             (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
  836.         patsLeft--;
  837.         patPtr--;
  838.         if ((patsLeft > 1) && (memcmp((char *) patPtr,
  839.             (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
  840.             patsLeft--;
  841.             patPtr--;
  842.             Tcl_DStringAppend(&ds, "Triple-", 7);
  843.         } else {
  844.             Tcl_DStringAppend(&ds, "Double-", 7);
  845.         }
  846.         }
  847.  
  848.         for (needMods = patPtr->needMods, modPtr = modArray;
  849.             needMods != 0; modPtr++) {
  850.         if (modPtr->mask & needMods) {
  851.             needMods &= ~modPtr->mask;
  852.             Tcl_DStringAppend(&ds, modPtr->name, -1);
  853.             Tcl_DStringAppend(&ds, "-", 1);
  854.         }
  855.         }
  856.  
  857.         for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  858.         if (eiPtr->type == patPtr->eventType) {
  859.             Tcl_DStringAppend(&ds, eiPtr->name, -1);
  860.             if (patPtr->detail != 0) {
  861.             Tcl_DStringAppend(&ds, "-", 1);
  862.             }
  863.             break;
  864.         }
  865.         }
  866.  
  867.         if (patPtr->detail != 0) {
  868.         if ((patPtr->eventType == KeyPress)
  869.             || (patPtr->eventType == KeyRelease)) {
  870.             char *string;
  871.  
  872.             string = TkKeysymToString((KeySym) patPtr->detail);
  873.             if (string != NULL) {
  874.             Tcl_DStringAppend(&ds, string, -1);
  875.             }
  876.         } else {
  877.             sprintf(buffer, "%d", patPtr->detail);
  878.             Tcl_DStringAppend(&ds, buffer, -1);
  879.         }
  880.         }
  881.         Tcl_DStringAppend(&ds, ">", 1);
  882.     }
  883.     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
  884.     }
  885.     Tcl_DStringFree(&ds);
  886. }
  887.  
  888. /*
  889.  *--------------------------------------------------------------
  890.  *
  891.  * Tk_DeleteAllBindings --
  892.  *
  893.  *    Remove all bindings associated with a given object in a
  894.  *    given binding table.
  895.  *
  896.  * Results:
  897.  *    All bindings associated with object are removed from
  898.  *    bindingTable.
  899.  *
  900.  * Side effects:
  901.  *    None.
  902.  *
  903.  *--------------------------------------------------------------
  904.  */
  905.  
  906. void
  907. Tk_DeleteAllBindings(bindingTable, object)
  908.     Tk_BindingTable bindingTable;    /* Table in which to delete
  909.                      * bindings. */
  910.     ClientData object;            /* Token for object. */
  911. {
  912.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  913.     register PatSeq *psPtr, *prevPtr;
  914.     PatSeq *nextPtr;
  915.     Tcl_HashEntry *hPtr;
  916.  
  917.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  918.     if (hPtr == NULL) {
  919.     return;
  920.     }
  921.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  922.         psPtr = nextPtr) {
  923.     nextPtr  = psPtr->nextObjPtr;
  924.  
  925.     /*
  926.      * Be sure to remove each binding from its hash chain in the
  927.      * pattern table.  If this is the last pattern in the chain,
  928.      * then delete the hash entry too.
  929.      */
  930.  
  931.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  932.     if (prevPtr == psPtr) {
  933.         if (psPtr->nextSeqPtr == NULL) {
  934.         Tcl_DeleteHashEntry(psPtr->hPtr);
  935.         } else {
  936.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  937.         }
  938.     } else {
  939.         for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  940.         if (prevPtr == NULL) {
  941.             panic("Tk_DeleteAllBindings couldn't find on hash chain");
  942.         }
  943.         if (prevPtr->nextSeqPtr == psPtr) {
  944.             prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  945.             break;
  946.         }
  947.         }
  948.     }
  949.     ckfree((char *) psPtr->command);
  950.     ckfree((char *) psPtr);
  951.     }
  952.     Tcl_DeleteHashEntry(hPtr);
  953. }
  954.  
  955. /*
  956.  *--------------------------------------------------------------
  957.  *
  958.  * Tk_BindEvent --
  959.  *
  960.  *    This procedure is invoked to process an X event.  The
  961.  *    event is added to those recorded for the binding table.
  962.  *    Then each of the objects at *objectPtr is checked in
  963.  *    order to see if it has a binding that matches the recent
  964.  *    events.  If so, that binding is invoked and the rest of
  965.  *    objects are skipped.
  966.  *
  967.  * Results:
  968.  *    None.
  969.  *
  970.  * Side effects:
  971.  *    Depends on the command associated with the matching
  972.  *    binding.
  973.  *
  974.  *--------------------------------------------------------------
  975.  */
  976.  
  977. void
  978. Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
  979.     Tk_BindingTable bindingTable;    /* Table in which to look for
  980.                      * bindings. */
  981.     XEvent *eventPtr;            /* What actually happened. */
  982.     Tk_Window tkwin;            /* Window on display where event
  983.                      * occurred (needed in order to
  984.                      * locate display information). */
  985.     int numObjects;            /* Number of objects at *objectPtr. */
  986.     ClientData *objectPtr;        /* Array of one or more objects
  987.                      * to check for a matching binding. */
  988. {
  989.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  990.     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
  991.     TkDisplay *oldDispPtr;
  992.     ScreenInfo *screenPtr;
  993.     XEvent *ringPtr;
  994.     PatSeq *matchPtr;
  995.     PatternTableKey key;
  996.     Tcl_HashEntry *hPtr;
  997.     int detail, code, oldScreen;
  998.     Tcl_Interp *interp;
  999.     Tcl_DString scripts, savedResult;
  1000.     char *p, *end;
  1001.  
  1002.     /*
  1003.      * Ignore the event completely if it is an Enter, Leave, FocusIn,
  1004.      * or FocusOut event with detail NotifyInferior.  The reason for
  1005.      * ignoring these events is that we don't want transitions between
  1006.      * a window and its children to visible to bindings on the parent:
  1007.      * this would cause problems for mega-widgets, since the internal
  1008.      * structure of a mega-widget isn't supposed to be visible to
  1009.      * people watching the parent.
  1010.      */
  1011.  
  1012.     if ((eventPtr->type == EnterNotify)  || (eventPtr->type == LeaveNotify)) {
  1013.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  1014.         return;
  1015.     }
  1016.     }
  1017.     if ((eventPtr->type == FocusIn)  || (eventPtr->type == FocusOut)) {
  1018.     if (eventPtr->xfocus.detail == NotifyInferior) {
  1019.         return;
  1020.     }
  1021.     }
  1022.  
  1023.     /*
  1024.      * Add the new event to the ring of saved events for the
  1025.      * binding table.  Two tricky points:
  1026.      *
  1027.      * 1. Combine consecutive MotionNotify events.  Do this by putting
  1028.      *    the new event *on top* of the previous event.
  1029.      * 2. If a modifier key is held down, it auto-repeats to generate
  1030.      *    continuous KeyPress and KeyRelease events.  These can flush
  1031.      *    the event ring so that valuable information is lost (such
  1032.      *    as repeated button clicks).  To handle this, check for the
  1033.      *    special case of a modifier KeyPress arriving when the previous
  1034.      *    two events are a KeyRelease and KeyPress of the same key.
  1035.      *    If this happens, mark the most recent event (the KeyRelease)
  1036.      *    invalid and put the new event on top of the event before that
  1037.      *    (the KeyPress).
  1038.      */
  1039.  
  1040.     if ((eventPtr->type == MotionNotify)
  1041.         && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {
  1042.     /*
  1043.      * Don't advance the ring pointer.
  1044.      */
  1045.     } else if (eventPtr->type == KeyPress) {
  1046.     int i;
  1047.     for (i = 0; ; i++) {
  1048.         if (i >= dispPtr->numModKeyCodes) {
  1049.         goto advanceRingPointer;
  1050.         }
  1051.         if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1052.         break;
  1053.         }
  1054.     }
  1055.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1056.     if ((ringPtr->type != KeyRelease)
  1057.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1058.         goto advanceRingPointer;
  1059.     }
  1060.     if (bindPtr->curEvent <= 0) {
  1061.         i = EVENT_BUFFER_SIZE - 1;
  1062.     } else {
  1063.         i = bindPtr->curEvent - 1;
  1064.     }
  1065.     ringPtr = &bindPtr->eventRing[i];
  1066.     if ((ringPtr->type != KeyPress)
  1067.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1068.         goto advanceRingPointer;
  1069.     }
  1070.     bindPtr->eventRing[bindPtr->curEvent].type = -1;
  1071.     bindPtr->curEvent = i;
  1072.     } else {
  1073.     advanceRingPointer:
  1074.     bindPtr->curEvent++;
  1075.     if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
  1076.         bindPtr->curEvent = 0;
  1077.     }
  1078.     }
  1079.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1080.     memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
  1081.     detail = 0;
  1082.     bindPtr->detailRing[bindPtr->curEvent] = 0;
  1083.     if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
  1084.     detail = (int) GetKeySym(dispPtr, ringPtr);
  1085.     if (detail == NoSymbol) {
  1086.         detail = 0;
  1087.     }
  1088.     } else if ((ringPtr->type == ButtonPress)
  1089.         || (ringPtr->type == ButtonRelease)) {
  1090.     detail = ringPtr->xbutton.button;
  1091.     }
  1092.     bindPtr->detailRing[bindPtr->curEvent] = detail;
  1093.  
  1094.     /*
  1095.      * Loop over all the objects, finding the binding script for each
  1096.      * one.  Append all of the binding scripts, with %-sequences expanded,
  1097.      * to "scripts", with null characters separating the scripts for
  1098.      * each object.
  1099.      */
  1100.  
  1101.     Tcl_DStringInit(&scripts);
  1102.     for ( ; numObjects > 0; numObjects--, objectPtr++) {
  1103.  
  1104.     /*
  1105.      * Match the new event against those recorded in the
  1106.      * pattern table, saving the longest matching pattern.
  1107.      * For events with details (button and key events) first
  1108.      * look for a binding for the specific key or button.
  1109.      * If none is found, then look for a binding for all
  1110.      * keys or buttons (detail of 0).
  1111.      */
  1112.     
  1113.     matchPtr = NULL;
  1114.     key.object = *objectPtr;
  1115.     key.type = ringPtr->type;
  1116.     key.detail = detail;
  1117.     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1118.     if (hPtr != NULL) {
  1119.         matchPtr = MatchPatterns(dispPtr, bindPtr,
  1120.             (PatSeq *) Tcl_GetHashValue(hPtr));
  1121.     }
  1122.     if ((detail != 0) && (matchPtr == NULL)) {
  1123.         key.detail = 0;
  1124.         hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1125.         if (hPtr != NULL) {
  1126.         matchPtr = MatchPatterns(dispPtr, bindPtr,
  1127.             (PatSeq *) Tcl_GetHashValue(hPtr));
  1128.         }
  1129.     }
  1130.     
  1131.     if (matchPtr != NULL) {
  1132.         ExpandPercents((TkWindow *) tkwin, matchPtr->command, eventPtr,
  1133.             (KeySym) detail, &scripts);
  1134.         Tcl_DStringAppend(&scripts, "", 1);
  1135.     }
  1136.     }
  1137.     if (Tcl_DStringLength(&scripts) == 0) {
  1138.     return;
  1139.     }
  1140.  
  1141.     /*
  1142.      * Now go back through and evaluate the script for each object,
  1143.      * in order, dealing with "break" and "continue" exceptions
  1144.      * appropriately.
  1145.      *
  1146.      * There are two tricks here:
  1147.      * 1. Bindings can be invoked from in the middle of Tcl commands,
  1148.      *    where interp->result is significant (for example, a widget
  1149.      *    might be deleted because of an error in creating it, so the
  1150.      *    result contains an error message that is eventually going to
  1151.      *    be returned by the creating command).  To preserve the result,
  1152.      *    we save it in a dynamic string.
  1153.      * 2. The binding's action can potentially delete the binding,
  1154.      *    so bindPtr may not point to anything valid once the action
  1155.      *    completes.  Thus we have to save bindPtr->interp in a
  1156.      *    local variable in order to restore the result.
  1157.      */
  1158.  
  1159.     interp = bindPtr->interp;
  1160.     Tcl_DStringInit(&savedResult);
  1161.  
  1162.     /*
  1163.      * Save information about the current screen, then invoke a script
  1164.      * if the screen has changed.
  1165.      */
  1166.  
  1167.     Tcl_DStringGetResult(interp, &savedResult);
  1168.     screenPtr = (ScreenInfo *) Tcl_GetAssocData(interp, "tkBind",
  1169.         (Tcl_InterpDeleteProc **) NULL);
  1170.     if (screenPtr == NULL) {
  1171.     screenPtr = (ScreenInfo *) ckalloc(sizeof(ScreenInfo));
  1172.     screenPtr->curDispPtr = NULL;
  1173.     screenPtr->curScreenIndex = -1;
  1174.     screenPtr->bindingDepth = 0;
  1175.     Tcl_SetAssocData(interp, "tkBind", FreeScreenInfo,
  1176.         (ClientData) screenPtr);
  1177.     }
  1178.     oldDispPtr = screenPtr->curDispPtr;
  1179.     oldScreen = screenPtr->curScreenIndex;
  1180.     if ((dispPtr != screenPtr->curDispPtr)
  1181.         || (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) {
  1182.     screenPtr->curDispPtr = dispPtr;
  1183.     screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin);
  1184.     ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex);
  1185.     }
  1186.  
  1187.     p = Tcl_DStringValue(&scripts);
  1188.     end = p + Tcl_DStringLength(&scripts);
  1189.     while (p != end) {
  1190.     screenPtr->bindingDepth += 1;
  1191.     Tcl_AllowExceptions(interp);
  1192.     code = Tcl_GlobalEval(interp, p);
  1193.     screenPtr->bindingDepth -= 1;
  1194.     if (code != TCL_OK) {
  1195.         if (code == TCL_CONTINUE) {
  1196.         /*
  1197.          * Do nothing:  just go on to the next script.
  1198.          */
  1199.         } else if (code == TCL_BREAK) {
  1200.         break;
  1201.         } else {
  1202.         Tcl_AddErrorInfo(interp, "\n    (command bound to event)");
  1203.         Tcl_BackgroundError(interp);
  1204.         break;
  1205.         }
  1206.     }
  1207.  
  1208.     /*
  1209.      * Skip over the current script and its terminating null character.
  1210.      */
  1211.  
  1212.     while (*p != 0) {
  1213.         p++;
  1214.     }
  1215.     p++;
  1216.     }
  1217.     if ((screenPtr->bindingDepth != 0) &&
  1218.             ((oldDispPtr != screenPtr->curDispPtr)
  1219.                     || (oldScreen != screenPtr->curScreenIndex))) {
  1220.  
  1221.     /*
  1222.      * Some other binding script is currently executing, but its
  1223.      * screen is no longer current.  Change the current display
  1224.      * back again.
  1225.      */
  1226.  
  1227.     screenPtr->curDispPtr = oldDispPtr;
  1228.     screenPtr->curScreenIndex = oldScreen;
  1229.     ChangeScreen(interp, oldDispPtr->name, oldScreen);
  1230.     }
  1231.     Tcl_DStringResult(interp, &savedResult);
  1232.     Tcl_DStringFree(&scripts);
  1233. }
  1234.  
  1235. /*
  1236.  *----------------------------------------------------------------------
  1237.  *
  1238.  * ChangeScreen --
  1239.  *
  1240.  *    This procedure is invoked whenever the current screen changes
  1241.  *    in an application.  It invokes a Tcl procedure named
  1242.  *    "tkScreenChanged", passing it the screen name as argument.
  1243.  *    tkScreenChanged does things like making the tkPriv variable
  1244.  *    point to an array for the current display.
  1245.  *
  1246.  * Results:
  1247.  *    None.
  1248.  *
  1249.  * Side effects:
  1250.  *    Depends on what tkScreenChanged does.  If an error occurs
  1251.  *    them tkError will be invoked.
  1252.  *
  1253.  *----------------------------------------------------------------------
  1254.  */
  1255.  
  1256. static void
  1257. ChangeScreen(interp, dispName, screenIndex)
  1258.     Tcl_Interp *interp;            /* Interpreter in which to invoke
  1259.                      * command. */
  1260.     char *dispName;            /* Name of new display. */
  1261.     int screenIndex;            /* Index of new screen. */
  1262. {
  1263.     Tcl_DString cmd;
  1264.     int code;
  1265.     char screen[30];
  1266.  
  1267.     Tcl_DStringInit(&cmd);
  1268.     Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);
  1269.     Tcl_DStringAppend(&cmd, dispName, -1);
  1270.     sprintf(screen, ".%d", screenIndex);
  1271.     Tcl_DStringAppend(&cmd, screen, -1);
  1272.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));
  1273.     if (code != TCL_OK) {
  1274.     Tcl_AddErrorInfo(interp,
  1275.         "\n    (changing screen in event binding)");
  1276.     Tcl_BackgroundError(interp);
  1277.     }
  1278. }
  1279.  
  1280. /*
  1281.  *----------------------------------------------------------------------
  1282.  *
  1283.  * FreeScreenInfo --
  1284.  *
  1285.  *    This procedure is invoked when an interpreter is deleted in
  1286.  *    order to free the ScreenInfo structure associated with the
  1287.  *    "tkBind" AssocData.
  1288.  *
  1289.  * Results:
  1290.  *    None.
  1291.  *
  1292.  * Side effects:
  1293.  *    Storage is freed.
  1294.  *
  1295.  *----------------------------------------------------------------------
  1296.  */
  1297.  
  1298. static void
  1299. FreeScreenInfo(clientData, interp)
  1300.     ClientData clientData;    /* Pointer to ScreenInfo structure. */
  1301.     Tcl_Interp *interp;        /* Interpreter that is being deleted. */
  1302. {
  1303.     ckfree((char *) clientData);
  1304. }
  1305.  
  1306. /*
  1307.  *----------------------------------------------------------------------
  1308.  *
  1309.  * FindSequence --
  1310.  *
  1311.  *    Find the entry in a binding table that corresponds to a
  1312.  *    particular pattern string, and return a pointer to that
  1313.  *    entry.
  1314.  *
  1315.  * Results:
  1316.  *    The return value is normally a pointer to the PatSeq
  1317.  *    in patternTable that corresponds to eventString.  If an error
  1318.  *    was found while parsing eventString, or if "create" is 0 and
  1319.  *    no pattern sequence previously existed, then NULL is returned
  1320.  *    and interp->result contains a message describing the problem.
  1321.  *    If no pattern sequence previously existed for eventString, then
  1322.  *    a new one is created with a NULL command field.  In a successful
  1323.  *    return, *maskPtr is filled in with a mask of the event types
  1324.  *    on which the pattern sequence depends.
  1325.  *
  1326.  * Side effects:
  1327.  *    A new pattern sequence may be created.
  1328.  *
  1329.  *----------------------------------------------------------------------
  1330.  */
  1331.  
  1332. static PatSeq *
  1333. FindSequence(interp, bindPtr, object, eventString, create, maskPtr)
  1334.     Tcl_Interp *interp;        /* Interpreter to use for error
  1335.                  * reporting. */
  1336.     BindingTable *bindPtr;    /* Table to use for lookup. */
  1337.     ClientData object;        /* Token for object(s) with which binding
  1338.                  * is associated. */
  1339.     char *eventString;        /* String description of pattern to
  1340.                  * match on.  See user documentation
  1341.                  * for details. */
  1342.     int create;            /* 0 means don't create the entry if
  1343.                  * it doesn't already exist.   Non-zero
  1344.                  * means create. */
  1345.     unsigned long *maskPtr;    /* *maskPtr is filled in with the event
  1346.                  * types on which this pattern sequence
  1347.                  * depends. */
  1348.  
  1349. {
  1350.     Pattern pats[EVENT_BUFFER_SIZE];
  1351.     int numPats;
  1352.     register char *p;
  1353.     register Pattern *patPtr;
  1354.     register PatSeq *psPtr;
  1355.     register Tcl_HashEntry *hPtr;
  1356. #define FIELD_SIZE 48
  1357.     char field[FIELD_SIZE];
  1358.     int flags, count, new;
  1359.     size_t sequenceSize;
  1360.     unsigned long eventMask;
  1361.     PatternTableKey key;
  1362.  
  1363.     /*
  1364.      *-------------------------------------------------------------
  1365.      * Step 1: parse the pattern string to produce an array
  1366.      * of Patterns.  The array is generated backwards, so
  1367.      * that the lowest-indexed pattern corresponds to the last
  1368.      * event that must occur.
  1369.      *-------------------------------------------------------------
  1370.      */
  1371.  
  1372.     p = eventString;
  1373.     flags = 0;
  1374.     eventMask = 0;
  1375.     for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
  1376.         numPats < EVENT_BUFFER_SIZE;
  1377.         numPats++, patPtr--) {
  1378.     patPtr->eventType = -1;
  1379.     patPtr->needMods = 0;
  1380.     patPtr->detail = 0;
  1381.     while (isspace(UCHAR(*p))) {
  1382.         p++;
  1383.     }
  1384.     if (*p == '\0') {
  1385.         break;
  1386.     }
  1387.  
  1388.     /*
  1389.      * Handle simple ASCII characters.
  1390.      */
  1391.  
  1392.     if (*p != '<') {
  1393.         char string[2];
  1394.  
  1395.         patPtr->eventType = KeyPress;
  1396.         eventMask |= KeyPressMask;
  1397.         string[0] = *p;
  1398.         string[1] = 0;
  1399.         patPtr->detail = TkStringToKeysym(string);
  1400.         if (patPtr->detail == NoSymbol) {
  1401.         if (isprint(UCHAR(*p))) {
  1402.             patPtr->detail = *p;
  1403.         } else {
  1404.             sprintf(interp->result,
  1405.                 "bad ASCII character 0x%x", (unsigned char) *p);
  1406.             return NULL;
  1407.         }
  1408.         }
  1409.         p++;
  1410.         continue;
  1411.     }
  1412.  
  1413.     /*
  1414.      * A fancier event description.  Must consist of
  1415.      * 1. open angle bracket.
  1416.      * 2. any number of modifiers, each followed by spaces
  1417.      *    or dashes.
  1418.      * 3. an optional event name.
  1419.      * 4. an option button or keysym name.  Either this or
  1420.      *    item 3 *must* be present;  if both are present
  1421.      *    then they are separated by spaces or dashes.
  1422.      * 5. a close angle bracket.
  1423.      */
  1424.  
  1425.     count = 1;
  1426.     p++;
  1427.     while (1) {
  1428.         register ModInfo *modPtr;
  1429.         p = GetField(p, field, FIELD_SIZE);
  1430.         hPtr = Tcl_FindHashEntry(&modTable, field);
  1431.         if (hPtr == NULL) {
  1432.         break;
  1433.         }
  1434.         modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
  1435.         patPtr->needMods |= modPtr->mask;
  1436.         if (modPtr->flags & (DOUBLE|TRIPLE)) {
  1437.         flags |= PAT_NEARBY;
  1438.         if (modPtr->flags & DOUBLE) {
  1439.             count = 2;
  1440.         } else {
  1441.             count = 3;
  1442.         }
  1443.         }
  1444.         while ((*p == '-') || isspace(UCHAR(*p))) {
  1445.         p++;
  1446.         }
  1447.     }
  1448.     hPtr = Tcl_FindHashEntry(&eventTable, field);
  1449.     if (hPtr != NULL) {
  1450.         register EventInfo *eiPtr;
  1451.         eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
  1452.         patPtr->eventType = eiPtr->type;
  1453.         eventMask |= eiPtr->eventMask;
  1454.         while ((*p == '-') || isspace(UCHAR(*p))) {
  1455.         p++;
  1456.         }
  1457.         p = GetField(p, field, FIELD_SIZE);
  1458.     }
  1459.     if (*field != '\0') {
  1460.         if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
  1461.         if (patPtr->eventType == -1) {
  1462.             patPtr->eventType = ButtonPress;
  1463.             eventMask |= ButtonPressMask;
  1464.         } else if ((patPtr->eventType == KeyPress)
  1465.             || (patPtr->eventType == KeyRelease)) {
  1466.             goto getKeysym;
  1467.         } else if ((patPtr->eventType != ButtonPress)
  1468.             && (patPtr->eventType != ButtonRelease)) {
  1469.             Tcl_AppendResult(interp, "specified button \"", field,
  1470.                 "\" for non-button event", (char *) NULL);
  1471.             return NULL;
  1472.         }
  1473.         patPtr->detail = (*field - '0');
  1474.         } else {
  1475.         getKeysym:
  1476.         patPtr->detail = TkStringToKeysym(field);
  1477.         if (patPtr->detail == NoSymbol) {
  1478.             Tcl_AppendResult(interp, "bad event type or keysym \"",
  1479.                 field, "\"", (char *) NULL);
  1480.             return NULL;
  1481.         }
  1482.         if (patPtr->eventType == -1) {
  1483.             patPtr->eventType = KeyPress;
  1484.             eventMask |= KeyPressMask;
  1485.         } else if ((patPtr->eventType != KeyPress)
  1486.             && (patPtr->eventType != KeyRelease)) {
  1487.             Tcl_AppendResult(interp, "specified keysym \"", field,
  1488.                 "\" for non-key event", (char *) NULL);
  1489.             return NULL;
  1490.         }
  1491.         }
  1492.     } else if (patPtr->eventType == -1) {
  1493.         interp->result = "no event type or button # or keysym";
  1494.         return NULL;
  1495.     }
  1496.     while ((*p == '-') || isspace(UCHAR(*p))) {
  1497.         p++;
  1498.     }
  1499.     if (*p != '>') {
  1500.         interp->result = "missing \">\" in binding";
  1501.         return NULL;
  1502.     }
  1503.     p++;
  1504.  
  1505.     /*
  1506.      * Replicate events for DOUBLE and TRIPLE.
  1507.      */
  1508.  
  1509.     if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
  1510.         patPtr[-1] = patPtr[0];
  1511.         patPtr--;
  1512.         numPats++;
  1513.         if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
  1514.         patPtr[-1] = patPtr[0];
  1515.         patPtr--;
  1516.         numPats++;
  1517.         }
  1518.     }
  1519.     }
  1520.  
  1521.     /*
  1522.      *-------------------------------------------------------------
  1523.      * Step 2: find the sequence in the binding table if it exists,
  1524.      * and add a new sequence to the table if it doesn't.
  1525.      *-------------------------------------------------------------
  1526.      */
  1527.  
  1528.     if (numPats == 0) {
  1529.     interp->result = "no events specified in binding";
  1530.     return NULL;
  1531.     }
  1532.     patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
  1533.     key.object = object;
  1534.     key.type = patPtr->eventType;
  1535.     key.detail = patPtr->detail;
  1536.     hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
  1537.     sequenceSize = numPats*sizeof(Pattern);
  1538.     if (!new) {
  1539.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  1540.         psPtr = psPtr->nextSeqPtr) {
  1541.         if ((numPats == psPtr->numPats)
  1542.             && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
  1543.             && (memcmp((char *) patPtr, (char *) psPtr->pats,
  1544.             sequenceSize) == 0)) {
  1545.         goto done;
  1546.         }
  1547.     }
  1548.     }
  1549.     if (!create) {
  1550.     if (new) {
  1551.         Tcl_DeleteHashEntry(hPtr);
  1552.     }
  1553.     Tcl_AppendResult(interp, "no binding exists for \"",
  1554.         eventString, "\"", (char *) NULL);
  1555.     return NULL;
  1556.     }
  1557.     psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
  1558.         + (numPats-1)*sizeof(Pattern)));
  1559.     psPtr->numPats = numPats;
  1560.     psPtr->command = NULL;
  1561.     psPtr->flags = flags;
  1562.     psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1563.     psPtr->hPtr = hPtr;
  1564.     Tcl_SetHashValue(hPtr, psPtr);
  1565.  
  1566.     /*
  1567.      * Link the pattern into the list associated with the object.
  1568.      */
  1569.  
  1570.     psPtr->object = object;
  1571.     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
  1572.     if (new) {
  1573.     psPtr->nextObjPtr = NULL;
  1574.     } else {
  1575.     psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1576.     }
  1577.     Tcl_SetHashValue(hPtr, psPtr);
  1578.  
  1579.     memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
  1580.  
  1581.     done:
  1582.     *maskPtr = eventMask;
  1583.     return psPtr;
  1584. }
  1585.  
  1586. /*
  1587.  *----------------------------------------------------------------------
  1588.  *
  1589.  * GetField --
  1590.  *
  1591.  *    Used to parse pattern descriptions.  Copies up to
  1592.  *    size characters from p to copy, stopping at end of
  1593.  *    string, space, "-", ">", or whenever size is
  1594.  *    exceeded.
  1595.  *
  1596.  * Results:
  1597.  *    The return value is a pointer to the character just
  1598.  *    after the last one copied (usually "-" or space or
  1599.  *    ">", but could be anything if size was exceeded).
  1600.  *    Also places NULL-terminated string (up to size
  1601.  *    character, including NULL), at copy.
  1602.  *
  1603.  * Side effects:
  1604.  *    None.
  1605.  *
  1606.  *----------------------------------------------------------------------
  1607.  */
  1608.  
  1609. static char *
  1610. GetField(p, copy, size)
  1611.     register char *p;        /* Pointer to part of pattern. */
  1612.     register char *copy;    /* Place to copy field. */
  1613.     int size;            /* Maximum number of characters to
  1614.                  * copy. */
  1615. {
  1616.     while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
  1617.         && (*p != '-') && (size > 1)) {
  1618.     *copy = *p;
  1619.     p++;
  1620.     copy++;
  1621.     size--;
  1622.     }
  1623.     *copy = '\0';
  1624.     return p;
  1625. }
  1626.  
  1627. /*
  1628.  *----------------------------------------------------------------------
  1629.  *
  1630.  * GetKeySym --
  1631.  *
  1632.  *    Given an X KeyPress or KeyRelease event, map the
  1633.  *    keycode in the event into a KeySym.
  1634.  *
  1635.  * Results:
  1636.  *    The return value is the KeySym corresponding to
  1637.  *    eventPtr, or NoSymbol if no matching Keysym could be
  1638.  *    found.
  1639.  *
  1640.  * Side effects:
  1641.  *    In the first call for a given display, keycode-to-
  1642.  *    KeySym maps get loaded.
  1643.  *
  1644.  *----------------------------------------------------------------------
  1645.  */
  1646.  
  1647. static KeySym
  1648. GetKeySym(dispPtr, eventPtr)
  1649.     register TkDisplay *dispPtr;    /* Display in which to
  1650.                      * map keycode. */
  1651.     register XEvent *eventPtr;        /* Description of X event. */
  1652. {
  1653.     KeySym sym;
  1654.     int index;
  1655.  
  1656.     /*
  1657.      * Refresh the mapping information if it's stale
  1658.      */
  1659.  
  1660.     if (dispPtr->bindInfoStale) {
  1661.     InitKeymapInfo(dispPtr);
  1662.     }
  1663.  
  1664.     /*
  1665.      * Figure out which of the four slots in the keymap vector to
  1666.      * use for this key.  Refer to Xlib documentation for more info
  1667.      * on how this computation works.
  1668.      */
  1669.  
  1670.     index = 0;
  1671.     if (eventPtr->xkey.state & dispPtr->modeModMask) {
  1672.     index = 2;
  1673.     }
  1674.     if ((eventPtr->xkey.state & ShiftMask)
  1675.         || ((dispPtr->lockUsage != LU_IGNORE)
  1676.         && (eventPtr->xkey.state & LockMask))) {
  1677.     index += 1;
  1678.     }
  1679.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index);
  1680.  
  1681.     /*
  1682.      * Special handling:  if the key was shifted because of Lock, but
  1683.      * lock is only caps lock, not shift lock, and the shifted keysym
  1684.      * isn't upper-case alphabetic, then switch back to the unshifted
  1685.      * keysym.
  1686.      */
  1687.  
  1688.     if ((index & 1) && !(eventPtr->xkey.state & ShiftMask)
  1689.         && (dispPtr->lockUsage == LU_CAPS)) {
  1690.     if (!(((sym >= XK_A) && (sym <= XK_Z))
  1691.         || ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
  1692.         || ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) {
  1693.         index &= ~1;
  1694.         sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  1695.             index);
  1696.     }
  1697.     }
  1698.  
  1699.     /*
  1700.      * Another bit of special handling:  if this is a shifted key and there
  1701.      * is no keysym defined, then use the keysym for the unshifted key.
  1702.      */
  1703.  
  1704.     if ((index & 1) && (sym == NoSymbol)) {
  1705.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  1706.             index & ~1);
  1707.     }
  1708.     return sym;
  1709. }
  1710.  
  1711. /*
  1712.  *----------------------------------------------------------------------
  1713.  *
  1714.  * MatchPatterns --
  1715.  *
  1716.  *    Given a list of pattern sequences and a list of
  1717.  *    recent events, return a pattern sequence that matches
  1718.  *    the event list.
  1719.  *
  1720.  * Results:
  1721.  *    The return value is NULL if no pattern matches the
  1722.  *    recent events from bindPtr.  If one or more patterns
  1723.  *    matches, then the longest (or most specific) matching
  1724.  *    pattern is returned.
  1725.  *
  1726.  * Side effects:
  1727.  *    None.
  1728.  *
  1729.  *----------------------------------------------------------------------
  1730.  */
  1731.  
  1732. static PatSeq *
  1733. MatchPatterns(dispPtr, bindPtr, psPtr)
  1734.     TkDisplay *dispPtr;        /* Display from which the event came. */
  1735.     BindingTable *bindPtr;    /* Information about binding table, such
  1736.                  * as ring of recent events. */
  1737.     register PatSeq *psPtr;    /* List of pattern sequences. */
  1738. {
  1739.     register PatSeq *bestPtr = NULL;
  1740.  
  1741.     /*
  1742.      * Iterate over all the pattern sequences.
  1743.      */
  1744.  
  1745.     for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
  1746.     register XEvent *eventPtr;
  1747.     register Pattern *patPtr;
  1748.     Window window;
  1749.     int *detailPtr;
  1750.     int patCount, ringCount, flags, state;
  1751.     int modMask;
  1752.  
  1753.     /*
  1754.      * Iterate over all the patterns in a sequence to be
  1755.      * sure that they all match.
  1756.      */
  1757.  
  1758.     eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1759.     detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
  1760.     window = eventPtr->xany.window;
  1761.     patPtr = psPtr->pats;
  1762.     patCount = psPtr->numPats;
  1763.     ringCount = EVENT_BUFFER_SIZE;
  1764.     while (patCount > 0) {
  1765.         if (ringCount <= 0) {
  1766.         goto nextSequence;
  1767.         }
  1768.         if (eventPtr->xany.type != patPtr->eventType) {
  1769.         /*
  1770.          * Most of the event types are considered superfluous
  1771.          * in that they are ignored if they occur in the middle
  1772.          * of a pattern sequence and have mismatching types.  The
  1773.          * only ones that cannot be ignored are ButtonPress and
  1774.          * ButtonRelease events (if the next event in the pattern
  1775.          * is a KeyPress or KeyRelease) and KeyPress and KeyRelease
  1776.          * events (if the next pattern event is a ButtonPress or
  1777.          * ButtonRelease).  Here are some tricky cases to consider:
  1778.          * 1. Double-Button or Double-Key events.
  1779.          * 2. Double-ButtonRelease or Double-KeyRelease events.
  1780.          * 3. The arrival of various events like Enter and Leave
  1781.          *    and FocusIn and GraphicsExpose between two button
  1782.          *    presses or key presses.
  1783.          * 4. Modifier keys like Shift and Control shouldn't
  1784.          *    generate conflicts with button events.
  1785.          */
  1786.  
  1787.         if ((patPtr->eventType == KeyPress)
  1788.             || (patPtr->eventType == KeyRelease)) {
  1789.             if ((eventPtr->xany.type == ButtonPress)
  1790.                 || (eventPtr->xany.type == ButtonRelease)) {
  1791.             goto nextSequence;
  1792.             }
  1793.         } else if ((patPtr->eventType == ButtonPress)
  1794.             || (patPtr->eventType == ButtonRelease)) {
  1795.             if ((eventPtr->xany.type == KeyPress)
  1796.                 || (eventPtr->xany.type == KeyRelease)) {
  1797.             int i;
  1798.  
  1799.             /*
  1800.              * Ignore key events if they are modifier keys.
  1801.              */
  1802.  
  1803.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1804.                 if (dispPtr->modKeyCodes[i]
  1805.                     == eventPtr->xkey.keycode) {
  1806.                 /*
  1807.                  * This key is a modifier key, so ignore it.
  1808.                  */
  1809.                 goto nextEvent;
  1810.                 }
  1811.             }
  1812.             goto nextSequence;
  1813.             }
  1814.         }
  1815.         goto nextEvent;
  1816.         }
  1817.         if (eventPtr->xany.window != window) {
  1818.         goto nextSequence;
  1819.         }
  1820.  
  1821.         /*
  1822.          * Note: it's important for the keysym check to go before
  1823.          * the modifier check, so we can ignore unwanted modifier
  1824.          * keys before choking on the modifier check.
  1825.          */
  1826.  
  1827.         if ((patPtr->detail != 0)
  1828.             && (patPtr->detail != *detailPtr)) {
  1829.         /*
  1830.          * The detail appears not to match.  However, if the event
  1831.          * is a KeyPress for a modifier key then just ignore the
  1832.          * event.  Otherwise event sequences like "aD" never match
  1833.          * because the shift key goes down between the "a" and the
  1834.          * "D".
  1835.          */
  1836.  
  1837.         if (eventPtr->xany.type == KeyPress) {
  1838.             int i;
  1839.  
  1840.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1841.             if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1842.                 goto nextEvent;
  1843.             }
  1844.             }
  1845.         }
  1846.         goto nextSequence;
  1847.         }
  1848.         flags = flagArray[eventPtr->type];
  1849.         if (flags & KEY_BUTTON_MOTION) {
  1850.         state = eventPtr->xkey.state;
  1851.         } else if (flags & CROSSING) {
  1852.         state = eventPtr->xcrossing.state;
  1853.         } else {
  1854.         state = 0;
  1855.         }
  1856.         if (patPtr->needMods != 0) {
  1857.         modMask = patPtr->needMods;
  1858.         if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
  1859.             modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
  1860.         }
  1861.         if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
  1862.             modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
  1863.         }
  1864.         if ((state & modMask) != modMask) {
  1865.             goto nextSequence;
  1866.         }
  1867.         }
  1868.         if (psPtr->flags & PAT_NEARBY) {
  1869.         register XEvent *firstPtr;
  1870.         int timeDiff;
  1871.  
  1872.         firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1873.         timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;
  1874.         if ((firstPtr->xkey.x_root
  1875.                 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
  1876.             || (firstPtr->xkey.x_root
  1877.                 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
  1878.             || (firstPtr->xkey.y_root
  1879.                 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
  1880.             || (firstPtr->xkey.y_root
  1881.                 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
  1882.             || (timeDiff > NEARBY_MS)) {
  1883.             goto nextSequence;
  1884.         }
  1885.         }
  1886.         patPtr++;
  1887.         patCount--;
  1888.         nextEvent:
  1889.         if (eventPtr == bindPtr->eventRing) {
  1890.         eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
  1891.         detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
  1892.         } else {
  1893.         eventPtr--;
  1894.         detailPtr--;
  1895.         }
  1896.         ringCount--;
  1897.     }
  1898.  
  1899.     /*
  1900.      * This sequence matches.  If we've already got another match,
  1901.      * pick whichever is most specific.  Detail is most important,
  1902.      * then needMods.
  1903.      */
  1904.  
  1905.     if (bestPtr != NULL) {
  1906.         register Pattern *patPtr2;
  1907.         int i;
  1908.  
  1909.         if (psPtr->numPats != bestPtr->numPats) {
  1910.         if (bestPtr->numPats > psPtr->numPats) {
  1911.             goto nextSequence;
  1912.         } else {
  1913.             goto newBest;
  1914.         }
  1915.         }
  1916.         for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
  1917.             i < psPtr->numPats; i++, patPtr++, patPtr2++) {
  1918.         if (patPtr->detail != patPtr2->detail) {
  1919.             if (patPtr->detail == 0) {
  1920.             goto nextSequence;
  1921.             } else {
  1922.             goto newBest;
  1923.             }
  1924.         }
  1925.         if (patPtr->needMods != patPtr2->needMods) {
  1926.             if ((patPtr->needMods & patPtr2->needMods)
  1927.                 == patPtr->needMods) {
  1928.             goto nextSequence;
  1929.             } else if ((patPtr->needMods & patPtr2->needMods)
  1930.                 == patPtr2->needMods) {
  1931.             goto newBest;
  1932.             }
  1933.         }
  1934.         }
  1935.         goto nextSequence;    /* Tie goes to newest pattern. */
  1936.     }
  1937.     newBest:
  1938.     bestPtr = psPtr;
  1939.  
  1940.     nextSequence: continue;
  1941.     }
  1942.     return bestPtr;
  1943. }
  1944.  
  1945. /*
  1946.  *--------------------------------------------------------------
  1947.  *
  1948.  * ExpandPercents --
  1949.  *
  1950.  *    Given a command and an event, produce a new command
  1951.  *    by replacing % constructs in the original command
  1952.  *    with information from the X event.
  1953.  *
  1954.  * Results:
  1955.  *    The new expanded command is appended to the dynamic string
  1956.  *    given by dsPtr.
  1957.  *
  1958.  * Side effects:
  1959.  *    None.
  1960.  *
  1961.  *--------------------------------------------------------------
  1962.  */
  1963.  
  1964. static void
  1965. ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
  1966.     TkWindow *winPtr;        /* Window where event occurred:  needed to
  1967.                  * get input context. */
  1968.     register char *before;    /* Command containing percent
  1969.                  * expressions to be replaced. */
  1970.     register XEvent *eventPtr;    /* X event containing information
  1971.                  * to be used in % replacements. */
  1972.     KeySym keySym;        /* KeySym: only relevant for
  1973.                  * KeyPress and KeyRelease events). */
  1974.     Tcl_DString *dsPtr;        /* Dynamic string in which to append
  1975.                  * new command. */
  1976. {
  1977.     int spaceNeeded, cvtFlags;    /* Used to substitute string as proper Tcl
  1978.                  * list element. */
  1979.     int number, flags, length;
  1980. #define NUM_SIZE 40
  1981.     register char *string;
  1982.     char numStorage[NUM_SIZE+1];
  1983.  
  1984.     if (eventPtr->type < TK_LASTEVENT) {
  1985.     flags = flagArray[eventPtr->type];
  1986.     } else {
  1987.     flags = 0;
  1988.     }
  1989.     while (1) {
  1990.     /*
  1991.      * Find everything up to the next % character and append it
  1992.      * to the result string.
  1993.      */
  1994.  
  1995.     for (string = before; (*string != 0) && (*string != '%'); string++) {
  1996.         /* Empty loop body. */
  1997.     }
  1998.     if (string != before) {
  1999.         Tcl_DStringAppend(dsPtr, before, string-before);
  2000.         before = string;
  2001.     }
  2002.     if (*before == 0) {
  2003.         break;
  2004.     }
  2005.  
  2006.     /*
  2007.      * There's a percent sequence here.  Process it.
  2008.      */
  2009.  
  2010.     number = 0;
  2011.     string = "??";
  2012.     switch (before[1]) {
  2013.         case '#':
  2014.         number = eventPtr->xany.serial;
  2015.         goto doNumber;
  2016.         case 'a':
  2017.         sprintf(numStorage, "0x%x", (int) eventPtr->xconfigure.above);
  2018.         string = numStorage;
  2019.         goto doString;
  2020.         case 'b':
  2021.         number = eventPtr->xbutton.button;
  2022.         goto doNumber;
  2023.         case 'c':
  2024.         if (flags & EXPOSE) {
  2025.             number = eventPtr->xexpose.count;
  2026.         } else if (flags & MAPPING) {
  2027.             number = eventPtr->xmapping.count;
  2028.         }
  2029.         goto doNumber;
  2030.         case 'd':
  2031.         if (flags & (CROSSING|FOCUS)) {
  2032.             if (flags & FOCUS) {
  2033.             number = eventPtr->xfocus.detail;
  2034.             } else {
  2035.             number = eventPtr->xcrossing.detail;
  2036.             }
  2037.             switch (number) {
  2038.             case NotifyAncestor:
  2039.                 string = "NotifyAncestor";
  2040.                 break;
  2041.             case NotifyVirtual:
  2042.                 string = "NotifyVirtual";
  2043.                 break;
  2044.             case NotifyInferior:
  2045.                 string = "NotifyInferior";
  2046.                 break;
  2047.             case NotifyNonlinear:
  2048.                 string = "NotifyNonlinear";
  2049.                 break;
  2050.             case NotifyNonlinearVirtual:
  2051.                 string = "NotifyNonlinearVirtual";
  2052.                 break;
  2053.             case NotifyPointer:
  2054.                 string = "NotifyPointer";
  2055.                 break;
  2056.             case NotifyPointerRoot:
  2057.                 string = "NotifyPointerRoot";
  2058.                 break;
  2059.             case NotifyDetailNone:
  2060.                 string = "NotifyDetailNone";
  2061.                 break;
  2062.             }
  2063.         } else if (flags & CONFIG_REQ) {
  2064.             switch (eventPtr->xconfigurerequest.detail) {
  2065.             case Above:
  2066.                 string = "Above";
  2067.                 break;
  2068.             case Below:
  2069.                 string = "Below";
  2070.                 break;
  2071.             case TopIf:
  2072.                 string = "TopIf";
  2073.                 break;
  2074.             case BottomIf:
  2075.                 string = "BottomIf";
  2076.                 break;
  2077.             case Opposite:
  2078.                 string = "Opposite";
  2079.                 break;
  2080.             }
  2081.         }
  2082.         goto doString;
  2083.         case 'f':
  2084.         number = eventPtr->xcrossing.focus;
  2085.         goto doNumber;
  2086.         case 'h':
  2087.         if (flags & EXPOSE) {
  2088.             number = eventPtr->xexpose.height;
  2089.         } else if (flags & (CONFIG|CONFIG_REQ)) {
  2090.             number = eventPtr->xconfigure.height;
  2091.         } else if (flags & RESIZE_REQ) {
  2092.             number = eventPtr->xresizerequest.height;
  2093.         }
  2094.         goto doNumber;
  2095.         case 'k':
  2096.         number = eventPtr->xkey.keycode;
  2097.         goto doNumber;
  2098.         case 'm':
  2099.         if (flags & CROSSING) {
  2100.             number = eventPtr->xcrossing.mode;
  2101.         } else if (flags & FOCUS) {
  2102.             number = eventPtr->xfocus.mode;
  2103.         }
  2104.         switch (number) {
  2105.             case NotifyNormal:
  2106.             string = "NotifyNormal";
  2107.             break;
  2108.             case NotifyGrab:
  2109.             string = "NotifyGrab";
  2110.             break;
  2111.             case NotifyUngrab:
  2112.             string = "NotifyUngrab";
  2113.             break;
  2114.             case NotifyWhileGrabbed:
  2115.             string = "NotifyWhileGrabbed";
  2116.             break;
  2117.         }
  2118.         goto doString;
  2119.         case 'o':
  2120.         if (flags & CREATE) {
  2121.             number = eventPtr->xcreatewindow.override_redirect;
  2122.         } else if (flags & MAP) {
  2123.             number = eventPtr->xmap.override_redirect;
  2124.         } else if (flags & REPARENT) {
  2125.             number = eventPtr->xreparent.override_redirect;
  2126.         } else if (flags & CONFIG) {
  2127.             number = eventPtr->xconfigure.override_redirect;
  2128.         }
  2129.         goto doNumber;
  2130.         case 'p':
  2131.         switch (eventPtr->xcirculate.place) {
  2132.             case PlaceOnTop:
  2133.             string = "PlaceOnTop";
  2134.             break;
  2135.             case PlaceOnBottom:
  2136.             string = "PlaceOnBottom";
  2137.             break;
  2138.         }
  2139.         goto doString;
  2140.         case 's':
  2141.         if (flags & KEY_BUTTON_MOTION) {
  2142.             number = eventPtr->xkey.state;
  2143.         } else if (flags & CROSSING) {
  2144.             number = eventPtr->xcrossing.state;
  2145.         } else if (flags & VISIBILITY) {
  2146.             switch (eventPtr->xvisibility.state) {
  2147.             case VisibilityUnobscured:
  2148.                 string = "VisibilityUnobscured";
  2149.                 break;
  2150.             case VisibilityPartiallyObscured:
  2151.                 string = "VisibilityPartiallyObscured";
  2152.                 break;
  2153.             case VisibilityFullyObscured:
  2154.                 string = "VisibilityFullyObscured";
  2155.                 break;
  2156.             }
  2157.             goto doString;
  2158.         }
  2159.         goto doNumber;
  2160.         case 't':
  2161.         if (flags & PROP) {
  2162.             number = (int) eventPtr->xproperty.time;
  2163.         } else if (flags & SEL_CLEAR) {
  2164.             number = (int) eventPtr->xselectionclear.time;
  2165.         } else if (flags & KEY_BUTTON_MOTION) {
  2166.             number = (int) eventPtr->xkey.time;
  2167.         } else if (flags & SEL_REQ) {
  2168.             number = (int) eventPtr->xselectionrequest.time;
  2169.         } else if (flags & SEL_NOTIFY) {
  2170.             number = (int) eventPtr->xselection.time;
  2171.         } else if (flags & CROSSING) {
  2172.             number = (int) eventPtr->xcrossing.time;
  2173.         }
  2174.         goto doNumber;
  2175.         case 'v':
  2176.         number = eventPtr->xconfigurerequest.value_mask;
  2177.         goto doNumber;
  2178.         case 'w':
  2179.         if (flags & EXPOSE) {
  2180.             number = eventPtr->xexpose.width;
  2181.         } else if (flags & (CONFIG|CONFIG_REQ)) {
  2182.             number = eventPtr->xconfigure.width;
  2183.         } else if (flags & RESIZE_REQ) {
  2184.             number = eventPtr->xresizerequest.width;
  2185.         }
  2186.         goto doNumber;
  2187.         case 'x':
  2188.         if (flags & KEY_BUTTON_MOTION) {
  2189.             number = eventPtr->xkey.x;
  2190.         } else if (flags & EXPOSE) {
  2191.             number = eventPtr->xexpose.x;
  2192.         } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
  2193.             number = eventPtr->xcreatewindow.x;
  2194.         } else if (flags & REPARENT) {
  2195.             number = eventPtr->xreparent.x;
  2196.         } else if (flags & CROSSING) {
  2197.             number = eventPtr->xcrossing.x;
  2198.         }
  2199.         goto doNumber;
  2200.         case 'y':
  2201.         if (flags & KEY_BUTTON_MOTION) {
  2202.             number = eventPtr->xkey.y;
  2203.         } else if (flags & EXPOSE) {
  2204.             number = eventPtr->xexpose.y;
  2205.         } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
  2206.             number = eventPtr->xcreatewindow.y;
  2207.         } else if (flags & REPARENT) {
  2208.             number = eventPtr->xreparent.y;
  2209.         } else if (flags & CROSSING) {
  2210.             number = eventPtr->xcrossing.y;
  2211.  
  2212.         }
  2213.         goto doNumber;
  2214.         case 'A':
  2215.         if ((eventPtr->type == KeyPress)
  2216.             || (eventPtr->type == KeyRelease)) {
  2217.             int numChars;
  2218.  
  2219.             /*
  2220.              * If we're using input methods and this is a keypress
  2221.              * event, invoke XmbLookupString.  Otherwise just use
  2222.              * the older XLookupString.
  2223.              */
  2224.  
  2225. #ifdef TK_USE_INPUT_METHODS
  2226.             Status status;
  2227.             if ((winPtr->inputContext != NULL)
  2228.                 && (eventPtr->type == KeyPress)) {
  2229.                         numChars = XmbLookupString(winPtr->inputContext,
  2230.                                 &eventPtr->xkey, numStorage, NUM_SIZE,
  2231.                                 (KeySym *) NULL, &status);
  2232.             if ((status != XLookupChars)
  2233.                 && (status != XLookupBoth)) {
  2234.                 numChars = 0;
  2235.             }
  2236.                     } else {
  2237.                         numChars = XLookupString(&eventPtr->xkey, numStorage,
  2238.                                 NUM_SIZE, (KeySym *) NULL,
  2239.                                 (XComposeStatus *) NULL);
  2240.             }
  2241. #else /* TK_USE_INPUT_METHODS */
  2242.             numChars = XLookupString(&eventPtr->xkey, numStorage,
  2243.                 NUM_SIZE, (KeySym *) NULL,
  2244.                 (XComposeStatus *) NULL);
  2245. #endif /* TK_USE_INPUT_METHODS */
  2246.             numStorage[numChars] = '\0';
  2247.             string = numStorage;
  2248.         }
  2249.         goto doString;
  2250.         case 'B':
  2251.         number = eventPtr->xcreatewindow.border_width;
  2252.         goto doNumber;
  2253.         case 'E':
  2254.         number = (int) eventPtr->xany.send_event;
  2255.         goto doNumber;
  2256.         case 'K':
  2257.         if ((eventPtr->type == KeyPress)
  2258.             || (eventPtr->type == KeyRelease)) {
  2259.             char *name;
  2260.  
  2261.             name = TkKeysymToString(keySym);
  2262.             if (name != NULL) {
  2263.             string = name;
  2264.             }
  2265.         }
  2266.         goto doString;
  2267.         case 'N':
  2268.         number = (int) keySym;
  2269.         goto doNumber;
  2270.         case 'R':
  2271.         number = (int) eventPtr->xkey.root;
  2272.         goto doNumber;
  2273.         case 'S':
  2274.         sprintf(numStorage, "0x%x", (int) eventPtr->xkey.subwindow);
  2275.         string = numStorage;
  2276.         goto doString;
  2277.         case 'T':
  2278.         number = eventPtr->type;
  2279.         goto doNumber;
  2280.         case 'W': {
  2281.         Tk_Window tkwin;
  2282.  
  2283.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2284.             eventPtr->xany.window);
  2285.         if (tkwin != NULL) {
  2286.             string = Tk_PathName(tkwin);
  2287.         } else {
  2288.             string = "??";
  2289.         }
  2290.         goto doString;
  2291.         }
  2292.         case 'X': {
  2293.         Tk_Window tkwin;
  2294.         int x, y;
  2295.         int width, height;
  2296.  
  2297.         number = eventPtr->xkey.x_root;
  2298.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2299.             eventPtr->xany.window);
  2300.         if (tkwin != NULL) {
  2301.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2302.             number -= x;
  2303.         }
  2304.         goto doNumber;
  2305.         }
  2306.         case 'Y': {
  2307.         Tk_Window tkwin;
  2308.         int x, y;
  2309.         int width, height;
  2310.  
  2311.         number = eventPtr->xkey.y_root;
  2312.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2313.             eventPtr->xany.window);
  2314.         if (tkwin != NULL) {
  2315.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2316.             number -= y;
  2317.         }
  2318.         goto doNumber;
  2319.         }
  2320.         default:
  2321.         numStorage[0] = before[1];
  2322.         numStorage[1] = '\0';
  2323.         string = numStorage;
  2324.         goto doString;
  2325.     }
  2326.  
  2327.     doNumber:
  2328.     sprintf(numStorage, "%d", number);
  2329.     string = numStorage;
  2330.  
  2331.     doString:
  2332.     spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
  2333.     length = Tcl_DStringLength(dsPtr);
  2334.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2335.     spaceNeeded = Tcl_ConvertElement(string,
  2336.         Tcl_DStringValue(dsPtr) + length,
  2337.         cvtFlags | TCL_DONT_USE_BRACES);
  2338.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2339.     before += 2;
  2340.     }
  2341. }
  2342.  
  2343. /*
  2344.  *----------------------------------------------------------------------
  2345.  *
  2346.  * TkCopyAndGlobalEval --
  2347.  *
  2348.  *    This procedure makes a copy of a script then calls Tcl_GlobalEval
  2349.  *    to evaluate it.  It's used in situations where the execution of
  2350.  *    a command may cause the original command string to be reallocated.
  2351.  *
  2352.  * Results:
  2353.  *    Returns the result of evaluating script, including both a standard
  2354.  *    Tcl completion code and a string in interp->result.
  2355.  *
  2356.  * Side effects:
  2357.  *    None.
  2358.  *
  2359.  *----------------------------------------------------------------------
  2360.  */
  2361.  
  2362. int
  2363. TkCopyAndGlobalEval(interp, script)
  2364.     Tcl_Interp *interp;            /* Interpreter in which to evaluate
  2365.                      * script. */
  2366.     char *script;            /* Script to evaluate. */
  2367. {
  2368.     Tcl_DString buffer;
  2369.     int code;
  2370.  
  2371.     Tcl_DStringInit(&buffer);
  2372.     Tcl_DStringAppend(&buffer, script, -1);
  2373.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
  2374.     Tcl_DStringFree(&buffer);
  2375.     return code;
  2376. }
  2377.  
  2378. /*
  2379.  *--------------------------------------------------------------
  2380.  *
  2381.  * InitKeymapInfo --
  2382.  *
  2383.  *    This procedure is invoked to scan keymap information
  2384.  *    to recompute stuff that's important for binding, such
  2385.  *    as the modifier key (if any) that corresponds to "mode
  2386.  *    switch".
  2387.  *
  2388.  * Results:
  2389.  *    None.
  2390.  *
  2391.  * Side effects:
  2392.  *    Keymap-related information in dispPtr is updated.
  2393.  *
  2394.  *--------------------------------------------------------------
  2395.  */
  2396.  
  2397. static void
  2398. InitKeymapInfo(dispPtr)
  2399.     TkDisplay *dispPtr;        /* Display for which to recompute keymap
  2400.                  * information. */
  2401. {
  2402.     XModifierKeymap *modMapPtr;
  2403.     register KeyCode *codePtr;
  2404.     KeySym keysym;
  2405.     int count, i, j, max, arraySize;
  2406. #define KEYCODE_ARRAY_SIZE 20
  2407.  
  2408.     dispPtr->bindInfoStale = 0;
  2409.     modMapPtr = XGetModifierMapping(dispPtr->display);
  2410.  
  2411.     /*
  2412.      * Check the keycodes associated with the Lock modifier.  If
  2413.      * any of them is associated with the XK_Shift_Lock modifier,
  2414.      * then Lock has to be interpreted as Shift Lock, not Caps Lock.
  2415.      */
  2416.  
  2417.     dispPtr->lockUsage = LU_IGNORE;
  2418.     codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;
  2419.     for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {
  2420.     if (*codePtr == 0) {
  2421.         continue;
  2422.     }
  2423.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  2424.     if (keysym == XK_Shift_Lock) {
  2425.         dispPtr->lockUsage = LU_SHIFT;
  2426.         break;
  2427.     }
  2428.     if (keysym == XK_Caps_Lock) {
  2429.         dispPtr->lockUsage = LU_CAPS;
  2430.         break;
  2431.     }
  2432.     }
  2433.  
  2434.     /*
  2435.      * Look through the keycodes associated with modifiers to see if
  2436.      * the the "mode switch", "meta", or "alt" keysyms are associated
  2437.      * with any modifiers.  If so, remember their modifier mask bits.
  2438.      */
  2439.  
  2440.     dispPtr->modeModMask = 0;
  2441.     dispPtr->metaModMask = 0;
  2442.     dispPtr->altModMask = 0;
  2443.     codePtr = modMapPtr->modifiermap;
  2444.     max = 8*modMapPtr->max_keypermod;
  2445.     for (i = 0; i < max; i++, codePtr++) {
  2446.     if (*codePtr == 0) {
  2447.         continue;
  2448.     }
  2449.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  2450.     if (keysym == XK_Mode_switch) {
  2451.         dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2452.     }
  2453.     if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
  2454.         dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2455.     }
  2456.     if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
  2457.         dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2458.     }
  2459.     }
  2460.  
  2461.     /*
  2462.      * Create an array of the keycodes for all modifier keys.
  2463.      */
  2464.  
  2465.     if (dispPtr->modKeyCodes != NULL) {
  2466.     ckfree((char *) dispPtr->modKeyCodes);
  2467.     }
  2468.     dispPtr->numModKeyCodes = 0;
  2469.     arraySize = KEYCODE_ARRAY_SIZE;
  2470.     dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)
  2471.         (KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));
  2472.     for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {
  2473.     if (*codePtr == 0) {
  2474.         continue;
  2475.     }
  2476.  
  2477.     /*
  2478.      * Make sure that the keycode isn't already in the array.
  2479.      */
  2480.  
  2481.     for (j = 0; j < dispPtr->numModKeyCodes; j++) {
  2482.         if (dispPtr->modKeyCodes[j] == *codePtr) {
  2483.         goto nextModCode;
  2484.         }
  2485.     }
  2486.     if (dispPtr->numModKeyCodes >= arraySize) {
  2487.         KeyCode *new;
  2488.  
  2489.         /*
  2490.          * Ran out of space in the array;  grow it.
  2491.          */
  2492.  
  2493.         arraySize *= 2;
  2494.         new = (KeyCode *) ckalloc((unsigned)
  2495.             (arraySize * sizeof(KeyCode)));
  2496.         memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,
  2497.             (dispPtr->numModKeyCodes * sizeof(KeyCode)));
  2498.         ckfree((char *) dispPtr->modKeyCodes);
  2499.         dispPtr->modKeyCodes = new;
  2500.     }
  2501.     dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;
  2502.     dispPtr->numModKeyCodes++;
  2503.     nextModCode: continue;
  2504.     }
  2505.     XFreeModifiermap(modMapPtr);
  2506. }
  2507.  
  2508. /*
  2509.  *----------------------------------------------------------------------
  2510.  *
  2511.  * TkStringToKeysym --
  2512.  *
  2513.  *    This procedure finds the keysym associated with a given keysym
  2514.  *    name.
  2515.  *
  2516.  * Results:
  2517.  *    The return value is the keysym that corresponds to name, or
  2518.  *    NoSymbol if there is no such keysym.
  2519.  *
  2520.  * Side effects:
  2521.  *    None.
  2522.  *
  2523.  *----------------------------------------------------------------------
  2524.  */
  2525.  
  2526. KeySym
  2527. TkStringToKeysym(name)
  2528.     char *name;            /* Name of a keysym. */
  2529. {
  2530. #ifdef REDO_KEYSYM_LOOKUP
  2531.     Tcl_HashEntry *hPtr;
  2532.  
  2533.     hPtr = Tcl_FindHashEntry(&keySymTable, name);
  2534.     if (hPtr != NULL) {
  2535.     return (KeySym) Tcl_GetHashValue(hPtr);
  2536.     }
  2537. #endif /* REDO_KEYSYM_LOOKUP */
  2538.     return XStringToKeysym(name);
  2539. }
  2540.  
  2541. /*
  2542.  *----------------------------------------------------------------------
  2543.  *
  2544.  * TkKeysymToString --
  2545.  *
  2546.  *    This procedure finds the keysym name associated with a given
  2547.  *    keysym.
  2548.  *
  2549.  * Results:
  2550.  *    The return value is a pointer to a static string containing
  2551.  *    the name of the given keysym, or NULL if there is no known name.
  2552.  *
  2553.  * Side effects:
  2554.  *    None.
  2555.  *
  2556.  *----------------------------------------------------------------------
  2557.  */
  2558.  
  2559. char *
  2560. TkKeysymToString(keysym)
  2561.     KeySym keysym;
  2562. {
  2563. #ifdef REDO_KEYSYM_LOOKUP
  2564.     Tcl_HashEntry *hPtr;
  2565.  
  2566.     hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym);
  2567.     if (hPtr != NULL) {
  2568.     return (char *) Tcl_GetHashValue(hPtr);
  2569.     }
  2570. #endif /* REDO_KEYSYM_LOOKUP */
  2571.     return XKeysymToString(keysym);
  2572. }
  2573.