4 * This file provides procedures that associate Tcl commands
5 * with X events or sequences of X events.
7 * Copyright 1989-1991 Regents of the University of California
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
18 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkBind.c,v 1.48 92/08/10 16:55:24 ouster Exp $ SPRITE (Berkeley)";
25 * The structure below represents a binding table. A binding table
26 * represents a domain in which event bindings may occur. It includes
27 * a space of objects relative to which events occur (usually windows,
28 * but not always), a history of recent events in the domain, and
29 * a set of mappings that associate particular Tcl commands with sequences
30 * of events in the domain. Multiple binding tables may exist at once,
31 * either because there are multiple applications open, or because there
32 * are multiple domains within an application with separate event
33 * bindings for each (for example, each canvas widget has a separate
34 * binding table for associating events with the items in the canvas).
37 #define EVENT_BUFFER_SIZE 10
38 typedef struct BindingTable
{
39 XEvent eventRing
[EVENT_BUFFER_SIZE
];/* Circular queue of recent events
40 * (higher indices are for more recent
42 int detailRing
[EVENT_BUFFER_SIZE
]; /* "Detail" information (keySym or
43 * button or 0) for each entry in
45 int curEvent
; /* Index in eventRing of most recent
46 * event. Newer events have higher
48 Tcl_HashTable patternTable
; /* Used to map from an event to a list
49 * of patterns that may match that
50 * event. Keys are PatternTableKey
51 * structs, values are (PatSeq *). */
52 Tcl_HashTable objectTable
; /* Used to map from an object to a list
53 * of patterns associated with that
54 * object. Keys are ClientData,
55 * values are (PatSeq *). */
56 Tcl_Interp
*interp
; /* Interpreter in which commands are
61 * Structures of the following form are used as keys in the patternTable
62 * for a binding table:
65 typedef struct PatternTableKey
{
66 ClientData object
; /* Identifies object (or class of objects)
67 * relative to which event occurred. For
68 * example, in the widget binding table for
69 * an application this is the path name of
70 * a widget, or a widget class, or "all". */
71 int type
; /* Type of event (from X). */
72 int detail
; /* Additional information, such as
73 * keysym or button, or 0 if nothing
78 * The following structure defines a pattern, which is matched
79 * against X events as part of the process of converting X events
83 typedef struct Pattern
{
84 int eventType
; /* Type of X event, e.g. ButtonPress. */
85 int needMods
; /* Mask of modifiers that must be
86 * present (0 means no modifiers are
88 int hateMods
; /* Mask of modifiers that must not be
89 * present (0 means any modifiers are
91 int detail
; /* Additional information that must
92 * match event. Normally this is 0,
93 * meaning no additional information
94 * must match. For KeyPress and
95 * KeyRelease events, a keySym may
96 * be specified to select a
97 * particular keystroke (0 means any
98 * keystrokes). For button events,
99 * specifies a particular button (0
100 * means any buttons are OK). */
104 * The structure below defines a pattern sequence, which consists
105 * of one or more patterns. In order to trigger, a pattern
106 * sequence must match the most recent X events (first pattern
107 * to most recent event, next pattern to next event, and so on).
110 typedef struct PatSeq
{
111 int numPats
; /* Number of patterns in sequence
113 char *command
; /* Command to invoke when this
114 * pattern sequence matches (malloc-ed). */
115 int flags
; /* Miscellaneous flag values; see
116 * below for definitions. */
117 struct PatSeq
*nextSeqPtr
;
118 /* Next in list of all pattern
119 * sequences that have the same
120 * initial pattern. NULL means
122 Tcl_HashEntry
*hPtr
; /* Pointer to hash table entry for
123 * the initial pattern. This is the
124 * head of the list of which nextSeqPtr
126 ClientData object
; /* Identifies object with which event is
127 * associated (e.g. window). */
128 struct PatSeq
*nextObjPtr
;
129 /* Next in list of all pattern
130 * sequences for the same object
131 * (NULL for end of list). Needed to
132 * implement Tk_DeleteAllBindings. */
133 Pattern pats
[1]; /* Array of "numPats" patterns. Only
134 * one element is declared here but
135 * in actuality enough space will be
136 * allocated for "numPats" patterns.
137 * To match, pats[0] must match event
138 * n, pats[1] must match event n-1,
143 * Flag values for PatSeq structures:
145 * PAT_NEARBY 1 means that all of the events matching
146 * this sequence must occur with nearby X
147 * and Y mouse coordinates and close in time.
148 * This is typically used to restrict multiple
150 * PAT_PERCENTS 1 means that the command for this pattern
151 * requires percent substitution. 0 means there
152 * are no percents in the command.
156 #define PAT_PERCENTS 2
159 * Constants that define how close together two events must be
160 * in milliseconds or pixels to meet the PAT_NEARBY constraint:
163 #define NEARBY_PIXELS 5
164 #define NEARBY_MS 500
167 * The data structure and hash table below are used to map from
168 * textual keysym names to keysym numbers. This structure is
169 * present here because the corresponding X procedures are
174 char *name
; /* Name of keysym. */
175 KeySym value
; /* Numeric identifier for keysym. */
177 KeySymInfo keyArray
[] = {
179 #include "ks_names.h"
183 static Tcl_HashTable keySymTable
; /* Hashed form of above structure. */
185 static int initialized
= 0;
188 * A hash table is kept to map from the string names of event
189 * modifiers to information about those modifiers. The structure
190 * for storing this information, and the hash table built at
191 * initialization time, are defined below.
195 char *name
; /* Name of modifier. */
196 int mask
; /* Button/modifier mask value, * such as Button1Mask. */
197 int flags
; /* Various flags; see below for
202 * Flags for ModInfo structures:
204 * DOUBLE - Non-zero means duplicate this event,
205 * e.g. for double-clicks.
206 * TRIPLE - Non-zero means triplicate this event,
207 * e.g. for triple-clicks.
208 * ANY - Non-zero means that this event allows
209 * any unspecified modifiers.
216 static ModInfo modArray
[] = {
217 "Control", ControlMask
, 0,
218 "Shift", ShiftMask
, 0,
220 "B1", Button1Mask
, 0,
221 "Button1", Button1Mask
, 0,
222 "B2", Button2Mask
, 0,
223 "Button2", Button2Mask
, 0,
224 "B3", Button3Mask
, 0,
225 "Button3", Button3Mask
, 0,
226 "B4", Button4Mask
, 0,
227 "Button4", Button4Mask
, 0,
228 "B5", Button5Mask
, 0,
229 "Button5", Button5Mask
, 0,
232 "Meta", META_MASK
, 0,
247 static Tcl_HashTable modTable
;
250 * This module also keeps a hash table mapping from event names
251 * to information about those events. The structure, an array
252 * to use to initialize the hash table, and the hash table are
257 char *name
; /* Name of event. */
258 int type
; /* Event type for X, such as
260 int eventMask
; /* Mask bits (for XSelectInput)
261 * for this event type. */
265 * Note: some of the masks below are an OR-ed combination of
266 * several masks. This is necessary because X doesn't report
267 * up events unless you also ask for down events. Also, X
268 * doesn't report button state in motion events unless you've
269 * asked about button events.
272 static EventInfo eventArray
[] = {
273 "Motion", MotionNotify
,
274 ButtonPressMask
|PointerMotionMask
,
275 "Button", ButtonPress
, ButtonPressMask
,
276 "ButtonPress", ButtonPress
, ButtonPressMask
,
277 "ButtonRelease", ButtonRelease
,
278 ButtonPressMask
|ButtonReleaseMask
,
279 "Colormap", ColormapNotify
, ColormapChangeMask
,
280 "Enter", EnterNotify
, EnterWindowMask
,
281 "Leave", LeaveNotify
, LeaveWindowMask
,
282 "Expose", Expose
, ExposureMask
,
283 "FocusIn", FocusIn
, FocusChangeMask
,
284 "FocusOut", FocusOut
, FocusChangeMask
,
285 "Keymap", KeymapNotify
, KeymapStateMask
,
286 "Key", KeyPress
, KeyPressMask
,
287 "KeyPress", KeyPress
, KeyPressMask
,
288 "KeyRelease", KeyRelease
,
289 KeyPressMask
|KeyReleaseMask
,
290 "Property", PropertyNotify
, PropertyChangeMask
,
291 "ResizeRequest", ResizeRequest
, ResizeRedirectMask
,
292 "Circulate", CirculateNotify
, StructureNotifyMask
,
293 "Configure", ConfigureNotify
, StructureNotifyMask
,
294 "Destroy", DestroyNotify
, StructureNotifyMask
,
295 "Gravity", GravityNotify
, StructureNotifyMask
,
296 "Map", MapNotify
, StructureNotifyMask
,
297 "Reparent", ReparentNotify
, StructureNotifyMask
,
298 "Unmap", UnmapNotify
, StructureNotifyMask
,
299 "Visibility", VisibilityNotify
, VisibilityChangeMask
,
300 "CirculateRequest", CirculateRequest
, SubstructureRedirectMask
,
301 "ConfigureRequest", ConfigureRequest
, SubstructureRedirectMask
,
302 "MapRequest", MapRequest
, SubstructureRedirectMask
,
303 (char *) NULL
, 0, 0};
304 static Tcl_HashTable eventTable
;
307 * The defines and table below are used to classify events into
308 * various groups. The reason for this is that logically identical
309 * fields (e.g. "state") appear at different places in different
310 * types of events. The classification masks can be used to figure
311 * out quickly where to extract information from events.
314 #define KEY_BUTTON_MOTION 0x1
318 #define VISIBILITY 0x10
321 #define REPARENT 0x80
323 #define CONFIG_REQ 0x200
324 #define RESIZE_REQ 0x400
325 #define GRAVITY 0x800
327 #define SEL_CLEAR 0x2000
328 #define SEL_REQ 0x4000
329 #define SEL_NOTIFY 0x8000
330 #define COLORMAP 0x10000
331 #define MAPPING 0x20000
333 static int flagArray
[LASTEvent
] = {
336 /* KeyPress */ KEY_BUTTON_MOTION
,
337 /* KeyRelease */ KEY_BUTTON_MOTION
,
338 /* ButtonPress */ KEY_BUTTON_MOTION
,
339 /* ButtonRelease */ KEY_BUTTON_MOTION
,
340 /* MotionNotify */ KEY_BUTTON_MOTION
,
341 /* EnterNotify */ CROSSING
,
342 /* LeaveNotify */ CROSSING
,
344 /* FocusOut */ FOCUS
,
345 /* KeymapNotify */ 0,
347 /* GraphicsExpose */ EXPOSE
,
349 /* VisibilityNotify */ VISIBILITY
,
350 /* CreateNotify */ CREATE
,
351 /* DestroyNotify */ 0,
355 /* ReparentNotify */ REPARENT
,
356 /* ConfigureNotify */ CONFIG
,
357 /* ConfigureRequest */ CONFIG_REQ
,
358 /* GravityNotify */ 0,
359 /* ResizeRequest */ RESIZE_REQ
,
360 /* CirculateNotify */ 0,
361 /* CirculateRequest */ 0,
362 /* PropertyNotify */ PROP
,
363 /* SelectionClear */ SEL_CLEAR
,
364 /* SelectionRequest */ SEL_REQ
,
365 /* SelectionNotify */ SEL_NOTIFY
,
366 /* ColormapNotify */ COLORMAP
,
367 /* ClientMessage */ 0,
368 /* MappingNotify */ MAPPING
372 * Forward declarations for procedures defined later in this
376 static char * ExpandPercents
_ANSI_ARGS_((char *before
,
377 XEvent
*eventPtr
, KeySym keySym
, char *after
,
379 static PatSeq
* FindSequence
_ANSI_ARGS_((Tcl_Interp
*interp
,
380 BindingTable
*bindPtr
, ClientData object
,
381 char *eventString
, int create
,
382 unsigned long *maskPtr
));
383 static char * GetField
_ANSI_ARGS_((char *p
, char *copy
, int size
));
384 static KeySym GetKeySym
_ANSI_ARGS_((TkDisplay
*dispPtr
,
386 static PatSeq
* MatchPatterns
_ANSI_ARGS_((TkDisplay
*dispPtr
,
387 BindingTable
*bindPtr
, PatSeq
*psPtr
));
390 *--------------------------------------------------------------
392 * Tk_CreateBindingTable --
394 * Set up a new domain in which event bindings may be created.
397 * The return value is a token for the new table, which must
398 * be passed to procedures like Tk_CreatBinding.
401 * Memory is allocated for the new table.
403 *--------------------------------------------------------------
407 Tk_CreateBindingTable (
408 Tcl_Interp
*interp
/* Interpreter to associate with the binding
409 * table: commands are executed in this
413 register BindingTable
*bindPtr
;
417 * If this is the first time a binding table has been created,
418 * initialize the global data structures.
422 register KeySymInfo
*kPtr
;
423 register Tcl_HashEntry
*hPtr
;
424 register ModInfo
*modPtr
;
425 register EventInfo
*eiPtr
;
430 Tcl_InitHashTable(&keySymTable
, TCL_STRING_KEYS
);
431 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
432 hPtr
= Tcl_CreateHashEntry(&keySymTable
, kPtr
->name
, &dummy
);
433 Tcl_SetHashValue(hPtr
, kPtr
->value
);
436 Tcl_InitHashTable(&modTable
, TCL_STRING_KEYS
);
437 for (modPtr
= modArray
; modPtr
->name
!= NULL
; modPtr
++) {
438 hPtr
= Tcl_CreateHashEntry(&modTable
, modPtr
->name
, &dummy
);
439 Tcl_SetHashValue(hPtr
, modPtr
);
442 Tcl_InitHashTable(&eventTable
, TCL_STRING_KEYS
);
443 for (eiPtr
= eventArray
; eiPtr
->name
!= NULL
; eiPtr
++) {
444 hPtr
= Tcl_CreateHashEntry(&eventTable
, eiPtr
->name
, &dummy
);
445 Tcl_SetHashValue(hPtr
, eiPtr
);
450 * Create and initialize a new binding table.
453 bindPtr
= (BindingTable
*) ckalloc(sizeof(BindingTable
));
454 for (i
= 0; i
< EVENT_BUFFER_SIZE
; i
++) {
455 bindPtr
->eventRing
[i
].type
= -1;
457 bindPtr
->curEvent
= 0;
458 Tcl_InitHashTable(&bindPtr
->patternTable
,
459 sizeof(PatternTableKey
)/sizeof(int));
460 Tcl_InitHashTable(&bindPtr
->objectTable
, TCL_ONE_WORD_KEYS
);
461 bindPtr
->interp
= interp
;
462 return (Tk_BindingTable
) bindPtr
;
466 *--------------------------------------------------------------
468 * Tk_DeleteBindingTable --
470 * Destroy a binding table and free up all its memory.
471 * The caller should not use bindingTable again after
472 * this procedure returns.
480 *--------------------------------------------------------------
484 Tk_DeleteBindingTable (
485 Tk_BindingTable bindingTable
/* Token for the binding table to
489 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
490 PatSeq
*psPtr
, *nextPtr
;
492 Tcl_HashSearch search
;
495 * Find and delete all of the patterns associated with the binding
499 for (hPtr
= Tcl_FirstHashEntry(&bindPtr
->patternTable
, &search
);
500 hPtr
!= NULL
; hPtr
= Tcl_NextHashEntry(&search
)) {
501 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
502 psPtr
!= NULL
; psPtr
= nextPtr
) {
503 nextPtr
= psPtr
->nextSeqPtr
;
504 Tk_EventuallyFree((ClientData
) psPtr
->command
,
505 (Tk_FreeProc
*) free
);
506 ckfree((char *) psPtr
);
511 * Clean up the rest of the information associated with the
515 Tcl_DeleteHashTable(&bindPtr
->patternTable
);
516 Tcl_DeleteHashTable(&bindPtr
->objectTable
);
517 ckfree((char *) bindPtr
);
521 *--------------------------------------------------------------
523 * Tk_CreateBinding --
525 * Add a binding to a binding table, so that future calls to
526 * Tk_BindEvent may execute the command in the binding.
529 * The return value is 0 if an error occurred while setting
530 * up the binding. In this case, an error message will be
531 * left in interp->result. If all went well then the return
532 * value is a mask of the event types that must be made
533 * available to Tk_BindEvent in order to properly detect when
534 * this binding triggers. This value can be used to determine
535 * what events to select for in a window, for example.
538 * The new binding may cause future calls to Tk_BindEvent to
539 * behave differently than they did previously.
541 *--------------------------------------------------------------
546 Tcl_Interp
*interp
, /* Used for error reporting. */
547 Tk_BindingTable bindingTable
, /* Table in which to create binding. */
548 ClientData object
, /* Token for object with which binding
550 char *eventString
, /* String describing event sequence
551 * that triggers binding. */
552 char *command
, /* Contains Tcl command to execute
553 * when binding triggers. */
554 int append
/* 0 means replace any existing
555 * binding for eventString; 1 means
556 * append to that binding. */
559 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
560 register PatSeq
*psPtr
;
561 unsigned long eventMask
;
563 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 1, &eventMask
);
567 if (append
&& (psPtr
->command
!= NULL
)) {
571 length
= strlen(psPtr
->command
) + strlen(command
) + 3;
572 new = (char *) ckalloc((unsigned) length
);
573 sprintf(new, "%s; %s", psPtr
->command
, command
);
574 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
575 psPtr
->command
= new;
577 if (psPtr
->command
!= NULL
) {
578 Tk_EventuallyFree((ClientData
) psPtr
->command
,
579 (Tk_FreeProc
*) free
);
581 psPtr
->command
= (char *) ckalloc((unsigned) (strlen(command
) + 1));
582 strcpy(psPtr
->command
, command
);
586 * See if the command contains percents and thereby requires
587 * percent substitution.
590 if (strchr(psPtr
->command
, '%') != NULL
) {
591 psPtr
->flags
|= PAT_PERCENTS
;
597 *--------------------------------------------------------------
599 * Tk_DeleteBinding --
601 * Remove an event binding from a binding table.
604 * The result is a standard Tcl return value. If an error
605 * occurs then interp->result will contain an error message.
608 * The binding given by object and eventString is removed
611 *--------------------------------------------------------------
616 Tcl_Interp
*interp
, /* Used for error reporting. */
617 Tk_BindingTable bindingTable
, /* Table in which to delete binding. */
618 ClientData object
, /* Token for object with which binding
620 char *eventString
/* String describing event sequence
621 * that triggers binding. */
624 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
625 register PatSeq
*psPtr
, *prevPtr
;
626 unsigned long eventMask
;
629 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 0, &eventMask
);
631 Tcl_ResetResult(interp
);
636 * Unlink the binding from the list for its object, then from the
637 * list for its pattern.
640 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
642 panic("Tk_DeleteBinding couldn't find object table entry");
644 prevPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
645 if (prevPtr
== psPtr
) {
646 Tcl_SetHashValue(hPtr
, psPtr
->nextObjPtr
);
648 for ( ; ; prevPtr
= prevPtr
->nextObjPtr
) {
649 if (prevPtr
== NULL
) {
650 panic("Tk_DeleteBinding couldn't find on object list");
652 if (prevPtr
->nextObjPtr
== psPtr
) {
653 prevPtr
->nextObjPtr
= psPtr
->nextObjPtr
;
658 prevPtr
= (PatSeq
*) Tcl_GetHashValue(psPtr
->hPtr
);
659 if (prevPtr
== psPtr
) {
660 if (psPtr
->nextSeqPtr
== NULL
) {
661 Tcl_DeleteHashEntry(psPtr
->hPtr
);
663 Tcl_SetHashValue(psPtr
->hPtr
, psPtr
->nextSeqPtr
);
666 for ( ; ; prevPtr
= prevPtr
->nextSeqPtr
) {
667 if (prevPtr
== NULL
) {
668 panic("Tk_DeleteBinding couldn't find on hash chain");
670 if (prevPtr
->nextSeqPtr
== psPtr
) {
671 prevPtr
->nextSeqPtr
= psPtr
->nextSeqPtr
;
676 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
677 ckfree((char *) psPtr
);
682 *--------------------------------------------------------------
686 * Return the command associated with a given event string.
689 * The return value is a pointer to the command string
690 * associated with eventString for object in the domain
691 * given by bindingTable. If there is no binding for
692 * eventString, or if eventString is improperly formed,
693 * then NULL is returned and an error message is left in
694 * interp->result. The return value is semi-static: it
695 * will persist until the binding is changed or deleted.
700 *--------------------------------------------------------------
705 Tcl_Interp
*interp
, /* Interpreter for error reporting. */
706 Tk_BindingTable bindingTable
, /* Table in which to look for
708 ClientData object
, /* Token for object with which binding
710 char *eventString
/* String describing event sequence
711 * that triggers binding. */
714 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
715 register PatSeq
*psPtr
;
716 unsigned long eventMask
;
718 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 0, &eventMask
);
722 return psPtr
->command
;
726 *--------------------------------------------------------------
728 * Tk_GetAllBindings --
730 * Return a list of event strings for all the bindings
731 * associated with a given object.
734 * There is no return value. Interp->result is modified to
735 * hold a Tcl list with one entry for each binding associated
736 * with object in bindingTable. Each entry in the list
737 * contains the event string associated with one binding.
742 *--------------------------------------------------------------
747 Tcl_Interp
*interp
, /* Interpreter for error reporting. */
748 Tk_BindingTable bindingTable
, /* Table in which to look for
750 ClientData object
/* Token for object. */
754 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
755 register PatSeq
*psPtr
;
756 register Pattern
*patPtr
;
758 char string
[200*EVENT_BUFFER_SIZE
];
760 int patsLeft
, needMods
;
761 register ModInfo
*modPtr
;
763 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
767 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
768 psPtr
= psPtr
->nextObjPtr
) {
773 * For each binding, output information about each of the
774 * patterns in its sequence. The order of the patterns in
775 * the sequence is backwards from the order in which they
779 for (patsLeft
= psPtr
->numPats
,
780 patPtr
= &psPtr
->pats
[psPtr
->numPats
- 1];
781 patsLeft
> 0; patsLeft
--, patPtr
--) {
784 * Check for simple case of an ASCII character.
787 if ((patPtr
->eventType
== KeyPress
)
788 && (patPtr
->needMods
== 0)
789 && (patPtr
->hateMods
== ~ShiftMask
)
790 && isascii(patPtr
->detail
) && isprint(patPtr
->detail
)
791 && (patPtr
->detail
!= '<')
792 && (patPtr
->detail
!= ' ')) {
800 * It's a more general event specification. First check
801 * for "Double" or "Triple", then "Any", then modifiers,
802 * the event type, then keysym or button detail.
807 if ((patsLeft
> 1) && (memcmp((char *) patPtr
,
808 (char *) (patPtr
-1), sizeof(Pattern
)) == 0)) {
811 if ((patsLeft
> 1) && (memcmp((char *) patPtr
,
812 (char *) (patPtr
-1), sizeof(Pattern
)) == 0)) {
815 strcpy(p
, "Triple-");
817 strcpy(p
, "Double-");
822 if (patPtr
->hateMods
== 0) {
827 for (needMods
= patPtr
->needMods
, modPtr
= modArray
;
828 needMods
!= 0; modPtr
++) {
829 if (modPtr
->mask
& needMods
) {
830 needMods
&= ~modPtr
->mask
;
831 strcpy(p
, modPtr
->name
);
838 if ((patPtr
->eventType
!= KeyPress
)
839 || (patPtr
->detail
== 0)) {
840 register EventInfo
*eiPtr
;
842 for (eiPtr
= eventArray
; eiPtr
->name
!= NULL
; eiPtr
++) {
843 if (eiPtr
->type
== patPtr
->eventType
) {
844 strcpy(p
, eiPtr
->name
);
846 if (patPtr
->detail
!= 0) {
855 if (patPtr
->detail
!= 0) {
856 if ((patPtr
->eventType
== KeyPress
)
857 || (patPtr
->eventType
== KeyRelease
)) {
858 register KeySymInfo
*kPtr
;
860 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
861 if (patPtr
->detail
== (int) kPtr
->value
) {
862 sprintf(p
, "%.100s", kPtr
->name
);
868 sprintf(p
, "%d", patPtr
->detail
);
876 if ((p
- string
) >= sizeof(string
)) {
877 panic("Tk_GetAllBindings overflowed buffer");
879 Tcl_AppendElement(interp
, string
, 0);
884 *--------------------------------------------------------------
886 * Tk_DeleteAllBindings --
888 * Remove all bindings associated with a given object in a
889 * given binding table.
892 * All bindings associated with object are removed from
898 *--------------------------------------------------------------
902 Tk_DeleteAllBindings (
903 Tk_BindingTable bindingTable
, /* Table in which to delete
905 ClientData object
/* Token for object. */
908 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
909 register PatSeq
*psPtr
, *prevPtr
;
913 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
917 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
919 nextPtr
= psPtr
->nextObjPtr
;
922 * Be sure to remove each binding from its hash chain in the
923 * pattern table. If this is the last pattern in the chain,
924 * then delete the hash entry too.
927 prevPtr
= (PatSeq
*) Tcl_GetHashValue(psPtr
->hPtr
);
928 if (prevPtr
== psPtr
) {
929 if (psPtr
->nextSeqPtr
== NULL
) {
930 Tcl_DeleteHashEntry(psPtr
->hPtr
);
932 Tcl_SetHashValue(psPtr
->hPtr
, psPtr
->nextSeqPtr
);
935 for ( ; ; prevPtr
= prevPtr
->nextSeqPtr
) {
936 if (prevPtr
== NULL
) {
937 panic("Tk_DeleteAllBindings couldn't find on hash chain");
939 if (prevPtr
->nextSeqPtr
== psPtr
) {
940 prevPtr
->nextSeqPtr
= psPtr
->nextSeqPtr
;
945 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
946 ckfree((char *) psPtr
);
948 Tcl_DeleteHashEntry(hPtr
);
952 *--------------------------------------------------------------
956 * This procedure is invoked to process an X event. The
957 * event is added to those recorded for the binding table.
958 * Then each of the objects at *objectPtr is checked in
959 * order to see if it has a binding that matches the recent
960 * events. If so, that binding is invoked and the rest of
961 * objects are skipped.
967 * Depends on the command associated with the matching
970 *--------------------------------------------------------------
975 Tk_BindingTable bindingTable
, /* Table in which to look for
977 XEvent
*eventPtr
, /* What actually happened. */
978 Tk_Window tkwin
, /* Window on display where event
979 * occurred (needed in order to
980 * locate display information). */
981 int numObjects
, /* Number of objects at *objectPtr. */
982 ClientData
*objectPtr
/* Array of one or more objects
983 * to check for a matching binding. */
986 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
987 TkDisplay
*dispPtr
= ((TkWindow
*) tkwin
)->dispPtr
;
995 * Add the new event to the ring of saved events for the
996 * binding table. Consecutive MotionNotify events get combined:
997 * if both the new event and the previous event are MotionNotify,
998 * then put the new event *on top* of the previous event.
1001 if ((eventPtr
->type
!= MotionNotify
)
1002 || (bindPtr
->eventRing
[bindPtr
->curEvent
].type
!= MotionNotify
)) {
1003 bindPtr
->curEvent
++;
1004 if (bindPtr
->curEvent
>= EVENT_BUFFER_SIZE
) {
1005 bindPtr
->curEvent
= 0;
1008 ringPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1009 memcpy((VOID
*) ringPtr
, (VOID
*) eventPtr
, sizeof(XEvent
));
1011 bindPtr
->detailRing
[bindPtr
->curEvent
] = 0;
1012 if ((ringPtr
->type
== KeyPress
) || (ringPtr
->type
== KeyRelease
)) {
1013 detail
= (int) GetKeySym(dispPtr
, ringPtr
);
1014 if (detail
== NoSymbol
) {
1017 } else if ((ringPtr
->type
== ButtonPress
)
1018 || (ringPtr
->type
== ButtonRelease
)) {
1019 detail
= ringPtr
->xbutton
.button
;
1021 bindPtr
->detailRing
[bindPtr
->curEvent
] = detail
;
1024 * Loop over all the objects, matching the new event against
1028 for ( ; numObjects
> 0; numObjects
--, objectPtr
++) {
1031 * Match the new event against those recorded in the
1032 * pattern table, saving the longest matching pattern.
1033 * For events with details (button and key events) first
1034 * look for a binding for the specific key or button.
1035 * If none is found, then look for a binding for all
1036 * keys or buttons (detail of 0).
1040 key
.object
= *objectPtr
;
1041 key
.type
= ringPtr
->type
;
1042 key
.detail
= detail
;
1043 hPtr
= Tcl_FindHashEntry(&bindPtr
->patternTable
, (char *) &key
);
1045 matchPtr
= MatchPatterns(dispPtr
, bindPtr
,
1046 (PatSeq
*) Tcl_GetHashValue(hPtr
));
1048 if ((detail
!= 0) && (matchPtr
== NULL
)) {
1050 hPtr
= Tcl_FindHashEntry(&bindPtr
->patternTable
, (char *) &key
);
1052 matchPtr
= MatchPatterns(dispPtr
, bindPtr
,
1053 (PatSeq
*) Tcl_GetHashValue(hPtr
));
1057 if (matchPtr
!= NULL
) {
1060 * %-substitution can increase the length of the command.
1061 * This code handles three cases: (a) no substitution;
1062 * (b) substitution results in short command (use space
1063 * on stack); and (c) substitution results in long
1064 * command (malloc it).
1067 #define STATIC_SPACE 200
1068 char shortSpace
[STATIC_SPACE
];
1071 if (matchPtr
->flags
& PAT_PERCENTS
) {
1073 p
= ExpandPercents(matchPtr
->command
, eventPtr
,
1074 (KeySym
) detail
, shortSpace
, STATIC_SPACE
);
1075 result
= Tcl_GlobalEval(bindPtr
->interp
, p
);
1076 if (p
!= shortSpace
) {
1081 * The code below is tricky in order allow the binding to
1082 * be modified or deleted as part of the command that the
1083 * binding invokes. Must make sure that the actual command
1084 * string isn't freed until the command completes, and must
1085 * copy the address of this string into a local variable
1086 * in case it's modified by the command.
1089 char *cmd
= matchPtr
->command
;
1091 Tk_Preserve((ClientData
) cmd
);
1092 result
= Tcl_GlobalEval(bindPtr
->interp
, cmd
);
1093 Tk_Release((ClientData
) cmd
);
1095 if (result
!= TCL_OK
) {
1096 Tcl_AddErrorInfo(bindPtr
->interp
,
1097 "\n (command bound to event)");
1098 TkBindError(bindPtr
->interp
);
1106 *----------------------------------------------------------------------
1110 * Find the entry in a binding table that corresponds to a
1111 * particular pattern string, and return a pointer to that
1115 * The return value is normally a pointer to the PatSeq
1116 * in patternTable that corresponds to eventString. If an error
1117 * was found while parsing eventString, or if "create" is 0 and
1118 * no pattern sequence previously existed, then NULL is returned
1119 * and interp->result contains a message describing the problem.
1120 * If no pattern sequence previously existed for eventString, then
1121 * a new one is created with a NULL command field. In a successful
1122 * return, *maskPtr is filled in with a mask of the event types
1123 * on which the pattern sequence depends.
1126 * A new pattern sequence may be created.
1128 *----------------------------------------------------------------------
1133 Tcl_Interp
*interp
, /* Interpreter to use for error
1135 BindingTable
*bindPtr
, /* Table to use for lookup. */
1136 ClientData object
, /* Token for object(s) with which binding
1138 char *eventString
, /* String description of pattern to
1139 * match on. See user documentation
1141 int create
, /* 0 means don't create the entry if
1142 * it doesn't already exist. Non-zero
1144 unsigned long *maskPtr
/* *maskPtr is filled in with the event
1145 * types on which this pattern sequence
1150 Pattern pats
[EVENT_BUFFER_SIZE
];
1153 register Pattern
*patPtr
;
1154 register PatSeq
*psPtr
;
1155 register Tcl_HashEntry
*hPtr
;
1156 #define FIELD_SIZE 20
1157 char field
[FIELD_SIZE
];
1158 int flags
, any
, count
, new, sequenceSize
;
1159 unsigned long eventMask
;
1160 PatternTableKey key
;
1163 *-------------------------------------------------------------
1164 * Step 1: parse the pattern string to produce an array
1165 * of Patterns. The array is generated backwards, so
1166 * that the lowest-indexed pattern corresponds to the last
1167 * event that must occur.
1168 *-------------------------------------------------------------
1174 for (numPats
= 0, patPtr
= &pats
[EVENT_BUFFER_SIZE
-1];
1175 numPats
< EVENT_BUFFER_SIZE
;
1176 numPats
++, patPtr
--) {
1177 patPtr
->eventType
= -1;
1178 patPtr
->needMods
= 0;
1179 patPtr
->hateMods
= ~0;
1181 while (isspace(*p
)) {
1189 * Handle simple ASCII characters. Note: the shift
1190 * modifier is ignored in this case (it's really part
1191 * of the character, rather than a "modifier").
1197 patPtr
->eventType
= KeyPress
;
1198 eventMask
|= KeyPressMask
;
1201 hPtr
= Tcl_FindHashEntry(&keySymTable
, string
);
1203 patPtr
->detail
= (int) Tcl_GetHashValue(hPtr
);
1206 patPtr
->detail
= *p
;
1208 sprintf(interp
->result
,
1209 "bad ASCII character 0x%x", *p
);
1213 patPtr
->hateMods
= ~ShiftMask
;
1219 * A fancier event description. Must consist of
1220 * 1. open angle bracket.
1221 * 2. any number of modifiers, each followed by spaces
1223 * 3. an optional event name.
1224 * 4. an option button or keysym name. Either this or
1225 * item 3 *must* be present; if both are present
1226 * then they are separated by spaces or dashes.
1227 * 5. a close angle bracket.
1234 register ModInfo
*modPtr
;
1235 p
= GetField(p
, field
, FIELD_SIZE
);
1236 hPtr
= Tcl_FindHashEntry(&modTable
, field
);
1240 modPtr
= (ModInfo
*) Tcl_GetHashValue(hPtr
);
1241 patPtr
->needMods
|= modPtr
->mask
;
1242 if (modPtr
->flags
& (DOUBLE
|TRIPLE
)) {
1243 flags
|= PAT_NEARBY
;
1244 if (modPtr
->flags
& DOUBLE
) {
1250 if (modPtr
->flags
& ANY
) {
1253 while ((*p
== '-') || isspace(*p
)) {
1258 patPtr
->hateMods
= 0;
1260 patPtr
->hateMods
= ~patPtr
->needMods
;
1262 hPtr
= Tcl_FindHashEntry(&eventTable
, field
);
1264 register EventInfo
*eiPtr
;
1265 eiPtr
= (EventInfo
*) Tcl_GetHashValue(hPtr
);
1266 patPtr
->eventType
= eiPtr
->type
;
1267 eventMask
|= eiPtr
->eventMask
;
1268 while ((*p
== '-') || isspace(*p
)) {
1271 p
= GetField(p
, field
, FIELD_SIZE
);
1273 if (*field
!= '\0') {
1274 if ((*field
>= '1') && (*field
<= '5') && (field
[1] == '\0')) {
1275 static int masks
[] = {~0, ~Button1Mask
, ~Button2Mask
,
1276 ~Button3Mask
, ~Button4Mask
, ~Button5Mask
};
1278 if (patPtr
->eventType
== -1) {
1279 patPtr
->eventType
= ButtonPress
;
1280 eventMask
|= ButtonPressMask
;
1281 } else if ((patPtr
->eventType
== KeyPress
)
1282 || (patPtr
->eventType
== KeyRelease
)) {
1284 } else if ((patPtr
->eventType
!= ButtonPress
)
1285 && (patPtr
->eventType
!= ButtonRelease
)) {
1286 Tcl_AppendResult(interp
, "specified button \"", field
,
1287 "\" for non-button event", (char *) NULL
);
1290 patPtr
->detail
= (*field
- '0');
1293 * Ignore this button as a modifier: its state is already
1297 patPtr
->needMods
&= masks
[patPtr
->detail
];
1298 patPtr
->hateMods
&= masks
[patPtr
->detail
];
1301 hPtr
= Tcl_FindHashEntry(&keySymTable
, (char *) field
);
1303 Tcl_AppendResult(interp
, "bad event type or keysym \"",
1304 field
, "\"", (char *) NULL
);
1307 if (patPtr
->eventType
== -1) {
1308 patPtr
->eventType
= KeyPress
;
1309 eventMask
|= KeyPressMask
;
1310 } else if ((patPtr
->eventType
!= KeyPress
)
1311 && (patPtr
->eventType
!= KeyRelease
)) {
1312 Tcl_AppendResult(interp
, "specified keysym \"", field
,
1313 "\" for non-key event", (char *) NULL
);
1316 patPtr
->detail
= (int) Tcl_GetHashValue(hPtr
);
1319 * Don't get upset about the shift modifier with keys:
1320 * if the key doesn't permit the shift modifier then
1321 * that will already be factored in when translating
1322 * from keycode to keysym in Tk_BindEvent. If the keysym
1323 * has both a shifted and unshifted form, we want to allow
1324 * the shifted form to be specified explicitly, though.
1327 patPtr
->hateMods
&= ~ShiftMask
;
1329 } else if (patPtr
->eventType
== -1) {
1330 interp
->result
= "no event type or button # or keysym";
1333 while ((*p
== '-') || isspace(*p
)) {
1337 interp
->result
= "missing \">\" in binding";
1343 * Replicate events for DOUBLE and TRIPLE.
1346 if ((count
> 1) && (numPats
< EVENT_BUFFER_SIZE
-1)) {
1347 patPtr
[-1] = patPtr
[0];
1350 if ((count
== 3) && (numPats
< EVENT_BUFFER_SIZE
-1)) {
1351 patPtr
[-1] = patPtr
[0];
1359 *-------------------------------------------------------------
1360 * Step 2: find the sequence in the binding table if it exists,
1361 * and add a new sequence to the table if it doesn't.
1362 *-------------------------------------------------------------
1366 interp
->result
= "no events specified in binding";
1369 patPtr
= &pats
[EVENT_BUFFER_SIZE
-numPats
];
1370 key
.object
= object
;
1371 key
.type
= patPtr
->eventType
;
1372 key
.detail
= patPtr
->detail
;
1373 hPtr
= Tcl_CreateHashEntry(&bindPtr
->patternTable
, (char *) &key
, &new);
1374 sequenceSize
= numPats
*sizeof(Pattern
);
1376 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
1377 psPtr
= psPtr
->nextSeqPtr
) {
1378 if ((numPats
== psPtr
->numPats
)
1379 && ((flags
& PAT_NEARBY
) == (psPtr
->flags
& PAT_NEARBY
))
1380 && (memcmp((char *) patPtr
, (char *) psPtr
->pats
,
1381 sequenceSize
) == 0)) {
1382 *maskPtr
= eventMask
; /*don't forget to pass back the mask*/
1389 Tcl_DeleteHashEntry(hPtr
);
1391 Tcl_AppendResult(interp
, "no binding exists for \"",
1392 eventString
, "\"", (char *) NULL
);
1395 psPtr
= (PatSeq
*) ckalloc((unsigned) (sizeof(PatSeq
)
1396 + (numPats
-1)*sizeof(Pattern
)));
1397 psPtr
->numPats
= numPats
;
1398 psPtr
->command
= NULL
;
1399 psPtr
->flags
= flags
;
1400 psPtr
->nextSeqPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
1402 Tcl_SetHashValue(hPtr
, psPtr
);
1405 * Link the pattern into the list associated with the object.
1408 psPtr
->object
= object
;
1409 hPtr
= Tcl_CreateHashEntry(&bindPtr
->objectTable
, (char *) object
, &new);
1411 psPtr
->nextObjPtr
= NULL
;
1413 psPtr
->nextObjPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
1415 Tcl_SetHashValue(hPtr
, psPtr
);
1417 memcpy((VOID
*) psPtr
->pats
, (VOID
*) patPtr
, sequenceSize
);
1420 *maskPtr
= eventMask
;
1425 *----------------------------------------------------------------------
1429 * Used to parse pattern descriptions. Copies up to
1430 * size characters from p to copy, stopping at end of
1431 * string, space, "-", ">", or whenever size is
1435 * The return value is a pointer to the character just
1436 * after the last one copied (usually "-" or space or
1437 * ">", but could be anything if size was exceeded).
1438 * Also places NULL-terminated string (up to size
1439 * character, including NULL), at copy.
1444 *----------------------------------------------------------------------
1449 register char *p
, /* Pointer to part of pattern. */
1450 register char *copy
, /* Place to copy field. */
1451 int size
/* Maximum number of characters to
1455 while ((*p
!= '\0') && !isspace(*p
) && (*p
!= '>')
1456 && (*p
!= '-') && (size
> 1)) {
1467 *----------------------------------------------------------------------
1471 * Given an X KeyPress or KeyRelease event, map the
1472 * keycode in the event into a KeySym.
1475 * The return value is the KeySym corresponding to
1476 * eventPtr, or NoSymbol if no matching Keysym could be
1480 * In the first call for a given display, keycode-to-
1481 * KeySym maps get loaded.
1483 *----------------------------------------------------------------------
1488 register TkDisplay
*dispPtr
, /* Display in which to
1490 register XEvent
*eventPtr
/* Description of X event. */
1497 * Read the key mapping information from the server if
1498 * we don't have it already.
1501 if (dispPtr
->symsPerCode
== 0) {
1502 Display
*dpy
= dispPtr
->display
;
1505 XDisplayKeycodes(dpy
, &dispPtr
->firstKeycode
, &dispPtr
->lastKeycode
);
1507 dispPtr
->firstKeycode
=
1509 dispPtr
->lastKeycode
=
1512 dispPtr
->keySyms
= XGetKeyboardMapping(dpy
,
1513 dispPtr
->firstKeycode
, dispPtr
->lastKeycode
+ 1
1514 - dispPtr
->firstKeycode
, &dispPtr
->symsPerCode
);
1518 * Compute the lower-case KeySym for this keycode. May
1519 * have to convert an upper-case KeySym to a lower-case
1520 * one if the list only has a single element.
1523 if ((eventPtr
->xkey
.keycode
< dispPtr
->firstKeycode
)
1524 || (eventPtr
->xkey
.keycode
> dispPtr
->lastKeycode
)) {
1527 symPtr
= &dispPtr
->keySyms
[(eventPtr
->xkey
.keycode
1528 - dispPtr
->firstKeycode
) * dispPtr
->symsPerCode
];
1530 if ((dispPtr
->symsPerCode
== 1) || (symPtr
[1] == NoSymbol
)) {
1531 if ((sym
>= XK_A
) && (sym
<= XK_Z
)) {
1532 sym
+= (XK_a
- XK_A
);
1533 } else if ((sym
>= XK_Agrave
) && (sym
<= XK_Odiaeresis
)) {
1534 sym
+= (XK_agrave
- XK_Agrave
);
1535 } else if ((sym
>= XK_Ooblique
) && (sym
<= XK_Thorn
)) {
1536 sym
+= (XK_oslash
- XK_Ooblique
);
1541 * See whether the key is shifted or caps-locked. If so,
1542 * use an upper-case equivalent if provided, or compute
1543 * one (for caps-lock, just compute upper-case: don't
1544 * use shifted KeySym since that would shift non-alphabetic
1548 if (eventPtr
->xkey
.state
& ShiftMask
) {
1549 if ((dispPtr
->symsPerCode
> 1) && (symPtr
[1] != NoSymbol
)) {
1553 if ((sym
>= XK_a
) && (sym
<= XK_z
)) {
1554 sym
+= (XK_A
- XK_a
);
1555 } else if ((sym
>= XK_agrave
) && (sym
<= XK_adiaeresis
)) {
1556 sym
+= (XK_Agrave
- XK_agrave
);
1557 } else if ((sym
>= XK_oslash
) && (sym
<= XK_thorn
)) {
1558 sym
+= (XK_Ooblique
- XK_oslash
);
1562 if (eventPtr
->xkey
.state
& LockMask
) {
1569 *----------------------------------------------------------------------
1573 * Given a list of pattern sequences and a list of
1574 * recent events, return a pattern sequence that matches
1578 * The return value is NULL if no pattern matches the
1579 * recent events from bindPtr. If one or more patterns
1580 * matches, then the longest (or most specific) matching
1581 * pattern is returned.
1586 *----------------------------------------------------------------------
1592 BindingTable
*bindPtr
, /* Information about binding table, such
1593 * as ring of recent events. */
1594 register PatSeq
*psPtr
/* List of pattern sequences. */
1597 register PatSeq
*bestPtr
= NULL
;
1600 * Iterate over all the pattern sequences.
1603 for ( ; psPtr
!= NULL
; psPtr
= psPtr
->nextSeqPtr
) {
1604 register XEvent
*eventPtr
;
1605 register Pattern
*patPtr
;
1608 int patCount
, ringCount
, flags
, state
;
1611 * Iterate over all the patterns in a sequence to be
1612 * sure that they all match.
1615 eventPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1616 detailPtr
= &bindPtr
->detailRing
[bindPtr
->curEvent
];
1617 window
= eventPtr
->xany
.window
;
1618 patPtr
= psPtr
->pats
;
1619 patCount
= psPtr
->numPats
;
1620 ringCount
= EVENT_BUFFER_SIZE
;
1621 while (patCount
> 0) {
1622 if (ringCount
<= 0) {
1625 if (eventPtr
->xany
.type
!= patPtr
->eventType
) {
1627 * If the event is a mouse motion, button release,
1628 * or key release event, and it didn't match
1629 * the pattern, then just skip the event and try
1630 * the next event against the same pattern.
1633 if ((eventPtr
->xany
.type
== MotionNotify
)
1634 || (eventPtr
->xany
.type
== ButtonRelease
)
1635 || (eventPtr
->xany
.type
== KeyRelease
)
1636 || (eventPtr
->xany
.type
== NoExpose
)
1637 || (eventPtr
->xany
.type
== EnterNotify
)
1638 || (eventPtr
->xany
.type
== LeaveNotify
)
1639 || (eventPtr
->xany
.type
== GraphicsExpose
)) {
1644 if (eventPtr
->xany
.window
!= window
) {
1648 flags
= flagArray
[eventPtr
->type
];
1649 if (flags
& KEY_BUTTON_MOTION
) {
1650 state
= eventPtr
->xkey
.state
;
1651 } else if (flags
& CROSSING
) {
1652 state
= eventPtr
->xcrossing
.state
;
1656 if (patPtr
->needMods
!= 0) {
1657 int modMask
= patPtr
->needMods
;
1659 if (!dispPtr
->metaModMask
&& !dispPtr
->altModMask
&& !dispPtr
->modeModMask
) {
1661 XModifierKeymap
*modMapPtr
;
1665 modMapPtr
= XGetModifierMapping(dispPtr
->display
);
1666 codePtr
= modMapPtr
->modifiermap
;
1667 max
= 8*modMapPtr
->max_keypermod
;
1669 for (i
= 0; i
< max
; i
++, codePtr
++) {
1670 if (*codePtr
== 0) {
1673 keysym
= XKeycodeToKeysym(dispPtr
->display
, *codePtr
, 0);
1674 if (keysym
== XK_Mode_switch
) {
1675 dispPtr
->modeModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1677 if ((keysym
== XK_Meta_L
) || (keysym
== XK_Meta_R
)) {
1678 dispPtr
->metaModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1680 if ((keysym
== XK_Alt_L
) || (keysym
== XK_Alt_R
)) {
1681 dispPtr
->altModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1685 if ((modMask
& META_MASK
) && (dispPtr
->metaModMask
!= 0)) {
1686 modMask
= (modMask
& ~META_MASK
) | dispPtr
->metaModMask
;
1688 if ((modMask
& ALT_MASK
) && (dispPtr
->altModMask
!= 0)) {
1689 modMask
= (modMask
& ~ALT_MASK
) | dispPtr
->altModMask
;
1692 if ((state
& META_MASK
) && (dispPtr
->metaModMask
!= 0)) {
1693 state
= (state
& ~META_MASK
) | dispPtr
->metaModMask
;
1695 if ((state
& ALT_MASK
) && (dispPtr
->altModMask
!= 0)) {
1696 state
= (state
& ~ALT_MASK
) | dispPtr
->altModMask
;
1699 if ((state
& modMask
) != modMask
) {
1704 if ((state
& patPtr
->hateMods
) != 0) {
1708 if ((patPtr
->detail
!= 0)
1709 && (patPtr
->detail
!= *detailPtr
)) {
1712 if (psPtr
->flags
& PAT_NEARBY
) {
1713 register XEvent
*firstPtr
;
1715 firstPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1716 if ((firstPtr
->xkey
.x_root
1717 < (eventPtr
->xkey
.x_root
- NEARBY_PIXELS
))
1718 || (firstPtr
->xkey
.x_root
1719 > (eventPtr
->xkey
.x_root
+ NEARBY_PIXELS
))
1720 || (firstPtr
->xkey
.y_root
1721 < (eventPtr
->xkey
.y_root
- NEARBY_PIXELS
))
1722 || (firstPtr
->xkey
.y_root
1723 > (eventPtr
->xkey
.y_root
+ NEARBY_PIXELS
))
1724 || (firstPtr
->xkey
.time
1725 > (eventPtr
->xkey
.time
+ NEARBY_MS
))) {
1732 if (eventPtr
== bindPtr
->eventRing
) {
1733 eventPtr
= &bindPtr
->eventRing
[EVENT_BUFFER_SIZE
-1];
1734 detailPtr
= &bindPtr
->detailRing
[EVENT_BUFFER_SIZE
-1];
1743 * This sequence matches. If we've already got another match,
1744 * pick whichever is most specific. Detail is most important,
1745 * then needMods, then hateMods.
1748 if (bestPtr
!= NULL
) {
1749 register Pattern
*patPtr2
;
1752 if (psPtr
->numPats
!= bestPtr
->numPats
) {
1753 if (bestPtr
->numPats
> psPtr
->numPats
) {
1759 for (i
= 0, patPtr
= psPtr
->pats
, patPtr2
= bestPtr
->pats
;
1760 i
< psPtr
->numPats
; i
++,patPtr
++, patPtr2
++) {
1761 if (patPtr
->detail
!= patPtr2
->detail
) {
1762 if (patPtr
->detail
== 0) {
1768 if (patPtr
->needMods
!= patPtr2
->needMods
) {
1769 if ((patPtr
->needMods
& patPtr2
->needMods
)
1770 == patPtr
->needMods
) {
1776 if (patPtr
->hateMods
!= patPtr2
->hateMods
) {
1777 if ((patPtr
->hateMods
& patPtr2
->hateMods
)
1778 == patPtr2
->hateMods
) {
1785 goto nextSequence
; /* Tie goes to newest pattern. */
1790 nextSequence
: continue;
1796 *--------------------------------------------------------------
1800 * Given a command and an event, produce a new command
1801 * by replacing % constructs in the original command
1802 * with information from the X event.
1805 * The return result is a pointer to the new %-substituted
1806 * command. If the command fits in the space at after, then
1807 * the return value is after. If the command is too large
1808 * to fit at after, then the return value is a pointer to
1809 * a malloc-ed buffer holding the command; in this case it
1810 * is the caller's responsibility to free up the buffer when
1816 *--------------------------------------------------------------
1821 register char *before
, /* Command containing percent
1822 * expressions to be replaced. */
1823 register XEvent
*eventPtr
, /* X event containing information
1824 * to be used in % replacements. */
1825 KeySym keySym
, /* KeySym: only relevant for
1826 * KeyPress and KeyRelease events). */
1827 char *after
, /* Place to generate new expanded
1828 * command. Must contain at least
1829 * "afterSize" bytes of space. */
1830 int afterSize
/* Number of bytes of space available at
1834 register char *buffer
; /* Pointer to buffer currently being used
1835 * as destination. */
1836 register char *dst
; /* Pointer to next place to store character
1837 * in substituted string. */
1838 int spaceLeft
; /* Indicates how many more non-null bytes
1839 * may be stored at *dst before space
1841 int spaceNeeded
, cvtFlags
; /* Used to substitute string as proper Tcl
1845 register char *string
;
1846 char numStorage
[NUM_SIZE
+1];
1848 if (eventPtr
->type
< LASTEvent
) {
1849 flags
= flagArray
[eventPtr
->type
];
1853 dst
= buffer
= after
;
1854 spaceLeft
= afterSize
- 1;
1855 while (*before
!= 0) {
1856 if (*before
!= '%') {
1859 * Expand the destination string if necessary.
1862 if (spaceLeft
<= 0) {
1865 newSpace
= (char *) ckalloc((unsigned) (2*afterSize
));
1866 memcpy((VOID
*) newSpace
, (VOID
*) buffer
, afterSize
);
1868 dst
= newSpace
+ (dst
- buffer
);
1869 if (buffer
!= after
) {
1873 spaceLeft
= afterSize
- (dst
-buffer
) - 1;
1884 switch (before
[1]) {
1886 number
= eventPtr
->xany
.serial
;
1889 number
= (int) eventPtr
->xconfigure
.above
;
1892 number
= eventPtr
->xbutton
.button
;
1895 if (flags
& EXPOSE
) {
1896 number
= eventPtr
->xexpose
.count
;
1897 } else if (flags
& MAPPING
) {
1898 number
= eventPtr
->xmapping
.count
;
1902 if (flags
& (CROSSING
|FOCUS
)) {
1903 switch (eventPtr
->xcrossing
.detail
) {
1904 case NotifyAncestor
:
1905 string
= "NotifyAncestor";
1908 string
= "NotifyVirtual";
1910 case NotifyInferior
:
1911 string
= "NotifyInferior";
1913 case NotifyNonlinear
:
1914 string
= "NotifyNonlinear";
1916 case NotifyNonlinearVirtual
:
1917 string
= "NotifyNonlinearVirtual";
1920 string
= "NotifyPointer";
1922 case NotifyPointerRoot
:
1923 string
= "NotifyPointerRoot";
1925 case NotifyDetailNone
:
1926 string
= "NotifyDetailNone";
1929 } else if (flags
& CONFIG_REQ
) {
1930 switch (eventPtr
->xconfigurerequest
.detail
) {
1941 string
= "BottomIf";
1944 string
= "Opposite";
1950 number
= eventPtr
->xcrossing
.focus
;
1953 if (flags
& EXPOSE
) {
1954 number
= eventPtr
->xexpose
.height
;
1955 } else if (flags
& (CONFIG
|CONFIG_REQ
)) {
1956 number
= eventPtr
->xconfigure
.height
;
1957 } else if (flags
& RESIZE_REQ
) {
1958 number
= eventPtr
->xresizerequest
.height
;
1962 number
= eventPtr
->xkey
.keycode
;
1965 if (flags
& CROSSING
) {
1966 number
= eventPtr
->xcrossing
.mode
;
1967 } else if (flags
& FOCUS
) {
1968 number
= eventPtr
->xfocus
.mode
;
1972 string
= "NotifyNormal";
1975 string
= "NotifyGrab";
1978 string
= "NotifyUngrab";
1980 case NotifyWhileGrabbed
:
1981 string
= "NotifyWhileGrabbed";
1986 if (flags
& CREATE
) {
1987 number
= eventPtr
->xcreatewindow
.override_redirect
;
1988 } else if (flags
& MAP
) {
1989 number
= eventPtr
->xmap
.override_redirect
;
1990 } else if (flags
& REPARENT
) {
1991 number
= eventPtr
->xreparent
.override_redirect
;
1992 } else if (flags
& CONFIG
) {
1993 number
= eventPtr
->xconfigure
.override_redirect
;
1997 switch (eventPtr
->xcirculate
.place
) {
1999 string
= "PlaceOnTop";
2002 string
= "PlaceOnBottom";
2007 if (flags
& KEY_BUTTON_MOTION
) {
2008 number
= eventPtr
->xkey
.state
;
2009 } else if (flags
& CROSSING
) {
2010 number
= eventPtr
->xcrossing
.state
;
2011 } else if (flags
& VISIBILITY
) {
2012 switch (eventPtr
->xvisibility
.state
) {
2013 case VisibilityUnobscured
:
2014 string
= "VisibilityUnobscured";
2016 case VisibilityPartiallyObscured
:
2017 string
= "VisibilityPartiallyObscured";
2019 case VisibilityFullyObscured
:
2020 string
= "VisibilityFullyObscured";
2027 if (flags
& (KEY_BUTTON_MOTION
|PROP
|SEL_CLEAR
)) {
2028 number
= (int) eventPtr
->xkey
.time
;
2029 } else if (flags
& SEL_REQ
) {
2030 number
= (int) eventPtr
->xselectionrequest
.time
;
2031 } else if (flags
& SEL_NOTIFY
) {
2032 number
= (int) eventPtr
->xselection
.time
;
2036 number
= eventPtr
->xconfigurerequest
.value_mask
;
2039 if (flags
& EXPOSE
) {
2040 number
= eventPtr
->xexpose
.width
;
2041 } else if (flags
& (CONFIG
|CONFIG_REQ
)) {
2042 number
= eventPtr
->xconfigure
.width
;
2043 } else if (flags
& RESIZE_REQ
) {
2044 number
= eventPtr
->xresizerequest
.width
;
2048 if (flags
& KEY_BUTTON_MOTION
) {
2049 number
= eventPtr
->xkey
.x
;
2050 } else if (flags
& EXPOSE
) {
2051 number
= eventPtr
->xexpose
.x
;
2052 } else if (flags
& (CREATE
|CONFIG
|GRAVITY
|CONFIG_REQ
)) {
2053 number
= eventPtr
->xcreatewindow
.x
;
2054 } else if (flags
& REPARENT
) {
2055 number
= eventPtr
->xreparent
.x
;
2056 } else if (flags
& CROSSING
) {
2057 number
= eventPtr
->xcrossing
.x
;
2061 if (flags
& KEY_BUTTON_MOTION
) {
2062 number
= eventPtr
->xkey
.y
;
2063 } else if (flags
& EXPOSE
) {
2064 number
= eventPtr
->xexpose
.y
;
2065 } else if (flags
& (CREATE
|CONFIG
|GRAVITY
|CONFIG_REQ
)) {
2066 number
= eventPtr
->xcreatewindow
.y
;
2067 } else if (flags
& REPARENT
) {
2068 number
= eventPtr
->xreparent
.y
;
2069 } else if (flags
& CROSSING
) {
2070 number
= eventPtr
->xcrossing
.y
;
2075 if ((eventPtr
->type
== KeyPress
)
2076 || (eventPtr
->type
== KeyRelease
)) {
2079 numChars
= XLookupString(&eventPtr
->xkey
, numStorage
,
2080 NUM_SIZE
, (KeySym
*) NULL
,
2081 (XComposeStatus
*) NULL
);
2082 numStorage
[numChars
] = '\0';
2083 string
= numStorage
;
2087 number
= eventPtr
->xcreatewindow
.border_width
;
2090 number
= (int) eventPtr
->xany
.display
;
2093 number
= (int) eventPtr
->xany
.send_event
;
2096 if ((eventPtr
->type
== KeyPress
)
2097 || (eventPtr
->type
== KeyRelease
)) {
2098 register KeySymInfo
*kPtr
;
2100 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
2101 if (kPtr
->value
== keySym
) {
2102 string
= kPtr
->name
;
2109 number
= (int) keySym
;
2112 number
= (int) eventPtr
->xkey
.root
;
2115 number
= (int) eventPtr
->xkey
.subwindow
;
2118 number
= eventPtr
->type
;
2123 if (XFindContext(eventPtr
->xany
.display
, eventPtr
->xany
.window
,
2124 tkWindowContext
, (void *) &winPtr
) == 0) {
2125 string
= winPtr
->pathName
;
2132 number
= eventPtr
->xkey
.x_root
;
2135 number
= eventPtr
->xkey
.y_root
;
2138 numStorage
[0] = before
[1];
2139 numStorage
[1] = '\0';
2140 string
= numStorage
;
2145 sprintf(numStorage
, "%d", number
);
2146 string
= numStorage
;
2149 spaceNeeded
= Tcl_ScanElement(string
, &cvtFlags
);
2150 if (spaceNeeded
>= spaceLeft
) {
2153 newSpace
= (char *) ckalloc((unsigned)
2154 (afterSize
+ spaceNeeded
+ 50));
2155 memcpy((VOID
*) newSpace
, (VOID
*) buffer
, afterSize
);
2156 afterSize
+= spaceNeeded
+ 50;
2157 dst
= newSpace
+ (dst
- buffer
);
2158 if (buffer
!= after
) {
2162 spaceLeft
= afterSize
- (dst
-buffer
) - 1;
2164 spaceNeeded
= Tcl_ConvertElement(string
, dst
,
2165 cvtFlags
| TCL_DONT_USE_BRACES
);
2167 spaceLeft
-= spaceNeeded
;
2175 *----------------------------------------------------------------------
2179 * This procedure is invoked to handle errors that occur in Tcl
2180 * commands that are invoked in "background" (e.g. from event or
2187 * The command "tkerror" is invoked to process the error, passing
2188 * it the error message. If that fails, then an error message
2189 * is output on stderr.
2191 *----------------------------------------------------------------------
2196 Tcl_Interp
*interp
/* Interpreter in which an error has
2203 char *errorInfo
, *tmp
;
2206 error
= (char *) ckalloc((unsigned) (strlen(interp
->result
) + 1));
2207 strcpy(error
, interp
->result
);
2208 tmp
= Tcl_GetVar(interp
, "errorInfo", TCL_GLOBAL_ONLY
);
2212 errorInfo
= (char *) ckalloc((unsigned) (strlen(tmp
) + 1));
2213 strcpy(errorInfo
, tmp
);
2215 argv
[0] = "tkerror";
2217 command
= Tcl_Merge(2, argv
);
2218 result
= Tcl_GlobalEval(interp
, command
);
2219 if (result
!= TCL_OK
) {
2220 if (strcmp(interp
->result
, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2221 fprintf(stderr
, "%s\n", errorInfo
);
2223 fprintf(stderr
, "tkerror failed to handle background error.\n");
2224 fprintf(stderr
, " Original error: %s\n", error
);
2225 fprintf(stderr
, " Error in tkerror: %s\n", interp
->result
);
2228 Tcl_ResetResult(interp
);
2231 if (errorInfo
!= error
) {