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