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