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(interp
)
408 Tcl_Interp
*interp
; /* Interpreter to associate with the binding
409 * table: commands are executed in this
412 register BindingTable
*bindPtr
;
416 * If this is the first time a binding table has been created,
417 * initialize the global data structures.
421 register KeySymInfo
*kPtr
;
422 register Tcl_HashEntry
*hPtr
;
423 register ModInfo
*modPtr
;
424 register EventInfo
*eiPtr
;
429 Tcl_InitHashTable(&keySymTable
, TCL_STRING_KEYS
);
430 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
431 hPtr
= Tcl_CreateHashEntry(&keySymTable
, kPtr
->name
, &dummy
);
432 Tcl_SetHashValue(hPtr
, kPtr
->value
);
435 Tcl_InitHashTable(&modTable
, TCL_STRING_KEYS
);
436 for (modPtr
= modArray
; modPtr
->name
!= NULL
; modPtr
++) {
437 hPtr
= Tcl_CreateHashEntry(&modTable
, modPtr
->name
, &dummy
);
438 Tcl_SetHashValue(hPtr
, modPtr
);
441 Tcl_InitHashTable(&eventTable
, TCL_STRING_KEYS
);
442 for (eiPtr
= eventArray
; eiPtr
->name
!= NULL
; eiPtr
++) {
443 hPtr
= Tcl_CreateHashEntry(&eventTable
, eiPtr
->name
, &dummy
);
444 Tcl_SetHashValue(hPtr
, eiPtr
);
449 * Create and initialize a new binding table.
452 bindPtr
= (BindingTable
*) ckalloc(sizeof(BindingTable
));
453 for (i
= 0; i
< EVENT_BUFFER_SIZE
; i
++) {
454 bindPtr
->eventRing
[i
].type
= -1;
456 bindPtr
->curEvent
= 0;
457 Tcl_InitHashTable(&bindPtr
->patternTable
,
458 sizeof(PatternTableKey
)/sizeof(int));
459 Tcl_InitHashTable(&bindPtr
->objectTable
, TCL_ONE_WORD_KEYS
);
460 bindPtr
->interp
= interp
;
461 return (Tk_BindingTable
) bindPtr
;
465 *--------------------------------------------------------------
467 * Tk_DeleteBindingTable --
469 * Destroy a binding table and free up all its memory.
470 * The caller should not use bindingTable again after
471 * this procedure returns.
479 *--------------------------------------------------------------
483 Tk_DeleteBindingTable(bindingTable
)
484 Tk_BindingTable bindingTable
; /* Token for the binding table to
487 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
488 PatSeq
*psPtr
, *nextPtr
;
490 Tcl_HashSearch search
;
493 * Find and delete all of the patterns associated with the binding
497 for (hPtr
= Tcl_FirstHashEntry(&bindPtr
->patternTable
, &search
);
498 hPtr
!= NULL
; hPtr
= Tcl_NextHashEntry(&search
)) {
499 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
500 psPtr
!= NULL
; psPtr
= nextPtr
) {
501 nextPtr
= psPtr
->nextSeqPtr
;
502 Tk_EventuallyFree((ClientData
) psPtr
->command
,
503 (Tk_FreeProc
*) free
);
504 ckfree((char *) psPtr
);
509 * Clean up the rest of the information associated with the
513 Tcl_DeleteHashTable(&bindPtr
->patternTable
);
514 Tcl_DeleteHashTable(&bindPtr
->objectTable
);
515 ckfree((char *) bindPtr
);
519 *--------------------------------------------------------------
521 * Tk_CreateBinding --
523 * Add a binding to a binding table, so that future calls to
524 * Tk_BindEvent may execute the command in the binding.
527 * The return value is 0 if an error occurred while setting
528 * up the binding. In this case, an error message will be
529 * left in interp->result. If all went well then the return
530 * value is a mask of the event types that must be made
531 * available to Tk_BindEvent in order to properly detect when
532 * this binding triggers. This value can be used to determine
533 * what events to select for in a window, for example.
536 * The new binding may cause future calls to Tk_BindEvent to
537 * behave differently than they did previously.
539 *--------------------------------------------------------------
543 Tk_CreateBinding(interp
, bindingTable
, object
, eventString
, command
, append
)
544 Tcl_Interp
*interp
; /* Used for error reporting. */
545 Tk_BindingTable bindingTable
; /* Table in which to create binding. */
546 ClientData object
; /* Token for object with which binding
548 char *eventString
; /* String describing event sequence
549 * that triggers binding. */
550 char *command
; /* Contains Tcl command to execute
551 * when binding triggers. */
552 int append
; /* 0 means replace any existing
553 * binding for eventString; 1 means
554 * append to that binding. */
556 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
557 register PatSeq
*psPtr
;
558 unsigned long eventMask
;
560 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 1, &eventMask
);
564 if (append
&& (psPtr
->command
!= NULL
)) {
568 length
= strlen(psPtr
->command
) + strlen(command
) + 3;
569 new = (char *) ckalloc((unsigned) length
);
570 sprintf(new, "%s; %s", psPtr
->command
, command
);
571 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
572 psPtr
->command
= new;
574 if (psPtr
->command
!= NULL
) {
575 Tk_EventuallyFree((ClientData
) psPtr
->command
,
576 (Tk_FreeProc
*) free
);
578 psPtr
->command
= (char *) ckalloc((unsigned) (strlen(command
) + 1));
579 strcpy(psPtr
->command
, command
);
583 * See if the command contains percents and thereby requires
584 * percent substitution.
587 if (strchr(psPtr
->command
, '%') != NULL
) {
588 psPtr
->flags
|= PAT_PERCENTS
;
594 *--------------------------------------------------------------
596 * Tk_DeleteBinding --
598 * Remove an event binding from a binding table.
601 * The result is a standard Tcl return value. If an error
602 * occurs then interp->result will contain an error message.
605 * The binding given by object and eventString is removed
608 *--------------------------------------------------------------
612 Tk_DeleteBinding(interp
, bindingTable
, object
, eventString
)
613 Tcl_Interp
*interp
; /* Used for error reporting. */
614 Tk_BindingTable bindingTable
; /* Table in which to delete binding. */
615 ClientData object
; /* Token for object with which binding
617 char *eventString
; /* String describing event sequence
618 * that triggers binding. */
620 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
621 register PatSeq
*psPtr
, *prevPtr
;
622 unsigned long eventMask
;
625 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 0, &eventMask
);
627 Tcl_ResetResult(interp
);
632 * Unlink the binding from the list for its object, then from the
633 * list for its pattern.
636 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
638 panic("Tk_DeleteBinding couldn't find object table entry");
640 prevPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
641 if (prevPtr
== psPtr
) {
642 Tcl_SetHashValue(hPtr
, psPtr
->nextObjPtr
);
644 for ( ; ; prevPtr
= prevPtr
->nextObjPtr
) {
645 if (prevPtr
== NULL
) {
646 panic("Tk_DeleteBinding couldn't find on object list");
648 if (prevPtr
->nextObjPtr
== psPtr
) {
649 prevPtr
->nextObjPtr
= psPtr
->nextObjPtr
;
654 prevPtr
= (PatSeq
*) Tcl_GetHashValue(psPtr
->hPtr
);
655 if (prevPtr
== psPtr
) {
656 if (psPtr
->nextSeqPtr
== NULL
) {
657 Tcl_DeleteHashEntry(psPtr
->hPtr
);
659 Tcl_SetHashValue(psPtr
->hPtr
, psPtr
->nextSeqPtr
);
662 for ( ; ; prevPtr
= prevPtr
->nextSeqPtr
) {
663 if (prevPtr
== NULL
) {
664 panic("Tk_DeleteBinding couldn't find on hash chain");
666 if (prevPtr
->nextSeqPtr
== psPtr
) {
667 prevPtr
->nextSeqPtr
= psPtr
->nextSeqPtr
;
672 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
673 ckfree((char *) psPtr
);
678 *--------------------------------------------------------------
682 * Return the command associated with a given event string.
685 * The return value is a pointer to the command string
686 * associated with eventString for object in the domain
687 * given by bindingTable. If there is no binding for
688 * eventString, or if eventString is improperly formed,
689 * then NULL is returned and an error message is left in
690 * interp->result. The return value is semi-static: it
691 * will persist until the binding is changed or deleted.
696 *--------------------------------------------------------------
700 Tk_GetBinding(interp
, bindingTable
, object
, eventString
)
701 Tcl_Interp
*interp
; /* Interpreter for error reporting. */
702 Tk_BindingTable bindingTable
; /* Table in which to look for
704 ClientData object
; /* Token for object with which binding
706 char *eventString
; /* String describing event sequence
707 * that triggers binding. */
709 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
710 register PatSeq
*psPtr
;
711 unsigned long eventMask
;
713 psPtr
= FindSequence(interp
, bindPtr
, object
, eventString
, 0, &eventMask
);
717 return psPtr
->command
;
721 *--------------------------------------------------------------
723 * Tk_GetAllBindings --
725 * Return a list of event strings for all the bindings
726 * associated with a given object.
729 * There is no return value. Interp->result is modified to
730 * hold a Tcl list with one entry for each binding associated
731 * with object in bindingTable. Each entry in the list
732 * contains the event string associated with one binding.
737 *--------------------------------------------------------------
741 Tk_GetAllBindings(interp
, bindingTable
, object
)
742 Tcl_Interp
*interp
; /* Interpreter for error reporting. */
743 Tk_BindingTable bindingTable
; /* Table in which to look for
745 ClientData object
; /* Token for object. */
748 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
749 register PatSeq
*psPtr
;
750 register Pattern
*patPtr
;
752 char string
[200*EVENT_BUFFER_SIZE
];
754 int patsLeft
, needMods
;
755 register ModInfo
*modPtr
;
757 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
761 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
762 psPtr
= psPtr
->nextObjPtr
) {
767 * For each binding, output information about each of the
768 * patterns in its sequence. The order of the patterns in
769 * the sequence is backwards from the order in which they
773 for (patsLeft
= psPtr
->numPats
,
774 patPtr
= &psPtr
->pats
[psPtr
->numPats
- 1];
775 patsLeft
> 0; patsLeft
--, patPtr
--) {
778 * Check for simple case of an ASCII character.
781 if ((patPtr
->eventType
== KeyPress
)
782 && (patPtr
->needMods
== 0)
783 && (patPtr
->hateMods
== ~ShiftMask
)
784 && isascii(patPtr
->detail
) && isprint(patPtr
->detail
)
785 && (patPtr
->detail
!= '<')
786 && (patPtr
->detail
!= ' ')) {
794 * It's a more general event specification. First check
795 * for "Double" or "Triple", then "Any", then modifiers,
796 * the event type, then keysym or button detail.
801 if ((patsLeft
> 1) && (memcmp((char *) patPtr
,
802 (char *) (patPtr
-1), sizeof(Pattern
)) == 0)) {
805 if ((patsLeft
> 1) && (memcmp((char *) patPtr
,
806 (char *) (patPtr
-1), sizeof(Pattern
)) == 0)) {
809 strcpy(p
, "Triple-");
811 strcpy(p
, "Double-");
816 if (patPtr
->hateMods
== 0) {
821 for (needMods
= patPtr
->needMods
, modPtr
= modArray
;
822 needMods
!= 0; modPtr
++) {
823 if (modPtr
->mask
& needMods
) {
824 needMods
&= ~modPtr
->mask
;
825 strcpy(p
, modPtr
->name
);
832 if ((patPtr
->eventType
!= KeyPress
)
833 || (patPtr
->detail
== 0)) {
834 register EventInfo
*eiPtr
;
836 for (eiPtr
= eventArray
; eiPtr
->name
!= NULL
; eiPtr
++) {
837 if (eiPtr
->type
== patPtr
->eventType
) {
838 strcpy(p
, eiPtr
->name
);
840 if (patPtr
->detail
!= 0) {
849 if (patPtr
->detail
!= 0) {
850 if ((patPtr
->eventType
== KeyPress
)
851 || (patPtr
->eventType
== KeyRelease
)) {
852 register KeySymInfo
*kPtr
;
854 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
855 if (patPtr
->detail
== (int) kPtr
->value
) {
856 sprintf(p
, "%.100s", kPtr
->name
);
862 sprintf(p
, "%d", patPtr
->detail
);
870 if ((p
- string
) >= sizeof(string
)) {
871 panic("Tk_GetAllBindings overflowed buffer");
873 Tcl_AppendElement(interp
, string
, 0);
878 *--------------------------------------------------------------
880 * Tk_DeleteAllBindings --
882 * Remove all bindings associated with a given object in a
883 * given binding table.
886 * All bindings associated with object are removed from
892 *--------------------------------------------------------------
896 Tk_DeleteAllBindings(bindingTable
, object
)
897 Tk_BindingTable bindingTable
; /* Table in which to delete
899 ClientData object
; /* Token for object. */
901 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
902 register PatSeq
*psPtr
, *prevPtr
;
906 hPtr
= Tcl_FindHashEntry(&bindPtr
->objectTable
, (char *) object
);
910 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
912 nextPtr
= psPtr
->nextObjPtr
;
915 * Be sure to remove each binding from its hash chain in the
916 * pattern table. If this is the last pattern in the chain,
917 * then delete the hash entry too.
920 prevPtr
= (PatSeq
*) Tcl_GetHashValue(psPtr
->hPtr
);
921 if (prevPtr
== psPtr
) {
922 if (psPtr
->nextSeqPtr
== NULL
) {
923 Tcl_DeleteHashEntry(psPtr
->hPtr
);
925 Tcl_SetHashValue(psPtr
->hPtr
, psPtr
->nextSeqPtr
);
928 for ( ; ; prevPtr
= prevPtr
->nextSeqPtr
) {
929 if (prevPtr
== NULL
) {
930 panic("Tk_DeleteAllBindings couldn't find on hash chain");
932 if (prevPtr
->nextSeqPtr
== psPtr
) {
933 prevPtr
->nextSeqPtr
= psPtr
->nextSeqPtr
;
938 Tk_EventuallyFree((ClientData
) psPtr
->command
, (Tk_FreeProc
*) free
);
939 ckfree((char *) psPtr
);
941 Tcl_DeleteHashEntry(hPtr
);
945 *--------------------------------------------------------------
949 * This procedure is invoked to process an X event. The
950 * event is added to those recorded for the binding table.
951 * Then each of the objects at *objectPtr is checked in
952 * order to see if it has a binding that matches the recent
953 * events. If so, that binding is invoked and the rest of
954 * objects are skipped.
960 * Depends on the command associated with the matching
963 *--------------------------------------------------------------
967 Tk_BindEvent(bindingTable
, eventPtr
, tkwin
, numObjects
, objectPtr
)
968 Tk_BindingTable bindingTable
; /* Table in which to look for
970 XEvent
*eventPtr
; /* What actually happened. */
971 Tk_Window tkwin
; /* Window on display where event
972 * occurred (needed in order to
973 * locate display information). */
974 int numObjects
; /* Number of objects at *objectPtr. */
975 ClientData
*objectPtr
; /* Array of one or more objects
976 * to check for a matching binding. */
978 BindingTable
*bindPtr
= (BindingTable
*) bindingTable
;
979 TkDisplay
*dispPtr
= ((TkWindow
*) tkwin
)->dispPtr
;
987 * Add the new event to the ring of saved events for the
988 * binding table. Consecutive MotionNotify events get combined:
989 * if both the new event and the previous event are MotionNotify,
990 * then put the new event *on top* of the previous event.
993 if ((eventPtr
->type
!= MotionNotify
)
994 || (bindPtr
->eventRing
[bindPtr
->curEvent
].type
!= MotionNotify
)) {
996 if (bindPtr
->curEvent
>= EVENT_BUFFER_SIZE
) {
997 bindPtr
->curEvent
= 0;
1000 ringPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1001 memcpy((VOID
*) ringPtr
, (VOID
*) eventPtr
, sizeof(XEvent
));
1003 bindPtr
->detailRing
[bindPtr
->curEvent
] = 0;
1004 if ((ringPtr
->type
== KeyPress
) || (ringPtr
->type
== KeyRelease
)) {
1005 detail
= (int) GetKeySym(dispPtr
, ringPtr
);
1006 if (detail
== NoSymbol
) {
1009 } else if ((ringPtr
->type
== ButtonPress
)
1010 || (ringPtr
->type
== ButtonRelease
)) {
1011 detail
= ringPtr
->xbutton
.button
;
1013 bindPtr
->detailRing
[bindPtr
->curEvent
] = detail
;
1016 * Loop over all the objects, matching the new event against
1020 for ( ; numObjects
> 0; numObjects
--, objectPtr
++) {
1023 * Match the new event against those recorded in the
1024 * pattern table, saving the longest matching pattern.
1025 * For events with details (button and key events) first
1026 * look for a binding for the specific key or button.
1027 * If none is found, then look for a binding for all
1028 * keys or buttons (detail of 0).
1032 key
.object
= *objectPtr
;
1033 key
.type
= ringPtr
->type
;
1034 key
.detail
= detail
;
1035 hPtr
= Tcl_FindHashEntry(&bindPtr
->patternTable
, (char *) &key
);
1037 matchPtr
= MatchPatterns(dispPtr
, bindPtr
,
1038 (PatSeq
*) Tcl_GetHashValue(hPtr
));
1040 if ((detail
!= 0) && (matchPtr
== NULL
)) {
1042 hPtr
= Tcl_FindHashEntry(&bindPtr
->patternTable
, (char *) &key
);
1044 matchPtr
= MatchPatterns(dispPtr
, bindPtr
,
1045 (PatSeq
*) Tcl_GetHashValue(hPtr
));
1049 if (matchPtr
!= NULL
) {
1052 * %-substitution can increase the length of the command.
1053 * This code handles three cases: (a) no substitution;
1054 * (b) substitution results in short command (use space
1055 * on stack); and (c) substitution results in long
1056 * command (malloc it).
1059 #define STATIC_SPACE 200
1060 char shortSpace
[STATIC_SPACE
];
1063 if (matchPtr
->flags
& PAT_PERCENTS
) {
1065 p
= ExpandPercents(matchPtr
->command
, eventPtr
,
1066 (KeySym
) detail
, shortSpace
, STATIC_SPACE
);
1067 result
= Tcl_GlobalEval(bindPtr
->interp
, p
);
1068 if (p
!= shortSpace
) {
1073 * The code below is tricky in order allow the binding to
1074 * be modified or deleted as part of the command that the
1075 * binding invokes. Must make sure that the actual command
1076 * string isn't freed until the command completes, and must
1077 * copy the address of this string into a local variable
1078 * in case it's modified by the command.
1081 char *cmd
= matchPtr
->command
;
1083 Tk_Preserve((ClientData
) cmd
);
1084 result
= Tcl_GlobalEval(bindPtr
->interp
, cmd
);
1085 Tk_Release((ClientData
) cmd
);
1087 if (result
!= TCL_OK
) {
1088 Tcl_AddErrorInfo(bindPtr
->interp
,
1089 "\n (command bound to event)");
1090 TkBindError(bindPtr
->interp
);
1098 *----------------------------------------------------------------------
1102 * Find the entry in a binding table that corresponds to a
1103 * particular pattern string, and return a pointer to that
1107 * The return value is normally a pointer to the PatSeq
1108 * in patternTable that corresponds to eventString. If an error
1109 * was found while parsing eventString, or if "create" is 0 and
1110 * no pattern sequence previously existed, then NULL is returned
1111 * and interp->result contains a message describing the problem.
1112 * If no pattern sequence previously existed for eventString, then
1113 * a new one is created with a NULL command field. In a successful
1114 * return, *maskPtr is filled in with a mask of the event types
1115 * on which the pattern sequence depends.
1118 * A new pattern sequence may be created.
1120 *----------------------------------------------------------------------
1124 FindSequence(interp
, bindPtr
, object
, eventString
, create
, maskPtr
)
1125 Tcl_Interp
*interp
; /* Interpreter to use for error
1127 BindingTable
*bindPtr
; /* Table to use for lookup. */
1128 ClientData object
; /* Token for object(s) with which binding
1130 char *eventString
; /* String description of pattern to
1131 * match on. See user documentation
1133 int create
; /* 0 means don't create the entry if
1134 * it doesn't already exist. Non-zero
1136 unsigned long *maskPtr
; /* *maskPtr is filled in with the event
1137 * types on which this pattern sequence
1141 Pattern pats
[EVENT_BUFFER_SIZE
];
1144 register Pattern
*patPtr
;
1145 register PatSeq
*psPtr
;
1146 register Tcl_HashEntry
*hPtr
;
1147 #define FIELD_SIZE 20
1148 char field
[FIELD_SIZE
];
1149 int flags
, any
, count
, new, sequenceSize
;
1150 unsigned long eventMask
;
1151 PatternTableKey key
;
1154 *-------------------------------------------------------------
1155 * Step 1: parse the pattern string to produce an array
1156 * of Patterns. The array is generated backwards, so
1157 * that the lowest-indexed pattern corresponds to the last
1158 * event that must occur.
1159 *-------------------------------------------------------------
1165 for (numPats
= 0, patPtr
= &pats
[EVENT_BUFFER_SIZE
-1];
1166 numPats
< EVENT_BUFFER_SIZE
;
1167 numPats
++, patPtr
--) {
1168 patPtr
->eventType
= -1;
1169 patPtr
->needMods
= 0;
1170 patPtr
->hateMods
= ~0;
1172 while (isspace(*p
)) {
1180 * Handle simple ASCII characters. Note: the shift
1181 * modifier is ignored in this case (it's really part
1182 * of the character, rather than a "modifier").
1188 patPtr
->eventType
= KeyPress
;
1189 eventMask
|= KeyPressMask
;
1192 hPtr
= Tcl_FindHashEntry(&keySymTable
, string
);
1194 patPtr
->detail
= (int) Tcl_GetHashValue(hPtr
);
1197 patPtr
->detail
= *p
;
1199 sprintf(interp
->result
,
1200 "bad ASCII character 0x%x", *p
);
1204 patPtr
->hateMods
= ~ShiftMask
;
1210 * A fancier event description. Must consist of
1211 * 1. open angle bracket.
1212 * 2. any number of modifiers, each followed by spaces
1214 * 3. an optional event name.
1215 * 4. an option button or keysym name. Either this or
1216 * item 3 *must* be present; if both are present
1217 * then they are separated by spaces or dashes.
1218 * 5. a close angle bracket.
1225 register ModInfo
*modPtr
;
1226 p
= GetField(p
, field
, FIELD_SIZE
);
1227 hPtr
= Tcl_FindHashEntry(&modTable
, field
);
1231 modPtr
= (ModInfo
*) Tcl_GetHashValue(hPtr
);
1232 patPtr
->needMods
|= modPtr
->mask
;
1233 if (modPtr
->flags
& (DOUBLE
|TRIPLE
)) {
1234 flags
|= PAT_NEARBY
;
1235 if (modPtr
->flags
& DOUBLE
) {
1241 if (modPtr
->flags
& ANY
) {
1244 while ((*p
== '-') || isspace(*p
)) {
1249 patPtr
->hateMods
= 0;
1251 patPtr
->hateMods
= ~patPtr
->needMods
;
1253 hPtr
= Tcl_FindHashEntry(&eventTable
, field
);
1255 register EventInfo
*eiPtr
;
1256 eiPtr
= (EventInfo
*) Tcl_GetHashValue(hPtr
);
1257 patPtr
->eventType
= eiPtr
->type
;
1258 eventMask
|= eiPtr
->eventMask
;
1259 while ((*p
== '-') || isspace(*p
)) {
1262 p
= GetField(p
, field
, FIELD_SIZE
);
1264 if (*field
!= '\0') {
1265 if ((*field
>= '1') && (*field
<= '5') && (field
[1] == '\0')) {
1266 static int masks
[] = {~0, ~Button1Mask
, ~Button2Mask
,
1267 ~Button3Mask
, ~Button4Mask
, ~Button5Mask
};
1269 if (patPtr
->eventType
== -1) {
1270 patPtr
->eventType
= ButtonPress
;
1271 eventMask
|= ButtonPressMask
;
1272 } else if ((patPtr
->eventType
== KeyPress
)
1273 || (patPtr
->eventType
== KeyRelease
)) {
1275 } else if ((patPtr
->eventType
!= ButtonPress
)
1276 && (patPtr
->eventType
!= ButtonRelease
)) {
1277 Tcl_AppendResult(interp
, "specified button \"", field
,
1278 "\" for non-button event", (char *) NULL
);
1281 patPtr
->detail
= (*field
- '0');
1284 * Ignore this button as a modifier: its state is already
1288 patPtr
->needMods
&= masks
[patPtr
->detail
];
1289 patPtr
->hateMods
&= masks
[patPtr
->detail
];
1292 hPtr
= Tcl_FindHashEntry(&keySymTable
, (char *) field
);
1294 Tcl_AppendResult(interp
, "bad event type or keysym \"",
1295 field
, "\"", (char *) NULL
);
1298 if (patPtr
->eventType
== -1) {
1299 patPtr
->eventType
= KeyPress
;
1300 eventMask
|= KeyPressMask
;
1301 } else if ((patPtr
->eventType
!= KeyPress
)
1302 && (patPtr
->eventType
!= KeyRelease
)) {
1303 Tcl_AppendResult(interp
, "specified keysym \"", field
,
1304 "\" for non-key event", (char *) NULL
);
1307 patPtr
->detail
= (int) Tcl_GetHashValue(hPtr
);
1310 * Don't get upset about the shift modifier with keys:
1311 * if the key doesn't permit the shift modifier then
1312 * that will already be factored in when translating
1313 * from keycode to keysym in Tk_BindEvent. If the keysym
1314 * has both a shifted and unshifted form, we want to allow
1315 * the shifted form to be specified explicitly, though.
1318 patPtr
->hateMods
&= ~ShiftMask
;
1320 } else if (patPtr
->eventType
== -1) {
1321 interp
->result
= "no event type or button # or keysym";
1324 while ((*p
== '-') || isspace(*p
)) {
1328 interp
->result
= "missing \">\" in binding";
1334 * Replicate events for DOUBLE and TRIPLE.
1337 if ((count
> 1) && (numPats
< EVENT_BUFFER_SIZE
-1)) {
1338 patPtr
[-1] = patPtr
[0];
1341 if ((count
== 3) && (numPats
< EVENT_BUFFER_SIZE
-1)) {
1342 patPtr
[-1] = patPtr
[0];
1350 *-------------------------------------------------------------
1351 * Step 2: find the sequence in the binding table if it exists,
1352 * and add a new sequence to the table if it doesn't.
1353 *-------------------------------------------------------------
1357 interp
->result
= "no events specified in binding";
1360 patPtr
= &pats
[EVENT_BUFFER_SIZE
-numPats
];
1361 key
.object
= object
;
1362 key
.type
= patPtr
->eventType
;
1363 key
.detail
= patPtr
->detail
;
1364 hPtr
= Tcl_CreateHashEntry(&bindPtr
->patternTable
, (char *) &key
, &new);
1365 sequenceSize
= numPats
*sizeof(Pattern
);
1367 for (psPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
); psPtr
!= NULL
;
1368 psPtr
= psPtr
->nextSeqPtr
) {
1369 if ((numPats
== psPtr
->numPats
)
1370 && ((flags
& PAT_NEARBY
) == (psPtr
->flags
& PAT_NEARBY
))
1371 && (memcmp((char *) patPtr
, (char *) psPtr
->pats
,
1372 sequenceSize
) == 0)) {
1373 *maskPtr
= eventMask
; /*don't forget to pass back the mask*/
1380 Tcl_DeleteHashEntry(hPtr
);
1382 Tcl_AppendResult(interp
, "no binding exists for \"",
1383 eventString
, "\"", (char *) NULL
);
1386 psPtr
= (PatSeq
*) ckalloc((unsigned) (sizeof(PatSeq
)
1387 + (numPats
-1)*sizeof(Pattern
)));
1388 psPtr
->numPats
= numPats
;
1389 psPtr
->command
= NULL
;
1390 psPtr
->flags
= flags
;
1391 psPtr
->nextSeqPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
1393 Tcl_SetHashValue(hPtr
, psPtr
);
1396 * Link the pattern into the list associated with the object.
1399 psPtr
->object
= object
;
1400 hPtr
= Tcl_CreateHashEntry(&bindPtr
->objectTable
, (char *) object
, &new);
1402 psPtr
->nextObjPtr
= NULL
;
1404 psPtr
->nextObjPtr
= (PatSeq
*) Tcl_GetHashValue(hPtr
);
1406 Tcl_SetHashValue(hPtr
, psPtr
);
1408 memcpy((VOID
*) psPtr
->pats
, (VOID
*) patPtr
, sequenceSize
);
1411 *maskPtr
= eventMask
;
1416 *----------------------------------------------------------------------
1420 * Used to parse pattern descriptions. Copies up to
1421 * size characters from p to copy, stopping at end of
1422 * string, space, "-", ">", or whenever size is
1426 * The return value is a pointer to the character just
1427 * after the last one copied (usually "-" or space or
1428 * ">", but could be anything if size was exceeded).
1429 * Also places NULL-terminated string (up to size
1430 * character, including NULL), at copy.
1435 *----------------------------------------------------------------------
1439 GetField(p
, copy
, size
)
1440 register char *p
; /* Pointer to part of pattern. */
1441 register char *copy
; /* Place to copy field. */
1442 int size
; /* Maximum number of characters to
1445 while ((*p
!= '\0') && !isspace(*p
) && (*p
!= '>')
1446 && (*p
!= '-') && (size
> 1)) {
1457 *----------------------------------------------------------------------
1461 * Given an X KeyPress or KeyRelease event, map the
1462 * keycode in the event into a KeySym.
1465 * The return value is the KeySym corresponding to
1466 * eventPtr, or NoSymbol if no matching Keysym could be
1470 * In the first call for a given display, keycode-to-
1471 * KeySym maps get loaded.
1473 *----------------------------------------------------------------------
1477 GetKeySym(dispPtr
, eventPtr
)
1478 register TkDisplay
*dispPtr
; /* Display in which to
1480 register XEvent
*eventPtr
; /* Description of X event. */
1486 * Read the key mapping information from the server if
1487 * we don't have it already.
1490 if (dispPtr
->symsPerCode
== 0) {
1491 Display
*dpy
= dispPtr
->display
;
1494 XDisplayKeycodes(dpy
, &dispPtr
->firstKeycode
, &dispPtr
->lastKeycode
);
1496 dispPtr
->firstKeycode
=
1498 dispPtr
->lastKeycode
=
1501 dispPtr
->keySyms
= XGetKeyboardMapping(dpy
,
1502 dispPtr
->firstKeycode
, dispPtr
->lastKeycode
+ 1
1503 - dispPtr
->firstKeycode
, &dispPtr
->symsPerCode
);
1507 * Compute the lower-case KeySym for this keycode. May
1508 * have to convert an upper-case KeySym to a lower-case
1509 * one if the list only has a single element.
1512 if ((eventPtr
->xkey
.keycode
< dispPtr
->firstKeycode
)
1513 || (eventPtr
->xkey
.keycode
> dispPtr
->lastKeycode
)) {
1516 symPtr
= &dispPtr
->keySyms
[(eventPtr
->xkey
.keycode
1517 - dispPtr
->firstKeycode
) * dispPtr
->symsPerCode
];
1519 if ((dispPtr
->symsPerCode
== 1) || (symPtr
[1] == NoSymbol
)) {
1520 if ((sym
>= XK_A
) && (sym
<= XK_Z
)) {
1521 sym
+= (XK_a
- XK_A
);
1522 } else if ((sym
>= XK_Agrave
) && (sym
<= XK_Odiaeresis
)) {
1523 sym
+= (XK_agrave
- XK_Agrave
);
1524 } else if ((sym
>= XK_Ooblique
) && (sym
<= XK_Thorn
)) {
1525 sym
+= (XK_oslash
- XK_Ooblique
);
1530 * See whether the key is shifted or caps-locked. If so,
1531 * use an upper-case equivalent if provided, or compute
1532 * one (for caps-lock, just compute upper-case: don't
1533 * use shifted KeySym since that would shift non-alphabetic
1537 if (eventPtr
->xkey
.state
& ShiftMask
) {
1538 if ((dispPtr
->symsPerCode
> 1) && (symPtr
[1] != NoSymbol
)) {
1542 if ((sym
>= XK_a
) && (sym
<= XK_z
)) {
1543 sym
+= (XK_A
- XK_a
);
1544 } else if ((sym
>= XK_agrave
) && (sym
<= XK_adiaeresis
)) {
1545 sym
+= (XK_Agrave
- XK_agrave
);
1546 } else if ((sym
>= XK_oslash
) && (sym
<= XK_thorn
)) {
1547 sym
+= (XK_Ooblique
- XK_oslash
);
1551 if (eventPtr
->xkey
.state
& LockMask
) {
1558 *----------------------------------------------------------------------
1562 * Given a list of pattern sequences and a list of
1563 * recent events, return a pattern sequence that matches
1567 * The return value is NULL if no pattern matches the
1568 * recent events from bindPtr. If one or more patterns
1569 * matches, then the longest (or most specific) matching
1570 * pattern is returned.
1575 *----------------------------------------------------------------------
1579 MatchPatterns(dispPtr
, bindPtr
, psPtr
)
1581 BindingTable
*bindPtr
; /* Information about binding table, such
1582 * as ring of recent events. */
1583 register PatSeq
*psPtr
; /* List of pattern sequences. */
1585 register PatSeq
*bestPtr
= NULL
;
1588 * Iterate over all the pattern sequences.
1591 for ( ; psPtr
!= NULL
; psPtr
= psPtr
->nextSeqPtr
) {
1592 register XEvent
*eventPtr
;
1593 register Pattern
*patPtr
;
1596 int patCount
, ringCount
, flags
, state
;
1599 * Iterate over all the patterns in a sequence to be
1600 * sure that they all match.
1603 eventPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1604 detailPtr
= &bindPtr
->detailRing
[bindPtr
->curEvent
];
1605 window
= eventPtr
->xany
.window
;
1606 patPtr
= psPtr
->pats
;
1607 patCount
= psPtr
->numPats
;
1608 ringCount
= EVENT_BUFFER_SIZE
;
1609 while (patCount
> 0) {
1610 if (ringCount
<= 0) {
1613 if (eventPtr
->xany
.type
!= patPtr
->eventType
) {
1615 * If the event is a mouse motion, button release,
1616 * or key release event, and it didn't match
1617 * the pattern, then just skip the event and try
1618 * the next event against the same pattern.
1621 if ((eventPtr
->xany
.type
== MotionNotify
)
1622 || (eventPtr
->xany
.type
== ButtonRelease
)
1623 || (eventPtr
->xany
.type
== KeyRelease
)
1624 || (eventPtr
->xany
.type
== NoExpose
)
1625 || (eventPtr
->xany
.type
== EnterNotify
)
1626 || (eventPtr
->xany
.type
== LeaveNotify
)
1627 || (eventPtr
->xany
.type
== GraphicsExpose
)) {
1632 if (eventPtr
->xany
.window
!= window
) {
1636 flags
= flagArray
[eventPtr
->type
];
1637 if (flags
& KEY_BUTTON_MOTION
) {
1638 state
= eventPtr
->xkey
.state
;
1639 } else if (flags
& CROSSING
) {
1640 state
= eventPtr
->xcrossing
.state
;
1644 if (patPtr
->needMods
!= 0) {
1645 int modMask
= patPtr
->needMods
;
1647 if (!dispPtr
->metaModMask
&& !dispPtr
->altModMask
&& !dispPtr
->modeModMask
) {
1649 XModifierKeymap
*modMapPtr
;
1653 modMapPtr
= XGetModifierMapping(dispPtr
->display
);
1654 codePtr
= modMapPtr
->modifiermap
;
1655 max
= 8*modMapPtr
->max_keypermod
;
1657 for (i
= 0; i
< max
; i
++, codePtr
++) {
1658 if (*codePtr
== 0) {
1661 keysym
= XKeycodeToKeysym(dispPtr
->display
, *codePtr
, 0);
1662 if (keysym
== XK_Mode_switch
) {
1663 dispPtr
->modeModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1665 if ((keysym
== XK_Meta_L
) || (keysym
== XK_Meta_R
)) {
1666 dispPtr
->metaModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1668 if ((keysym
== XK_Alt_L
) || (keysym
== XK_Alt_R
)) {
1669 dispPtr
->altModMask
|= ShiftMask
<< (i
/modMapPtr
->max_keypermod
);
1673 if ((modMask
& META_MASK
) && (dispPtr
->metaModMask
!= 0)) {
1674 modMask
= (modMask
& ~META_MASK
) | dispPtr
->metaModMask
;
1676 if ((modMask
& ALT_MASK
) && (dispPtr
->altModMask
!= 0)) {
1677 modMask
= (modMask
& ~ALT_MASK
) | dispPtr
->altModMask
;
1680 if ((state
& META_MASK
) && (dispPtr
->metaModMask
!= 0)) {
1681 state
= (state
& ~META_MASK
) | dispPtr
->metaModMask
;
1683 if ((state
& ALT_MASK
) && (dispPtr
->altModMask
!= 0)) {
1684 state
= (state
& ~ALT_MASK
) | dispPtr
->altModMask
;
1687 if ((state
& modMask
) != modMask
) {
1692 if ((state
& patPtr
->hateMods
) != 0) {
1696 if ((patPtr
->detail
!= 0)
1697 && (patPtr
->detail
!= *detailPtr
)) {
1700 if (psPtr
->flags
& PAT_NEARBY
) {
1701 register XEvent
*firstPtr
;
1703 firstPtr
= &bindPtr
->eventRing
[bindPtr
->curEvent
];
1704 if ((firstPtr
->xkey
.x_root
1705 < (eventPtr
->xkey
.x_root
- NEARBY_PIXELS
))
1706 || (firstPtr
->xkey
.x_root
1707 > (eventPtr
->xkey
.x_root
+ NEARBY_PIXELS
))
1708 || (firstPtr
->xkey
.y_root
1709 < (eventPtr
->xkey
.y_root
- NEARBY_PIXELS
))
1710 || (firstPtr
->xkey
.y_root
1711 > (eventPtr
->xkey
.y_root
+ NEARBY_PIXELS
))
1712 || (firstPtr
->xkey
.time
1713 > (eventPtr
->xkey
.time
+ NEARBY_MS
))) {
1720 if (eventPtr
== bindPtr
->eventRing
) {
1721 eventPtr
= &bindPtr
->eventRing
[EVENT_BUFFER_SIZE
-1];
1722 detailPtr
= &bindPtr
->detailRing
[EVENT_BUFFER_SIZE
-1];
1731 * This sequence matches. If we've already got another match,
1732 * pick whichever is most specific. Detail is most important,
1733 * then needMods, then hateMods.
1736 if (bestPtr
!= NULL
) {
1737 register Pattern
*patPtr2
;
1740 if (psPtr
->numPats
!= bestPtr
->numPats
) {
1741 if (bestPtr
->numPats
> psPtr
->numPats
) {
1747 for (i
= 0, patPtr
= psPtr
->pats
, patPtr2
= bestPtr
->pats
;
1748 i
< psPtr
->numPats
; i
++,patPtr
++, patPtr2
++) {
1749 if (patPtr
->detail
!= patPtr2
->detail
) {
1750 if (patPtr
->detail
== 0) {
1756 if (patPtr
->needMods
!= patPtr2
->needMods
) {
1757 if ((patPtr
->needMods
& patPtr2
->needMods
)
1758 == patPtr
->needMods
) {
1764 if (patPtr
->hateMods
!= patPtr2
->hateMods
) {
1765 if ((patPtr
->hateMods
& patPtr2
->hateMods
)
1766 == patPtr2
->hateMods
) {
1773 goto nextSequence
; /* Tie goes to newest pattern. */
1778 nextSequence
: continue;
1784 *--------------------------------------------------------------
1788 * Given a command and an event, produce a new command
1789 * by replacing % constructs in the original command
1790 * with information from the X event.
1793 * The return result is a pointer to the new %-substituted
1794 * command. If the command fits in the space at after, then
1795 * the return value is after. If the command is too large
1796 * to fit at after, then the return value is a pointer to
1797 * a malloc-ed buffer holding the command; in this case it
1798 * is the caller's responsibility to free up the buffer when
1804 *--------------------------------------------------------------
1808 ExpandPercents(before
, eventPtr
, keySym
, after
, afterSize
)
1809 register char *before
; /* Command containing percent
1810 * expressions to be replaced. */
1811 register XEvent
*eventPtr
; /* X event containing information
1812 * to be used in % replacements. */
1813 KeySym keySym
; /* KeySym: only relevant for
1814 * KeyPress and KeyRelease events). */
1815 char *after
; /* Place to generate new expanded
1816 * command. Must contain at least
1817 * "afterSize" bytes of space. */
1818 int afterSize
; /* Number of bytes of space available at
1821 register char *buffer
; /* Pointer to buffer currently being used
1822 * as destination. */
1823 register char *dst
; /* Pointer to next place to store character
1824 * in substituted string. */
1825 int spaceLeft
; /* Indicates how many more non-null bytes
1826 * may be stored at *dst before space
1828 int spaceNeeded
, cvtFlags
; /* Used to substitute string as proper Tcl
1832 register char *string
;
1833 char numStorage
[NUM_SIZE
+1];
1835 if (eventPtr
->type
< LASTEvent
) {
1836 flags
= flagArray
[eventPtr
->type
];
1840 dst
= buffer
= after
;
1841 spaceLeft
= afterSize
- 1;
1842 while (*before
!= 0) {
1843 if (*before
!= '%') {
1846 * Expand the destination string if necessary.
1849 if (spaceLeft
<= 0) {
1852 newSpace
= (char *) ckalloc((unsigned) (2*afterSize
));
1853 memcpy((VOID
*) newSpace
, (VOID
*) buffer
, afterSize
);
1855 dst
= newSpace
+ (dst
- buffer
);
1856 if (buffer
!= after
) {
1860 spaceLeft
= afterSize
- (dst
-buffer
) - 1;
1871 switch (before
[1]) {
1873 number
= eventPtr
->xany
.serial
;
1876 number
= (int) eventPtr
->xconfigure
.above
;
1879 number
= eventPtr
->xbutton
.button
;
1882 if (flags
& EXPOSE
) {
1883 number
= eventPtr
->xexpose
.count
;
1884 } else if (flags
& MAPPING
) {
1885 number
= eventPtr
->xmapping
.count
;
1889 if (flags
& (CROSSING
|FOCUS
)) {
1890 switch (eventPtr
->xcrossing
.detail
) {
1891 case NotifyAncestor
:
1892 string
= "NotifyAncestor";
1895 string
= "NotifyVirtual";
1897 case NotifyInferior
:
1898 string
= "NotifyInferior";
1900 case NotifyNonlinear
:
1901 string
= "NotifyNonlinear";
1903 case NotifyNonlinearVirtual
:
1904 string
= "NotifyNonlinearVirtual";
1907 string
= "NotifyPointer";
1909 case NotifyPointerRoot
:
1910 string
= "NotifyPointerRoot";
1912 case NotifyDetailNone
:
1913 string
= "NotifyDetailNone";
1916 } else if (flags
& CONFIG_REQ
) {
1917 switch (eventPtr
->xconfigurerequest
.detail
) {
1928 string
= "BottomIf";
1931 string
= "Opposite";
1937 number
= eventPtr
->xcrossing
.focus
;
1940 if (flags
& EXPOSE
) {
1941 number
= eventPtr
->xexpose
.height
;
1942 } else if (flags
& (CONFIG
|CONFIG_REQ
)) {
1943 number
= eventPtr
->xconfigure
.height
;
1944 } else if (flags
& RESIZE_REQ
) {
1945 number
= eventPtr
->xresizerequest
.height
;
1949 number
= eventPtr
->xkey
.keycode
;
1952 if (flags
& CROSSING
) {
1953 number
= eventPtr
->xcrossing
.mode
;
1954 } else if (flags
& FOCUS
) {
1955 number
= eventPtr
->xfocus
.mode
;
1959 string
= "NotifyNormal";
1962 string
= "NotifyGrab";
1965 string
= "NotifyUngrab";
1967 case NotifyWhileGrabbed
:
1968 string
= "NotifyWhileGrabbed";
1973 if (flags
& CREATE
) {
1974 number
= eventPtr
->xcreatewindow
.override_redirect
;
1975 } else if (flags
& MAP
) {
1976 number
= eventPtr
->xmap
.override_redirect
;
1977 } else if (flags
& REPARENT
) {
1978 number
= eventPtr
->xreparent
.override_redirect
;
1979 } else if (flags
& CONFIG
) {
1980 number
= eventPtr
->xconfigure
.override_redirect
;
1984 switch (eventPtr
->xcirculate
.place
) {
1986 string
= "PlaceOnTop";
1989 string
= "PlaceOnBottom";
1994 if (flags
& KEY_BUTTON_MOTION
) {
1995 number
= eventPtr
->xkey
.state
;
1996 } else if (flags
& CROSSING
) {
1997 number
= eventPtr
->xcrossing
.state
;
1998 } else if (flags
& VISIBILITY
) {
1999 switch (eventPtr
->xvisibility
.state
) {
2000 case VisibilityUnobscured
:
2001 string
= "VisibilityUnobscured";
2003 case VisibilityPartiallyObscured
:
2004 string
= "VisibilityPartiallyObscured";
2006 case VisibilityFullyObscured
:
2007 string
= "VisibilityFullyObscured";
2014 if (flags
& (KEY_BUTTON_MOTION
|PROP
|SEL_CLEAR
)) {
2015 number
= (int) eventPtr
->xkey
.time
;
2016 } else if (flags
& SEL_REQ
) {
2017 number
= (int) eventPtr
->xselectionrequest
.time
;
2018 } else if (flags
& SEL_NOTIFY
) {
2019 number
= (int) eventPtr
->xselection
.time
;
2023 number
= eventPtr
->xconfigurerequest
.value_mask
;
2026 if (flags
& EXPOSE
) {
2027 number
= eventPtr
->xexpose
.width
;
2028 } else if (flags
& (CONFIG
|CONFIG_REQ
)) {
2029 number
= eventPtr
->xconfigure
.width
;
2030 } else if (flags
& RESIZE_REQ
) {
2031 number
= eventPtr
->xresizerequest
.width
;
2035 if (flags
& KEY_BUTTON_MOTION
) {
2036 number
= eventPtr
->xkey
.x
;
2037 } else if (flags
& EXPOSE
) {
2038 number
= eventPtr
->xexpose
.x
;
2039 } else if (flags
& (CREATE
|CONFIG
|GRAVITY
|CONFIG_REQ
)) {
2040 number
= eventPtr
->xcreatewindow
.x
;
2041 } else if (flags
& REPARENT
) {
2042 number
= eventPtr
->xreparent
.x
;
2043 } else if (flags
& CROSSING
) {
2044 number
= eventPtr
->xcrossing
.x
;
2048 if (flags
& KEY_BUTTON_MOTION
) {
2049 number
= eventPtr
->xkey
.y
;
2050 } else if (flags
& EXPOSE
) {
2051 number
= eventPtr
->xexpose
.y
;
2052 } else if (flags
& (CREATE
|CONFIG
|GRAVITY
|CONFIG_REQ
)) {
2053 number
= eventPtr
->xcreatewindow
.y
;
2054 } else if (flags
& REPARENT
) {
2055 number
= eventPtr
->xreparent
.y
;
2056 } else if (flags
& CROSSING
) {
2057 number
= eventPtr
->xcrossing
.y
;
2062 if ((eventPtr
->type
== KeyPress
)
2063 || (eventPtr
->type
== KeyRelease
)) {
2066 numChars
= XLookupString(&eventPtr
->xkey
, numStorage
,
2067 NUM_SIZE
, (KeySym
*) NULL
,
2068 (XComposeStatus
*) NULL
);
2069 numStorage
[numChars
] = '\0';
2070 string
= numStorage
;
2074 number
= eventPtr
->xcreatewindow
.border_width
;
2077 number
= (int) eventPtr
->xany
.display
;
2080 number
= (int) eventPtr
->xany
.send_event
;
2083 if ((eventPtr
->type
== KeyPress
)
2084 || (eventPtr
->type
== KeyRelease
)) {
2085 register KeySymInfo
*kPtr
;
2087 for (kPtr
= keyArray
; kPtr
->name
!= NULL
; kPtr
++) {
2088 if (kPtr
->value
== keySym
) {
2089 string
= kPtr
->name
;
2096 number
= (int) keySym
;
2099 number
= (int) eventPtr
->xkey
.root
;
2102 number
= (int) eventPtr
->xkey
.subwindow
;
2105 number
= eventPtr
->type
;
2110 if (XFindContext(eventPtr
->xany
.display
, eventPtr
->xany
.window
,
2111 tkWindowContext
, (void *) &winPtr
) == 0) {
2112 string
= winPtr
->pathName
;
2119 number
= eventPtr
->xkey
.x_root
;
2122 number
= eventPtr
->xkey
.y_root
;
2125 numStorage
[0] = before
[1];
2126 numStorage
[1] = '\0';
2127 string
= numStorage
;
2132 sprintf(numStorage
, "%d", number
);
2133 string
= numStorage
;
2136 spaceNeeded
= Tcl_ScanElement(string
, &cvtFlags
);
2137 if (spaceNeeded
>= spaceLeft
) {
2140 newSpace
= (char *) ckalloc((unsigned)
2141 (afterSize
+ spaceNeeded
+ 50));
2142 memcpy((VOID
*) newSpace
, (VOID
*) buffer
, afterSize
);
2143 afterSize
+= spaceNeeded
+ 50;
2144 dst
= newSpace
+ (dst
- buffer
);
2145 if (buffer
!= after
) {
2149 spaceLeft
= afterSize
- (dst
-buffer
) - 1;
2151 spaceNeeded
= Tcl_ConvertElement(string
, dst
,
2152 cvtFlags
| TCL_DONT_USE_BRACES
);
2154 spaceLeft
-= spaceNeeded
;
2162 *----------------------------------------------------------------------
2166 * This procedure is invoked to handle errors that occur in Tcl
2167 * commands that are invoked in "background" (e.g. from event or
2174 * The command "tkerror" is invoked to process the error, passing
2175 * it the error message. If that fails, then an error message
2176 * is output on stderr.
2178 *----------------------------------------------------------------------
2183 Tcl_Interp
*interp
; /* Interpreter in which an error has
2189 char *errorInfo
, *tmp
;
2192 error
= (char *) ckalloc((unsigned) (strlen(interp
->result
) + 1));
2193 strcpy(error
, interp
->result
);
2194 tmp
= Tcl_GetVar(interp
, "errorInfo", TCL_GLOBAL_ONLY
);
2198 errorInfo
= (char *) ckalloc((unsigned) (strlen(tmp
) + 1));
2199 strcpy(errorInfo
, tmp
);
2201 argv
[0] = "tkerror";
2203 command
= Tcl_Merge(2, argv
);
2204 result
= Tcl_GlobalEval(interp
, command
);
2205 if (result
!= TCL_OK
) {
2206 if (strcmp(interp
->result
, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2207 fprintf(stderr
, "%s\n", errorInfo
);
2209 fprintf(stderr
, "tkerror failed to handle background error.\n");
2210 fprintf(stderr
, " Original error: %s\n", error
);
2211 fprintf(stderr
, " Error in tkerror: %s\n", interp
->result
);
2214 Tcl_ResetResult(interp
);
2217 if (errorInfo
!= error
) {