4 * This file provides procedures that associate Tcl commands
5 * with events or sequences of events.
7 * Copyright (c) 1989-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9 * Copyright (c) 1995 Christian Werner
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
19 * The structure below represents a binding table. A binding table
20 * represents a domain in which event bindings may occur. It includes
21 * a space of objects relative to which events occur (usually windows,
22 * but not always), a history of recent events in the domain, and
23 * a set of mappings that associate particular Tcl commands with sequences
24 * of events in the domain. Multiple binding tables may exist at once,
25 * either because there are multiple applications open, or because there
26 * are multiple domains within an application with separate event
27 * bindings for each (for example, each canvas widget has a separate
28 * binding table for associating events with the items in the canvas).
30 * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
31 * below 30. To see this, consider a triple mouse button click while
32 * the Shift key is down (and auto-repeating). There may be as many
33 * as 3 auto-repeat events after each mouse button press or release
34 * (see the first large comment block within Ck_BindEvent for more on
35 * this), for a total of 20 events to cover the three button presses
36 * and two intervening releases. If you reduce EVENT_BUFFER_SIZE too
37 * much, shift multi-clicks will be lost.
41 #define EVENT_BUFFER_SIZE 30
42 typedef struct BindingTable {
43 CkEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
44 * (higher indices are for more recent
46 int detailRing[EVENT_BUFFER_SIZE]; /* "Detail" information (keycodes for
47 * each entry in eventRing. */
48 int curEvent; /* Index in eventRing of most recent
49 * event. Newer events have higher
51 Tcl_HashTable patternTable; /* Used to map from an event to a list
52 * of patterns that may match that
53 * event. Keys are PatternTableKey
54 * structs, values are (PatSeq *). */
55 Tcl_HashTable objectTable; /* Used to map from an object to a list
56 * of patterns associated with that
57 * object. Keys are ClientData,
58 * values are (PatSeq *). */
59 Tcl_Interp *interp; /* Interpreter in which commands are
64 * Structures of the following form are used as keys in the patternTable
65 * for a binding table:
68 typedef struct PatternTableKey {
69 ClientData object; /* Identifies object (or class of objects)
70 * relative to which event occurred. For
71 * example, in the widget binding table for
72 * an application this is the path name of
73 * a widget, or a widget class, or "all". */
74 int type; /* Type of event. */
75 int detail; /* Additional information, such as
76 * keycode, or 0 if nothing
81 * The following structure defines a pattern, which is matched
82 * against events as part of the process of converting events
86 typedef struct Pattern {
87 int eventType; /* Type of event. */
88 int detail; /* Additional information that must
89 * match event. Normally this is 0,
90 * meaning no additional information
91 * must match. For keystrokes this
92 * is the keycode. Keycode 0 means
93 * any keystroke, keycode -1 means
94 * control keystroke. */
98 * The structure below defines a pattern sequence, which consists
99 * of one or more patterns. In order to trigger, a pattern
100 * sequence must match the most recent X events (first pattern
101 * to most recent event, next pattern to next event, and so on).
104 typedef struct PatSeq {
105 int numPats; /* Number of patterns in sequence
107 char *command; /* Command to invoke when this
108 * pattern sequence matches (malloc-ed). */
109 struct PatSeq *nextSeqPtr;
110 /* Next in list of all pattern
111 * sequences that have the same
112 * initial pattern. NULL means
114 Tcl_HashEntry *hPtr; /* Pointer to hash table entry for
115 * the initial pattern. This is the
116 * head of the list of which nextSeqPtr
118 ClientData object; /* Identifies object with which event is
119 * associated (e.g. window). */
120 struct PatSeq *nextObjPtr;
121 /* Next in list of all pattern
122 * sequences for the same object
123 * (NULL for end of list). Needed to
124 * implement Tk_DeleteAllBindings. */
125 Pattern pats[1]; /* Array of "numPats" patterns. Only
126 * one element is declared here but
127 * in actuality enough space will be
128 * allocated for "numPats" patterns.
129 * To match, pats[0] must match event
130 * n, pats[1] must match event n-1,
135 char *name; /* Name of keysym. */
136 KeySym value; /* Numeric identifier for keysym. */
137 char *tiname; /* Terminfo name of keysym. */
139 static KeySymInfo keyArray[] = {
140 #include "ks_names.h"
143 static Tcl_HashTable keySymTable; /* Hashed form of above structure. */
144 static Tcl_HashTable revKeySymTable; /* Ditto, reversed. */
146 static int initialized = 0;
149 * This module also keeps a hash table mapping from event names
150 * to information about those events. The structure, an array
151 * to use to initialize the hash table, and the hash table are
156 char *name; /* Name of event. */
157 int type; /* Event type for X, such as
159 int eventMask; /* Mask bits for this event type. */
162 static EventInfo eventArray[] = {
163 {"Expose", CK_EV_EXPOSE, CK_EV_EXPOSE},
164 {"FocusIn", CK_EV_FOCUSIN, CK_EV_FOCUSIN},
165 {"FocusOut", CK_EV_FOCUSOUT, CK_EV_FOCUSOUT},
166 {"Key", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
167 {"KeyPress", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
168 {"Control", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
169 {"Destroy", CK_EV_DESTROY, CK_EV_DESTROY},
170 {"Map", CK_EV_MAP, CK_EV_MAP},
171 {"Unmap", CK_EV_UNMAP, CK_EV_UNMAP},
172 {"Button", CK_EV_MOUSE_DOWN, CK_EV_MOUSE_DOWN},
173 {"ButtonPress", CK_EV_MOUSE_DOWN, CK_EV_MOUSE_DOWN},
174 {"ButtonRelease", CK_EV_MOUSE_UP, CK_EV_MOUSE_UP},
175 {"BarCode", CK_EV_BARCODE, CK_EV_BARCODE},
176 {(char *) NULL, 0, 0}
178 static Tcl_HashTable eventTable;
181 * Prototypes for local procedures defined in this file:
184 static void ExpandPercents _ANSI_ARGS_((CkWindow *winPtr,
185 char *before, CkEvent *eventPtr, KeySym keySym,
186 Tcl_DString *dsPtr));
187 static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
188 BindingTable *bindPtr, ClientData object,
189 char *eventString, int create));
190 static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));
191 static PatSeq * MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr,
195 *--------------------------------------------------------------
197 * Ck_CreateBindingTable --
199 * Set up a new domain in which event bindings may be created.
202 * The return value is a token for the new table, which must
203 * be passed to procedures like Ck_CreateBinding.
206 * Memory is allocated for the new table.
208 *--------------------------------------------------------------
212 Ck_CreateBindingTable(interp)
213 Tcl_Interp *interp; /* Interpreter to associate with the binding
214 * table: commands are executed in this
217 BindingTable *bindPtr;
221 * If this is the first time a binding table has been created,
222 * initialize the global data structures.
231 Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
232 Tcl_InitHashTable(&revKeySymTable, TCL_ONE_WORD_KEYS);
233 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
234 hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
235 Tcl_SetHashValue(hPtr, (char *) kPtr);
236 hPtr = Tcl_CreateHashEntry(&revKeySymTable, (char *) kPtr->value,
238 Tcl_SetHashValue(hPtr, (char *) kPtr);
240 Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
241 for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
242 hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
243 Tcl_SetHashValue(hPtr, eiPtr);
249 * Create and initialize a new binding table.
252 bindPtr = (BindingTable *) ckalloc(sizeof (BindingTable));
253 for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
254 bindPtr->eventRing[i].type = -1;
256 bindPtr->curEvent = 0;
257 Tcl_InitHashTable(&bindPtr->patternTable,
258 sizeof(PatternTableKey)/sizeof(int));
259 Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
260 bindPtr->interp = interp;
261 return (Ck_BindingTable) bindPtr;
265 *--------------------------------------------------------------
267 * Ck_DeleteBindingTable --
269 * Destroy a binding table and free up all its memory.
270 * The caller should not use bindingTable again after
271 * this procedure returns.
279 *--------------------------------------------------------------
283 Ck_DeleteBindingTable(bindingTable)
284 Ck_BindingTable bindingTable; /* Token for the binding table to
287 BindingTable *bindPtr = (BindingTable *) bindingTable;
288 PatSeq *psPtr, *nextPtr;
290 Tcl_HashSearch search;
293 * Find and delete all of the patterns associated with the binding
297 for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
298 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
299 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
300 psPtr != NULL; psPtr = nextPtr) {
301 nextPtr = psPtr->nextSeqPtr;
302 ckfree((char *) psPtr->command);
303 ckfree((char *) psPtr);
308 * Clean up the rest of the information associated with the
312 Tcl_DeleteHashTable(&bindPtr->patternTable);
313 Tcl_DeleteHashTable(&bindPtr->objectTable);
314 ckfree((char *) bindPtr);
318 *--------------------------------------------------------------
320 * Ck_CreateBinding --
322 * Add a binding to a binding table, so that future calls to
323 * Ck_BindEvent may execute the command in the binding.
326 * The return value is TCL_ERROR if an error occurred while setting
327 * up the binding. In this case, an error message will be
328 * left in interp->result. If all went well then the return
332 * The new binding may cause future calls to Ck_BindEvent to
333 * behave differently than they did previously.
335 *--------------------------------------------------------------
339 Ck_CreateBinding(interp, bindingTable, object, eventString, command, append)
340 Tcl_Interp *interp; /* Used for error reporting. */
341 Ck_BindingTable bindingTable; /* Table in which to create binding. */
342 ClientData object; /* Token for object with which binding
344 char *eventString; /* String describing event sequence
345 * that triggers binding. */
346 char *command; /* Contains Tcl command to execute
347 * when binding triggers. */
348 int append; /* 0 means replace any existing
349 * binding for eventString; 1 means
350 * append to that binding. */
352 BindingTable *bindPtr = (BindingTable *) bindingTable;
355 psPtr = FindSequence(interp, bindPtr, object, eventString, 1);
358 if (append && (psPtr->command != NULL)) {
362 length = strlen(psPtr->command) + strlen(command) + 2;
363 new = (char *) ckalloc((unsigned) length);
364 sprintf(new, "%s\n%s", psPtr->command, command);
365 ckfree((char *) psPtr->command);
366 psPtr->command = new;
368 if (psPtr->command != NULL) {
369 ckfree((char *) psPtr->command);
371 psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
372 strcpy(psPtr->command, command);
378 *--------------------------------------------------------------
380 * Ck_DeleteBinding --
382 * Remove an event binding from a binding table.
385 * The result is a standard Tcl return value. If an error
386 * occurs then interp->result will contain an error message.
389 * The binding given by object and eventString is removed
392 *--------------------------------------------------------------
396 Ck_DeleteBinding(interp, bindingTable, object, eventString)
397 Tcl_Interp *interp; /* Used for error reporting. */
398 Ck_BindingTable bindingTable; /* Table in which to delete binding. */
399 ClientData object; /* Token for object with which binding
401 char *eventString; /* String describing event sequence
402 * that triggers binding. */
404 BindingTable *bindPtr = (BindingTable *) bindingTable;
405 register PatSeq *psPtr, *prevPtr;
408 psPtr = FindSequence(interp, bindPtr, object, eventString, 0);
410 Tcl_ResetResult(interp);
415 * Unlink the binding from the list for its object, then from the
416 * list for its pattern.
419 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
421 panic("Ck_DeleteBinding couldn't find object table entry");
423 prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
424 if (prevPtr == psPtr) {
425 Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
427 for ( ; ; prevPtr = prevPtr->nextObjPtr) {
428 if (prevPtr == NULL) {
429 panic("Ck_DeleteBinding couldn't find on object list");
431 if (prevPtr->nextObjPtr == psPtr) {
432 prevPtr->nextObjPtr = psPtr->nextObjPtr;
437 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
438 if (prevPtr == psPtr) {
439 if (psPtr->nextSeqPtr == NULL) {
440 Tcl_DeleteHashEntry(psPtr->hPtr);
442 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
445 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
446 if (prevPtr == NULL) {
447 panic("Tk_DeleteBinding couldn't find on hash chain");
449 if (prevPtr->nextSeqPtr == psPtr) {
450 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
455 ckfree((char *) psPtr->command);
456 ckfree((char *) psPtr);
461 *--------------------------------------------------------------
465 * Return the command associated with a given event string.
468 * The return value is a pointer to the command string
469 * associated with eventString for object in the domain
470 * given by bindingTable. If there is no binding for
471 * eventString, or if eventString is improperly formed,
472 * then NULL is returned and an error message is left in
473 * interp->result. The return value is semi-static: it
474 * will persist until the binding is changed or deleted.
479 *--------------------------------------------------------------
483 Ck_GetBinding(interp, bindingTable, object, eventString)
484 Tcl_Interp *interp; /* Interpreter for error reporting. */
485 Ck_BindingTable bindingTable; /* Table in which to look for
487 ClientData object; /* Token for object with which binding
489 char *eventString; /* String describing event sequence
490 * that triggers binding. */
492 BindingTable *bindPtr = (BindingTable *) bindingTable;
495 psPtr = FindSequence(interp, bindPtr, object, eventString, 0);
499 return psPtr->command;
503 *--------------------------------------------------------------
505 * Ck_GetAllBindings --
507 * Return a list of event strings for all the bindings
508 * associated with a given object.
511 * There is no return value. Interp->result is modified to
512 * hold a Tcl list with one entry for each binding associated
513 * with object in bindingTable. Each entry in the list
514 * contains the event string associated with one binding.
519 *--------------------------------------------------------------
523 Ck_GetAllBindings(interp, bindingTable, object)
524 Tcl_Interp *interp; /* Interpreter returning result or
526 Ck_BindingTable bindingTable; /* Table in which to look for
528 ClientData object; /* Token for object. */
531 BindingTable *bindPtr = (BindingTable *) bindingTable;
532 register PatSeq *psPtr;
533 register Pattern *patPtr;
538 register EventInfo *eiPtr;
540 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
544 Tcl_DStringInit(&ds);
545 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
546 psPtr = psPtr->nextObjPtr) {
547 Tcl_DStringTrunc(&ds, 0);
550 * For each binding, output information about each of the
551 * patterns in its sequence. The order of the patterns in
552 * the sequence is backwards from the order in which they
556 for (patsLeft = psPtr->numPats,
557 patPtr = &psPtr->pats[psPtr->numPats - 1];
558 patsLeft > 0; patsLeft--, patPtr--) {
561 * Check for button presses.
564 if ((patPtr->eventType == CK_EV_MOUSE_DOWN)
565 && (patPtr->detail != 0)) {
566 sprintf(buffer, "<%d>", patPtr->detail);
567 Tcl_DStringAppend(&ds, buffer, -1);
572 * Check for simple case of an ASCII character.
575 if ((patPtr->eventType == CK_EV_KEYPRESS)
576 && (patPtr->detail < 128)
577 && isprint((unsigned char) patPtr->detail)
578 && (patPtr->detail != '<')
579 && (patPtr->detail != ' ')) {
581 Tcl_DStringAppend(&ds, &c, 1);
586 * It's a more general event specification. First check
587 * event type, then keysym or button detail.
590 Tcl_DStringAppend(&ds, "<", 1);
592 for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
593 if (eiPtr->type == patPtr->eventType) {
594 if (patPtr->eventType == CK_EV_KEYPRESS &&
595 patPtr->detail == -1) {
596 Tcl_DStringAppend(&ds, "Control", -1);
599 if (patPtr->eventType == CK_EV_KEYPRESS &&
600 patPtr->detail > 0 && patPtr->detail < 0x20) {
603 string = CkKeysymToString((KeySym) patPtr->detail, 0);
604 if (string == NULL) {
605 sprintf(buffer, "Control-%c",
606 patPtr->detail + 0x40);
609 Tcl_DStringAppend(&ds, string, -1);
612 Tcl_DStringAppend(&ds, eiPtr->name, -1);
613 if (patPtr->detail != 0) {
614 Tcl_DStringAppend(&ds, "-", 1);
620 if (patPtr->detail != 0) {
621 if (patPtr->eventType == CK_EV_KEYPRESS) {
624 string = CkKeysymToString((KeySym) patPtr->detail, 0);
625 if (string != NULL) {
626 Tcl_DStringAppend(&ds, string, -1);
629 sprintf(buffer, "%d", patPtr->detail);
630 Tcl_DStringAppend(&ds, buffer, -1);
634 Tcl_DStringAppend(&ds, ">", 1);
636 Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
638 Tcl_DStringFree(&ds);
642 *--------------------------------------------------------------
644 * Ck_DeleteAllBindings --
646 * Remove all bindings associated with a given object in a
647 * given binding table.
650 * All bindings associated with object are removed from
656 *--------------------------------------------------------------
660 Ck_DeleteAllBindings(bindingTable, object)
661 Ck_BindingTable bindingTable; /* Table in which to delete
663 ClientData object; /* Token for object. */
665 BindingTable *bindPtr = (BindingTable *) bindingTable;
666 PatSeq *psPtr, *prevPtr;
670 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
674 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
676 nextPtr = psPtr->nextObjPtr;
679 * Be sure to remove each binding from its hash chain in the
680 * pattern table. If this is the last pattern in the chain,
681 * then delete the hash entry too.
684 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
685 if (prevPtr == psPtr) {
686 if (psPtr->nextSeqPtr == NULL) {
687 Tcl_DeleteHashEntry(psPtr->hPtr);
689 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
692 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
693 if (prevPtr == NULL) {
694 panic("Ck_DeleteAllBindings couldn't find on hash chain");
696 if (prevPtr->nextSeqPtr == psPtr) {
697 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
702 ckfree((char *) psPtr->command);
703 ckfree((char *) psPtr);
705 Tcl_DeleteHashEntry(hPtr);
709 *--------------------------------------------------------------
713 * This procedure is invoked to process an event. The
714 * event is added to those recorded for the binding table.
715 * Then each of the objects at *objectPtr is checked in
716 * order to see if it has a binding that matches the recent
717 * events. If so, that binding is invoked and the rest of
718 * objects are skipped.
724 * Depends on the command associated with the matching
727 *--------------------------------------------------------------
731 Ck_BindEvent(bindingTable, eventPtr, winPtr, numObjects, objectPtr)
732 Ck_BindingTable bindingTable; /* Table in which to look for
734 CkEvent *eventPtr; /* What actually happened. */
735 CkWindow *winPtr; /* Window where event occurred. */
736 int numObjects; /* Number of objects at *objectPtr. */
737 ClientData *objectPtr; /* Array of one or more objects
738 * to check for a matching binding. */
740 BindingTable *bindPtr = (BindingTable *) bindingTable;
748 Tcl_DString scripts, savedResult;
752 * Add the new event to the ring of saved events for the
757 if (bindPtr->curEvent >= EVENT_BUFFER_SIZE)
758 bindPtr->curEvent = 0;
759 ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
760 memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof (CkEvent));
762 bindPtr->detailRing[bindPtr->curEvent] = 0;
763 if (ringPtr->type == CK_EV_KEYPRESS)
764 detail = ringPtr->key.keycode;
765 else if (ringPtr->type == CK_EV_MOUSE_DOWN ||
766 ringPtr->type == CK_EV_MOUSE_UP)
767 detail = ringPtr->mouse.button;
768 bindPtr->detailRing[bindPtr->curEvent] = detail;
771 * Loop over all the objects, finding the binding script for each
772 * one. Append all of the binding scripts, with %-sequences expanded,
773 * to "scripts", with null characters separating the scripts for
777 Tcl_DStringInit(&scripts);
778 for ( ; numObjects > 0; numObjects--, objectPtr++) {
781 * Match the new event against those recorded in the
782 * pattern table, saving the longest matching pattern.
783 * For events with details (key events) first
784 * look for a binding for the specific key or button.
785 * If none is found, then look for a binding for all
786 * control-keys (detail of -1, if the keycode is a control
787 * character), else look for a binding for all keys
792 key.object = *objectPtr;
793 key.type = ringPtr->type;
795 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
797 matchPtr = MatchPatterns(bindPtr,
798 (PatSeq *) Tcl_GetHashValue(hPtr));
800 if (ringPtr->type == CK_EV_KEYPRESS && detail > 0 && detail < 0x20 &&
803 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
805 matchPtr = MatchPatterns(bindPtr,
806 (PatSeq *) Tcl_GetHashValue(hPtr));
809 if (detail != 0 && matchPtr == NULL) {
811 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
813 matchPtr = MatchPatterns(bindPtr,
814 (PatSeq *) Tcl_GetHashValue(hPtr));
818 if (matchPtr != NULL) {
819 ExpandPercents(winPtr, matchPtr->command, eventPtr,
820 (KeySym) detail, &scripts);
821 Tcl_DStringAppend(&scripts, "", 1);
826 * Now go back through and evaluate the script for each object,
827 * in order, dealing with "break" and "continue" exceptions
830 * There are two tricks here:
831 * 1. Bindings can be invoked from in the middle of Tcl commands,
832 * where interp->result is significant (for example, a widget
833 * might be deleted because of an error in creating it, so the
834 * result contains an error message that is eventually going to
835 * be returned by the creating command). To preserve the result,
836 * we save it in a dynamic string.
837 * 2. The binding's action can potentially delete the binding,
838 * so bindPtr may not point to anything valid once the action
839 * completes. Thus we have to save bindPtr->interp in a
840 * local variable in order to restore the result.
841 * 3. When the screen changes, must invoke a Tcl script to update
842 * Tcl level information such as tkPriv.
845 mainPtr = winPtr->mainPtr;
846 interp = bindPtr->interp;
847 Tcl_DStringInit(&savedResult);
848 Tcl_DStringGetResult(interp, &savedResult);
849 p = Tcl_DStringValue(&scripts);
850 end = p + Tcl_DStringLength(&scripts);
852 Tcl_AllowExceptions(interp);
853 code = Tcl_GlobalEval(interp, p);
854 if (code != TCL_OK) {
855 if (code == TCL_CONTINUE) {
857 * Do nothing: just go on to the next script.
859 } else if (code == TCL_BREAK) {
862 Tcl_AddErrorInfo(interp, "\n (command bound to event)");
863 Tk_BackgroundError(interp);
869 * Skip over the current script and its terminating null character.
877 Tcl_DStringResult(interp, &savedResult);
878 Tcl_DStringFree(&scripts);
882 *----------------------------------------------------------------------
886 * Find the entry in a binding table that corresponds to a
887 * particular pattern string, and return a pointer to that
891 * The return value is normally a pointer to the PatSeq
892 * in patternTable that corresponds to eventString. If an error
893 * was found while parsing eventString, or if "create" is 0 and
894 * no pattern sequence previously existed, then NULL is returned
895 * and interp->result contains a message describing the problem.
896 * If no pattern sequence previously existed for eventString, then
897 * a new one is created with a NULL command field. In a successful
898 * return, *maskPtr is filled in with a mask of the event types
899 * on which the pattern sequence depends.
902 * A new pattern sequence may be created.
904 *----------------------------------------------------------------------
908 FindSequence(interp, bindPtr, object, eventString, create)
909 Tcl_Interp *interp; /* Interpreter to use for error
911 BindingTable *bindPtr; /* Table to use for lookup. */
912 ClientData object; /* Token for object(s) with which binding
914 char *eventString; /* String description of pattern to
915 * match on. See user documentation
917 int create; /* 0 means don't create the entry if
918 * it doesn't already exist. Non-zero
922 Pattern pats[EVENT_BUFFER_SIZE];
925 register Pattern *patPtr;
926 register PatSeq *psPtr;
927 register Tcl_HashEntry *hPtr;
928 #define FIELD_SIZE 48
929 char field[FIELD_SIZE];
932 unsigned long eventMask;
936 *-------------------------------------------------------------
937 * Step 1: parse the pattern string to produce an array
938 * of Patterns. The array is generated backwards, so
939 * that the lowest-indexed pattern corresponds to the last
940 * event that must occur.
941 *-------------------------------------------------------------
946 for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
947 numPats < EVENT_BUFFER_SIZE;
948 numPats++, patPtr--) {
949 patPtr->eventType = -1;
951 while (isspace((unsigned char) *p)) {
959 * Handle simple ASCII characters.
965 patPtr->eventType = CK_EV_KEYPRESS;
968 patPtr->detail = CkStringToKeysym(string);
969 if (patPtr->detail == NoSymbol) {
970 if (isprint((unsigned char) *p)) {
973 sprintf(interp->result,
974 "bad ASCII character 0x%x", (unsigned char) *p);
985 * Abbrevated button press event.
988 if (isdigit((unsigned char) *p) && p[1] == '>') {
989 register EventInfo *eiPtr;
991 hPtr = Tcl_FindHashEntry(&eventTable, "ButtonPress");
992 eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
993 patPtr->eventType = eiPtr->type;
994 eventMask |= eiPtr->eventMask;
995 patPtr->detail = *p - '0';
1001 * A fancier event description. Must consist of
1002 * 1. open angle bracket.
1003 * 2. optional event name.
1004 * 3. an option keysym name. Either this or
1005 * item 2 *must* be present; if both are present
1006 * then they are separated by spaces or dashes.
1007 * 4. a close angle bracket.
1011 p = GetField(p, field, FIELD_SIZE);
1012 hPtr = Tcl_FindHashEntry(&eventTable, field);
1014 register EventInfo *eiPtr;
1016 eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
1017 patPtr->eventType = eiPtr->type;
1018 eventMask |= eiPtr->eventMask;
1019 isCtrl = strcmp(eiPtr->name, "Control") == 0;
1021 patPtr->detail = -1;
1023 while ((*p == '-') || isspace((unsigned char) *p)) {
1026 p = GetField(p, field, FIELD_SIZE);
1028 if (*field != '\0') {
1029 if (patPtr->eventType == CK_EV_MOUSE_DOWN ||
1030 patPtr->eventType == CK_EV_MOUSE_UP) {
1031 if (!isdigit((unsigned char) *field) && field[1] != '\0') {
1032 Tcl_AppendResult(interp, "bad mouse button \"",
1033 field, "\"", (char *) NULL);
1036 patPtr->detail = field[0] - '0';
1040 patPtr->detail = CkStringToKeysym(field);
1041 if (patPtr->detail == NoSymbol) {
1043 Tcl_AppendResult(interp, "bad event type or keysym \"",
1044 field, "\"", (char *) NULL);
1046 } else if (patPtr->eventType == CK_EV_KEYPRESS && isCtrl) {
1047 patPtr->detail -= 0x40;
1048 if (patPtr->detail >= 0x20)
1049 patPtr->detail -= 0x20;
1050 if (patPtr->detail < 0 || patPtr->detail >= 0x20)
1053 if (patPtr->eventType == -1) {
1054 patPtr->eventType = CK_EV_KEYPRESS;
1055 } else if (patPtr->eventType != CK_EV_KEYPRESS) {
1056 Tcl_AppendResult(interp, "specified keysym \"", field,
1057 "\" for non-key event", (char *) NULL);
1060 } else if (patPtr->eventType == -1) {
1061 interp->result = "no event type or keysym";
1064 while ((*p == '-') || isspace((unsigned char) *p)) {
1069 interp->result = "missing \">\" in binding";
1077 *-------------------------------------------------------------
1078 * Step 2: find the sequence in the binding table if it exists,
1079 * and add a new sequence to the table if it doesn't.
1080 *-------------------------------------------------------------
1084 interp->result = "no events specified in binding";
1087 patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
1088 key.object = object;
1089 key.type = patPtr->eventType;
1090 key.detail = patPtr->detail;
1091 hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
1092 sequenceSize = numPats*sizeof(Pattern);
1094 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1095 psPtr = psPtr->nextSeqPtr) {
1096 if ((numPats == psPtr->numPats)
1097 && (memcmp((char *) patPtr, (char *) psPtr->pats,
1098 sequenceSize) == 0)) {
1105 Tcl_DeleteHashEntry(hPtr);
1107 Tcl_AppendResult(interp, "no binding exists for \"",
1108 eventString, "\"", (char *) NULL);
1111 psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
1112 + (numPats-1)*sizeof(Pattern)));
1113 psPtr->numPats = numPats;
1114 psPtr->command = NULL;
1115 psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1117 Tcl_SetHashValue(hPtr, psPtr);
1120 * Link the pattern into the list associated with the object.
1123 psPtr->object = object;
1124 hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
1126 psPtr->nextObjPtr = NULL;
1128 psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1130 Tcl_SetHashValue(hPtr, psPtr);
1132 memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
1139 *----------------------------------------------------------------------
1143 * Used to parse pattern descriptions. Copies up to
1144 * size characters from p to copy, stopping at end of
1145 * string, space, "-", ">", or whenever size is
1149 * The return value is a pointer to the character just
1150 * after the last one copied (usually "-" or space or
1151 * ">", but could be anything if size was exceeded).
1152 * Also places NULL-terminated string (up to size
1153 * character, including NULL), at copy.
1158 *----------------------------------------------------------------------
1162 GetField(p, copy, size)
1163 register char *p; /* Pointer to part of pattern. */
1164 register char *copy; /* Place to copy field. */
1165 int size; /* Maximum number of characters to
1168 while ((*p != '\0') && !isspace((unsigned char) *p) && (*p != '>')
1169 && (*p != '-') && (size > 1)) {
1180 *----------------------------------------------------------------------
1184 * Given a list of pattern sequences and a list of
1185 * recent events, return a pattern sequence that matches
1189 * The return value is NULL if no pattern matches the
1190 * recent events from bindPtr. If one or more patterns
1191 * matches, then the longest (or most specific) matching
1192 * pattern is returned.
1197 *----------------------------------------------------------------------
1201 MatchPatterns(bindPtr, psPtr)
1202 BindingTable *bindPtr; /* Information about binding table, such
1203 * as ring of recent events. */
1204 register PatSeq *psPtr; /* List of pattern sequences. */
1206 register PatSeq *bestPtr = NULL;
1209 * Iterate over all the pattern sequences.
1212 for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1213 register CkEvent *eventPtr;
1214 register Pattern *patPtr;
1217 int patCount, ringCount;
1220 * Iterate over all the patterns in a sequence to be
1221 * sure that they all match.
1224 eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1225 detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1226 winPtr = eventPtr->any.winPtr;
1227 patPtr = psPtr->pats;
1228 patCount = psPtr->numPats;
1229 ringCount = EVENT_BUFFER_SIZE;
1230 while (patCount > 0) {
1231 if (ringCount <= 0) {
1234 if (eventPtr->any.type != patPtr->eventType) {
1235 if (patPtr->eventType == CK_EV_KEYPRESS)
1238 if (eventPtr->any.winPtr != winPtr)
1242 * Note: it's important for the keysym check to go before
1243 * the modifier check, so we can ignore unwanted modifier
1244 * keys before choking on the modifier check.
1247 if ((patPtr->detail != 0) && (patPtr->detail != -1)
1248 && (patPtr->detail != *detailPtr))
1251 if ((patPtr->detail == -1) && (*detailPtr >= 0x20))
1257 if (eventPtr == bindPtr->eventRing) {
1258 eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1259 detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1268 * This sequence matches. If we've already got another match,
1269 * pick whichever is most specific.
1272 if (bestPtr != NULL) {
1273 register Pattern *patPtr2;
1276 if (psPtr->numPats != bestPtr->numPats) {
1277 if (bestPtr->numPats > psPtr->numPats) {
1283 for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
1284 i < psPtr->numPats; i++, patPtr++, patPtr2++) {
1285 if (patPtr->detail != patPtr2->detail) {
1286 if (patPtr->detail == -1 && patPtr2->detail == 0) {
1288 } else if (patPtr->detail == 0 || patPtr->detail == -1) {
1295 goto nextSequence; /* Tie goes to newest pattern. */
1300 nextSequence: continue;
1306 *--------------------------------------------------------------
1310 * Given a command and an event, produce a new command
1311 * by replacing % constructs in the original command
1312 * with information from the X event.
1315 * The new expanded command is appended to the dynamic string
1321 *--------------------------------------------------------------
1325 ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
1326 CkWindow *winPtr; /* Window where event occurred: needed to
1327 * get input context. */
1328 register char *before; /* Command containing percent
1329 * expressions to be replaced. */
1330 register CkEvent *eventPtr; /* Event containing information
1331 * to be used in % replacements. */
1332 KeySym keySym; /* KeySym: only relevant for
1333 * CK_EV_KEYPRESS events). */
1334 Tcl_DString *dsPtr; /* Dynamic string in which to append
1337 int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1341 char *string, *string2;
1342 char numStorage[NUM_SIZE+1];
1346 * Find everything up to the next % character and append it
1347 * to the result string.
1350 for (string = before; (*string != 0) && (*string != '%'); string++) {
1351 /* Empty loop body. */
1353 if (string != before) {
1354 Tcl_DStringAppend(dsPtr, before, string-before);
1362 * There's a percent sequence here. Process it.
1367 switch (before[1]) {
1369 number = eventPtr->key.keycode;
1372 if (eventPtr->type == CK_EV_KEYPRESS) {
1375 if ((eventPtr->key.keycode & ~0xff) == 0 &&
1376 eventPtr->key.keycode != 0) {
1378 char c = eventPtr->key.keycode;
1381 Tcl_ExternalToUtf(NULL, winPtr->mainPtr->isoEncoding,
1383 numStorage + numChars,
1384 sizeof (numStorage) - numChars,
1388 numStorage[numChars++] = eventPtr->key.keycode;
1391 numStorage[numChars] = '\0';
1392 string = numStorage;
1393 } else if (eventPtr->type == CK_EV_BARCODE) {
1394 string = CkGetBarcodeData(winPtr->mainPtr);
1395 if (string == NULL) {
1396 numStorage[0] = '\0';
1397 string = numStorage;
1402 if (eventPtr->type == CK_EV_KEYPRESS) {
1405 name = CkKeysymToString(keySym, 1);
1412 number = (int) keySym;
1415 if (Tcl_FindHashEntry(&winPtr->mainPtr->winTable,
1416 (char *) eventPtr->any.winPtr) != NULL) {
1417 string = eventPtr->any.winPtr->pathName;
1423 if (eventPtr->type == CK_EV_MOUSE_UP ||
1424 eventPtr->type == CK_EV_MOUSE_DOWN) {
1425 number = eventPtr->mouse.x;
1429 if (eventPtr->type == CK_EV_MOUSE_UP ||
1430 eventPtr->type == CK_EV_MOUSE_DOWN) {
1431 number = eventPtr->mouse.y;
1435 if (eventPtr->type == CK_EV_MOUSE_UP ||
1436 eventPtr->type == CK_EV_MOUSE_DOWN) {
1437 number = eventPtr->mouse.button;
1441 if (eventPtr->type == CK_EV_MOUSE_UP ||
1442 eventPtr->type == CK_EV_MOUSE_DOWN) {
1443 number = eventPtr->mouse.rootx;
1447 if (eventPtr->type == CK_EV_MOUSE_UP ||
1448 eventPtr->type == CK_EV_MOUSE_DOWN) {
1449 number = eventPtr->mouse.rooty;
1453 numStorage[0] = before[1];
1454 numStorage[1] = '\0';
1455 string = numStorage;
1460 sprintf(numStorage, "%d", number);
1461 string = numStorage;
1464 spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
1465 string2 = ckalloc(spaceNeeded + 1);
1466 spaceNeeded = Tcl_ConvertElement(string, string2,
1467 cvtFlags | TCL_DONT_USE_BRACES);
1468 Tcl_DStringAppend(dsPtr, string2, -1);
1469 ckfree((char *) string2);
1475 *----------------------------------------------------------------------
1477 * CkStringToKeysym --
1479 * This procedure finds the keysym associated with a given keysym
1483 * The return value is the keysym that corresponds to name, or
1484 * NoSymbol if there is no such keysym.
1489 *----------------------------------------------------------------------
1493 CkStringToKeysym(name)
1494 char *name; /* Name of a keysym. */
1496 Tcl_HashEntry *hPtr;
1498 hPtr = Tcl_FindHashEntry(&keySymTable, name);
1500 return ((KeySymInfo *) Tcl_GetHashValue(hPtr))->value;
1506 *----------------------------------------------------------------------
1508 * CkKeysymToString --
1510 * This procedure finds the keysym name associated with a given
1514 * The return value is the keysym name that corresponds to name,
1515 * or NoSymbol if there is no name.
1520 *----------------------------------------------------------------------
1524 CkKeysymToString(keySym, printControl)
1528 Tcl_HashEntry *hPtr;
1529 static char buffer[64];
1531 hPtr = Tcl_FindHashEntry(&revKeySymTable, (char *) keySym);
1533 return ((KeySymInfo *) Tcl_GetHashValue(hPtr))->name;
1535 if (printControl && keySym >= 0x00 && keySym < 0x20) {
1537 sprintf(buffer, "Control-%c", keySym);
1540 return printControl ? "NoSymbol" : NULL;
1544 *----------------------------------------------------------------------
1548 * This procedure checks if the terminal has a key for given keysym.
1551 * TCL_OK or TCL_ERROR, a string is left in interp->result.
1556 *----------------------------------------------------------------------
1560 CkTermHasKey(interp, name)
1561 Tcl_Interp *interp; /* Interpreter used for result. */
1562 char *name; /* Name of a keysym. */
1564 #if !defined(__WIN32__) && !defined(DJGPP)
1565 Tcl_HashEntry *hPtr;
1566 char *tiname, *tivalue;
1567 extern char *tigetstr();
1571 if (strncmp("Control-", name, 8) == 0) {
1572 if (sscanf(name, "Control-%7s", buf) != 1 || strlen(buf) != 1)
1574 if (buf[0] < 'A' && buf[0] > 'z')
1576 interp->result = "1";
1579 #if defined(__WIN32__) || defined(DJGPP)
1580 interp->result = "1";
1583 hPtr = Tcl_FindHashEntry(&keySymTable, name);
1586 tiname = ((KeySymInfo *) Tcl_GetHashValue(hPtr))->tiname;
1587 if (tiname == NULL || ((tivalue = tigetstr(tiname)) != NULL &&
1588 tivalue != (char *) -1))
1589 interp->result = "1";
1591 interp->result = "0";
1594 if (strlen(name) == 1) {
1595 if (name[0] > 0x01 && name[0] < ' ') {
1596 interp->result = "1";
1599 hPtr = Tcl_FindHashEntry(&revKeySymTable, (char *)
1600 ((int) ((unsigned char) name[0])));
1606 Tcl_AppendResult(interp, "invalid key symbol \"", name,
1607 "\"", (char *) NULL);
1612 *----------------------------------------------------------------------
1616 * This procedure returns a list of all key names.
1619 * Always TCL_OK and list in interp->result.
1624 *----------------------------------------------------------------------
1628 CkAllKeyNames(interp)
1629 Tcl_Interp *interp; /* Interpreter used for result. */
1634 for (i = 0x01; i < ' '; i++) {
1639 sprintf(buf, "Control-%c", tolower(code));
1640 Tcl_AppendElement(interp, buf);
1642 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
1643 Tcl_AppendElement(interp, kPtr->name);