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