/[dtapublic]/projs/dtats/trunk/shared_source/c_tk_base_7_5_w_mods/tkbind.c
ViewVC logotype

Annotation of /projs/dtats/trunk/shared_source/c_tk_base_7_5_w_mods/tkbind.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 98 - (hide annotations) (download)
Sun Dec 18 00:57:31 2016 UTC (7 years, 7 months ago) by dashley
File MIME type: text/plain
File size: 130553 byte(s)
Reorganization.
1 dashley 71 /* $Header$ */
2    
3     /*
4     * tkBind.c --
5     *
6     * This file provides procedures that associate Tcl commands
7     * with X events or sequences of X events.
8     *
9     * Copyright (c) 1989-1994 The Regents of the University of California.
10     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11     * Copyright (c) 1998 by Scriptics Corporation.
12     *
13     * See the file "license.terms" for information on usage and redistribution
14     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15     *
16     * RCS: @(#) $Id: tkbind.c,v 1.1.1.1 2001/06/13 04:54:18 dtashley Exp $
17     */
18    
19     #include "tkPort.h"
20     #include "tkInt.h"
21    
22     #ifdef __WIN32__
23     #include "tkWinInt.h"
24     #endif
25    
26     /*
27     * File structure:
28     *
29     * Structure definitions and static variables.
30     *
31     * Init/Free this package.
32     *
33     * Tcl "bind" command (actually located in tkCmds.c).
34     * "bind" command implementation.
35     * "bind" implementation helpers.
36     *
37     * Tcl "event" command.
38     * "event" command implementation.
39     * "event" implementation helpers.
40     *
41     * Package-specific common helpers.
42     *
43     * Non-package-specific helpers.
44     */
45    
46    
47     /*
48     * The following union is used to hold the detail information from an
49     * XEvent (including Tk's XVirtualEvent extension).
50     */
51     typedef union {
52     KeySym keySym; /* KeySym that corresponds to xkey.keycode. */
53     int button; /* Button that was pressed (xbutton.button). */
54     Tk_Uid name; /* Tk_Uid of virtual event. */
55     ClientData clientData; /* Used when type of Detail is unknown, and to
56     * ensure that all bytes of Detail are initialized
57     * when this structure is used in a hash key. */
58     } Detail;
59    
60     /*
61     * The structure below represents a binding table. A binding table
62     * represents a domain in which event bindings may occur. It includes
63     * a space of objects relative to which events occur (usually windows,
64     * but not always), a history of recent events in the domain, and
65     * a set of mappings that associate particular Tcl commands with sequences
66     * of events in the domain. Multiple binding tables may exist at once,
67     * either because there are multiple applications open, or because there
68     * are multiple domains within an application with separate event
69     * bindings for each (for example, each canvas widget has a separate
70     * binding table for associating events with the items in the canvas).
71     *
72     * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
73     * below 30. To see this, consider a triple mouse button click while
74     * the Shift key is down (and auto-repeating). There may be as many
75     * as 3 auto-repeat events after each mouse button press or release
76     * (see the first large comment block within Tk_BindEvent for more on
77     * this), for a total of 20 events to cover the three button presses
78     * and two intervening releases. If you reduce EVENT_BUFFER_SIZE too
79     * much, shift multi-clicks will be lost.
80     *
81     */
82    
83     #define EVENT_BUFFER_SIZE 30
84     typedef struct BindingTable {
85     XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
86     * (higher indices are for more recent
87     * events). */
88     Detail detailRing[EVENT_BUFFER_SIZE];/* "Detail" information (keySym,
89     * button, Tk_Uid, or 0) for each
90     * entry in eventRing. */
91     int curEvent; /* Index in eventRing of most recent
92     * event. Newer events have higher
93     * indices. */
94     Tcl_HashTable patternTable; /* Used to map from an event to a
95     * list of patterns that may match that
96     * event. Keys are PatternTableKey
97     * structs, values are (PatSeq *). */
98     Tcl_HashTable objectTable; /* Used to map from an object to a
99     * list of patterns associated with
100     * that object. Keys are ClientData,
101     * values are (PatSeq *). */
102     Tcl_Interp *interp; /* Interpreter in which commands are
103     * executed. */
104     } BindingTable;
105    
106     /*
107     * The following structure represents virtual event table. A virtual event
108     * table provides a way to map from platform-specific physical events such
109     * as button clicks or key presses to virtual events such as <<Paste>>,
110     * <<Close>>, or <<ScrollWindow>>.
111     *
112     * A virtual event is usually never part of the event stream, but instead is
113     * synthesized inline by matching low-level events. However, a virtual
114     * event may be generated by platform-specific code or by Tcl scripts. In
115     * that case, no lookup of the virtual event will need to be done using
116     * this table, because the virtual event is actually in the event stream.
117     */
118    
119     typedef struct VirtualEventTable {
120     Tcl_HashTable patternTable; /* Used to map from a physical event to
121     * a list of patterns that may match that
122     * event. Keys are PatternTableKey
123     * structs, values are (PatSeq *). */
124     Tcl_HashTable nameTable; /* Used to map a virtual event name to
125     * the array of physical events that can
126     * trigger it. Keys are the Tk_Uid names
127     * of the virtual events, values are
128     * PhysicalsOwned structs. */
129     } VirtualEventTable;
130    
131     /*
132     * The following structure is used as a key in a patternTable for both
133     * binding tables and a virtual event tables.
134     *
135     * In a binding table, the object field corresponds to the binding tag
136     * for the widget whose bindings are being accessed.
137     *
138     * In a virtual event table, the object field is always NULL. Virtual
139     * events are a global definiton and are not tied to a particular
140     * binding tag.
141     *
142     * The same key is used for both types of pattern tables so that the
143     * helper functions that traverse and match patterns will work for both
144     * binding tables and virtual event tables.
145     */
146     typedef struct PatternTableKey {
147     ClientData object; /* For binding table, identifies the binding
148     * tag of the object (or class of objects)
149     * relative to which the event occurred.
150     * For virtual event table, always NULL. */
151     int type; /* Type of event (from X). */
152     Detail detail; /* Additional information, such as keysym,
153     * button, Tk_Uid, or 0 if nothing
154     * additional. */
155     } PatternTableKey;
156    
157     /*
158     * The following structure defines a pattern, which is matched against X
159     * events as part of the process of converting X events into Tcl commands.
160     */
161    
162     typedef struct Pattern {
163     int eventType; /* Type of X event, e.g. ButtonPress. */
164     int needMods; /* Mask of modifiers that must be
165     * present (0 means no modifiers are
166     * required). */
167     Detail detail; /* Additional information that must
168     * match event. Normally this is 0,
169     * meaning no additional information
170     * must match. For KeyPress and
171     * KeyRelease events, a keySym may
172     * be specified to select a
173     * particular keystroke (0 means any
174     * keystrokes). For button events,
175     * specifies a particular button (0
176     * means any buttons are OK). For virtual
177     * events, specifies the Tk_Uid of the
178     * virtual event name (never 0). */
179     } Pattern;
180    
181     /*
182     * The following structure defines a pattern sequence, which consists of one
183     * or more patterns. In order to trigger, a pattern sequence must match
184     * the most recent X events (first pattern to most recent event, next
185     * pattern to next event, and so on). It is used as the hash value in a
186     * patternTable for both binding tables and virtual event tables.
187     *
188     * In a binding table, it is the sequence of physical events that make up
189     * a binding for an object.
190     *
191     * In a virtual event table, it is the sequence of physical events that
192     * define a virtual event.
193     *
194     * The same structure is used for both types of pattern tables so that the
195     * helper functions that traverse and match patterns will work for both
196     * binding tables and virtual event tables.
197     */
198    
199     typedef struct PatSeq {
200     int numPats; /* Number of patterns in sequence (usually
201     * 1). */
202     TkBindEvalProc *eventProc; /* The procedure that will be invoked on
203     * the clientData when this pattern sequence
204     * matches. */
205     TkBindFreeProc *freeProc; /* The procedure that will be invoked to
206     * release the clientData when this pattern
207     * sequence is freed. */
208     ClientData clientData; /* Arbitray data passed to eventProc and
209     * freeProc when sequence matches. */
210     int flags; /* Miscellaneous flag values; see below for
211     * definitions. */
212     int refCount; /* Number of times that this binding is in
213     * the midst of executing. If greater than 1,
214     * then a recursive invocation is happening.
215     * Only when this is zero can the binding
216     * actually be freed. */
217     struct PatSeq *nextSeqPtr; /* Next in list of all pattern sequences
218     * that have the same initial pattern. NULL
219     * means end of list. */
220     Tcl_HashEntry *hPtr; /* Pointer to hash table entry for the
221     * initial pattern. This is the head of the
222     * list of which nextSeqPtr forms a part. */
223     struct VirtualOwners *voPtr;/* In a binding table, always NULL. In a
224     * virtual event table, identifies the array
225     * of virtual events that can be triggered by
226     * this event. */
227     struct PatSeq *nextObjPtr; /* In a binding table, next in list of all
228     * pattern sequences for the same object (NULL
229     * for end of list). Needed to implement
230     * Tk_DeleteAllBindings. In a virtual event
231     * table, always NULL. */
232     Pattern pats[1]; /* Array of "numPats" patterns. Only one
233     * element is declared here but in actuality
234     * enough space will be allocated for "numPats"
235     * patterns. To match, pats[0] must match
236     * event n, pats[1] must match event n-1, etc.
237     */
238     } PatSeq;
239    
240     /*
241     * Flag values for PatSeq structures:
242     *
243     * PAT_NEARBY 1 means that all of the events matching
244     * this sequence must occur with nearby X
245     * and Y mouse coordinates and close in time.
246     * This is typically used to restrict multiple
247     * button presses.
248     * MARKED_DELETED 1 means that this binding has been marked as deleted
249     * and removed from the binding table, but its memory
250     * could not be released because it was already queued for
251     * execution. When the binding is actually about to be
252     * executed, this flag will be checked and the binding
253     * skipped if set.
254     */
255    
256     #define PAT_NEARBY 0x1
257     #define MARKED_DELETED 0x2
258    
259     /*
260     * Constants that define how close together two events must be
261     * in milliseconds or pixels to meet the PAT_NEARBY constraint:
262     */
263    
264     #define NEARBY_PIXELS 5
265     #define NEARBY_MS 500
266    
267    
268     /*
269     * The following structure keeps track of all the virtual events that are
270     * associated with a particular physical event. It is pointed to by the
271     * voPtr field in a PatSeq in the patternTable of a virtual event table.
272     */
273    
274     typedef struct VirtualOwners {
275     int numOwners; /* Number of virtual events to trigger. */
276     Tcl_HashEntry *owners[1]; /* Array of pointers to entries in
277     * nameTable. Enough space will
278     * actually be allocated for numOwners
279     * hash entries. */
280     } VirtualOwners;
281    
282     /*
283     * The following structure is used in the nameTable of a virtual event
284     * table to associate a virtual event with all the physical events that can
285     * trigger it.
286     */
287     typedef struct PhysicalsOwned {
288     int numOwned; /* Number of physical events owned. */
289     PatSeq *patSeqs[1]; /* Array of pointers to physical event
290     * patterns. Enough space will actually
291     * be allocated to hold numOwned. */
292     } PhysicalsOwned;
293    
294     /*
295     * One of the following structures exists for each interpreter. This
296     * structure keeps track of the current display and screen in the
297     * interpreter, so that a script can be invoked whenever the display/screen
298     * changes (the script does things like point tkPriv at a display-specific
299     * structure).
300     */
301    
302     typedef struct {
303     TkDisplay *curDispPtr; /* Display for last binding command invoked
304     * in this application. */
305     int curScreenIndex; /* Index of screen for last binding command. */
306     int bindingDepth; /* Number of active instances of Tk_BindEvent
307     * in this application. */
308     } ScreenInfo;
309    
310     /*
311     * The following structure is used to keep track of all the C bindings that
312     * are awaiting invocation and whether the window they refer to has been
313     * destroyed. If the window is destroyed, then all pending callbacks for
314     * that window will be cancelled. The Tcl bindings will still all be
315     * invoked, however.
316     */
317    
318     typedef struct PendingBinding {
319     struct PendingBinding *nextPtr;
320     /* Next in chain of pending bindings, in
321     * case a recursive binding evaluation is in
322     * progress. */
323     Tk_Window tkwin; /* The window that the following bindings
324     * depend upon. */
325     int deleted; /* Set to non-zero by window cleanup code
326     * if tkwin is deleted. */
327     PatSeq *matchArray[5]; /* Array of pending C bindings. The actual
328     * size of this depends on how many C bindings
329     * matched the event passed to Tk_BindEvent.
330     * THIS FIELD MUST BE THE LAST IN THE
331     * STRUCTURE. */
332     } PendingBinding;
333    
334     /*
335     * The following structure keeps track of all the information local to
336     * the binding package on a per interpreter basis.
337     */
338    
339     typedef struct BindInfo {
340     VirtualEventTable virtualEventTable;
341     /* The virtual events that exist in this
342     * interpreter. */
343     ScreenInfo screenInfo; /* Keeps track of the current display and
344     * screen, so it can be restored after
345     * a binding has executed. */
346     PendingBinding *pendingList;/* The list of pending C bindings, kept in
347     * case a C or Tcl binding causes the target
348     * window to be deleted. */
349     int deleted; /* 1 the application has been deleted but
350     * the structure has been preserved. */
351     } BindInfo;
352    
353     /*
354     * In X11R4 and earlier versions, XStringToKeysym is ridiculously
355     * slow. The data structure and hash table below, along with the
356     * code that uses them, implement a fast mapping from strings to
357     * keysyms. In X11R5 and later releases XStringToKeysym is plenty
358     * fast so this stuff isn't needed. The #define REDO_KEYSYM_LOOKUP
359     * is normally undefined, so that XStringToKeysym gets used. It
360     * can be set in the Makefile to enable the use of the hash table
361     * below.
362     */
363    
364     #ifdef REDO_KEYSYM_LOOKUP
365     typedef struct {
366     char *name; /* Name of keysym. */
367     KeySym value; /* Numeric identifier for keysym. */
368     } KeySymInfo;
369     static KeySymInfo keyArray[] = {
370     #ifndef lint
371     #include "ks_names.h"
372     #endif
373     {(char *) NULL, 0}
374     };
375     static Tcl_HashTable keySymTable; /* keyArray hashed by keysym value. */
376     static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */
377     #endif /* REDO_KEYSYM_LOOKUP */
378    
379     /*
380     * Set to non-zero when the package-wide static variables have been
381     * initialized.
382     */
383    
384     static int initialized = 0;
385     TCL_DECLARE_MUTEX(bindMutex)
386    
387     /*
388     * A hash table is kept to map from the string names of event
389     * modifiers to information about those modifiers. The structure
390     * for storing this information, and the hash table built at
391     * initialization time, are defined below.
392     */
393    
394     typedef struct {
395     char *name; /* Name of modifier. */
396     int mask; /* Button/modifier mask value, * such as Button1Mask. */
397     int flags; /* Various flags; see below for
398     * definitions. */
399     } ModInfo;
400    
401     /*
402     * Flags for ModInfo structures:
403     *
404     * DOUBLE - Non-zero means duplicate this event,
405     * e.g. for double-clicks.
406     * TRIPLE - Non-zero means triplicate this event,
407     * e.g. for triple-clicks.
408     * QUADRUPLE - Non-zero means quadruple this event,
409     * e.g. for 4-fold-clicks.
410     * MULT_CLICKS - Combination of all of above.
411     */
412    
413     #define DOUBLE 1
414     #define TRIPLE 2
415     #define QUADRUPLE 4
416     #define MULT_CLICKS 7
417    
418     static ModInfo modArray[] = {
419     {"Control", ControlMask, 0},
420     {"Shift", ShiftMask, 0},
421     {"Lock", LockMask, 0},
422     {"Meta", META_MASK, 0},
423     {"M", META_MASK, 0},
424     {"Alt", ALT_MASK, 0},
425     {"B1", Button1Mask, 0},
426     {"Button1", Button1Mask, 0},
427     {"B2", Button2Mask, 0},
428     {"Button2", Button2Mask, 0},
429     {"B3", Button3Mask, 0},
430     {"Button3", Button3Mask, 0},
431     {"B4", Button4Mask, 0},
432     {"Button4", Button4Mask, 0},
433     {"B5", Button5Mask, 0},
434     {"Button5", Button5Mask, 0},
435     {"Mod1", Mod1Mask, 0},
436     {"M1", Mod1Mask, 0},
437     {"Command", Mod1Mask, 0},
438     {"Mod2", Mod2Mask, 0},
439     {"M2", Mod2Mask, 0},
440     {"Option", Mod2Mask, 0},
441     {"Mod3", Mod3Mask, 0},
442     {"M3", Mod3Mask, 0},
443     {"Mod4", Mod4Mask, 0},
444     {"M4", Mod4Mask, 0},
445     {"Mod5", Mod5Mask, 0},
446     {"M5", Mod5Mask, 0},
447     {"Double", 0, DOUBLE},
448     {"Triple", 0, TRIPLE},
449     {"Quadruple", 0, QUADRUPLE},
450     {"Any", 0, 0}, /* Ignored: historical relic. */
451     {NULL, 0, 0}
452     };
453     static Tcl_HashTable modTable;
454    
455     /*
456     * This module also keeps a hash table mapping from event names
457     * to information about those events. The structure, an array
458     * to use to initialize the hash table, and the hash table are
459     * all defined below.
460     */
461    
462     typedef struct {
463     char *name; /* Name of event. */
464     int type; /* Event type for X, such as
465     * ButtonPress. */
466     int eventMask; /* Mask bits (for XSelectInput)
467     * for this event type. */
468     } EventInfo;
469    
470     /*
471     * Note: some of the masks below are an OR-ed combination of
472     * several masks. This is necessary because X doesn't report
473     * up events unless you also ask for down events. Also, X
474     * doesn't report button state in motion events unless you've
475     * asked about button events.
476     */
477    
478     static EventInfo eventArray[] = {
479     {"Key", KeyPress, KeyPressMask},
480     {"KeyPress", KeyPress, KeyPressMask},
481     {"KeyRelease", KeyRelease, KeyPressMask|KeyReleaseMask},
482     {"Button", ButtonPress, ButtonPressMask},
483     {"ButtonPress", ButtonPress, ButtonPressMask},
484     {"ButtonRelease", ButtonRelease,
485     ButtonPressMask|ButtonReleaseMask},
486     {"Motion", MotionNotify,
487     ButtonPressMask|PointerMotionMask},
488     {"Enter", EnterNotify, EnterWindowMask},
489     {"Leave", LeaveNotify, LeaveWindowMask},
490     {"FocusIn", FocusIn, FocusChangeMask},
491     {"FocusOut", FocusOut, FocusChangeMask},
492     {"Expose", Expose, ExposureMask},
493     {"Visibility", VisibilityNotify, VisibilityChangeMask},
494     {"Destroy", DestroyNotify, StructureNotifyMask},
495     {"Unmap", UnmapNotify, StructureNotifyMask},
496     {"Map", MapNotify, StructureNotifyMask},
497     {"Reparent", ReparentNotify, StructureNotifyMask},
498     {"Configure", ConfigureNotify, StructureNotifyMask},
499     {"Gravity", GravityNotify, StructureNotifyMask},
500     {"Circulate", CirculateNotify, StructureNotifyMask},
501     {"Property", PropertyNotify, PropertyChangeMask},
502     {"Colormap", ColormapNotify, ColormapChangeMask},
503     {"Activate", ActivateNotify, ActivateMask},
504     {"Deactivate", DeactivateNotify, ActivateMask},
505     {"MouseWheel", MouseWheelEvent, MouseWheelMask},
506     {(char *) NULL, 0, 0}
507     };
508     static Tcl_HashTable eventTable;
509    
510     /*
511     * The defines and table below are used to classify events into
512     * various groups. The reason for this is that logically identical
513     * fields (e.g. "state") appear at different places in different
514     * types of events. The classification masks can be used to figure
515     * out quickly where to extract information from events.
516     */
517    
518     #define KEY 0x1
519     #define BUTTON 0x2
520     #define MOTION 0x4
521     #define CROSSING 0x8
522     #define FOCUS 0x10
523     #define EXPOSE 0x20
524     #define VISIBILITY 0x40
525     #define CREATE 0x80
526     #define DESTROY 0x100
527     #define UNMAP 0x200
528     #define MAP 0x400
529     #define REPARENT 0x800
530     #define CONFIG 0x1000
531     #define GRAVITY 0x2000
532     #define CIRC 0x4000
533     #define PROP 0x8000
534     #define COLORMAP 0x10000
535     #define VIRTUAL 0x20000
536     #define ACTIVATE 0x40000
537    
538     #define KEY_BUTTON_MOTION_VIRTUAL (KEY|BUTTON|MOTION|VIRTUAL)
539    
540     static int flagArray[TK_LASTEVENT] = {
541     /* Not used */ 0,
542     /* Not used */ 0,
543     /* KeyPress */ KEY,
544     /* KeyRelease */ KEY,
545     /* ButtonPress */ BUTTON,
546     /* ButtonRelease */ BUTTON,
547     /* MotionNotify */ MOTION,
548     /* EnterNotify */ CROSSING,
549     /* LeaveNotify */ CROSSING,
550     /* FocusIn */ FOCUS,
551     /* FocusOut */ FOCUS,
552     /* KeymapNotify */ 0,
553     /* Expose */ EXPOSE,
554     /* GraphicsExpose */ EXPOSE,
555     /* NoExpose */ 0,
556     /* VisibilityNotify */ VISIBILITY,
557     /* CreateNotify */ CREATE,
558     /* DestroyNotify */ DESTROY,
559     /* UnmapNotify */ UNMAP,
560     /* MapNotify */ MAP,
561     /* MapRequest */ 0,
562     /* ReparentNotify */ REPARENT,
563     /* ConfigureNotify */ CONFIG,
564     /* ConfigureRequest */ 0,
565     /* GravityNotify */ GRAVITY,
566     /* ResizeRequest */ 0,
567     /* CirculateNotify */ CIRC,
568     /* CirculateRequest */ 0,
569     /* PropertyNotify */ PROP,
570     /* SelectionClear */ 0,
571     /* SelectionRequest */ 0,
572     /* SelectionNotify */ 0,
573     /* ColormapNotify */ COLORMAP,
574     /* ClientMessage */ 0,
575     /* MappingNotify */ 0,
576     /* VirtualEvent */ VIRTUAL,
577     /* Activate */ ACTIVATE,
578     /* Deactivate */ ACTIVATE,
579     /* MouseWheel */ KEY
580     };
581    
582     /*
583     * The following table is used to map between the location where an
584     * generated event should be queued and the string used to specify the
585     * location.
586     */
587    
588     static TkStateMap queuePosition[] = {
589     {-1, "now"},
590     {TCL_QUEUE_HEAD, "head"},
591     {TCL_QUEUE_MARK, "mark"},
592     {TCL_QUEUE_TAIL, "tail"},
593     {-2, NULL}
594     };
595    
596     /*
597     * The following tables are used as a two-way map between X's internal
598     * numeric values for fields in an XEvent and the strings used in Tcl. The
599     * tables are used both when constructing an XEvent from user input and
600     * when providing data from an XEvent to the user.
601     */
602    
603     static TkStateMap notifyMode[] = {
604     {NotifyNormal, "NotifyNormal"},
605     {NotifyGrab, "NotifyGrab"},
606     {NotifyUngrab, "NotifyUngrab"},
607     {NotifyWhileGrabbed, "NotifyWhileGrabbed"},
608     {-1, NULL}
609     };
610    
611     static TkStateMap notifyDetail[] = {
612     {NotifyAncestor, "NotifyAncestor"},
613     {NotifyVirtual, "NotifyVirtual"},
614     {NotifyInferior, "NotifyInferior"},
615     {NotifyNonlinear, "NotifyNonlinear"},
616     {NotifyNonlinearVirtual, "NotifyNonlinearVirtual"},
617     {NotifyPointer, "NotifyPointer"},
618     {NotifyPointerRoot, "NotifyPointerRoot"},
619     {NotifyDetailNone, "NotifyDetailNone"},
620     {-1, NULL}
621     };
622    
623     static TkStateMap circPlace[] = {
624     {PlaceOnTop, "PlaceOnTop"},
625     {PlaceOnBottom, "PlaceOnBottom"},
626     {-1, NULL}
627     };
628    
629     static TkStateMap visNotify[] = {
630     {VisibilityUnobscured, "VisibilityUnobscured"},
631     {VisibilityPartiallyObscured, "VisibilityPartiallyObscured"},
632     {VisibilityFullyObscured, "VisibilityFullyObscured"},
633     {-1, NULL}
634     };
635    
636     /*
637     * Prototypes for local procedures defined in this file:
638     */
639    
640     static void ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,
641     char *dispName, int screenIndex));
642     static int CreateVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
643     VirtualEventTable *vetPtr, char *virtString,
644     char *eventString));
645     static int DeleteVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
646     VirtualEventTable *vetPtr, char *virtString,
647     char *eventString));
648     static void DeleteVirtualEventTable _ANSI_ARGS_((
649     VirtualEventTable *vetPtr));
650     static void ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,
651     char *before, XEvent *eventPtr, KeySym keySym,
652     Tcl_DString *dsPtr));
653     static void FreeTclBinding _ANSI_ARGS_((ClientData clientData));
654     static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
655     Tcl_HashTable *patternTablePtr, ClientData object,
656     char *eventString, int create, int allowVirtual,
657     unsigned long *maskPtr));
658     static void GetAllVirtualEvents _ANSI_ARGS_((Tcl_Interp *interp,
659     VirtualEventTable *vetPtr));
660     static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));
661     static void GetPatternString _ANSI_ARGS_((PatSeq *psPtr,
662     Tcl_DString *dsPtr));
663     static int GetVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
664     VirtualEventTable *vetPtr, char *virtString));
665     static Tk_Uid GetVirtualEventUid _ANSI_ARGS_((Tcl_Interp *interp,
666     char *virtString));
667     static int HandleEventGenerate _ANSI_ARGS_((Tcl_Interp *interp,
668     Tk_Window main, int objc,
669     Tcl_Obj *CONST objv[]));
670     static void InitVirtualEventTable _ANSI_ARGS_((
671     VirtualEventTable *vetPtr));
672     static PatSeq * MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,
673     BindingTable *bindPtr, PatSeq *psPtr,
674     PatSeq *bestPtr, ClientData *objectPtr,
675     PatSeq **sourcePtrPtr));
676     static int NameToWindow _ANSI_ARGS_((Tcl_Interp *interp,
677     Tk_Window main, Tcl_Obj *objPtr,
678     Tk_Window *tkwinPtr));
679     static int ParseEventDescription _ANSI_ARGS_((Tcl_Interp *interp,
680     char **eventStringPtr, Pattern *patPtr,
681     unsigned long *eventMaskPtr));
682     static void DoWarp _ANSI_ARGS_((ClientData clientData));
683    
684     /*
685     * The following define is used as a short circuit for the callback
686     * procedure to evaluate a TclBinding. The actual evaluation of the
687     * binding is handled inline, because special things have to be done
688     * with a Tcl binding before evaluation time.
689     */
690    
691     #define EvalTclBinding ((TkBindEvalProc *) 1)
692    
693    
694     /*
695     *---------------------------------------------------------------------------
696     *
697     * TkBindInit --
698     *
699     * This procedure is called when an application is created. It
700     * initializes all the structures used by bindings and virtual
701     * events. It must be called before any other functions in this
702     * file are called.
703     *
704     * Results:
705     * None.
706     *
707     * Side effects:
708     * Memory allocated.
709     *
710     *---------------------------------------------------------------------------
711     */
712    
713     void
714     TkBindInit(mainPtr)
715     TkMainInfo *mainPtr; /* The newly created application. */
716     {
717     BindInfo *bindInfoPtr;
718    
719     if (sizeof(XEvent) < sizeof(XVirtualEvent)) {
720     panic("TkBindInit: virtual events can't be supported");
721     }
722    
723     /*
724     * Initialize the static data structures used by the binding package.
725     * They are only initialized once, no matter how many interps are
726     * created.
727     */
728    
729     if (!initialized) {
730     Tcl_MutexLock(&bindMutex);
731     if (!initialized) {
732     Tcl_HashEntry *hPtr;
733     ModInfo *modPtr;
734     EventInfo *eiPtr;
735     int dummy;
736    
737     #ifdef REDO_KEYSYM_LOOKUP
738     KeySymInfo *kPtr;
739    
740     Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
741     Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS);
742     for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
743     hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
744     Tcl_SetHashValue(hPtr, kPtr->value);
745     hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value,
746     &dummy);
747     Tcl_SetHashValue(hPtr, kPtr->name);
748     }
749     #endif /* REDO_KEYSYM_LOOKUP */
750    
751     Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
752     for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
753     hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
754     Tcl_SetHashValue(hPtr, modPtr);
755     }
756    
757     Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
758     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
759     hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
760     Tcl_SetHashValue(hPtr, eiPtr);
761     }
762     initialized = 1;
763     }
764     Tcl_MutexUnlock(&bindMutex);
765     }
766    
767     mainPtr->bindingTable = Tk_CreateBindingTable(mainPtr->interp);
768    
769     bindInfoPtr = (BindInfo *) ckalloc(sizeof(BindInfo));
770     InitVirtualEventTable(&bindInfoPtr->virtualEventTable);
771     bindInfoPtr->screenInfo.curDispPtr = NULL;
772     bindInfoPtr->screenInfo.curScreenIndex = -1;
773     bindInfoPtr->screenInfo.bindingDepth = 0;
774     bindInfoPtr->pendingList = NULL;
775     bindInfoPtr->deleted = 0;
776     mainPtr->bindInfo = (TkBindInfo) bindInfoPtr;
777    
778     TkpInitializeMenuBindings(mainPtr->interp, mainPtr->bindingTable);
779     }
780    
781     /*
782     *---------------------------------------------------------------------------
783     *
784     * TkBindFree --
785     *
786     * This procedure is called when an application is deleted. It
787     * deletes all the structures used by bindings and virtual events.
788     *
789     * Results:
790     * None.
791     *
792     * Side effects:
793     * Memory freed.
794     *
795     *---------------------------------------------------------------------------
796     */
797    
798     void
799     TkBindFree(mainPtr)
800     TkMainInfo *mainPtr; /* The newly created application. */
801     {
802     BindInfo *bindInfoPtr;
803    
804     Tk_DeleteBindingTable(mainPtr->bindingTable);
805     mainPtr->bindingTable = NULL;
806    
807     bindInfoPtr = (BindInfo *) mainPtr->bindInfo;
808     DeleteVirtualEventTable(&bindInfoPtr->virtualEventTable);
809     bindInfoPtr->deleted = 1;
810     Tcl_EventuallyFree((ClientData) bindInfoPtr, Tcl_Free);
811     mainPtr->bindInfo = NULL;
812     }
813    
814     /*
815     *--------------------------------------------------------------
816     *
817     * Tk_CreateBindingTable --
818     *
819     * Set up a new domain in which event bindings may be created.
820     *
821     * Results:
822     * The return value is a token for the new table, which must
823     * be passed to procedures like Tk_CreateBinding.
824     *
825     * Side effects:
826     * Memory is allocated for the new table.
827     *
828     *--------------------------------------------------------------
829     */
830    
831     Tk_BindingTable
832     Tk_CreateBindingTable(interp)
833     Tcl_Interp *interp; /* Interpreter to associate with the binding
834     * table: commands are executed in this
835     * interpreter. */
836     {
837     BindingTable *bindPtr;
838     int i;
839    
840     /*
841     * Create and initialize a new binding table.
842     */
843    
844     bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
845     for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
846     bindPtr->eventRing[i].type = -1;
847     }
848     bindPtr->curEvent = 0;
849     Tcl_InitHashTable(&bindPtr->patternTable,
850     sizeof(PatternTableKey)/sizeof(int));
851     Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
852     bindPtr->interp = interp;
853     return (Tk_BindingTable) bindPtr;
854     }
855    
856     /*
857     *--------------------------------------------------------------
858     *
859     * Tk_DeleteBindingTable --
860     *
861     * Destroy a binding table and free up all its memory.
862     * The caller should not use bindingTable again after
863     * this procedure returns.
864     *
865     * Results:
866     * None.
867     *
868     * Side effects:
869     * Memory is freed.
870     *
871     *--------------------------------------------------------------
872     */
873    
874     void
875     Tk_DeleteBindingTable(bindingTable)
876     Tk_BindingTable bindingTable; /* Token for the binding table to
877     * destroy. */
878     {
879     BindingTable *bindPtr = (BindingTable *) bindingTable;
880     PatSeq *psPtr, *nextPtr;
881     Tcl_HashEntry *hPtr;
882     Tcl_HashSearch search;
883    
884     /*
885     * Find and delete all of the patterns associated with the binding
886     * table.
887     */
888    
889     for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
890     hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
891     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
892     psPtr != NULL; psPtr = nextPtr) {
893     nextPtr = psPtr->nextSeqPtr;
894     psPtr->flags |= MARKED_DELETED;
895     if (psPtr->refCount == 0) {
896     if (psPtr->freeProc != NULL) {
897     (*psPtr->freeProc)(psPtr->clientData);
898     }
899     ckfree((char *) psPtr);
900     }
901     }
902     }
903    
904     /*
905     * Clean up the rest of the information associated with the
906     * binding table.
907     */
908    
909     Tcl_DeleteHashTable(&bindPtr->patternTable);
910     Tcl_DeleteHashTable(&bindPtr->objectTable);
911     ckfree((char *) bindPtr);
912     }
913    
914     /*
915     *--------------------------------------------------------------
916     *
917     * Tk_CreateBinding --
918     *
919     * Add a binding to a binding table, so that future calls to
920     * Tk_BindEvent may execute the command in the binding.
921     *
922     * Results:
923     * The return value is 0 if an error occurred while setting
924     * up the binding. In this case, an error message will be
925     * left in the interp's result. If all went well then the return
926     * value is a mask of the event types that must be made
927     * available to Tk_BindEvent in order to properly detect when
928     * this binding triggers. This value can be used to determine
929     * what events to select for in a window, for example.
930     *
931     * Side effects:
932     * An existing binding on the same event sequence may be
933     * replaced.
934     * The new binding may cause future calls to Tk_BindEvent to
935     * behave differently than they did previously.
936     *
937     *--------------------------------------------------------------
938     */
939    
940     unsigned long
941     Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
942     Tcl_Interp *interp; /* Used for error reporting. */
943     Tk_BindingTable bindingTable;
944     /* Table in which to create binding. */
945     ClientData object; /* Token for object with which binding is
946     * associated. */
947     char *eventString; /* String describing event sequence that
948     * triggers binding. */
949     char *command; /* Contains Tcl command to execute when
950     * binding triggers. */
951     int append; /* 0 means replace any existing binding for
952     * eventString; 1 means append to that
953     * binding. If the existing binding is for a
954     * callback function and not a Tcl command
955     * string, the existing binding will always be
956     * replaced. */
957     {
958     BindingTable *bindPtr = (BindingTable *) bindingTable;
959     PatSeq *psPtr;
960     unsigned long eventMask;
961     char *new, *old;
962    
963     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
964     1, 1, &eventMask);
965     if (psPtr == NULL) {
966     return 0;
967     }
968     if (psPtr->eventProc == NULL) {
969     int new;
970     Tcl_HashEntry *hPtr;
971    
972     /*
973     * This pattern sequence was just created.
974     * Link the pattern into the list associated with the object, so
975     * that if the object goes away, these bindings will all
976     * automatically be deleted.
977     */
978    
979     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,
980     &new);
981     if (new) {
982     psPtr->nextObjPtr = NULL;
983     } else {
984     psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
985     }
986     Tcl_SetHashValue(hPtr, psPtr);
987     } else if (psPtr->eventProc != EvalTclBinding) {
988     /*
989     * Free existing procedural binding.
990     */
991    
992     if (psPtr->freeProc != NULL) {
993     (*psPtr->freeProc)(psPtr->clientData);
994     }
995     psPtr->clientData = NULL;
996     append = 0;
997     }
998    
999     old = (char *) psPtr->clientData;
1000     if ((append != 0) && (old != NULL)) {
1001     int length;
1002    
1003     length = strlen(old) + strlen(command) + 2;
1004     new = (char *) ckalloc((unsigned) length);
1005     sprintf(new, "%s\n%s", old, command);
1006     } else {
1007     new = (char *) ckalloc((unsigned) strlen(command) + 1);
1008     strcpy(new, command);
1009     }
1010     if (old != NULL) {
1011     ckfree(old);
1012     }
1013     psPtr->eventProc = EvalTclBinding;
1014     psPtr->freeProc = FreeTclBinding;
1015     psPtr->clientData = (ClientData) new;
1016     return eventMask;
1017     }
1018    
1019     /*
1020     *---------------------------------------------------------------------------
1021     *
1022     * TkCreateBindingProcedure --
1023     *
1024     * Add a C binding to a binding table, so that future calls to
1025     * Tk_BindEvent may callback the procedure in the binding.
1026     *
1027     * Results:
1028     * The return value is 0 if an error occurred while setting
1029     * up the binding. In this case, an error message will be
1030     * left in the interp's result. If all went well then the return
1031     * value is a mask of the event types that must be made
1032     * available to Tk_BindEvent in order to properly detect when
1033     * this binding triggers. This value can be used to determine
1034     * what events to select for in a window, for example.
1035     *
1036     * Side effects:
1037     * Any existing binding on the same event sequence will be
1038     * replaced.
1039     *
1040     *---------------------------------------------------------------------------
1041     */
1042    
1043     unsigned long
1044     TkCreateBindingProcedure(interp, bindingTable, object, eventString,
1045     eventProc, freeProc, clientData)
1046     Tcl_Interp *interp; /* Used for error reporting. */
1047     Tk_BindingTable bindingTable;
1048     /* Table in which to create binding. */
1049     ClientData object; /* Token for object with which binding is
1050     * associated. */
1051     char *eventString; /* String describing event sequence that
1052     * triggers binding. */
1053     TkBindEvalProc *eventProc; /* Procedure to invoke when binding
1054     * triggers. Must not be NULL. */
1055     TkBindFreeProc *freeProc; /* Procedure to invoke when binding is
1056     * freed. May be NULL for no procedure. */
1057     ClientData clientData; /* Arbitrary ClientData to pass to eventProc
1058     * and freeProc. */
1059     {
1060     BindingTable *bindPtr = (BindingTable *) bindingTable;
1061     PatSeq *psPtr;
1062     unsigned long eventMask;
1063    
1064     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
1065     1, 1, &eventMask);
1066     if (psPtr == NULL) {
1067     return 0;
1068     }
1069     if (psPtr->eventProc == NULL) {
1070     int new;
1071     Tcl_HashEntry *hPtr;
1072    
1073     /*
1074     * This pattern sequence was just created.
1075     * Link the pattern into the list associated with the object, so
1076     * that if the object goes away, these bindings will all
1077     * automatically be deleted.
1078     */
1079    
1080     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,
1081     &new);
1082     if (new) {
1083     psPtr->nextObjPtr = NULL;
1084     } else {
1085     psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1086     }
1087     Tcl_SetHashValue(hPtr, psPtr);
1088     } else {
1089    
1090     /*
1091     * Free existing callback.
1092     */
1093    
1094     if (psPtr->freeProc != NULL) {
1095     (*psPtr->freeProc)(psPtr->clientData);
1096     }
1097     }
1098    
1099     psPtr->eventProc = eventProc;
1100     psPtr->freeProc = freeProc;
1101     psPtr->clientData = clientData;
1102     return eventMask;
1103     }
1104    
1105     /*
1106     *--------------------------------------------------------------
1107     *
1108     * Tk_DeleteBinding --
1109     *
1110     * Remove an event binding from a binding table.
1111     *
1112     * Results:
1113     * The result is a standard Tcl return value. If an error
1114     * occurs then the interp's result will contain an error message.
1115     *
1116     * Side effects:
1117     * The binding given by object and eventString is removed
1118     * from bindingTable.
1119     *
1120     *--------------------------------------------------------------
1121     */
1122    
1123     int
1124     Tk_DeleteBinding(interp, bindingTable, object, eventString)
1125     Tcl_Interp *interp; /* Used for error reporting. */
1126     Tk_BindingTable bindingTable; /* Table in which to delete binding. */
1127     ClientData object; /* Token for object with which binding
1128     * is associated. */
1129     char *eventString; /* String describing event sequence
1130     * that triggers binding. */
1131     {
1132     BindingTable *bindPtr = (BindingTable *) bindingTable;
1133     PatSeq *psPtr, *prevPtr;
1134     unsigned long eventMask;
1135     Tcl_HashEntry *hPtr;
1136    
1137     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
1138     0, 1, &eventMask);
1139     if (psPtr == NULL) {
1140     Tcl_ResetResult(interp);
1141     return TCL_OK;
1142     }
1143    
1144     /*
1145     * Unlink the binding from the list for its object, then from the
1146     * list for its pattern.
1147     */
1148    
1149     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
1150     if (hPtr == NULL) {
1151     panic("Tk_DeleteBinding couldn't find object table entry");
1152     }
1153     prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1154     if (prevPtr == psPtr) {
1155     Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
1156     } else {
1157     for ( ; ; prevPtr = prevPtr->nextObjPtr) {
1158     if (prevPtr == NULL) {
1159     panic("Tk_DeleteBinding couldn't find on object list");
1160     }
1161     if (prevPtr->nextObjPtr == psPtr) {
1162     prevPtr->nextObjPtr = psPtr->nextObjPtr;
1163     break;
1164     }
1165     }
1166     }
1167     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
1168     if (prevPtr == psPtr) {
1169     if (psPtr->nextSeqPtr == NULL) {
1170     Tcl_DeleteHashEntry(psPtr->hPtr);
1171     } else {
1172     Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
1173     }
1174     } else {
1175     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
1176     if (prevPtr == NULL) {
1177     panic("Tk_DeleteBinding couldn't find on hash chain");
1178     }
1179     if (prevPtr->nextSeqPtr == psPtr) {
1180     prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
1181     break;
1182     }
1183     }
1184     }
1185    
1186     psPtr->flags |= MARKED_DELETED;
1187     if (psPtr->refCount == 0) {
1188     if (psPtr->freeProc != NULL) {
1189     (*psPtr->freeProc)(psPtr->clientData);
1190     }
1191     ckfree((char *) psPtr);
1192     }
1193     return TCL_OK;
1194     }
1195    
1196     /*
1197     *--------------------------------------------------------------
1198     *
1199     * Tk_GetBinding --
1200     *
1201     * Return the command associated with a given event string.
1202     *
1203     * Results:
1204     * The return value is a pointer to the command string
1205     * associated with eventString for object in the domain
1206     * given by bindingTable. If there is no binding for
1207     * eventString, or if eventString is improperly formed,
1208     * then NULL is returned and an error message is left in
1209     * the interp's result. The return value is semi-static: it
1210     * will persist until the binding is changed or deleted.
1211     *
1212     * Side effects:
1213     * None.
1214     *
1215     *--------------------------------------------------------------
1216     */
1217    
1218     char *
1219     Tk_GetBinding(interp, bindingTable, object, eventString)
1220     Tcl_Interp *interp; /* Interpreter for error reporting. */
1221     Tk_BindingTable bindingTable; /* Table in which to look for
1222     * binding. */
1223     ClientData object; /* Token for object with which binding
1224     * is associated. */
1225     char *eventString; /* String describing event sequence
1226     * that triggers binding. */
1227     {
1228     BindingTable *bindPtr = (BindingTable *) bindingTable;
1229     PatSeq *psPtr;
1230     unsigned long eventMask;
1231    
1232     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
1233     0, 1, &eventMask);
1234     if (psPtr == NULL) {
1235     return NULL;
1236     }
1237     if (psPtr->eventProc == EvalTclBinding) {
1238     return (char *) psPtr->clientData;
1239     }
1240     return "";
1241     }
1242    
1243     /*
1244     *--------------------------------------------------------------
1245     *
1246     * Tk_GetAllBindings --
1247     *
1248     * Return a list of event strings for all the bindings
1249     * associated with a given object.
1250     *
1251     * Results:
1252     * There is no return value. The interp's result is modified to
1253     * hold a Tcl list with one entry for each binding associated
1254     * with object in bindingTable. Each entry in the list
1255     * contains the event string associated with one binding.
1256     *
1257     * Side effects:
1258     * None.
1259     *
1260     *--------------------------------------------------------------
1261     */
1262    
1263     void
1264     Tk_GetAllBindings(interp, bindingTable, object)
1265     Tcl_Interp *interp; /* Interpreter returning result or
1266     * error. */
1267     Tk_BindingTable bindingTable; /* Table in which to look for
1268     * bindings. */
1269     ClientData object; /* Token for object. */
1270    
1271     {
1272     BindingTable *bindPtr = (BindingTable *) bindingTable;
1273     PatSeq *psPtr;
1274     Tcl_HashEntry *hPtr;
1275     Tcl_DString ds;
1276    
1277     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
1278     if (hPtr == NULL) {
1279     return;
1280     }
1281     Tcl_DStringInit(&ds);
1282     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1283     psPtr = psPtr->nextObjPtr) {
1284     /*
1285     * For each binding, output information about each of the
1286     * patterns in its sequence.
1287     */
1288    
1289     Tcl_DStringSetLength(&ds, 0);
1290     GetPatternString(psPtr, &ds);
1291     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
1292     }
1293     Tcl_DStringFree(&ds);
1294     }
1295    
1296     /*
1297     *--------------------------------------------------------------
1298     *
1299     * Tk_DeleteAllBindings --
1300     *
1301     * Remove all bindings associated with a given object in a
1302     * given binding table.
1303     *
1304     * Results:
1305     * All bindings associated with object are removed from
1306     * bindingTable.
1307     *
1308     * Side effects:
1309     * None.
1310     *
1311     *--------------------------------------------------------------
1312     */
1313    
1314     void
1315     Tk_DeleteAllBindings(bindingTable, object)
1316     Tk_BindingTable bindingTable; /* Table in which to delete
1317     * bindings. */
1318     ClientData object; /* Token for object. */
1319     {
1320     BindingTable *bindPtr = (BindingTable *) bindingTable;
1321     PatSeq *psPtr, *prevPtr;
1322     PatSeq *nextPtr;
1323     Tcl_HashEntry *hPtr;
1324    
1325     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
1326     if (hPtr == NULL) {
1327     return;
1328     }
1329     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1330     psPtr = nextPtr) {
1331     nextPtr = psPtr->nextObjPtr;
1332    
1333     /*
1334     * Be sure to remove each binding from its hash chain in the
1335     * pattern table. If this is the last pattern in the chain,
1336     * then delete the hash entry too.
1337     */
1338    
1339     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
1340     if (prevPtr == psPtr) {
1341     if (psPtr->nextSeqPtr == NULL) {
1342     Tcl_DeleteHashEntry(psPtr->hPtr);
1343     } else {
1344     Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
1345     }
1346     } else {
1347     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
1348     if (prevPtr == NULL) {
1349     panic("Tk_DeleteAllBindings couldn't find on hash chain");
1350     }
1351     if (prevPtr->nextSeqPtr == psPtr) {
1352     prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
1353     break;
1354     }
1355     }
1356     }
1357     psPtr->flags |= MARKED_DELETED;
1358    
1359     if (psPtr->refCount == 0) {
1360     if (psPtr->freeProc != NULL) {
1361     (*psPtr->freeProc)(psPtr->clientData);
1362     }
1363     ckfree((char *) psPtr);
1364     }
1365     }
1366     Tcl_DeleteHashEntry(hPtr);
1367     }
1368    
1369     /*
1370     *---------------------------------------------------------------------------
1371     *
1372     * Tk_BindEvent --
1373     *
1374     * This procedure is invoked to process an X event. The
1375     * event is added to those recorded for the binding table.
1376     * Then each of the objects at *objectPtr is checked in
1377     * order to see if it has a binding that matches the recent
1378     * events. If so, the most specific binding is invoked for
1379     * each object.
1380     *
1381     * Results:
1382     * None.
1383     *
1384     * Side effects:
1385     * Depends on the command associated with the matching binding.
1386     *
1387     * All Tcl bindings scripts for each object are accumulated before
1388     * the first binding is evaluated. If the action of a Tcl binding
1389     * is to change or delete a binding, or delete the window associated
1390     * with the binding, all the original Tcl binding scripts will still
1391     * fire. Contrast this with C binding procedures. If a pending C
1392     * binding (one that hasn't fired yet, but is queued to be fired for
1393     * this window) is deleted, it will not be called, and if it is
1394     * changed, then the new binding procedure will be called. If the
1395     * window itself is deleted, no further C binding procedures will be
1396     * called for this window. When both Tcl binding scripts and C binding
1397     * procedures are interleaved, the above rules still apply.
1398     *
1399     *---------------------------------------------------------------------------
1400     */
1401    
1402     void
1403     Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
1404     Tk_BindingTable bindingTable; /* Table in which to look for
1405     * bindings. */
1406     XEvent *eventPtr; /* What actually happened. */
1407     Tk_Window tkwin; /* Window on display where event
1408     * occurred (needed in order to
1409     * locate display information). */
1410     int numObjects; /* Number of objects at *objectPtr. */
1411     ClientData *objectPtr; /* Array of one or more objects
1412     * to check for a matching binding. */
1413     {
1414     BindingTable *bindPtr;
1415     TkDisplay *dispPtr;
1416     ScreenInfo *screenPtr;
1417     BindInfo *bindInfoPtr;
1418     TkDisplay *oldDispPtr;
1419     XEvent *ringPtr;
1420     PatSeq *vMatchDetailList, *vMatchNoDetailList;
1421     int flags, oldScreen, i, deferModal;
1422     unsigned int matchCount, matchSpace;
1423     Tcl_Interp *interp;
1424     Tcl_DString scripts, savedResult;
1425     Detail detail;
1426     char *p, *end;
1427     PendingBinding *pendingPtr;
1428     PendingBinding staticPending;
1429     TkWindow *winPtr = (TkWindow *)tkwin;
1430     PatternTableKey key;
1431    
1432     /*
1433     * Ignore events on windows that don't have names: these are windows
1434     * like wrapper windows that shouldn't be visible to the
1435     * application.
1436     */
1437    
1438     if (winPtr->pathName == NULL) {
1439     return;
1440     }
1441    
1442     /*
1443     * Ignore the event completely if it is an Enter, Leave, FocusIn,
1444     * or FocusOut event with detail NotifyInferior. The reason for
1445     * ignoring these events is that we don't want transitions between
1446     * a window and its children to visible to bindings on the parent:
1447     * this would cause problems for mega-widgets, since the internal
1448     * structure of a mega-widget isn't supposed to be visible to
1449     * people watching the parent.
1450     */
1451    
1452     if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
1453     if (eventPtr->xcrossing.detail == NotifyInferior) {
1454     return;
1455     }
1456     }
1457     if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
1458     if (eventPtr->xfocus.detail == NotifyInferior) {
1459     return;
1460     }
1461     }
1462    
1463     bindPtr = (BindingTable *) bindingTable;
1464     dispPtr = ((TkWindow *) tkwin)->dispPtr;
1465     bindInfoPtr = (BindInfo *) winPtr->mainPtr->bindInfo;
1466    
1467     /*
1468     * Add the new event to the ring of saved events for the
1469     * binding table. Two tricky points:
1470     *
1471     * 1. Combine consecutive MotionNotify events. Do this by putting
1472     * the new event *on top* of the previous event.
1473     * 2. If a modifier key is held down, it auto-repeats to generate
1474     * continuous KeyPress and KeyRelease events. These can flush
1475     * the event ring so that valuable information is lost (such
1476     * as repeated button clicks). To handle this, check for the
1477     * special case of a modifier KeyPress arriving when the previous
1478     * two events are a KeyRelease and KeyPress of the same key.
1479     * If this happens, mark the most recent event (the KeyRelease)
1480     * invalid and put the new event on top of the event before that
1481     * (the KeyPress).
1482     */
1483    
1484     if ((eventPtr->type == MotionNotify)
1485     && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {
1486     /*
1487     * Don't advance the ring pointer.
1488     */
1489     } else if (eventPtr->type == KeyPress) {
1490     int i;
1491     for (i = 0; ; i++) {
1492     if (i >= dispPtr->numModKeyCodes) {
1493     goto advanceRingPointer;
1494     }
1495     if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
1496     break;
1497     }
1498     }
1499     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1500     if ((ringPtr->type != KeyRelease)
1501     || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
1502     goto advanceRingPointer;
1503     }
1504     if (bindPtr->curEvent <= 0) {
1505     i = EVENT_BUFFER_SIZE - 1;
1506     } else {
1507     i = bindPtr->curEvent - 1;
1508     }
1509     ringPtr = &bindPtr->eventRing[i];
1510     if ((ringPtr->type != KeyPress)
1511     || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
1512     goto advanceRingPointer;
1513     }
1514     bindPtr->eventRing[bindPtr->curEvent].type = -1;
1515     bindPtr->curEvent = i;
1516     } else {
1517     advanceRingPointer:
1518     bindPtr->curEvent++;
1519     if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
1520     bindPtr->curEvent = 0;
1521     }
1522     }
1523     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1524     memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
1525     detail.clientData = 0;
1526     flags = flagArray[ringPtr->type];
1527     if (flags & KEY) {
1528     detail.keySym = TkpGetKeySym(dispPtr, ringPtr);
1529     if (detail.keySym == NoSymbol) {
1530     detail.keySym = 0;
1531     }
1532     } else if (flags & BUTTON) {
1533     detail.button = ringPtr->xbutton.button;
1534     } else if (flags & VIRTUAL) {
1535     detail.name = ((XVirtualEvent *) ringPtr)->name;
1536     }
1537     bindPtr->detailRing[bindPtr->curEvent] = detail;
1538    
1539     /*
1540     * Find out if there are any virtual events that correspond to this
1541     * physical event (or sequence of physical events).
1542     */
1543    
1544     vMatchDetailList = NULL;
1545     vMatchNoDetailList = NULL;
1546     memset(&key, 0, sizeof(key));
1547    
1548     if (ringPtr->type != VirtualEvent) {
1549     Tcl_HashTable *veptPtr;
1550     Tcl_HashEntry *hPtr;
1551    
1552     veptPtr = &bindInfoPtr->virtualEventTable.patternTable;
1553    
1554     key.object = NULL;
1555     key.type = ringPtr->type;
1556     key.detail = detail;
1557    
1558     hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
1559     if (hPtr != NULL) {
1560     vMatchDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
1561     }
1562    
1563     if (key.detail.clientData != 0) {
1564     key.detail.clientData = 0;
1565     hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
1566     if (hPtr != NULL) {
1567     vMatchNoDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
1568     }
1569     }
1570     }
1571    
1572     /*
1573     * Loop over all the binding tags, finding the binding script or
1574     * callback for each one. Append all of the binding scripts, with
1575     * %-sequences expanded, to "scripts", with null characters separating
1576     * the scripts for each object. Append all the callbacks to the array
1577     * of pending callbacks.
1578     */
1579    
1580     pendingPtr = &staticPending;
1581     matchCount = 0;
1582     matchSpace = sizeof(staticPending.matchArray) / sizeof(PatSeq *);
1583     Tcl_DStringInit(&scripts);
1584    
1585     for ( ; numObjects > 0; numObjects--, objectPtr++) {
1586     PatSeq *matchPtr, *sourcePtr;
1587     Tcl_HashEntry *hPtr;
1588    
1589     matchPtr = NULL;
1590     sourcePtr = NULL;
1591    
1592     /*
1593     * Match the new event against those recorded in the pattern table,
1594     * saving the longest matching pattern. For events with details
1595     * (button and key events), look for a binding for the specific
1596     * key or button. First see if the event matches a physical event
1597     * that the object is interested in, then look for a virtual event.
1598     */
1599    
1600     key.object = *objectPtr;
1601     key.type = ringPtr->type;
1602     key.detail = detail;
1603     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1604     if (hPtr != NULL) {
1605     matchPtr = MatchPatterns(dispPtr, bindPtr,
1606     (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
1607     &sourcePtr);
1608     }
1609    
1610     if (vMatchDetailList != NULL) {
1611     matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList,
1612     matchPtr, objectPtr, &sourcePtr);
1613     }
1614    
1615     /*
1616     * If no match was found, look for a binding for all keys or buttons
1617     * (detail of 0). Again, first match on a virtual event.
1618     */
1619    
1620     if ((detail.clientData != 0) && (matchPtr == NULL)) {
1621     key.detail.clientData = 0;
1622     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1623     if (hPtr != NULL) {
1624     matchPtr = MatchPatterns(dispPtr, bindPtr,
1625     (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
1626     &sourcePtr);
1627     }
1628    
1629     if (vMatchNoDetailList != NULL) {
1630     matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList,
1631     matchPtr, objectPtr, &sourcePtr);
1632     }
1633    
1634     }
1635    
1636     if (matchPtr != NULL) {
1637     if (sourcePtr->eventProc == NULL) {
1638     panic("Tk_BindEvent: missing command");
1639     }
1640     if (sourcePtr->eventProc == EvalTclBinding) {
1641     ExpandPercents(winPtr, (char *) sourcePtr->clientData,
1642     eventPtr, detail.keySym, &scripts);
1643     } else {
1644     if (matchCount >= matchSpace) {
1645     PendingBinding *new;
1646     unsigned int oldSize, newSize;
1647    
1648     oldSize = sizeof(staticPending)
1649     - sizeof(staticPending.matchArray)
1650     + matchSpace * sizeof(PatSeq*);
1651     matchSpace *= 2;
1652     newSize = sizeof(staticPending)
1653     - sizeof(staticPending.matchArray)
1654     + matchSpace * sizeof(PatSeq*);
1655     new = (PendingBinding *) ckalloc(newSize);
1656     memcpy((VOID *) new, (VOID *) pendingPtr, oldSize);
1657     if (pendingPtr != &staticPending) {
1658     ckfree((char *) pendingPtr);
1659     }
1660     pendingPtr = new;
1661     }
1662     sourcePtr->refCount++;
1663     pendingPtr->matchArray[matchCount] = sourcePtr;
1664     matchCount++;
1665     }
1666     /*
1667     * A "" is added to the scripts string to separate the
1668     * various scripts that should be invoked.
1669     */
1670    
1671     Tcl_DStringAppend(&scripts, "", 1);
1672     }
1673     }
1674     if (Tcl_DStringLength(&scripts) == 0) {
1675     return;
1676     }
1677    
1678     /*
1679     * Now go back through and evaluate the binding for each object,
1680     * in order, dealing with "break" and "continue" exceptions
1681     * appropriately.
1682     *
1683     * There are two tricks here:
1684     * 1. Bindings can be invoked from in the middle of Tcl commands,
1685     * where the interp's result is significant (for example, a widget
1686     * might be deleted because of an error in creating it, so the
1687     * result contains an error message that is eventually going to
1688     * be returned by the creating command). To preserve the result,
1689     * we save it in a dynamic string.
1690     * 2. The binding's action can potentially delete the binding,
1691     * so bindPtr may not point to anything valid once the action
1692     * completes. Thus we have to save bindPtr->interp in a
1693     * local variable in order to restore the result.
1694     */
1695    
1696     interp = bindPtr->interp;
1697     Tcl_DStringInit(&savedResult);
1698    
1699     /*
1700     * Save information about the current screen, then invoke a script
1701     * if the screen has changed.
1702     */
1703    
1704     Tcl_DStringGetResult(interp, &savedResult);
1705     screenPtr = &bindInfoPtr->screenInfo;
1706     oldDispPtr = screenPtr->curDispPtr;
1707     oldScreen = screenPtr->curScreenIndex;
1708     if ((dispPtr != screenPtr->curDispPtr)
1709     || (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) {
1710     screenPtr->curDispPtr = dispPtr;
1711     screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin);
1712     ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex);
1713     }
1714    
1715     if (matchCount > 0) {
1716     /*
1717     * Remember the list of pending C binding callbacks, so we can mark
1718     * them as deleted and not call them if the act of evaluating a C
1719     * or Tcl binding deletes a C binding callback or even the whole
1720     * window.
1721     */
1722    
1723     pendingPtr->nextPtr = bindInfoPtr->pendingList;
1724     pendingPtr->tkwin = tkwin;
1725     pendingPtr->deleted = 0;
1726     bindInfoPtr->pendingList = pendingPtr;
1727     }
1728    
1729     /*
1730     * Save the current value of the TK_DEFER_MODAL flag so we can
1731     * restore it at the end of the loop. Clear the flag so we can
1732     * detect any recursive requests for a modal loop.
1733     */
1734    
1735     flags = winPtr->flags;
1736     winPtr->flags &= ~TK_DEFER_MODAL;
1737    
1738     p = Tcl_DStringValue(&scripts);
1739     end = p + Tcl_DStringLength(&scripts);
1740     i = 0;
1741    
1742     /*
1743     * Be carefule when dereferencing screenPtr or bindInfoPtr. If we
1744     * evaluate something that destroys ".", bindInfoPtr would have been
1745     * freed, but we can tell that by first checking to see if
1746     * winPtr->mainPtr == NULL.
1747     */
1748    
1749     Tcl_Preserve((ClientData) bindInfoPtr);
1750     while (p < end) {
1751     int code;
1752    
1753     if (!bindInfoPtr->deleted) {
1754     screenPtr->bindingDepth++;
1755     }
1756     Tcl_AllowExceptions(interp);
1757    
1758     if (*p == '\0') {
1759     PatSeq *psPtr;
1760    
1761     psPtr = pendingPtr->matchArray[i];
1762     i++;
1763     code = TCL_OK;
1764     if ((pendingPtr->deleted == 0)
1765     && ((psPtr->flags & MARKED_DELETED) == 0)) {
1766     code = (*psPtr->eventProc)(psPtr->clientData, interp, eventPtr,
1767     tkwin, detail.keySym);
1768     }
1769     psPtr->refCount--;
1770     if ((psPtr->refCount == 0) && (psPtr->flags & MARKED_DELETED)) {
1771     if (psPtr->freeProc != NULL) {
1772     (*psPtr->freeProc)(psPtr->clientData);
1773     }
1774     ckfree((char *) psPtr);
1775     }
1776     } else {
1777     code = Tcl_GlobalEval(interp, p);
1778     p += strlen(p);
1779     }
1780     p++;
1781    
1782     if (!bindInfoPtr->deleted) {
1783     screenPtr->bindingDepth--;
1784     }
1785     if (code != TCL_OK) {
1786     if (code == TCL_CONTINUE) {
1787     /*
1788     * Do nothing: just go on to the next command.
1789     */
1790     } else if (code == TCL_BREAK) {
1791     break;
1792     } else {
1793     Tcl_AddErrorInfo(interp, "\n (command bound to event)");
1794     Tcl_BackgroundError(interp);
1795     break;
1796     }
1797     }
1798     }
1799    
1800     if (matchCount > 0 && !pendingPtr->deleted) {
1801     /*
1802     * Restore the original modal flag value and invoke the modal loop
1803     * if needed.
1804     */
1805    
1806     deferModal = winPtr->flags & TK_DEFER_MODAL;
1807     winPtr->flags = (winPtr->flags & (unsigned int) ~TK_DEFER_MODAL)
1808     | (flags & TK_DEFER_MODAL);
1809     if (deferModal) {
1810     (*winPtr->classProcsPtr->modalProc)(tkwin, eventPtr);
1811     }
1812     }
1813    
1814     if (!bindInfoPtr->deleted && (screenPtr->bindingDepth != 0)
1815     && ((oldDispPtr != screenPtr->curDispPtr)
1816     || (oldScreen != screenPtr->curScreenIndex))) {
1817    
1818     /*
1819     * Some other binding script is currently executing, but its
1820     * screen is no longer current. Change the current display
1821     * back again.
1822     */
1823    
1824     screenPtr->curDispPtr = oldDispPtr;
1825     screenPtr->curScreenIndex = oldScreen;
1826     ChangeScreen(interp, oldDispPtr->name, oldScreen);
1827     }
1828     Tcl_DStringResult(interp, &savedResult);
1829     Tcl_DStringFree(&scripts);
1830    
1831     if (matchCount > 0) {
1832     if (!bindInfoPtr->deleted) {
1833     /*
1834     * Delete the pending list from the list of pending scripts
1835     * for this window.
1836     */
1837    
1838     PendingBinding **curPtrPtr;
1839    
1840     for (curPtrPtr = &bindInfoPtr->pendingList; ; ) {
1841     if (*curPtrPtr == pendingPtr) {
1842     *curPtrPtr = pendingPtr->nextPtr;
1843     break;
1844     }
1845     curPtrPtr = &(*curPtrPtr)->nextPtr;
1846     }
1847     }
1848     if (pendingPtr != &staticPending) {
1849     ckfree((char *) pendingPtr);
1850     }
1851     }
1852     Tcl_Release((ClientData) bindInfoPtr);
1853     }
1854    
1855     /*
1856     *---------------------------------------------------------------------------
1857     *
1858     * TkBindDeadWindow --
1859     *
1860     * This procedure is invoked when it is determined that a window is
1861     * dead. It cleans up bind-related information about the window
1862     *
1863     * Results:
1864     * None.
1865     *
1866     * Side effects:
1867     * Any pending C bindings for this window are cancelled.
1868     *
1869     *---------------------------------------------------------------------------
1870     */
1871    
1872     void
1873     TkBindDeadWindow(winPtr)
1874     TkWindow *winPtr; /* The window that is being deleted. */
1875     {
1876     BindInfo *bindInfoPtr;
1877     PendingBinding *curPtr;
1878    
1879     bindInfoPtr = (BindInfo *) winPtr->mainPtr->bindInfo;
1880     curPtr = bindInfoPtr->pendingList;
1881     while (curPtr != NULL) {
1882     if (curPtr->tkwin == (Tk_Window) winPtr) {
1883     curPtr->deleted = 1;
1884     }
1885     curPtr = curPtr->nextPtr;
1886     }
1887     }
1888    
1889     /*
1890     *----------------------------------------------------------------------
1891     *
1892     * MatchPatterns --
1893     *
1894     * Given a list of pattern sequences and a list of recent events,
1895     * return the pattern sequence that best matches the event list,
1896     * if there is one.
1897     *
1898     * This procedure is used in two different ways. In the simplest
1899     * use, "object" is NULL and psPtr is a list of pattern sequences,
1900     * each of which corresponds to a binding. In this case, the
1901     * procedure finds the pattern sequences that match the event list
1902     * and returns the most specific of those, if there is more than one.
1903     *
1904     * In the second case, psPtr is a list of pattern sequences, each
1905     * of which corresponds to a definition for a virtual binding.
1906     * In order for one of these sequences to "match", it must match
1907     * the events (as above) but in addition there must be a binding
1908     * for its associated virtual event on the current object. The
1909     * "object" argument indicates which object the binding must be for.
1910     *
1911     * Results:
1912     * The return value is NULL if bestPtr is NULL and no pattern matches
1913     * the recent events from bindPtr. Otherwise the return value is
1914     * the most specific pattern sequence among bestPtr and all those
1915     * at psPtr that match the event list and object. If a pattern
1916     * sequence other than bestPtr is returned, then *bestCommandPtr
1917     * is filled in with a pointer to the command from the best sequence.
1918     *
1919     * Side effects:
1920     * None.
1921     *
1922     *----------------------------------------------------------------------
1923     */
1924     static PatSeq *
1925     MatchPatterns(dispPtr, bindPtr, psPtr, bestPtr, objectPtr, sourcePtrPtr)
1926     TkDisplay *dispPtr; /* Display from which the event came. */
1927     BindingTable *bindPtr; /* Information about binding table, such as
1928     * ring of recent events. */
1929     PatSeq *psPtr; /* List of pattern sequences. */
1930     PatSeq *bestPtr; /* The best match seen so far, from a
1931     * previous call to this procedure. NULL
1932     * means no prior best match. */
1933     ClientData *objectPtr; /* If NULL, the sequences at psPtr
1934     * correspond to "normal" bindings. If
1935     * non-NULL, the sequences at psPtr correspond
1936     * to virtual bindings; in order to match each
1937     * sequence must correspond to a virtual
1938     * binding for which a binding exists for
1939     * object in bindPtr. */
1940     PatSeq **sourcePtrPtr; /* Filled with the pattern sequence that
1941     * contains the eventProc and clientData
1942     * associated with the best match. If this
1943     * differs from the return value, it is the
1944     * virtual event that most closely matched the
1945     * return value (a physical event). Not
1946     * modified unless a result other than bestPtr
1947     * is returned. */
1948     {
1949     PatSeq *matchPtr, *bestSourcePtr, *sourcePtr;
1950    
1951     bestSourcePtr = *sourcePtrPtr;
1952    
1953     /*
1954     * Iterate over all the pattern sequences.
1955     */
1956    
1957     for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1958     XEvent *eventPtr;
1959     Pattern *patPtr;
1960     Window window;
1961     Detail *detailPtr;
1962     int patCount, ringCount, flags, state;
1963     int modMask;
1964    
1965     /*
1966     * Iterate over all the patterns in a sequence to be
1967     * sure that they all match.
1968     */
1969    
1970     eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1971     detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1972     window = eventPtr->xany.window;
1973     patPtr = psPtr->pats;
1974     patCount = psPtr->numPats;
1975     ringCount = EVENT_BUFFER_SIZE;
1976     while (patCount > 0) {
1977     if (ringCount <= 0) {
1978     goto nextSequence;
1979     }
1980     if (eventPtr->xany.type != patPtr->eventType) {
1981     /*
1982     * Most of the event types are considered superfluous
1983     * in that they are ignored if they occur in the middle
1984     * of a pattern sequence and have mismatching types. The
1985     * only ones that cannot be ignored are ButtonPress and
1986     * ButtonRelease events (if the next event in the pattern
1987     * is a KeyPress or KeyRelease) and KeyPress and KeyRelease
1988     * events (if the next pattern event is a ButtonPress or
1989     * ButtonRelease). Here are some tricky cases to consider:
1990     * 1. Double-Button or Double-Key events.
1991     * 2. Double-ButtonRelease or Double-KeyRelease events.
1992     * 3. The arrival of various events like Enter and Leave
1993     * and FocusIn and GraphicsExpose between two button
1994     * presses or key presses.
1995     * 4. Modifier keys like Shift and Control shouldn't
1996     * generate conflicts with button events.
1997     */
1998    
1999     if ((patPtr->eventType == KeyPress)
2000     || (patPtr->eventType == KeyRelease)) {
2001     if ((eventPtr->xany.type == ButtonPress)
2002     || (eventPtr->xany.type == ButtonRelease)) {
2003     goto nextSequence;
2004     }
2005     } else if ((patPtr->eventType == ButtonPress)
2006     || (patPtr->eventType == ButtonRelease)) {
2007     if ((eventPtr->xany.type == KeyPress)
2008     || (eventPtr->xany.type == KeyRelease)) {
2009     int i;
2010    
2011     /*
2012     * Ignore key events if they are modifier keys.
2013     */
2014    
2015     for (i = 0; i < dispPtr->numModKeyCodes; i++) {
2016     if (dispPtr->modKeyCodes[i]
2017     == eventPtr->xkey.keycode) {
2018     /*
2019     * This key is a modifier key, so ignore it.
2020     */
2021     goto nextEvent;
2022     }
2023     }
2024     goto nextSequence;
2025     }
2026     }
2027     goto nextEvent;
2028     }
2029     if (eventPtr->xany.window != window) {
2030     goto nextSequence;
2031     }
2032    
2033     /*
2034     * Note: it's important for the keysym check to go before
2035     * the modifier check, so we can ignore unwanted modifier
2036     * keys before choking on the modifier check.
2037     */
2038    
2039     if ((patPtr->detail.clientData != 0)
2040     && (patPtr->detail.clientData != detailPtr->clientData)) {
2041     /*
2042     * The detail appears not to match. However, if the event
2043     * is a KeyPress for a modifier key then just ignore the
2044     * event. Otherwise event sequences like "aD" never match
2045     * because the shift key goes down between the "a" and the
2046     * "D".
2047     */
2048    
2049     if (eventPtr->xany.type == KeyPress) {
2050     int i;
2051    
2052     for (i = 0; i < dispPtr->numModKeyCodes; i++) {
2053     if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
2054     goto nextEvent;
2055     }
2056     }
2057     }
2058     goto nextSequence;
2059     }
2060     flags = flagArray[eventPtr->type];
2061     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2062     state = eventPtr->xkey.state;
2063     } else if (flags & CROSSING) {
2064     state = eventPtr->xcrossing.state;
2065     } else {
2066     state = 0;
2067     }
2068     if (patPtr->needMods != 0) {
2069     modMask = patPtr->needMods;
2070     if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
2071     modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
2072     }
2073     if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
2074     modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
2075     }
2076    
2077     if ((state & META_MASK) && (dispPtr->metaModMask != 0)) {
2078     state = (state & ~META_MASK) | dispPtr->metaModMask;
2079     }
2080     if ((state & ALT_MASK) && (dispPtr->altModMask != 0)) {
2081     state = (state & ~ALT_MASK) | dispPtr->altModMask;
2082     }
2083    
2084     if ((state & modMask) != modMask) {
2085     goto nextSequence;
2086     }
2087     }
2088     if (psPtr->flags & PAT_NEARBY) {
2089     XEvent *firstPtr;
2090     int timeDiff;
2091    
2092     firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
2093     timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;
2094     if ((firstPtr->xkey.x_root
2095     < (eventPtr->xkey.x_root - NEARBY_PIXELS))
2096     || (firstPtr->xkey.x_root
2097     > (eventPtr->xkey.x_root + NEARBY_PIXELS))
2098     || (firstPtr->xkey.y_root
2099     < (eventPtr->xkey.y_root - NEARBY_PIXELS))
2100     || (firstPtr->xkey.y_root
2101     > (eventPtr->xkey.y_root + NEARBY_PIXELS))
2102     || (timeDiff > NEARBY_MS)) {
2103     goto nextSequence;
2104     }
2105     }
2106     patPtr++;
2107     patCount--;
2108     nextEvent:
2109     if (eventPtr == bindPtr->eventRing) {
2110     eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
2111     detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
2112     } else {
2113     eventPtr--;
2114     detailPtr--;
2115     }
2116     ringCount--;
2117     }
2118    
2119     matchPtr = psPtr;
2120     sourcePtr = psPtr;
2121    
2122     if (objectPtr != NULL) {
2123     int iVirt;
2124     VirtualOwners *voPtr;
2125     PatternTableKey key;
2126    
2127     /*
2128     * The sequence matches the physical constraints.
2129     * Is this object interested in any of the virtual events
2130     * that correspond to this sequence?
2131     */
2132    
2133     voPtr = psPtr->voPtr;
2134    
2135     memset(&key, 0, sizeof(key));
2136     key.object = *objectPtr;
2137     key.type = VirtualEvent;
2138     key.detail.clientData = 0;
2139    
2140     for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
2141     Tcl_HashEntry *hPtr = voPtr->owners[iVirt];
2142    
2143     key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr,
2144     hPtr);
2145     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable,
2146     (char *) &key);
2147     if (hPtr != NULL) {
2148    
2149     /*
2150     * This tag is interested in this virtual event and its
2151     * corresponding physical event is a good match with the
2152     * virtual event's definition.
2153     */
2154    
2155     PatSeq *virtMatchPtr;
2156    
2157     virtMatchPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
2158     if ((virtMatchPtr->numPats != 1)
2159     || (virtMatchPtr->nextSeqPtr != NULL)) {
2160     panic("MatchPattern: badly constructed virtual event");
2161     }
2162     sourcePtr = virtMatchPtr;
2163     goto match;
2164     }
2165     }
2166    
2167     /*
2168     * The physical event matches a virtual event's definition, but
2169     * the tag isn't interested in it.
2170     */
2171     goto nextSequence;
2172     }
2173     match:
2174    
2175     /*
2176     * This sequence matches. If we've already got another match,
2177     * pick whichever is most specific. Detail is most important,
2178     * then needMods.
2179     */
2180    
2181     if (bestPtr != NULL) {
2182     Pattern *patPtr2;
2183     int i;
2184    
2185     if (matchPtr->numPats != bestPtr->numPats) {
2186     if (bestPtr->numPats > matchPtr->numPats) {
2187     goto nextSequence;
2188     } else {
2189     goto newBest;
2190     }
2191     }
2192     for (i = 0, patPtr = matchPtr->pats, patPtr2 = bestPtr->pats;
2193     i < matchPtr->numPats; i++, patPtr++, patPtr2++) {
2194     if (patPtr->detail.clientData != patPtr2->detail.clientData) {
2195     if (patPtr->detail.clientData == 0) {
2196     goto nextSequence;
2197     } else {
2198     goto newBest;
2199     }
2200     }
2201     if (patPtr->needMods != patPtr2->needMods) {
2202     if ((patPtr->needMods & patPtr2->needMods)
2203     == patPtr->needMods) {
2204     goto nextSequence;
2205     } else if ((patPtr->needMods & patPtr2->needMods)
2206     == patPtr2->needMods) {
2207     goto newBest;
2208     }
2209     }
2210     }
2211     /*
2212     * Tie goes to current best pattern.
2213     *
2214     * (1) For virtual vs. virtual, the least recently defined
2215     * virtual wins, because virtuals are examined in order of
2216     * definition. This order is _not_ guaranteed in the
2217     * documentation.
2218     *
2219     * (2) For virtual vs. physical, the physical wins because all
2220     * the physicals are examined before the virtuals. This order
2221     * is guaranteed in the documentation.
2222     *
2223     * (3) For physical vs. physical pattern, the most recently
2224     * defined physical wins, because physicals are examined in
2225     * reverse order of definition. This order is guaranteed in
2226     * the documentation.
2227     */
2228    
2229     goto nextSequence;
2230     }
2231     newBest:
2232     bestPtr = matchPtr;
2233     bestSourcePtr = sourcePtr;
2234    
2235     nextSequence:
2236     continue;
2237     }
2238    
2239     *sourcePtrPtr = bestSourcePtr;
2240     return bestPtr;
2241     }
2242    
2243     /*
2244     *--------------------------------------------------------------
2245     *
2246     * ExpandPercents --
2247     *
2248     * Given a command and an event, produce a new command
2249     * by replacing % constructs in the original command
2250     * with information from the X event.
2251     *
2252     * Results:
2253     * The new expanded command is appended to the dynamic string
2254     * given by dsPtr.
2255     *
2256     * Side effects:
2257     * None.
2258     *
2259     *--------------------------------------------------------------
2260     */
2261    
2262     static void
2263     ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
2264     TkWindow *winPtr; /* Window where event occurred: needed to
2265     * get input context. */
2266     char *before; /* Command containing percent expressions
2267     * to be replaced. */
2268     XEvent *eventPtr; /* X event containing information to be
2269     * used in % replacements. */
2270     KeySym keySym; /* KeySym: only relevant for KeyPress and
2271     * KeyRelease events). */
2272     Tcl_DString *dsPtr; /* Dynamic string in which to append new
2273     * command. */
2274     {
2275     int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
2276     * list element. */
2277     int number, flags, length;
2278     #define NUM_SIZE 40
2279     char *string;
2280     Tcl_DString buf;
2281     char numStorage[NUM_SIZE+1];
2282    
2283     Tcl_DStringInit(&buf);
2284    
2285     if (eventPtr->type < TK_LASTEVENT) {
2286     flags = flagArray[eventPtr->type];
2287     } else {
2288     flags = 0;
2289     }
2290     while (1) {
2291     /*
2292     * Find everything up to the next % character and append it
2293     * to the result string.
2294     */
2295    
2296     for (string = before; (*string != 0) && (*string != '%'); string++) {
2297     /* Empty loop body. */
2298     }
2299     if (string != before) {
2300     Tcl_DStringAppend(dsPtr, before, string-before);
2301     before = string;
2302     }
2303     if (*before == 0) {
2304     break;
2305     }
2306    
2307     /*
2308     * There's a percent sequence here. Process it.
2309     */
2310    
2311     number = 0;
2312     string = "??";
2313     switch (before[1]) {
2314     case '#':
2315     number = eventPtr->xany.serial;
2316     goto doNumber;
2317     case 'a':
2318     if (flags & CONFIG) {
2319     TkpPrintWindowId(numStorage, eventPtr->xconfigure.above);
2320     string = numStorage;
2321     }
2322     goto doString;
2323     case 'b':
2324     number = eventPtr->xbutton.button;
2325     goto doNumber;
2326     case 'c':
2327     if (flags & EXPOSE) {
2328     number = eventPtr->xexpose.count;
2329     }
2330     goto doNumber;
2331     case 'd':
2332     if (flags & (CROSSING|FOCUS)) {
2333     if (flags & FOCUS) {
2334     number = eventPtr->xfocus.detail;
2335     } else {
2336     number = eventPtr->xcrossing.detail;
2337     }
2338     string = TkFindStateString(notifyDetail, number);
2339     }
2340     goto doString;
2341     case 'f':
2342     number = eventPtr->xcrossing.focus;
2343     goto doNumber;
2344     case 'h':
2345     if (flags & EXPOSE) {
2346     number = eventPtr->xexpose.height;
2347     } else if (flags & (CONFIG)) {
2348     number = eventPtr->xconfigure.height;
2349     }
2350     goto doNumber;
2351     case 'k':
2352     number = eventPtr->xkey.keycode;
2353     goto doNumber;
2354     case 'm':
2355     if (flags & CROSSING) {
2356     number = eventPtr->xcrossing.mode;
2357     } else if (flags & FOCUS) {
2358     number = eventPtr->xfocus.mode;
2359     }
2360     string = TkFindStateString(notifyMode, number);
2361     goto doString;
2362     case 'o':
2363     if (flags & CREATE) {
2364     number = eventPtr->xcreatewindow.override_redirect;
2365     } else if (flags & MAP) {
2366     number = eventPtr->xmap.override_redirect;
2367     } else if (flags & REPARENT) {
2368     number = eventPtr->xreparent.override_redirect;
2369     } else if (flags & CONFIG) {
2370     number = eventPtr->xconfigure.override_redirect;
2371     }
2372     goto doNumber;
2373     case 'p':
2374     string = TkFindStateString(circPlace, eventPtr->xcirculate.place);
2375     goto doString;
2376     case 's':
2377     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2378     number = eventPtr->xkey.state;
2379     } else if (flags & CROSSING) {
2380     number = eventPtr->xcrossing.state;
2381     } else if (flags & VISIBILITY) {
2382     string = TkFindStateString(visNotify,
2383     eventPtr->xvisibility.state);
2384     goto doString;
2385     }
2386     goto doNumber;
2387     case 't':
2388     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2389     number = (int) eventPtr->xkey.time;
2390     } else if (flags & CROSSING) {
2391     number = (int) eventPtr->xcrossing.time;
2392     } else if (flags & PROP) {
2393     number = (int) eventPtr->xproperty.time;
2394     }
2395     goto doNumber;
2396     case 'v':
2397     number = eventPtr->xconfigurerequest.value_mask;
2398     goto doNumber;
2399     case 'w':
2400     if (flags & EXPOSE) {
2401     number = eventPtr->xexpose.width;
2402     } else if (flags & CONFIG) {
2403     number = eventPtr->xconfigure.width;
2404     }
2405     goto doNumber;
2406     case 'x':
2407     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2408     number = eventPtr->xkey.x;
2409     } else if (flags & CROSSING) {
2410     number = eventPtr->xcrossing.x;
2411     } else if (flags & EXPOSE) {
2412     number = eventPtr->xexpose.x;
2413     } else if (flags & (CREATE|CONFIG|GRAVITY)) {
2414     number = eventPtr->xcreatewindow.x;
2415     } else if (flags & REPARENT) {
2416     number = eventPtr->xreparent.x;
2417     }
2418     goto doNumber;
2419     case 'y':
2420     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2421     number = eventPtr->xkey.y;
2422     } else if (flags & EXPOSE) {
2423     number = eventPtr->xexpose.y;
2424     } else if (flags & (CREATE|CONFIG|GRAVITY)) {
2425     number = eventPtr->xcreatewindow.y;
2426     } else if (flags & REPARENT) {
2427     number = eventPtr->xreparent.y;
2428     } else if (flags & CROSSING) {
2429     number = eventPtr->xcrossing.y;
2430    
2431     }
2432     goto doNumber;
2433     case 'A':
2434     if (flags & KEY) {
2435     Tcl_DStringFree(&buf);
2436     string = TkpGetString(winPtr, eventPtr, &buf);
2437     }
2438     goto doString;
2439     case 'B':
2440     number = eventPtr->xcreatewindow.border_width;
2441     goto doNumber;
2442     case 'D':
2443     /*
2444     * This is used only by the MouseWheel event.
2445     */
2446    
2447     number = eventPtr->xkey.keycode;
2448     goto doNumber;
2449     case 'E':
2450     number = (int) eventPtr->xany.send_event;
2451     goto doNumber;
2452     case 'K':
2453     if (flags & KEY) {
2454     char *name;
2455    
2456     name = TkKeysymToString(keySym);
2457     if (name != NULL) {
2458     string = name;
2459     }
2460     }
2461     goto doString;
2462     case 'N':
2463     number = (int) keySym;
2464     goto doNumber;
2465     case 'R':
2466     TkpPrintWindowId(numStorage, eventPtr->xkey.root);
2467     string = numStorage;
2468     goto doString;
2469     case 'S':
2470     TkpPrintWindowId(numStorage, eventPtr->xkey.subwindow);
2471     string = numStorage;
2472     goto doString;
2473     case 'T':
2474     number = eventPtr->type;
2475     goto doNumber;
2476     case 'W': {
2477     Tk_Window tkwin;
2478    
2479     tkwin = Tk_IdToWindow(eventPtr->xany.display,
2480     eventPtr->xany.window);
2481     if (tkwin != NULL) {
2482     string = Tk_PathName(tkwin);
2483     } else {
2484     string = "??";
2485     }
2486     goto doString;
2487     }
2488     case 'X': {
2489     Tk_Window tkwin;
2490     int x, y;
2491     int width, height;
2492    
2493     number = eventPtr->xkey.x_root;
2494     tkwin = Tk_IdToWindow(eventPtr->xany.display,
2495     eventPtr->xany.window);
2496     if (tkwin != NULL) {
2497     Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
2498     number -= x;
2499     }
2500     goto doNumber;
2501     }
2502     case 'Y': {
2503     Tk_Window tkwin;
2504     int x, y;
2505     int width, height;
2506    
2507     number = eventPtr->xkey.y_root;
2508     tkwin = Tk_IdToWindow(eventPtr->xany.display,
2509     eventPtr->xany.window);
2510     if (tkwin != NULL) {
2511     Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
2512     number -= y;
2513     }
2514     goto doNumber;
2515     }
2516     default:
2517     numStorage[0] = before[1];
2518     numStorage[1] = '\0';
2519     string = numStorage;
2520     goto doString;
2521     }
2522    
2523     doNumber:
2524     sprintf(numStorage, "%d", number);
2525     string = numStorage;
2526    
2527     doString:
2528     spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2529     length = Tcl_DStringLength(dsPtr);
2530     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2531     spaceNeeded = Tcl_ConvertElement(string,
2532     Tcl_DStringValue(dsPtr) + length,
2533     cvtFlags | TCL_DONT_USE_BRACES);
2534     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2535     before += 2;
2536     }
2537     Tcl_DStringFree(&buf);
2538     }
2539    
2540     /*
2541     *----------------------------------------------------------------------
2542     *
2543     * ChangeScreen --
2544     *
2545     * This procedure is invoked whenever the current screen changes
2546     * in an application. It invokes a Tcl procedure named
2547     * "tkScreenChanged", passing it the screen name as argument.
2548     * tkScreenChanged does things like making the tkPriv variable
2549     * point to an array for the current display.
2550     *
2551     * Results:
2552     * None.
2553     *
2554     * Side effects:
2555     * Depends on what tkScreenChanged does. If an error occurs
2556     * them tkError will be invoked.
2557     *
2558     *----------------------------------------------------------------------
2559     */
2560    
2561     static void
2562     ChangeScreen(interp, dispName, screenIndex)
2563     Tcl_Interp *interp; /* Interpreter in which to invoke
2564     * command. */
2565     char *dispName; /* Name of new display. */
2566     int screenIndex; /* Index of new screen. */
2567     {
2568     Tcl_DString cmd;
2569     int code;
2570     char screen[TCL_INTEGER_SPACE];
2571    
2572     Tcl_DStringInit(&cmd);
2573     Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);
2574     Tcl_DStringAppend(&cmd, dispName, -1);
2575     sprintf(screen, ".%d", screenIndex);
2576     Tcl_DStringAppend(&cmd, screen, -1);
2577     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));
2578     if (code != TCL_OK) {
2579     Tcl_AddErrorInfo(interp,
2580     "\n (changing screen in event binding)");
2581     Tcl_BackgroundError(interp);
2582     }
2583     }
2584    
2585    
2586     /*
2587     *----------------------------------------------------------------------
2588     *
2589     * Tk_EventCmd --
2590     *
2591     * This procedure is invoked to process the "event" Tcl command.
2592     * It is used to define and generate events.
2593     *
2594     * Results:
2595     * A standard Tcl result.
2596     *
2597     * Side effects:
2598     * See the user documentation.
2599     *
2600     *----------------------------------------------------------------------
2601     */
2602    
2603     int
2604     Tk_EventObjCmd(clientData, interp, objc, objv)
2605     ClientData clientData; /* Main window associated with interpreter. */
2606     Tcl_Interp *interp; /* Current interpreter. */
2607     int objc; /* Number of arguments. */
2608     Tcl_Obj *CONST objv[]; /* Argument objects. */
2609     {
2610     int index;
2611     Tk_Window tkwin;
2612     VirtualEventTable *vetPtr;
2613     TkBindInfo bindInfo;
2614     static char *optionStrings[] = {
2615     "add", "delete", "generate", "info",
2616     NULL
2617     };
2618     enum options {
2619     EVENT_ADD, EVENT_DELETE, EVENT_GENERATE, EVENT_INFO
2620     };
2621    
2622     tkwin = (Tk_Window) clientData;
2623     bindInfo = ((TkWindow *) tkwin)->mainPtr->bindInfo;
2624     vetPtr = &((BindInfo *) bindInfo)->virtualEventTable;
2625    
2626     if (objc < 2) {
2627     Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
2628     return TCL_ERROR;
2629     }
2630     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
2631     &index) != TCL_OK) {
2632     return TCL_ERROR;
2633     }
2634    
2635     switch ((enum options) index) {
2636     case EVENT_ADD: {
2637     int i;
2638     char *name, *event;
2639    
2640     if (objc < 4) {
2641     Tcl_WrongNumArgs(interp, 2, objv,
2642     "virtual sequence ?sequence ...?");
2643     return TCL_ERROR;
2644     }
2645     name = Tcl_GetStringFromObj(objv[2], NULL);
2646     for (i = 3; i < objc; i++) {
2647     event = Tcl_GetStringFromObj(objv[i], NULL);
2648     if (CreateVirtualEvent(interp, vetPtr, name, event) != TCL_OK) {
2649     return TCL_ERROR;
2650     }
2651     }
2652     break;
2653     }
2654     case EVENT_DELETE: {
2655     int i;
2656     char *name, *event;
2657    
2658     if (objc < 3) {
2659     Tcl_WrongNumArgs(interp, 2, objv,
2660     "virtual ?sequence sequence ...?");
2661     return TCL_ERROR;
2662     }
2663     name = Tcl_GetStringFromObj(objv[2], NULL);
2664     if (objc == 3) {
2665     return DeleteVirtualEvent(interp, vetPtr, name, NULL);
2666     }
2667     for (i = 3; i < objc; i++) {
2668     event = Tcl_GetStringFromObj(objv[i], NULL);
2669     if (DeleteVirtualEvent(interp, vetPtr, name, event) != TCL_OK) {
2670     return TCL_ERROR;
2671     }
2672     }
2673     break;
2674     }
2675     case EVENT_GENERATE: {
2676     if (objc < 4) {
2677     Tcl_WrongNumArgs(interp, 2, objv, "window event ?options?");
2678     return TCL_ERROR;
2679     }
2680     return HandleEventGenerate(interp, tkwin, objc - 2, objv + 2);
2681     }
2682     case EVENT_INFO: {
2683     if (objc == 2) {
2684     GetAllVirtualEvents(interp, vetPtr);
2685     return TCL_OK;
2686     } else if (objc == 3) {
2687     return GetVirtualEvent(interp, vetPtr,
2688     Tcl_GetStringFromObj(objv[2], NULL));
2689     } else {
2690     Tcl_WrongNumArgs(interp, 2, objv, "?virtual?");
2691     return TCL_ERROR;
2692     }
2693     }
2694     }
2695     return TCL_OK;
2696     }
2697    
2698     /*
2699     *---------------------------------------------------------------------------
2700     *
2701     * InitVirtualEventTable --
2702     *
2703     * Given storage for a virtual event table, set up the fields to
2704     * prepare a new domain in which virtual events may be defined.
2705     *
2706     * Results:
2707     * None.
2708     *
2709     * Side effects:
2710     * *vetPtr is now initialized.
2711     *
2712     *---------------------------------------------------------------------------
2713     */
2714    
2715     static void
2716     InitVirtualEventTable(vetPtr)
2717     VirtualEventTable *vetPtr; /* Pointer to virtual event table. Memory
2718     * is supplied by the caller. */
2719     {
2720     Tcl_InitHashTable(&vetPtr->patternTable,
2721     sizeof(PatternTableKey) / sizeof(int));
2722     Tcl_InitHashTable(&vetPtr->nameTable, TCL_ONE_WORD_KEYS);
2723     }
2724    
2725     /*
2726     *---------------------------------------------------------------------------
2727     *
2728     * DeleteVirtualEventTable --
2729     *
2730     * Delete the contents of a virtual event table. The caller is
2731     * responsible for freeing any memory used by the table itself.
2732     *
2733     * Results:
2734     * None.
2735     *
2736     * Side effects:
2737     * Memory is freed.
2738     *
2739     *---------------------------------------------------------------------------
2740     */
2741    
2742     static void
2743     DeleteVirtualEventTable(vetPtr)
2744     VirtualEventTable *vetPtr; /* The virtual event table to delete. */
2745     {
2746     Tcl_HashEntry *hPtr;
2747     Tcl_HashSearch search;
2748     PatSeq *psPtr, *nextPtr;
2749    
2750     hPtr = Tcl_FirstHashEntry(&vetPtr->patternTable, &search);
2751     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2752     psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
2753     for ( ; psPtr != NULL; psPtr = nextPtr) {
2754     nextPtr = psPtr->nextSeqPtr;
2755     ckfree((char *) psPtr->voPtr);
2756     ckfree((char *) psPtr);
2757     }
2758     }
2759     Tcl_DeleteHashTable(&vetPtr->patternTable);
2760    
2761     hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search);
2762     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2763     ckfree((char *) Tcl_GetHashValue(hPtr));
2764     }
2765     Tcl_DeleteHashTable(&vetPtr->nameTable);
2766     }
2767    
2768     /*
2769     *----------------------------------------------------------------------
2770     *
2771     * CreateVirtualEvent --
2772     *
2773     * Add a new definition for a virtual event. If the virtual event
2774     * is already defined, the new definition augments those that
2775     * already exist.
2776     *
2777     * Results:
2778     * The return value is TCL_ERROR if an error occured while
2779     * creating the virtual binding. In this case, an error message
2780     * will be left in the interp's result. If all went well then the
2781     * return value is TCL_OK.
2782     *
2783     * Side effects:
2784     * The virtual event may cause future calls to Tk_BindEvent to
2785     * behave differently than they did previously.
2786     *
2787     *----------------------------------------------------------------------
2788     */
2789    
2790     static int
2791     CreateVirtualEvent(interp, vetPtr, virtString, eventString)
2792     Tcl_Interp *interp; /* Used for error reporting. */
2793     VirtualEventTable *vetPtr;/* Table in which to augment virtual event. */
2794     char *virtString; /* Name of new virtual event. */
2795     char *eventString; /* String describing physical event that
2796     * triggers virtual event. */
2797     {
2798     PatSeq *psPtr;
2799     int dummy;
2800     Tcl_HashEntry *vhPtr;
2801     unsigned long eventMask;
2802     PhysicalsOwned *poPtr;
2803     VirtualOwners *voPtr;
2804     Tk_Uid virtUid;
2805    
2806     virtUid = GetVirtualEventUid(interp, virtString);
2807     if (virtUid == NULL) {
2808     return TCL_ERROR;
2809     }
2810    
2811     /*
2812     * Find/create physical event
2813     */
2814    
2815     psPtr = FindSequence(interp, &vetPtr->patternTable, NULL, eventString,
2816     1, 0, &eventMask);
2817     if (psPtr == NULL) {
2818     return TCL_ERROR;
2819     }
2820    
2821     /*
2822     * Find/create virtual event.
2823     */
2824    
2825     vhPtr = Tcl_CreateHashEntry(&vetPtr->nameTable, virtUid, &dummy);
2826    
2827     /*
2828     * Make virtual event own the physical event.
2829     */
2830    
2831     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
2832     if (poPtr == NULL) {
2833     poPtr = (PhysicalsOwned *) ckalloc(sizeof(PhysicalsOwned));
2834     poPtr->numOwned = 0;
2835     } else {
2836     /*
2837     * See if this virtual event is already defined for this physical
2838     * event and just return if it is.
2839     */
2840    
2841     int i;
2842     for (i = 0; i < poPtr->numOwned; i++) {
2843     if (poPtr->patSeqs[i] == psPtr) {
2844     return TCL_OK;
2845     }
2846     }
2847     poPtr = (PhysicalsOwned *) ckrealloc((char *) poPtr,
2848     sizeof(PhysicalsOwned) + poPtr->numOwned * sizeof(PatSeq *));
2849     }
2850     Tcl_SetHashValue(vhPtr, (ClientData) poPtr);
2851     poPtr->patSeqs[poPtr->numOwned] = psPtr;
2852     poPtr->numOwned++;
2853    
2854     /*
2855     * Make physical event so it can trigger the virtual event.
2856     */
2857    
2858     voPtr = psPtr->voPtr;
2859     if (voPtr == NULL) {
2860     voPtr = (VirtualOwners *) ckalloc(sizeof(VirtualOwners));
2861     voPtr->numOwners = 0;
2862     } else {
2863     voPtr = (VirtualOwners *) ckrealloc((char *) voPtr,
2864     sizeof(VirtualOwners)
2865     + voPtr->numOwners * sizeof(Tcl_HashEntry *));
2866     }
2867     psPtr->voPtr = voPtr;
2868     voPtr->owners[voPtr->numOwners] = vhPtr;
2869     voPtr->numOwners++;
2870    
2871     return TCL_OK;
2872     }
2873    
2874     /*
2875     *--------------------------------------------------------------
2876     *
2877     * DeleteVirtualEvent --
2878     *
2879     * Remove the definition of a given virtual event. If the
2880     * event string is NULL, all definitions of the virtual event
2881     * will be removed. Otherwise, just the specified definition
2882     * of the virtual event will be removed.
2883     *
2884     * Results:
2885     * The result is a standard Tcl return value. If an error
2886     * occurs then the interp's result will contain an error message.
2887     * It is not an error to attempt to delete a virtual event that
2888     * does not exist or a definition that does not exist.
2889     *
2890     * Side effects:
2891     * The virtual event given by virtString may be removed from the
2892     * virtual event table.
2893     *
2894     *--------------------------------------------------------------
2895     */
2896    
2897     static int
2898     DeleteVirtualEvent(interp, vetPtr, virtString, eventString)
2899     Tcl_Interp *interp; /* Used for error reporting. */
2900     VirtualEventTable *vetPtr;/* Table in which to delete event. */
2901     char *virtString; /* String describing event sequence that
2902     * triggers binding. */
2903     char *eventString; /* The event sequence that should be deleted,
2904     * or NULL to delete all event sequences for
2905     * the entire virtual event. */
2906     {
2907     int iPhys;
2908     Tk_Uid virtUid;
2909     Tcl_HashEntry *vhPtr;
2910     PhysicalsOwned *poPtr;
2911     PatSeq *eventPSPtr;
2912    
2913     virtUid = GetVirtualEventUid(interp, virtString);
2914     if (virtUid == NULL) {
2915     return TCL_ERROR;
2916     }
2917    
2918     vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid);
2919     if (vhPtr == NULL) {
2920     return TCL_OK;
2921     }
2922     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
2923    
2924     eventPSPtr = NULL;
2925     if (eventString != NULL) {
2926     unsigned long eventMask;
2927    
2928     /*
2929     * Delete only the specific physical event associated with the
2930     * virtual event. If the physical event doesn't already exist, or
2931     * the virtual event doesn't own that physical event, return w/o
2932     * doing anything.
2933     */
2934    
2935     eventPSPtr = FindSequence(interp, &vetPtr->patternTable, NULL,
2936     eventString, 0, 0, &eventMask);
2937     if (eventPSPtr == NULL) {
2938     char *string;
2939    
2940     string = Tcl_GetStringResult(interp);
2941     return (string[0] != '\0') ? TCL_ERROR : TCL_OK;
2942     }
2943     }
2944    
2945     for (iPhys = poPtr->numOwned; --iPhys >= 0; ) {
2946     PatSeq *psPtr = poPtr->patSeqs[iPhys];
2947     if ((eventPSPtr == NULL) || (psPtr == eventPSPtr)) {
2948     int iVirt;
2949     VirtualOwners *voPtr;
2950    
2951     /*
2952     * Remove association between this physical event and the given
2953     * virtual event that it triggers.
2954     */
2955    
2956     voPtr = psPtr->voPtr;
2957     for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
2958     if (voPtr->owners[iVirt] == vhPtr) {
2959     break;
2960     }
2961     }
2962     if (iVirt == voPtr->numOwners) {
2963     panic("DeleteVirtualEvent: couldn't find owner");
2964     }
2965     voPtr->numOwners--;
2966     if (voPtr->numOwners == 0) {
2967     /*
2968     * Removed last reference to this physical event, so
2969     * remove it from physical->virtual map.
2970     */
2971     PatSeq *prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
2972     if (prevPtr == psPtr) {
2973     if (psPtr->nextSeqPtr == NULL) {
2974     Tcl_DeleteHashEntry(psPtr->hPtr);
2975     } else {
2976     Tcl_SetHashValue(psPtr->hPtr,
2977     psPtr->nextSeqPtr);
2978     }
2979     } else {
2980     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
2981     if (prevPtr == NULL) {
2982     panic("DeleteVirtualEvent couldn't find on hash chain");
2983     }
2984     if (prevPtr->nextSeqPtr == psPtr) {
2985     prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
2986     break;
2987     }
2988     }
2989     }
2990     ckfree((char *) psPtr->voPtr);
2991     ckfree((char *) psPtr);
2992     } else {
2993     /*
2994     * This physical event still triggers some other virtual
2995     * event(s). Consolidate the list of virtual owners for
2996     * this physical event so it no longer triggers the
2997     * given virtual event.
2998     */
2999     voPtr->owners[iVirt] = voPtr->owners[voPtr->numOwners];
3000     }
3001    
3002     /*
3003     * Now delete the virtual event's reference to the physical
3004     * event.
3005     */
3006    
3007     poPtr->numOwned--;
3008     if (eventPSPtr != NULL && poPtr->numOwned != 0) {
3009     /*
3010     * Just deleting this one physical event. Consolidate list
3011     * of owned physical events and return.
3012     */
3013    
3014     poPtr->patSeqs[iPhys] = poPtr->patSeqs[poPtr->numOwned];
3015     return TCL_OK;
3016     }
3017     }
3018     }
3019    
3020     if (poPtr->numOwned == 0) {
3021     /*
3022     * All the physical events for this virtual event were deleted,
3023     * either because there was only one associated physical event or
3024     * because the caller was deleting the entire virtual event. Now
3025     * the virtual event itself should be deleted.
3026     */
3027    
3028     ckfree((char *) poPtr);
3029     Tcl_DeleteHashEntry(vhPtr);
3030     }
3031     return TCL_OK;
3032     }
3033    
3034     /*
3035     *---------------------------------------------------------------------------
3036     *
3037     * GetVirtualEvent --
3038     *
3039     * Return the list of physical events that can invoke the
3040     * given virtual event.
3041     *
3042     * Results:
3043     * The return value is TCL_OK and the interp's result is filled with the
3044     * string representation of the physical events associated with the
3045     * virtual event; if there are no physical events for the given virtual
3046     * event, the interp's result is filled with and empty string. If the
3047     * virtual event string is improperly formed, then TCL_ERROR is
3048     * returned and an error message is left in the interp's result.
3049     *
3050     * Side effects:
3051     * None.
3052     *
3053     *---------------------------------------------------------------------------
3054     */
3055    
3056     static int
3057     GetVirtualEvent(interp, vetPtr, virtString)
3058     Tcl_Interp *interp; /* Interpreter for reporting. */
3059     VirtualEventTable *vetPtr;/* Table in which to look for event. */
3060     char *virtString; /* String describing virtual event. */
3061     {
3062     Tcl_HashEntry *vhPtr;
3063     Tcl_DString ds;
3064     int iPhys;
3065     PhysicalsOwned *poPtr;
3066     Tk_Uid virtUid;
3067    
3068     virtUid = GetVirtualEventUid(interp, virtString);
3069     if (virtUid == NULL) {
3070     return TCL_ERROR;
3071     }
3072    
3073     vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid);
3074     if (vhPtr == NULL) {
3075     return TCL_OK;
3076     }
3077    
3078     Tcl_DStringInit(&ds);
3079    
3080     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
3081     for (iPhys = 0; iPhys < poPtr->numOwned; iPhys++) {
3082     Tcl_DStringSetLength(&ds, 0);
3083     GetPatternString(poPtr->patSeqs[iPhys], &ds);
3084     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
3085     }
3086     Tcl_DStringFree(&ds);
3087    
3088     return TCL_OK;
3089     }
3090    
3091     /*
3092     *--------------------------------------------------------------
3093     *
3094     * GetAllVirtualEvents --
3095     *
3096     * Return a list that contains the names of all the virtual
3097     * event defined.
3098     *
3099     * Results:
3100     * There is no return value. The interp's result is modified to
3101     * hold a Tcl list with one entry for each virtual event in
3102     * nameTable.
3103     *
3104     * Side effects:
3105     * None.
3106     *
3107     *--------------------------------------------------------------
3108     */
3109    
3110     static void
3111     GetAllVirtualEvents(interp, vetPtr)
3112     Tcl_Interp *interp; /* Interpreter returning result. */
3113     VirtualEventTable *vetPtr;/* Table containing events. */
3114     {
3115     Tcl_HashEntry *hPtr;
3116     Tcl_HashSearch search;
3117     Tcl_DString ds;
3118    
3119     Tcl_DStringInit(&ds);
3120    
3121     hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search);
3122     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
3123     Tcl_DStringSetLength(&ds, 0);
3124     Tcl_DStringAppend(&ds, "<<", 2);
3125     Tcl_DStringAppend(&ds, Tcl_GetHashKey(hPtr->tablePtr, hPtr), -1);
3126     Tcl_DStringAppend(&ds, ">>", 2);
3127     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
3128     }
3129    
3130     Tcl_DStringFree(&ds);
3131     }
3132    
3133     /*
3134     *---------------------------------------------------------------------------
3135     *
3136     * HandleEventGenerate --
3137     *
3138     * Helper function for the "event generate" command. Generate and
3139     * process an XEvent, constructed from information parsed from the
3140     * event description string and its optional arguments.
3141     *
3142     * argv[0] contains name of the target window.
3143     * argv[1] contains pattern string for one event (e.g, <Control-v>).
3144     * argv[2..argc-1] contains -field/option pairs for specifying
3145     * additional detail in the generated event.
3146     *
3147     * Either virtual or physical events can be generated this way.
3148     * The event description string must contain the specification
3149     * for only one event.
3150     *
3151     * Results:
3152     * None.
3153     *
3154     * Side effects:
3155     * When constructing the event,
3156     * event.xany.serial is filled with the current X serial number.
3157     * event.xany.window is filled with the target window.
3158     * event.xany.display is filled with the target window's display.
3159     * Any other fields in eventPtr which are not specified by the pattern
3160     * string or the optional arguments, are set to 0.
3161     *
3162     * The event may be handled sychronously or asynchronously, depending
3163     * on the value specified by the optional "-when" option. The
3164     * default setting is synchronous.
3165     *
3166     *---------------------------------------------------------------------------
3167     */
3168     static int
3169     HandleEventGenerate(interp, mainWin, objc, objv)
3170     Tcl_Interp *interp; /* Interp for errors return and name lookup. */
3171     Tk_Window mainWin; /* Main window associated with interp. */
3172     int objc; /* Number of arguments. */
3173     Tcl_Obj *CONST objv[]; /* Argument objects. */
3174     {
3175     XEvent event;
3176     char *name, *p, *windowName;
3177     int count, flags, synch, i, number, warp;
3178     Tcl_QueuePosition pos;
3179     Pattern pat;
3180     Tk_Window tkwin, tkwin2;
3181     TkWindow *mainPtr;
3182     unsigned long eventMask;
3183     static char *fieldStrings[] = {
3184     "-when", "-above", "-borderwidth", "-button",
3185     "-count", "-delta", "-detail", "-focus",
3186     "-height",
3187     "-keycode", "-keysym", "-mode", "-override",
3188     "-place", "-root", "-rootx", "-rooty",
3189     "-sendevent", "-serial", "-state", "-subwindow",
3190     "-time", "-warp", "-width", "-window",
3191     "-x", "-y", NULL
3192     };
3193     enum field {
3194     EVENT_WHEN, EVENT_ABOVE, EVENT_BORDER, EVENT_BUTTON,
3195     EVENT_COUNT, EVENT_DELTA, EVENT_DETAIL, EVENT_FOCUS,
3196     EVENT_HEIGHT,
3197     EVENT_KEYCODE, EVENT_KEYSYM, EVENT_MODE, EVENT_OVERRIDE,
3198     EVENT_PLACE, EVENT_ROOT, EVENT_ROOTX, EVENT_ROOTY,
3199     EVENT_SEND, EVENT_SERIAL, EVENT_STATE, EVENT_SUBWINDOW,
3200     EVENT_TIME, EVENT_WARP, EVENT_WIDTH, EVENT_WINDOW,
3201     EVENT_X, EVENT_Y
3202     };
3203    
3204     windowName = Tcl_GetStringFromObj(objv[0], NULL);
3205     if (!windowName[0]) {
3206     tkwin = mainWin;
3207     } else if (NameToWindow(interp, mainWin, objv[0], &tkwin) != TCL_OK) {
3208     return TCL_ERROR;
3209     }
3210    
3211     mainPtr = (TkWindow *) mainWin;
3212     if ((tkwin == NULL)
3213     || (mainPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr)) {
3214     char *name;
3215    
3216     name = Tcl_GetStringFromObj(objv[0], NULL);
3217     Tcl_AppendResult(interp, "window id \"", name,
3218     "\" doesn't exist in this application", (char *) NULL);
3219     return TCL_ERROR;
3220     }
3221    
3222     name = Tcl_GetStringFromObj(objv[1], NULL);
3223    
3224     p = name;
3225     eventMask = 0;
3226     count = ParseEventDescription(interp, &p, &pat, &eventMask);
3227     if (count == 0) {
3228     return TCL_ERROR;
3229     }
3230     if (count != 1) {
3231     Tcl_SetResult(interp, "Double or Triple modifier not allowed",
3232     TCL_STATIC);
3233     return TCL_ERROR;
3234     }
3235     if (*p != '\0') {
3236     Tcl_SetResult(interp, "only one event specification allowed",
3237     TCL_STATIC);
3238     return TCL_ERROR;
3239     }
3240    
3241     memset((VOID *) &event, 0, sizeof(event));
3242     event.xany.type = pat.eventType;
3243     event.xany.serial = NextRequest(Tk_Display(tkwin));
3244     event.xany.send_event = False;
3245     if (windowName[0]) {
3246     event.xany.window = Tk_WindowId(tkwin);
3247     } else {
3248     event.xany.window = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
3249     }
3250     event.xany.display = Tk_Display(tkwin);
3251    
3252     flags = flagArray[event.xany.type];
3253     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
3254     event.xkey.state = pat.needMods;
3255     if ((flags & KEY) && (event.xany.type != MouseWheelEvent)) {
3256     TkpSetKeycodeAndState(tkwin, pat.detail.keySym, &event);
3257     } else if (flags & BUTTON) {
3258     event.xbutton.button = pat.detail.button;
3259     } else if (flags & VIRTUAL) {
3260     ((XVirtualEvent *) &event)->name = pat.detail.name;
3261     }
3262     }
3263     if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) {
3264     event.xcreatewindow.window = event.xany.window;
3265     }
3266    
3267     /*
3268     * Process the remaining arguments to fill in additional fields
3269     * of the event.
3270     */
3271    
3272     synch = 1;
3273     warp = 0;
3274     pos = TCL_QUEUE_TAIL;
3275     for (i = 2; i < objc; i += 2) {
3276     Tcl_Obj *optionPtr, *valuePtr;
3277     int index;
3278    
3279     optionPtr = objv[i];
3280     valuePtr = objv[i + 1];
3281    
3282     if (Tcl_GetIndexFromObj(interp, optionPtr, fieldStrings, "option",
3283     TCL_EXACT, &index) != TCL_OK) {
3284     return TCL_ERROR;
3285     }
3286     if (objc & 1) {
3287     /*
3288     * This test occurs after Tcl_GetIndexFromObj() so that
3289     * "event generate <Button> -xyz" will return the error message
3290     * that "-xyz" is a bad option, rather than that the value
3291     * for "-xyz" is missing.
3292     */
3293    
3294     Tcl_AppendResult(interp, "value for \"",
3295     Tcl_GetStringFromObj(optionPtr, NULL), "\" missing",
3296     (char *) NULL);
3297     return TCL_ERROR;
3298     }
3299    
3300     switch ((enum field) index) {
3301     case EVENT_WARP: {
3302     if (Tcl_GetBooleanFromObj(interp, valuePtr, &warp) != TCL_OK) {
3303     return TCL_ERROR;
3304     }
3305     if (!(flags & (KEY_BUTTON_MOTION_VIRTUAL))) {
3306     goto badopt;
3307     }
3308     break;
3309     }
3310     case EVENT_WHEN: {
3311     pos = (Tcl_QueuePosition) TkFindStateNumObj(interp, optionPtr,
3312     queuePosition, valuePtr);
3313     if ((int) pos < -1) {
3314     return TCL_ERROR;
3315     }
3316     synch = 0;
3317     if ((int) pos == -1) {
3318     synch = 1;
3319     }
3320     break;
3321     }
3322     case EVENT_ABOVE: {
3323     if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) {
3324     return TCL_ERROR;
3325     }
3326     if (flags & CONFIG) {
3327     event.xconfigure.above = Tk_WindowId(tkwin2);
3328     } else {
3329     goto badopt;
3330     }
3331     break;
3332     }
3333     case EVENT_BORDER: {
3334     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
3335     return TCL_ERROR;
3336     }
3337     if (flags & (CREATE|CONFIG)) {
3338     event.xcreatewindow.border_width = number;
3339     } else {
3340     goto badopt;
3341     }
3342     break;
3343     }
3344     case EVENT_BUTTON: {
3345     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3346     return TCL_ERROR;
3347     }
3348     if (flags & BUTTON) {
3349     event.xbutton.button = number;
3350     } else {
3351     goto badopt;
3352     }
3353     break;
3354     }
3355     case EVENT_COUNT: {
3356     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3357     return TCL_ERROR;
3358     }
3359     if (flags & EXPOSE) {
3360     event.xexpose.count = number;
3361     } else {
3362     goto badopt;
3363     }
3364     break;
3365     }
3366     case EVENT_DELTA: {
3367     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3368     return TCL_ERROR;
3369     }
3370     if ((flags & KEY) && (event.xkey.type == MouseWheelEvent)) {
3371     event.xkey.keycode = number;
3372     } else {
3373     goto badopt;
3374     }
3375     break;
3376     }
3377     case EVENT_DETAIL: {
3378     number = TkFindStateNumObj(interp, optionPtr, notifyDetail,
3379     valuePtr);
3380     if (number < 0) {
3381     return TCL_ERROR;
3382     }
3383     if (flags & FOCUS) {
3384     event.xfocus.detail = number;
3385     } else if (flags & CROSSING) {
3386     event.xcrossing.detail = number;
3387     } else {
3388     goto badopt;
3389     }
3390     break;
3391     }
3392     case EVENT_FOCUS: {
3393     if (Tcl_GetBooleanFromObj(interp, valuePtr, &number) != TCL_OK) {
3394     return TCL_ERROR;
3395     }
3396     if (flags & CROSSING) {
3397     event.xcrossing.focus = number;
3398     } else {
3399     goto badopt;
3400     }
3401     break;
3402     }
3403     case EVENT_HEIGHT: {
3404     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
3405     return TCL_ERROR;
3406     }
3407     if (flags & EXPOSE) {
3408     event.xexpose.height = number;
3409     } else if (flags & CONFIG) {
3410     event.xconfigure.height = number;
3411     } else {
3412     goto badopt;
3413     }
3414     break;
3415     }
3416     case EVENT_KEYCODE: {
3417     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3418     return TCL_ERROR;
3419     }
3420     if ((flags & KEY) && (event.xkey.type != MouseWheelEvent)) {
3421     event.xkey.keycode = number;
3422     } else {
3423     goto badopt;
3424     }
3425     break;
3426     }
3427     case EVENT_KEYSYM: {
3428     KeySym keysym;
3429     char *value;
3430    
3431     value = Tcl_GetStringFromObj(valuePtr, NULL);
3432     keysym = TkStringToKeysym(value);
3433     if (keysym == NoSymbol) {
3434     Tcl_AppendResult(interp, "unknown keysym \"", value, "\"",
3435     (char *) NULL);
3436     return TCL_ERROR;
3437     }
3438    
3439     TkpSetKeycodeAndState(tkwin, keysym, &event);
3440     if (event.xkey.keycode == 0) {
3441     Tcl_AppendResult(interp, "no keycode for keysym \"", value,
3442     "\"", (char *) NULL);
3443     return TCL_ERROR;
3444     }
3445     if (!(flags & KEY) || (event.xkey.type == MouseWheelEvent)) {
3446     goto badopt;
3447     }
3448     break;
3449     }
3450     case EVENT_MODE: {
3451     number = TkFindStateNumObj(interp, optionPtr, notifyMode,
3452     valuePtr);
3453     if (number < 0) {
3454     return TCL_ERROR;
3455     }
3456     if (flags & CROSSING) {
3457     event.xcrossing.mode = number;
3458     } else if (flags & FOCUS) {
3459     event.xfocus.mode = number;
3460     } else {
3461     goto badopt;
3462     }
3463     break;
3464     }
3465     case EVENT_OVERRIDE: {
3466     if (Tcl_GetBooleanFromObj(interp, valuePtr, &number) != TCL_OK) {
3467     return TCL_ERROR;
3468     }
3469     if (flags & CREATE) {
3470     event.xcreatewindow.override_redirect = number;
3471     } else if (flags & MAP) {
3472     event.xmap.override_redirect = number;
3473     } else if (flags & REPARENT) {
3474     event.xreparent.override_redirect = number;
3475     } else if (flags & CONFIG) {
3476     event.xconfigure.override_redirect = number;
3477     } else {
3478     goto badopt;
3479     }
3480     break;
3481     }
3482     case EVENT_PLACE: {
3483     number = TkFindStateNumObj(interp, optionPtr, circPlace,
3484     valuePtr);
3485     if (number < 0) {
3486     return TCL_ERROR;
3487     }
3488     if (flags & CIRC) {
3489     event.xcirculate.place = number;
3490     } else {
3491     goto badopt;
3492     }
3493     break;
3494     }
3495     case EVENT_ROOT: {
3496     if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) {
3497     return TCL_ERROR;
3498     }
3499     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3500     event.xkey.root = Tk_WindowId(tkwin2);
3501     } else {
3502     goto badopt;
3503     }
3504     break;
3505     }
3506     case EVENT_ROOTX: {
3507     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
3508     return TCL_ERROR;
3509     }
3510     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3511     event.xkey.x_root = number;
3512     } else {
3513     goto badopt;
3514     }
3515     break;
3516     }
3517     case EVENT_ROOTY: {
3518     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
3519     return TCL_ERROR;
3520     }
3521     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3522     event.xkey.y_root = number;
3523     } else {
3524     goto badopt;
3525     }
3526     break;
3527     }
3528     case EVENT_SEND: {
3529     CONST char *value;
3530    
3531     value = Tcl_GetStringFromObj(valuePtr, NULL);
3532     if (isdigit(UCHAR(value[0]))) {
3533     /*
3534     * Allow arbitrary integer values for the field; they
3535     * are needed by a few of the tests in the Tk test suite.
3536     */
3537    
3538     if (Tcl_GetIntFromObj(interp, valuePtr, &number)
3539     != TCL_OK) {
3540     return TCL_ERROR;
3541     }
3542     } else {
3543     if (Tcl_GetBooleanFromObj(interp, valuePtr, &number)
3544     != TCL_OK) {
3545     return TCL_ERROR;
3546     }
3547     }
3548     event.xany.send_event = number;
3549     break;
3550     }
3551     case EVENT_SERIAL: {
3552     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3553     return TCL_ERROR;
3554     }
3555     event.xany.serial = number;
3556     break;
3557     }
3558     case EVENT_STATE: {
3559     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3560     if (Tcl_GetIntFromObj(interp, valuePtr, &number)
3561     != TCL_OK) {
3562     return TCL_ERROR;
3563     }
3564     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
3565     event.xkey.state = number;
3566     } else {
3567     event.xcrossing.state = number;
3568     }
3569     } else if (flags & VISIBILITY) {
3570     number = TkFindStateNumObj(interp, optionPtr, visNotify,
3571     valuePtr);
3572     if (number < 0) {
3573     return TCL_ERROR;
3574     }
3575     event.xvisibility.state = number;
3576     } else {
3577     goto badopt;
3578     }
3579     break;
3580     }
3581     case EVENT_SUBWINDOW: {
3582     if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) {
3583     return TCL_ERROR;
3584     }
3585     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3586     event.xkey.subwindow = Tk_WindowId(tkwin2);
3587     } else {
3588     goto badopt;
3589     }
3590     break;
3591     }
3592     case EVENT_TIME: {
3593     if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
3594     return TCL_ERROR;
3595     }
3596     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3597     event.xkey.time = (Time) number;
3598     } else if (flags & PROP) {
3599     event.xproperty.time = (Time) number;
3600     } else {
3601     goto badopt;
3602     }
3603     break;
3604     }
3605     case EVENT_WIDTH: {
3606     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
3607     != TCL_OK) {
3608     return TCL_ERROR;
3609     }
3610     if (flags & EXPOSE) {
3611     event.xexpose.width = number;
3612     } else if (flags & (CREATE|CONFIG)) {
3613     event.xcreatewindow.width = number;
3614     } else {
3615     goto badopt;
3616     }
3617     break;
3618     }
3619     case EVENT_WINDOW: {
3620     if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) {
3621     return TCL_ERROR;
3622     }
3623     if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG
3624     |GRAVITY|CIRC)) {
3625     event.xcreatewindow.window = Tk_WindowId(tkwin2);
3626     } else {
3627     goto badopt;
3628     }
3629     break;
3630     }
3631     case EVENT_X: {
3632     int rootX, rootY;
3633    
3634     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
3635     != TCL_OK) {
3636     return TCL_ERROR;
3637     }
3638     Tk_GetRootCoords(tkwin, &rootX, &rootY);
3639     rootX += number;
3640     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3641     event.xkey.x = number;
3642     event.xkey.x_root = rootX;
3643     } else if (flags & EXPOSE) {
3644     event.xexpose.x = number;
3645     } else if (flags & (CREATE|CONFIG|GRAVITY)) {
3646     event.xcreatewindow.x = number;
3647     } else if (flags & REPARENT) {
3648     event.xreparent.x = number;
3649     } else {
3650     goto badopt;
3651     }
3652     break;
3653     }
3654     case EVENT_Y: {
3655     int rootX, rootY;
3656    
3657     if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
3658     != TCL_OK) {
3659     return TCL_ERROR;
3660     }
3661     Tk_GetRootCoords(tkwin, &rootX, &rootY);
3662     rootY += number;
3663     if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3664     event.xkey.y = number;
3665     event.xkey.y_root = rootY;
3666     } else if (flags & EXPOSE) {
3667     event.xexpose.y = number;
3668     } else if (flags & (CREATE|CONFIG|GRAVITY)) {
3669     event.xcreatewindow.y = number;
3670     } else if (flags & REPARENT) {
3671     event.xreparent.y = number;
3672     } else {
3673     goto badopt;
3674     }
3675     break;
3676     }
3677     }
3678     continue;
3679    
3680     badopt:
3681     Tcl_AppendResult(interp, name, " event doesn't accept \"",
3682     Tcl_GetStringFromObj(optionPtr, NULL), "\" option", NULL);
3683     return TCL_ERROR;
3684     }
3685     if (synch != 0) {
3686     Tk_HandleEvent(&event);
3687     } else {
3688     Tk_QueueWindowEvent(&event, pos);
3689     }
3690     /*
3691     * We only allow warping if the window is mapped
3692     */
3693     if ((warp != 0) && Tk_IsMapped(tkwin)) {
3694     TkDisplay *dispPtr;
3695     dispPtr = TkGetDisplay(event.xmotion.display);
3696     if (!dispPtr->warpInProgress) {
3697     Tcl_DoWhenIdle(DoWarp, (ClientData) dispPtr);
3698     dispPtr->warpInProgress = 1;
3699     }
3700     dispPtr->warpWindow = event.xany.window;
3701     dispPtr->warpX = event.xkey.x;
3702     dispPtr->warpY = event.xkey.y;
3703     }
3704     Tcl_ResetResult(interp);
3705     return TCL_OK;
3706    
3707     }
3708     static int
3709     NameToWindow(interp, mainWin, objPtr, tkwinPtr)
3710     Tcl_Interp *interp; /* Interp for error return and name lookup. */
3711     Tk_Window mainWin; /* Main window of application. */
3712     Tcl_Obj *objPtr; /* Contains name or id string of window. */
3713     Tk_Window *tkwinPtr; /* Filled with token for window. */
3714     {
3715     char *name;
3716     Tk_Window tkwin;
3717     int id;
3718    
3719     name = Tcl_GetStringFromObj(objPtr, NULL);
3720     if (name[0] == '.') {
3721     tkwin = Tk_NameToWindow(interp, name, mainWin);
3722     if (tkwin == NULL) {
3723     return TCL_ERROR;
3724     }
3725     *tkwinPtr = tkwin;
3726     } else {
3727     if (TkpScanWindowId(NULL, name, &id) != TCL_OK) {
3728     Tcl_AppendResult(interp, "bad window name/identifier \"",
3729     name, "\"", (char *) NULL);
3730     return TCL_ERROR;
3731     }
3732     *tkwinPtr = Tk_IdToWindow(Tk_Display(mainWin), (Window) id);
3733     }
3734     return TCL_OK;
3735     }
3736    
3737     /*
3738     *-------------------------------------------------------------------------
3739     *
3740     * DoWarp --
3741     *
3742     * Perform Warping of X pointer. Executed as an idle handler only.
3743     *
3744     * Results:
3745     * None
3746     *
3747     * Side effects:
3748     * X Pointer will move to a new location.
3749     *
3750     *-------------------------------------------------------------------------
3751     */
3752     static void
3753     DoWarp(clientData)
3754     ClientData clientData;
3755     {
3756     TkDisplay *dispPtr = (TkDisplay *) clientData;
3757    
3758     XWarpPointer(dispPtr->display, (Window) None, (Window) dispPtr->warpWindow,
3759     0, 0, 0, 0, (int) dispPtr->warpX, (int) dispPtr->warpY);
3760     XForceScreenSaver(dispPtr->display, ScreenSaverReset);
3761     dispPtr->warpInProgress = 0;
3762     }
3763    
3764     /*
3765     *-------------------------------------------------------------------------
3766     *
3767     * GetVirtualEventUid --
3768     *
3769     * Determine if the given string is in the proper format for a
3770     * virtual event.
3771     *
3772     * Results:
3773     * The return value is NULL if the virtual event string was
3774     * not in the proper format. In this case, an error message
3775     * will be left in the interp's result. Otherwise the return
3776     * value is a Tk_Uid that represents the virtual event.
3777     *
3778     * Side effects:
3779     * None.
3780     *
3781     *-------------------------------------------------------------------------
3782     */
3783     static Tk_Uid
3784     GetVirtualEventUid(interp, virtString)
3785     Tcl_Interp *interp;
3786     char *virtString;
3787     {
3788     Tk_Uid uid;
3789     int length;
3790    
3791     length = strlen(virtString);
3792    
3793     if (length < 5 || virtString[0] != '<' || virtString[1] != '<' ||
3794     virtString[length - 2] != '>' || virtString[length - 1] != '>') {
3795     Tcl_AppendResult(interp, "virtual event \"", virtString,
3796     "\" is badly formed", (char *) NULL);
3797     return NULL;
3798     }
3799     virtString[length - 2] = '\0';
3800     uid = Tk_GetUid(virtString + 2);
3801     virtString[length - 2] = '>';
3802    
3803     return uid;
3804     }
3805    
3806    
3807     /*
3808     *----------------------------------------------------------------------
3809     *
3810     * FindSequence --
3811     *
3812     * Find the entry in the pattern table that corresponds to a
3813     * particular pattern string, and return a pointer to that
3814     * entry.
3815     *
3816     * Results:
3817     * The return value is normally a pointer to the PatSeq
3818     * in patternTable that corresponds to eventString. If an error
3819     * was found while parsing eventString, or if "create" is 0 and
3820     * no pattern sequence previously existed, then NULL is returned
3821     * and the interp's result contains a message describing the problem.
3822     * If no pattern sequence previously existed for eventString, then
3823     * a new one is created with a NULL command field. In a successful
3824     * return, *maskPtr is filled in with a mask of the event types
3825     * on which the pattern sequence depends.
3826     *
3827     * Side effects:
3828     * A new pattern sequence may be allocated.
3829     *
3830     *----------------------------------------------------------------------
3831     */
3832    
3833     static PatSeq *
3834     FindSequence(interp, patternTablePtr, object, eventString, create,
3835     allowVirtual, maskPtr)
3836     Tcl_Interp *interp; /* Interpreter to use for error
3837     * reporting. */
3838     Tcl_HashTable *patternTablePtr; /* Table to use for lookup. */
3839     ClientData object; /* For binding table, token for object with
3840     * which binding is associated.
3841     * For virtual event table, NULL. */
3842     char *eventString; /* String description of pattern to
3843     * match on. See user documentation
3844     * for details. */
3845     int create; /* 0 means don't create the entry if
3846     * it doesn't already exist. Non-zero
3847     * means create. */
3848     int allowVirtual; /* 0 means that virtual events are not
3849     * allowed in the sequence. Non-zero
3850     * otherwise. */
3851     unsigned long *maskPtr; /* *maskPtr is filled in with the event
3852     * types on which this pattern sequence
3853     * depends. */
3854     {
3855    
3856     Pattern pats[EVENT_BUFFER_SIZE];
3857     int numPats, virtualFound;
3858     char *p;
3859     Pattern *patPtr;
3860     PatSeq *psPtr;
3861     Tcl_HashEntry *hPtr;
3862     int flags, count, new;
3863     size_t sequenceSize;
3864     unsigned long eventMask;
3865     PatternTableKey key;
3866    
3867     /*
3868     *-------------------------------------------------------------
3869     * Step 1: parse the pattern string to produce an array
3870     * of Patterns. The array is generated backwards, so
3871     * that the lowest-indexed pattern corresponds to the last
3872     * event that must occur.
3873     *-------------------------------------------------------------
3874     */
3875    
3876     p = eventString;
3877     flags = 0;
3878     eventMask = 0;
3879     virtualFound = 0;
3880    
3881     patPtr = &pats[EVENT_BUFFER_SIZE-1];
3882     for (numPats = 0; numPats < EVENT_BUFFER_SIZE; numPats++, patPtr--) {
3883     while (isspace(UCHAR(*p))) {
3884     p++;
3885     }
3886     if (*p == '\0') {
3887     break;
3888     }
3889    
3890     count = ParseEventDescription(interp, &p, patPtr, &eventMask);
3891     if (count == 0) {
3892     return NULL;
3893     }
3894    
3895     if (eventMask & VirtualEventMask) {
3896     if (allowVirtual == 0) {
3897     Tcl_SetResult(interp,
3898     "virtual event not allowed in definition of another virtual event",
3899     TCL_STATIC);
3900     return NULL;
3901     }
3902     virtualFound = 1;
3903     }
3904    
3905     /*
3906     * Replicate events for DOUBLE, TRIPLE, QUADRUPLE.
3907     */
3908    
3909     while ((count-- > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
3910     flags |= PAT_NEARBY;
3911     patPtr[-1] = patPtr[0];
3912     patPtr--;
3913     numPats++;
3914     }
3915     }
3916    
3917     /*
3918     *-------------------------------------------------------------
3919     * Step 2: find the sequence in the binding table if it exists,
3920     * and add a new sequence to the table if it doesn't.
3921     *-------------------------------------------------------------
3922     */
3923    
3924     if (numPats == 0) {
3925     Tcl_SetResult(interp, "no events specified in binding", TCL_STATIC);
3926     return NULL;
3927     }
3928     if ((numPats > 1) && (virtualFound != 0)) {
3929     Tcl_SetResult(interp, "virtual events may not be composed",
3930     TCL_STATIC);
3931     return NULL;
3932     }
3933    
3934     patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
3935     memset(&key, 0, sizeof(key));
3936     key.object = object;
3937     key.type = patPtr->eventType;
3938     key.detail = patPtr->detail;
3939     hPtr = Tcl_CreateHashEntry(patternTablePtr, (char *) &key, &new);
3940     sequenceSize = numPats*sizeof(Pattern);
3941     if (!new) {
3942     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
3943     psPtr = psPtr->nextSeqPtr) {
3944     if ((numPats == psPtr->numPats)
3945     && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
3946     && (memcmp((char *) patPtr, (char *) psPtr->pats,
3947     sequenceSize) == 0)) {
3948     goto done;
3949     }
3950     }
3951     }
3952     if (!create) {
3953     if (new) {
3954     Tcl_DeleteHashEntry(hPtr);
3955     }
3956     /*
3957     * No binding exists for the sequence, so return an empty error.
3958     * This is a special error that the caller will check for in order
3959     * to silently ignore this case. This is a hack that maintains
3960     * backward compatibility for Tk_GetBinding but the various "bind"
3961     * commands silently ignore missing bindings.
3962     */
3963    
3964     return NULL;
3965     }
3966     psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
3967     + (numPats-1)*sizeof(Pattern)));
3968     psPtr->numPats = numPats;
3969     psPtr->eventProc = NULL;
3970     psPtr->freeProc = NULL;
3971     psPtr->clientData = NULL;
3972     psPtr->flags = flags;
3973     psPtr->refCount = 0;
3974     psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
3975     psPtr->hPtr = hPtr;
3976     psPtr->voPtr = NULL;
3977     psPtr->nextObjPtr = NULL;
3978     Tcl_SetHashValue(hPtr, psPtr);
3979    
3980     memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
3981    
3982     done:
3983     *maskPtr = eventMask;
3984     return psPtr;
3985     }
3986    
3987     /*
3988     *---------------------------------------------------------------------------
3989     *
3990     * ParseEventDescription --
3991     *
3992     * Fill Pattern buffer with information about event from
3993     * event string.
3994     *
3995     * Results:
3996     * Leaves error message in interp and returns 0 if there was an
3997     * error due to a badly formed event string. Returns 1 if proper
3998     * event was specified, 2 if Double modifier was used in event
3999     * string, or 3 if Triple was used.
4000     *
4001     * Side effects:
4002     * On exit, eventStringPtr points to rest of event string (after the
4003     * closing '>', so that this procedure can be called repeatedly to
4004     * parse all the events in the entire sequence.
4005     *
4006     *---------------------------------------------------------------------------
4007     */
4008    
4009     static int
4010     ParseEventDescription(interp, eventStringPtr, patPtr,
4011     eventMaskPtr)
4012     Tcl_Interp *interp; /* For error messages. */
4013     char **eventStringPtr; /* On input, holds a pointer to start of
4014     * event string. On exit, gets pointer to
4015     * rest of string after parsed event. */
4016     Pattern *patPtr; /* Filled with the pattern parsed from the
4017     * event string. */
4018     unsigned long *eventMaskPtr;/* Filled with event mask of matched event. */
4019    
4020     {
4021     char *p;
4022     unsigned long eventMask;
4023     int count, eventFlags;
4024     #define FIELD_SIZE 48
4025     char field[FIELD_SIZE];
4026     Tcl_HashEntry *hPtr;
4027    
4028     p = *eventStringPtr;
4029    
4030     patPtr->eventType = -1;
4031     patPtr->needMods = 0;
4032     patPtr->detail.clientData = 0;
4033    
4034     eventMask = 0;
4035     count = 1;
4036    
4037     /*
4038     * Handle simple ASCII characters.
4039     */
4040    
4041     if (*p != '<') {
4042     char string[2];
4043    
4044     patPtr->eventType = KeyPress;
4045     eventMask = KeyPressMask;
4046     string[0] = *p;
4047     string[1] = 0;
4048     patPtr->detail.keySym = TkStringToKeysym(string);
4049     if (patPtr->detail.keySym == NoSymbol) {
4050     if (isprint(UCHAR(*p))) {
4051     patPtr->detail.keySym = *p;
4052     } else {
4053     char buf[64];
4054    
4055     sprintf(buf, "bad ASCII character 0x%x", (unsigned char) *p);
4056     Tcl_SetResult(interp, buf, TCL_VOLATILE);
4057     return 0;
4058     }
4059     }
4060     p++;
4061     goto end;
4062     }
4063    
4064     /*
4065     * A fancier event description. This can be either a virtual event
4066     * or a physical event.
4067     *
4068     * A virtual event description consists of:
4069     *
4070     * 1. double open angle brackets.
4071     * 2. virtual event name.
4072     * 3. double close angle brackets.
4073     *
4074     * A physical event description consists of:
4075     *
4076     * 1. open angle bracket.
4077     * 2. any number of modifiers, each followed by spaces
4078     * or dashes.
4079     * 3. an optional event name.
4080     * 4. an option button or keysym name. Either this or
4081     * item 3 *must* be present; if both are present
4082     * then they are separated by spaces or dashes.
4083     * 5. a close angle bracket.
4084     */
4085    
4086     p++;
4087     if (*p == '<') {
4088     /*
4089     * This is a virtual event: soak up all the characters up to
4090     * the next '>'.
4091     */
4092    
4093     char *field = p + 1;
4094     p = strchr(field, '>');
4095     if (p == field) {
4096     Tcl_SetResult(interp, "virtual event \"<<>>\" is badly formed",
4097     TCL_STATIC);
4098     return 0;
4099     }
4100     if ((p == NULL) || (p[1] != '>')) {
4101     Tcl_SetResult(interp, "missing \">\" in virtual binding",
4102     TCL_STATIC);
4103     return 0;
4104     }
4105     *p = '\0';
4106     patPtr->eventType = VirtualEvent;
4107     eventMask = VirtualEventMask;
4108     patPtr->detail.name = Tk_GetUid(field);
4109     *p = '>';
4110    
4111     p += 2;
4112     goto end;
4113     }
4114    
4115     while (1) {
4116     ModInfo *modPtr;
4117     p = GetField(p, field, FIELD_SIZE);
4118     if (*p == '>') {
4119     /*
4120     * This solves the problem of, e.g., <Control-M> being
4121     * misinterpreted as Control + Meta + missing keysym
4122     * instead of Control + KeyPress + M.
4123     */
4124     break;
4125     }
4126     hPtr = Tcl_FindHashEntry(&modTable, field);
4127     if (hPtr == NULL) {
4128     break;
4129     }
4130     modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
4131     patPtr->needMods |= modPtr->mask;
4132     if (modPtr->flags & (MULT_CLICKS)) {
4133     int i = modPtr->flags & MULT_CLICKS;
4134     count = 2;
4135     while (i >>= 1) count++;
4136     }
4137     while ((*p == '-') || isspace(UCHAR(*p))) {
4138     p++;
4139     }
4140     }
4141    
4142     eventFlags = 0;
4143     hPtr = Tcl_FindHashEntry(&eventTable, field);
4144     if (hPtr != NULL) {
4145     EventInfo *eiPtr;
4146     eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
4147    
4148     patPtr->eventType = eiPtr->type;
4149     eventFlags = flagArray[eiPtr->type];
4150     eventMask = eiPtr->eventMask;
4151     while ((*p == '-') || isspace(UCHAR(*p))) {
4152     p++;
4153     }
4154     p = GetField(p, field, FIELD_SIZE);
4155     }
4156     if (*field != '\0') {
4157     if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
4158     if (eventFlags == 0) {
4159     patPtr->eventType = ButtonPress;
4160     eventMask = ButtonPressMask;
4161     } else if (eventFlags & KEY) {
4162     goto getKeysym;
4163     } else if ((eventFlags & BUTTON) == 0) {
4164     Tcl_AppendResult(interp, "specified button \"", field,
4165     "\" for non-button event", (char *) NULL);
4166     return 0;
4167     }
4168     patPtr->detail.button = (*field - '0');
4169     } else {
4170     getKeysym:
4171     patPtr->detail.keySym = TkStringToKeysym(field);
4172     if (patPtr->detail.keySym == NoSymbol) {
4173     Tcl_AppendResult(interp, "bad event type or keysym \"",
4174     field, "\"", (char *) NULL);
4175     return 0;
4176     }
4177     if (eventFlags == 0) {
4178     patPtr->eventType = KeyPress;
4179     eventMask = KeyPressMask;
4180     } else if ((eventFlags & KEY) == 0) {
4181     Tcl_AppendResult(interp, "specified keysym \"", field,
4182     "\" for non-key event", (char *) NULL);
4183     return 0;
4184     }
4185     }
4186     } else if (eventFlags == 0) {
4187     Tcl_SetResult(interp, "no event type or button # or keysym",
4188     TCL_STATIC);
4189     return 0;
4190     }
4191    
4192     while ((*p == '-') || isspace(UCHAR(*p))) {
4193     p++;
4194     }
4195     if (*p != '>') {
4196     while (*p != '\0') {
4197     p++;
4198     if (*p == '>') {
4199     Tcl_SetResult(interp,
4200     "extra characters after detail in binding",
4201     TCL_STATIC);
4202     return 0;
4203     }
4204     }
4205     Tcl_SetResult(interp, "missing \">\" in binding", TCL_STATIC);
4206     return 0;
4207     }
4208     p++;
4209    
4210     end:
4211     *eventStringPtr = p;
4212     *eventMaskPtr |= eventMask;
4213     return count;
4214     }
4215    
4216     /*
4217     *----------------------------------------------------------------------
4218     *
4219     * GetField --
4220     *
4221     * Used to parse pattern descriptions. Copies up to
4222     * size characters from p to copy, stopping at end of
4223     * string, space, "-", ">", or whenever size is
4224     * exceeded.
4225     *
4226     * Results:
4227     * The return value is a pointer to the character just
4228     * after the last one copied (usually "-" or space or
4229     * ">", but could be anything if size was exceeded).
4230     * Also places NULL-terminated string (up to size
4231     * character, including NULL), at copy.
4232     *
4233     * Side effects:
4234     * None.
4235     *
4236     *----------------------------------------------------------------------
4237     */
4238    
4239     static char *
4240     GetField(p, copy, size)
4241     char *p; /* Pointer to part of pattern. */
4242     char *copy; /* Place to copy field. */
4243     int size; /* Maximum number of characters to
4244     * copy. */
4245     {
4246     while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
4247     && (*p != '-') && (size > 1)) {
4248     *copy = *p;
4249     p++;
4250     copy++;
4251     size--;
4252     }
4253     *copy = '\0';
4254     return p;
4255     }
4256    
4257     /*
4258     *---------------------------------------------------------------------------
4259     *
4260     * GetPatternString --
4261     *
4262     * Produce a string version of the given event, for displaying to
4263     * the user.
4264     *
4265     * Results:
4266     * The string is left in dsPtr.
4267     *
4268     * Side effects:
4269     * It is the caller's responsibility to initialize the DString before
4270     * and to free it after calling this procedure.
4271     *
4272     *---------------------------------------------------------------------------
4273     */
4274     static void
4275     GetPatternString(psPtr, dsPtr)
4276     PatSeq *psPtr;
4277     Tcl_DString *dsPtr;
4278     {
4279     Pattern *patPtr;
4280     char c, buffer[TCL_INTEGER_SPACE];
4281     int patsLeft, needMods;
4282     ModInfo *modPtr;
4283     EventInfo *eiPtr;
4284    
4285     /*
4286     * The order of the patterns in the sequence is backwards from the order
4287     * in which they must be output.
4288     */
4289    
4290     for (patsLeft = psPtr->numPats, patPtr = &psPtr->pats[psPtr->numPats - 1];
4291     patsLeft > 0; patsLeft--, patPtr--) {
4292    
4293     /*
4294     * Check for simple case of an ASCII character.
4295     */
4296    
4297     if ((patPtr->eventType == KeyPress)
4298     && ((psPtr->flags & PAT_NEARBY) == 0)
4299     && (patPtr->needMods == 0)
4300     && (patPtr->detail.keySym < 128)
4301     && isprint(UCHAR(patPtr->detail.keySym))
4302     && (patPtr->detail.keySym != '<')
4303     && (patPtr->detail.keySym != ' ')) {
4304    
4305     c = (char) patPtr->detail.keySym;
4306     Tcl_DStringAppend(dsPtr, &c, 1);
4307     continue;
4308     }
4309    
4310     /*
4311     * Check for virtual event.
4312     */
4313    
4314     if (patPtr->eventType == VirtualEvent) {
4315     Tcl_DStringAppend(dsPtr, "<<", 2);
4316     Tcl_DStringAppend(dsPtr, patPtr->detail.name, -1);
4317     Tcl_DStringAppend(dsPtr, ">>", 2);
4318     continue;
4319     }
4320    
4321     /*
4322     * It's a more general event specification. First check
4323     * for "Double", "Triple", "Quadruple", then modifiers,
4324     * then event type, then keysym or button detail.
4325     */
4326    
4327     Tcl_DStringAppend(dsPtr, "<", 1);
4328     if ((psPtr->flags & PAT_NEARBY) && (patsLeft > 1)
4329     && (memcmp((char *) patPtr, (char *) (patPtr-1),
4330     sizeof(Pattern)) == 0)) {
4331     patsLeft--;
4332     patPtr--;
4333     if ((patsLeft > 1) && (memcmp((char *) patPtr,
4334     (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
4335     patsLeft--;
4336     patPtr--;
4337     if ((patsLeft > 1) && (memcmp((char *) patPtr,
4338     (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
4339     patsLeft--;
4340     patPtr--;
4341     Tcl_DStringAppend(dsPtr, "Quadruple-", 10);
4342     } else {
4343     Tcl_DStringAppend(dsPtr, "Triple-", 7);
4344     }
4345     } else {
4346     Tcl_DStringAppend(dsPtr, "Double-", 7);
4347     }
4348     }
4349     for (needMods = patPtr->needMods, modPtr = modArray;
4350     needMods != 0; modPtr++) {
4351     if (modPtr->mask & needMods) {
4352     needMods &= ~modPtr->mask;
4353     Tcl_DStringAppend(dsPtr, modPtr->name, -1);
4354     Tcl_DStringAppend(dsPtr, "-", 1);
4355     }
4356     }
4357     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
4358     if (eiPtr->type == patPtr->eventType) {
4359     Tcl_DStringAppend(dsPtr, eiPtr->name, -1);
4360     if (patPtr->detail.clientData != 0) {
4361     Tcl_DStringAppend(dsPtr, "-", 1);
4362     }
4363     break;
4364     }
4365     }
4366    
4367     if (patPtr->detail.clientData != 0) {
4368     if ((patPtr->eventType == KeyPress)
4369     || (patPtr->eventType == KeyRelease)) {
4370     char *string;
4371    
4372     string = TkKeysymToString(patPtr->detail.keySym);
4373     if (string != NULL) {
4374     Tcl_DStringAppend(dsPtr, string, -1);
4375     }
4376     } else {
4377     sprintf(buffer, "%d", patPtr->detail.button);
4378     Tcl_DStringAppend(dsPtr, buffer, -1);
4379     }
4380     }
4381     Tcl_DStringAppend(dsPtr, ">", 1);
4382     }
4383     }
4384    
4385     /*
4386     *---------------------------------------------------------------------------
4387     *
4388     * EvalTclBinding --
4389     *
4390     * The procedure that is invoked by Tk_BindEvent when a Tcl binding
4391     * is fired.
4392     *
4393     * Results:
4394     * A standard Tcl result code, the result of globally evaluating the
4395     * percent-substitued binding string.
4396     *
4397     * Side effects:
4398     * Normal side effects due to eval.
4399     *
4400     *---------------------------------------------------------------------------
4401     */
4402    
4403     static void
4404     FreeTclBinding(clientData)
4405     ClientData clientData;
4406     {
4407     ckfree((char *) clientData);
4408     }
4409    
4410     /*
4411     *----------------------------------------------------------------------
4412     *
4413     * TkStringToKeysym --
4414     *
4415     * This procedure finds the keysym associated with a given keysym
4416     * name.
4417     *
4418     * Results:
4419     * The return value is the keysym that corresponds to name, or
4420     * NoSymbol if there is no such keysym.
4421     *
4422     * Side effects:
4423     * None.
4424     *
4425     *----------------------------------------------------------------------
4426     */
4427    
4428     KeySym
4429     TkStringToKeysym(name)
4430     char *name; /* Name of a keysym. */
4431     {
4432     #ifdef REDO_KEYSYM_LOOKUP
4433     Tcl_HashEntry *hPtr;
4434     KeySym keysym;
4435    
4436     hPtr = Tcl_FindHashEntry(&keySymTable, name);
4437     if (hPtr != NULL) {
4438     return (KeySym) Tcl_GetHashValue(hPtr);
4439     }
4440     if (strlen(name) == 1) {
4441     keysym = (KeySym) (unsigned char) name[0];
4442     if (TkKeysymToString(keysym) != NULL) {
4443     return keysym;
4444     }
4445     }
4446     #endif /* REDO_KEYSYM_LOOKUP */
4447     return XStringToKeysym(name);
4448     }
4449    
4450     /*
4451     *----------------------------------------------------------------------
4452     *
4453     * TkKeysymToString --
4454     *
4455     * This procedure finds the keysym name associated with a given
4456     * keysym.
4457     *
4458     * Results:
4459     * The return value is a pointer to a static string containing
4460     * the name of the given keysym, or NULL if there is no known name.
4461     *
4462     * Side effects:
4463     * None.
4464     *
4465     *----------------------------------------------------------------------
4466     */
4467    
4468     char *
4469     TkKeysymToString(keysym)
4470     KeySym keysym;
4471     {
4472     #ifdef REDO_KEYSYM_LOOKUP
4473     Tcl_HashEntry *hPtr;
4474    
4475     hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym);
4476     if (hPtr != NULL) {
4477     return (char *) Tcl_GetHashValue(hPtr);
4478     }
4479     #endif /* REDO_KEYSYM_LOOKUP */
4480     return XKeysymToString(keysym);
4481     }
4482    
4483     /*
4484     *----------------------------------------------------------------------
4485     *
4486     * TkCopyAndGlobalEval --
4487     *
4488     * This procedure makes a copy of a script then calls Tcl_GlobalEval
4489     * to evaluate it. It's used in situations where the execution of
4490     * a command may cause the original command string to be reallocated.
4491     *
4492     * Results:
4493     * Returns the result of evaluating script, including both a standard
4494     * Tcl completion code and a string in the interp's result.
4495     *
4496     * Side effects:
4497     * None.
4498     *
4499     *----------------------------------------------------------------------
4500     */
4501    
4502     int
4503     TkCopyAndGlobalEval(interp, script)
4504     Tcl_Interp *interp; /* Interpreter in which to evaluate
4505     * script. */
4506     char *script; /* Script to evaluate. */
4507     {
4508     Tcl_DString buffer;
4509     int code;
4510    
4511     Tcl_DStringInit(&buffer);
4512     Tcl_DStringAppend(&buffer, script, -1);
4513     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
4514     Tcl_DStringFree(&buffer);
4515     return code;
4516     }
4517    
4518     /* End of tkbind.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25