]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkbind.c
d83ca5e0c8c3dcfade5d415659133d725752d222
[micropolis] / src / tk / tkbind.c
1 /*
2 * tkBind.c --
3 *
4 * This file provides procedures that associate Tcl commands
5 * with X events or sequences of X events.
6 *
7 * Copyright 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.
15 */
16
17 #ifndef lint
18 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkBind.c,v 1.48 92/08/10 16:55:24 ouster Exp $ SPRITE (Berkeley)";
19 #endif /* not lint */
20
21 #include "tkconfig.h"
22 #include "tkint.h"
23
24 /*
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).
35 */
36
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
41 * events). */
42 int detailRing[EVENT_BUFFER_SIZE]; /* "Detail" information (keySym or
43 * button or 0) for each entry in
44 * eventRing. */
45 int curEvent; /* Index in eventRing of most recent
46 * event. Newer events have higher
47 * indices. */
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
57 * executed. */
58 } BindingTable;
59
60 /*
61 * Structures of the following form are used as keys in the patternTable
62 * for a binding table:
63 */
64
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
74 * additional.*/
75 } PatternTableKey;
76
77 /*
78 * The following structure defines a pattern, which is matched
79 * against X events as part of the process of converting X events
80 * into Tcl commands.
81 */
82
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
87 * required). */
88 int hateMods; /* Mask of modifiers that must not be
89 * present (0 means any modifiers are
90 * OK). */
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). */
101 } Pattern;
102
103 /*
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).
108 */
109
110 typedef struct PatSeq {
111 int numPats; /* Number of patterns in sequence
112 * (usually 1). */
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
121 * end of list. */
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
125 * forms a part. */
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,
139 * etc. */
140 } PatSeq;
141
142 /*
143 * Flag values for PatSeq structures:
144 *
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
149 * button presses.
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.
153 */
154
155 #define PAT_NEARBY 1
156 #define PAT_PERCENTS 2
157
158 /*
159 * Constants that define how close together two events must be
160 * in milliseconds or pixels to meet the PAT_NEARBY constraint:
161 */
162
163 #define NEARBY_PIXELS 5
164 #define NEARBY_MS 500
165
166 /*
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
170 * ridiculously slow.
171 */
172
173 typedef struct {
174 char *name; /* Name of keysym. */
175 KeySym value; /* Numeric identifier for keysym. */
176 } KeySymInfo;
177 KeySymInfo keyArray[] = {
178 #ifndef lint
179 #include "ks_names.h"
180 #endif
181 (char *) NULL, 0
182 };
183 static Tcl_HashTable keySymTable; /* Hashed form of above structure. */
184
185 static int initialized = 0;
186
187 /*
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.
192 */
193
194 typedef struct {
195 char *name; /* Name of modifier. */
196 int mask; /* Button/modifier mask value, * such as Button1Mask. */
197 int flags; /* Various flags; see below for
198 * definitions. */
199 } ModInfo;
200
201 /*
202 * Flags for ModInfo structures:
203 *
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.
210 */
211
212 #define DOUBLE 1
213 #define TRIPLE 2
214 #define ANY 4
215
216 static ModInfo modArray[] = {
217 "Control", ControlMask, 0,
218 "Shift", ShiftMask, 0,
219 "Lock", LockMask, 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,
230 "Mod1", Mod1Mask, 0,
231 "M1", Mod1Mask, 0,
232 "Meta", Mod1Mask, 0,
233 "M", Mod1Mask, 0,
234 "Mod2", Mod2Mask, 0,
235 "M2", Mod2Mask, 0,
236 "Alt", Mod2Mask, 0,
237 "Mod3", Mod3Mask, 0,
238 "M3", Mod3Mask, 0,
239 "Mod4", Mod4Mask, 0,
240 "M4", Mod4Mask, 0,
241 "Mod5", Mod5Mask, 0,
242 "M5", Mod5Mask, 0,
243 "Double", 0, DOUBLE,
244 "Triple", 0, TRIPLE,
245 "Any", 0, ANY,
246 NULL, 0, 0};
247 static Tcl_HashTable modTable;
248
249 /*
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
253 * all defined below.
254 */
255
256 typedef struct {
257 char *name; /* Name of event. */
258 int type; /* Event type for X, such as
259 * ButtonPress. */
260 int eventMask; /* Mask bits (for XSelectInput)
261 * for this event type. */
262 } EventInfo;
263
264 /*
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.
270 */
271
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;
305
306 /*
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.
312 */
313
314 #define KEY_BUTTON_MOTION 0x1
315 #define CROSSING 0x2
316 #define FOCUS 0x4
317 #define EXPOSE 0x8
318 #define VISIBILITY 0x10
319 #define CREATE 0x20
320 #define MAP 0x40
321 #define REPARENT 0x80
322 #define CONFIG 0x100
323 #define CONFIG_REQ 0x200
324 #define RESIZE_REQ 0x400
325 #define GRAVITY 0x800
326 #define PROP 0x0100
327 #define SEL_CLEAR 0x2000
328 #define SEL_REQ 0x4000
329 #define SEL_NOTIFY 0x8000
330 #define COLORMAP 0x10000
331 #define MAPPING 0x20000
332
333 static int flagArray[LASTEvent] = {
334 /* Not used */ 0,
335 /* Not used */ 0,
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,
343 /* FocusIn */ FOCUS,
344 /* FocusOut */ FOCUS,
345 /* KeymapNotify */ 0,
346 /* Expose */ EXPOSE,
347 /* GraphicsExpose */ EXPOSE,
348 /* NoExpose */ 0,
349 /* VisibilityNotify */ VISIBILITY,
350 /* CreateNotify */ CREATE,
351 /* DestroyNotify */ 0,
352 /* UnmapNotify */ 0,
353 /* MapNotify */ MAP,
354 /* MapRequest */ 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
369 };
370
371 /*
372 * Forward declarations for procedures defined later in this
373 * file:
374 */
375
376 static char * ExpandPercents _ANSI_ARGS_((char *before,
377 XEvent *eventPtr, KeySym keySym, char *after,
378 int afterSize));
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,
385 XEvent *eventPtr));
386 static PatSeq * MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr,
387 PatSeq *psPtr));
388 \f
389 /*
390 *--------------------------------------------------------------
391 *
392 * Tk_CreateBindingTable --
393 *
394 * Set up a new domain in which event bindings may be created.
395 *
396 * Results:
397 * The return value is a token for the new table, which must
398 * be passed to procedures like Tk_CreatBinding.
399 *
400 * Side effects:
401 * Memory is allocated for the new table.
402 *
403 *--------------------------------------------------------------
404 */
405
406 Tk_BindingTable
407 Tk_CreateBindingTable(interp)
408 Tcl_Interp *interp; /* Interpreter to associate with the binding
409 * table: commands are executed in this
410 * interpreter. */
411 {
412 register BindingTable *bindPtr;
413 int i;
414
415 /*
416 * If this is the first time a binding table has been created,
417 * initialize the global data structures.
418 */
419
420 if (!initialized) {
421 register KeySymInfo *kPtr;
422 register Tcl_HashEntry *hPtr;
423 register ModInfo *modPtr;
424 register EventInfo *eiPtr;
425 int dummy;
426
427 initialized = 1;
428
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);
433 }
434
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);
439 }
440
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);
445 }
446 }
447
448 /*
449 * Create and initialize a new binding table.
450 */
451
452 bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
453 for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
454 bindPtr->eventRing[i].type = -1;
455 }
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;
462 }
463 \f
464 /*
465 *--------------------------------------------------------------
466 *
467 * Tk_DeleteBindingTable --
468 *
469 * Destroy a binding table and free up all its memory.
470 * The caller should not use bindingTable again after
471 * this procedure returns.
472 *
473 * Results:
474 * None.
475 *
476 * Side effects:
477 * Memory is freed.
478 *
479 *--------------------------------------------------------------
480 */
481
482 void
483 Tk_DeleteBindingTable(bindingTable)
484 Tk_BindingTable bindingTable; /* Token for the binding table to
485 * destroy. */
486 {
487 BindingTable *bindPtr = (BindingTable *) bindingTable;
488 PatSeq *psPtr, *nextPtr;
489 Tcl_HashEntry *hPtr;
490 Tcl_HashSearch search;
491
492 /*
493 * Find and delete all of the patterns associated with the binding
494 * table.
495 */
496
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);
505 }
506 }
507
508 /*
509 * Clean up the rest of the information associated with the
510 * binding table.
511 */
512
513 Tcl_DeleteHashTable(&bindPtr->patternTable);
514 Tcl_DeleteHashTable(&bindPtr->objectTable);
515 ckfree((char *) bindPtr);
516 }
517 \f
518 /*
519 *--------------------------------------------------------------
520 *
521 * Tk_CreateBinding --
522 *
523 * Add a binding to a binding table, so that future calls to
524 * Tk_BindEvent may execute the command in the binding.
525 *
526 * Results:
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.
534 *
535 * Side effects:
536 * The new binding may cause future calls to Tk_BindEvent to
537 * behave differently than they did previously.
538 *
539 *--------------------------------------------------------------
540 */
541
542 unsigned long
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
547 * is associated. */
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. */
555 {
556 BindingTable *bindPtr = (BindingTable *) bindingTable;
557 register PatSeq *psPtr;
558 unsigned long eventMask;
559
560 psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask);
561 if (psPtr == NULL) {
562 return 0;
563 }
564 if (append && (psPtr->command != NULL)) {
565 int length;
566 char *new;
567
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;
573 } else {
574 if (psPtr->command != NULL) {
575 Tk_EventuallyFree((ClientData) psPtr->command,
576 (Tk_FreeProc *) free);
577 }
578 psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
579 strcpy(psPtr->command, command);
580 }
581
582 /*
583 * See if the command contains percents and thereby requires
584 * percent substitution.
585 */
586
587 if (strchr(psPtr->command, '%') != NULL) {
588 psPtr->flags |= PAT_PERCENTS;
589 }
590 return eventMask;
591 }
592 \f
593 /*
594 *--------------------------------------------------------------
595 *
596 * Tk_DeleteBinding --
597 *
598 * Remove an event binding from a binding table.
599 *
600 * Results:
601 * The result is a standard Tcl return value. If an error
602 * occurs then interp->result will contain an error message.
603 *
604 * Side effects:
605 * The binding given by object and eventString is removed
606 * from bindingTable.
607 *
608 *--------------------------------------------------------------
609 */
610
611 int
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
616 * is associated. */
617 char *eventString; /* String describing event sequence
618 * that triggers binding. */
619 {
620 BindingTable *bindPtr = (BindingTable *) bindingTable;
621 register PatSeq *psPtr, *prevPtr;
622 unsigned long eventMask;
623 Tcl_HashEntry *hPtr;
624
625 psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
626 if (psPtr == NULL) {
627 Tcl_ResetResult(interp);
628 return TCL_OK;
629 }
630
631 /*
632 * Unlink the binding from the list for its object, then from the
633 * list for its pattern.
634 */
635
636 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
637 if (hPtr == NULL) {
638 panic("Tk_DeleteBinding couldn't find object table entry");
639 }
640 prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
641 if (prevPtr == psPtr) {
642 Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
643 } else {
644 for ( ; ; prevPtr = prevPtr->nextObjPtr) {
645 if (prevPtr == NULL) {
646 panic("Tk_DeleteBinding couldn't find on object list");
647 }
648 if (prevPtr->nextObjPtr == psPtr) {
649 prevPtr->nextObjPtr = psPtr->nextObjPtr;
650 break;
651 }
652 }
653 }
654 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
655 if (prevPtr == psPtr) {
656 if (psPtr->nextSeqPtr == NULL) {
657 Tcl_DeleteHashEntry(psPtr->hPtr);
658 } else {
659 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
660 }
661 } else {
662 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
663 if (prevPtr == NULL) {
664 panic("Tk_DeleteBinding couldn't find on hash chain");
665 }
666 if (prevPtr->nextSeqPtr == psPtr) {
667 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
668 break;
669 }
670 }
671 }
672 Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
673 ckfree((char *) psPtr);
674 return TCL_OK;
675 }
676 \f
677 /*
678 *--------------------------------------------------------------
679 *
680 * Tk_GetBinding --
681 *
682 * Return the command associated with a given event string.
683 *
684 * Results:
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.
692 *
693 * Side effects:
694 * None.
695 *
696 *--------------------------------------------------------------
697 */
698
699 char *
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
703 * binding. */
704 ClientData object; /* Token for object with which binding
705 * is associated. */
706 char *eventString; /* String describing event sequence
707 * that triggers binding. */
708 {
709 BindingTable *bindPtr = (BindingTable *) bindingTable;
710 register PatSeq *psPtr;
711 unsigned long eventMask;
712
713 psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
714 if (psPtr == NULL) {
715 return NULL;
716 }
717 return psPtr->command;
718 }
719 \f
720 /*
721 *--------------------------------------------------------------
722 *
723 * Tk_GetAllBindings --
724 *
725 * Return a list of event strings for all the bindings
726 * associated with a given object.
727 *
728 * Results:
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.
733 *
734 * Side effects:
735 * None.
736 *
737 *--------------------------------------------------------------
738 */
739
740 void
741 Tk_GetAllBindings(interp, bindingTable, object)
742 Tcl_Interp *interp; /* Interpreter for error reporting. */
743 Tk_BindingTable bindingTable; /* Table in which to look for
744 * bindings. */
745 ClientData object; /* Token for object. */
746
747 {
748 BindingTable *bindPtr = (BindingTable *) bindingTable;
749 register PatSeq *psPtr;
750 register Pattern *patPtr;
751 Tcl_HashEntry *hPtr;
752 char string[200*EVENT_BUFFER_SIZE];
753 register char *p;
754 int patsLeft, needMods;
755 register ModInfo *modPtr;
756
757 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
758 if (hPtr == NULL) {
759 return;
760 }
761 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
762 psPtr = psPtr->nextObjPtr) {
763
764 p = string;
765
766 /*
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
770 * must be output.
771 */
772
773 for (patsLeft = psPtr->numPats,
774 patPtr = &psPtr->pats[psPtr->numPats - 1];
775 patsLeft > 0; patsLeft--, patPtr--) {
776
777 /*
778 * Check for simple case of an ASCII character.
779 */
780
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 != ' ')) {
787
788 *p = patPtr->detail;
789 p++;
790 continue;
791 }
792
793 /*
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.
797 */
798
799 *p = '<';
800 p++;
801 if ((patsLeft > 1) && (memcmp((char *) patPtr,
802 (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
803 patsLeft--;
804 patPtr--;
805 if ((patsLeft > 1) && (memcmp((char *) patPtr,
806 (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
807 patsLeft--;
808 patPtr--;
809 strcpy(p, "Triple-");
810 } else {
811 strcpy(p, "Double-");
812 }
813 p += strlen(p);
814 }
815
816 if (patPtr->hateMods == 0) {
817 strcpy(p, "Any-");
818 p += strlen(p);
819 }
820
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);
826 p += strlen(p);
827 *p = '-';
828 p++;
829 }
830 }
831
832 if ((patPtr->eventType != KeyPress)
833 || (patPtr->detail == 0)) {
834 register EventInfo *eiPtr;
835
836 for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
837 if (eiPtr->type == patPtr->eventType) {
838 strcpy(p, eiPtr->name);
839 p += strlen(p);
840 if (patPtr->detail != 0) {
841 *p = '-';
842 p++;
843 }
844 break;
845 }
846 }
847 }
848
849 if (patPtr->detail != 0) {
850 if ((patPtr->eventType == KeyPress)
851 || (patPtr->eventType == KeyRelease)) {
852 register KeySymInfo *kPtr;
853
854 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
855 if (patPtr->detail == (int) kPtr->value) {
856 sprintf(p, "%.100s", kPtr->name);
857 p += strlen(p);
858 break;
859 }
860 }
861 } else {
862 sprintf(p, "%d", patPtr->detail);
863 p += strlen(p);
864 }
865 }
866 *p = '>';
867 p++;
868 }
869 *p = 0;
870 if ((p - string) >= sizeof(string)) {
871 panic("Tk_GetAllBindings overflowed buffer");
872 }
873 Tcl_AppendElement(interp, string, 0);
874 }
875 }
876 \f
877 /*
878 *--------------------------------------------------------------
879 *
880 * Tk_DeleteAllBindings --
881 *
882 * Remove all bindings associated with a given object in a
883 * given binding table.
884 *
885 * Results:
886 * All bindings associated with object are removed from
887 * bindingTable.
888 *
889 * Side effects:
890 * None.
891 *
892 *--------------------------------------------------------------
893 */
894
895 void
896 Tk_DeleteAllBindings(bindingTable, object)
897 Tk_BindingTable bindingTable; /* Table in which to delete
898 * bindings. */
899 ClientData object; /* Token for object. */
900 {
901 BindingTable *bindPtr = (BindingTable *) bindingTable;
902 register PatSeq *psPtr, *prevPtr;
903 PatSeq *nextPtr;
904 Tcl_HashEntry *hPtr;
905
906 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
907 if (hPtr == NULL) {
908 return;
909 }
910 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
911 psPtr = nextPtr) {
912 nextPtr = psPtr->nextObjPtr;
913
914 /*
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.
918 */
919
920 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
921 if (prevPtr == psPtr) {
922 if (psPtr->nextSeqPtr == NULL) {
923 Tcl_DeleteHashEntry(psPtr->hPtr);
924 } else {
925 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
926 }
927 } else {
928 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
929 if (prevPtr == NULL) {
930 panic("Tk_DeleteAllBindings couldn't find on hash chain");
931 }
932 if (prevPtr->nextSeqPtr == psPtr) {
933 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
934 break;
935 }
936 }
937 }
938 Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
939 ckfree((char *) psPtr);
940 }
941 Tcl_DeleteHashEntry(hPtr);
942 }
943 \f
944 /*
945 *--------------------------------------------------------------
946 *
947 * Tk_BindEvent --
948 *
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.
955 *
956 * Results:
957 * None.
958 *
959 * Side effects:
960 * Depends on the command associated with the matching
961 * binding.
962 *
963 *--------------------------------------------------------------
964 */
965
966 void
967 Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
968 Tk_BindingTable bindingTable; /* Table in which to look for
969 * bindings. */
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. */
977 {
978 BindingTable *bindPtr = (BindingTable *) bindingTable;
979 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
980 XEvent *ringPtr;
981 PatSeq *matchPtr;
982 PatternTableKey key;
983 Tcl_HashEntry *hPtr;
984 int detail;
985
986 /*
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.
991 */
992
993 if ((eventPtr->type != MotionNotify)
994 || (bindPtr->eventRing[bindPtr->curEvent].type != MotionNotify)) {
995 bindPtr->curEvent++;
996 if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
997 bindPtr->curEvent = 0;
998 }
999 }
1000 ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1001 memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
1002 detail = 0;
1003 bindPtr->detailRing[bindPtr->curEvent] = 0;
1004 if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
1005 detail = (int) GetKeySym(dispPtr, ringPtr);
1006 if (detail == NoSymbol) {
1007 detail = 0;
1008 }
1009 } else if ((ringPtr->type == ButtonPress)
1010 || (ringPtr->type == ButtonRelease)) {
1011 detail = ringPtr->xbutton.button;
1012 }
1013 bindPtr->detailRing[bindPtr->curEvent] = detail;
1014
1015 /*
1016 * Loop over all the objects, matching the new event against
1017 * each in turn.
1018 */
1019
1020 for ( ; numObjects > 0; numObjects--, objectPtr++) {
1021
1022 /*
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).
1029 */
1030
1031 matchPtr = NULL;
1032 key.object = *objectPtr;
1033 key.type = ringPtr->type;
1034 key.detail = detail;
1035 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1036 if (hPtr != NULL) {
1037 matchPtr = MatchPatterns(bindPtr,
1038 (PatSeq *) Tcl_GetHashValue(hPtr));
1039 }
1040 if ((detail != 0) && (matchPtr == NULL)) {
1041 key.detail = 0;
1042 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1043 if (hPtr != NULL) {
1044 matchPtr = MatchPatterns(bindPtr,
1045 (PatSeq *) Tcl_GetHashValue(hPtr));
1046 }
1047 }
1048
1049 if (matchPtr != NULL) {
1050
1051 /*
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).
1057 */
1058
1059 #define STATIC_SPACE 200
1060 char shortSpace[STATIC_SPACE];
1061 int result;
1062
1063 if (matchPtr->flags & PAT_PERCENTS) {
1064 char *p;
1065 p = ExpandPercents(matchPtr->command, eventPtr,
1066 (KeySym) detail, shortSpace, STATIC_SPACE);
1067 result = Tcl_GlobalEval(bindPtr->interp, p);
1068 if (p != shortSpace) {
1069 ckfree(p);
1070 }
1071 } else {
1072 /*
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.
1079 */
1080
1081 char *cmd = matchPtr->command;
1082
1083 Tk_Preserve((ClientData) cmd);
1084 result = Tcl_GlobalEval(bindPtr->interp, cmd);
1085 Tk_Release((ClientData) cmd);
1086 }
1087 if (result != TCL_OK) {
1088 Tcl_AddErrorInfo(bindPtr->interp,
1089 "\n (command bound to event)");
1090 TkBindError(bindPtr->interp);
1091 }
1092 return;
1093 }
1094 }
1095 }
1096 \f
1097 /*
1098 *----------------------------------------------------------------------
1099 *
1100 * FindSequence --
1101 *
1102 * Find the entry in a binding table that corresponds to a
1103 * particular pattern string, and return a pointer to that
1104 * entry.
1105 *
1106 * Results:
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.
1116 *
1117 * Side effects:
1118 * A new pattern sequence may be created.
1119 *
1120 *----------------------------------------------------------------------
1121 */
1122
1123 static PatSeq *
1124 FindSequence(interp, bindPtr, object, eventString, create, maskPtr)
1125 Tcl_Interp *interp; /* Interpreter to use for error
1126 * reporting. */
1127 BindingTable *bindPtr; /* Table to use for lookup. */
1128 ClientData object; /* Token for object(s) with which binding
1129 * is associated. */
1130 char *eventString; /* String description of pattern to
1131 * match on. See user documentation
1132 * for details. */
1133 int create; /* 0 means don't create the entry if
1134 * it doesn't already exist. Non-zero
1135 * means create. */
1136 unsigned long *maskPtr; /* *maskPtr is filled in with the event
1137 * types on which this pattern sequence
1138 * depends. */
1139
1140 {
1141 Pattern pats[EVENT_BUFFER_SIZE];
1142 int numPats;
1143 register char *p;
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;
1152
1153 /*
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 *-------------------------------------------------------------
1160 */
1161
1162 p = eventString;
1163 flags = 0;
1164 eventMask = 0;
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;
1171 patPtr->detail = 0;
1172 while (isspace(*p)) {
1173 p++;
1174 }
1175 if (*p == '\0') {
1176 break;
1177 }
1178
1179 /*
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").
1183 */
1184
1185 if (*p != '<') {
1186 char string[2];
1187
1188 patPtr->eventType = KeyPress;
1189 eventMask |= KeyPressMask;
1190 string[0] = *p;
1191 string[1] = 0;
1192 hPtr = Tcl_FindHashEntry(&keySymTable, string);
1193 if (hPtr != NULL) {
1194 patPtr->detail = (int) Tcl_GetHashValue(hPtr);
1195 } else {
1196 if (isprint(*p)) {
1197 patPtr->detail = *p;
1198 } else {
1199 sprintf(interp->result,
1200 "bad ASCII character 0x%x", *p);
1201 return NULL;
1202 }
1203 }
1204 patPtr->hateMods = ~ShiftMask;
1205 p++;
1206 continue;
1207 }
1208
1209 /*
1210 * A fancier event description. Must consist of
1211 * 1. open angle bracket.
1212 * 2. any number of modifiers, each followed by spaces
1213 * or dashes.
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.
1219 */
1220
1221 any = 0;
1222 count = 1;
1223 p++;
1224 while (1) {
1225 register ModInfo *modPtr;
1226 p = GetField(p, field, FIELD_SIZE);
1227 hPtr = Tcl_FindHashEntry(&modTable, field);
1228 if (hPtr == NULL) {
1229 break;
1230 }
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) {
1236 count = 2;
1237 } else {
1238 count = 3;
1239 }
1240 }
1241 if (modPtr->flags & ANY) {
1242 any = 1;
1243 }
1244 while ((*p == '-') || isspace(*p)) {
1245 p++;
1246 }
1247 }
1248 if (any) {
1249 patPtr->hateMods = 0;
1250 } else {
1251 patPtr->hateMods = ~patPtr->needMods;
1252 }
1253 hPtr = Tcl_FindHashEntry(&eventTable, field);
1254 if (hPtr != NULL) {
1255 register EventInfo *eiPtr;
1256 eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
1257 patPtr->eventType = eiPtr->type;
1258 eventMask |= eiPtr->eventMask;
1259 while ((*p == '-') || isspace(*p)) {
1260 p++;
1261 }
1262 p = GetField(p, field, FIELD_SIZE);
1263 }
1264 if (*field != '\0') {
1265 if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
1266 static int masks[] = {~0, ~Button1Mask, ~Button2Mask,
1267 ~Button3Mask, ~Button4Mask, ~Button5Mask};
1268
1269 if (patPtr->eventType == -1) {
1270 patPtr->eventType = ButtonPress;
1271 eventMask |= ButtonPressMask;
1272 } else if ((patPtr->eventType == KeyPress)
1273 || (patPtr->eventType == KeyRelease)) {
1274 goto getKeysym;
1275 } else if ((patPtr->eventType != ButtonPress)
1276 && (patPtr->eventType != ButtonRelease)) {
1277 Tcl_AppendResult(interp, "specified button \"", field,
1278 "\" for non-button event", (char *) NULL);
1279 return NULL;
1280 }
1281 patPtr->detail = (*field - '0');
1282
1283 /*
1284 * Ignore this button as a modifier: its state is already
1285 * fixed.
1286 */
1287
1288 patPtr->needMods &= masks[patPtr->detail];
1289 patPtr->hateMods &= masks[patPtr->detail];
1290 } else {
1291 getKeysym:
1292 hPtr = Tcl_FindHashEntry(&keySymTable, (char *) field);
1293 if (hPtr == NULL) {
1294 Tcl_AppendResult(interp, "bad event type or keysym \"",
1295 field, "\"", (char *) NULL);
1296 return NULL;
1297 }
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);
1305 return NULL;
1306 }
1307 patPtr->detail = (int) Tcl_GetHashValue(hPtr);
1308
1309 /*
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.
1316 */
1317
1318 patPtr->hateMods &= ~ShiftMask;
1319 }
1320 } else if (patPtr->eventType == -1) {
1321 interp->result = "no event type or button # or keysym";
1322 return NULL;
1323 }
1324 while ((*p == '-') || isspace(*p)) {
1325 p++;
1326 }
1327 if (*p != '>') {
1328 interp->result = "missing \">\" in binding";
1329 return NULL;
1330 }
1331 p++;
1332
1333 /*
1334 * Replicate events for DOUBLE and TRIPLE.
1335 */
1336
1337 if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
1338 patPtr[-1] = patPtr[0];
1339 patPtr--;
1340 numPats++;
1341 if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
1342 patPtr[-1] = patPtr[0];
1343 patPtr--;
1344 numPats++;
1345 }
1346 }
1347 }
1348
1349 /*
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 *-------------------------------------------------------------
1354 */
1355
1356 if (numPats == 0) {
1357 interp->result = "no events specified in binding";
1358 return NULL;
1359 }
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);
1366 if (!new) {
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*/
1374 goto done;
1375 }
1376 }
1377 }
1378 if (!create) {
1379 if (new) {
1380 Tcl_DeleteHashEntry(hPtr);
1381 }
1382 Tcl_AppendResult(interp, "no binding exists for \"",
1383 eventString, "\"", (char *) NULL);
1384 return NULL;
1385 }
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);
1392 psPtr->hPtr = hPtr;
1393 Tcl_SetHashValue(hPtr, psPtr);
1394
1395 /*
1396 * Link the pattern into the list associated with the object.
1397 */
1398
1399 psPtr->object = object;
1400 hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
1401 if (new) {
1402 psPtr->nextObjPtr = NULL;
1403 } else {
1404 psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1405 }
1406 Tcl_SetHashValue(hPtr, psPtr);
1407
1408 memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
1409
1410 done:
1411 *maskPtr = eventMask;
1412 return psPtr;
1413 }
1414 \f
1415 /*
1416 *----------------------------------------------------------------------
1417 *
1418 * GetField --
1419 *
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
1423 * exceeded.
1424 *
1425 * Results:
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.
1431 *
1432 * Side effects:
1433 * None.
1434 *
1435 *----------------------------------------------------------------------
1436 */
1437
1438 static char *
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
1443 * copy. */
1444 {
1445 while ((*p != '\0') && !isspace(*p) && (*p != '>')
1446 && (*p != '-') && (size > 1)) {
1447 *copy = *p;
1448 p++;
1449 copy++;
1450 size--;
1451 }
1452 *copy = '\0';
1453 return p;
1454 }
1455 \f
1456 /*
1457 *----------------------------------------------------------------------
1458 *
1459 * GetKeySym --
1460 *
1461 * Given an X KeyPress or KeyRelease event, map the
1462 * keycode in the event into a KeySym.
1463 *
1464 * Results:
1465 * The return value is the KeySym corresponding to
1466 * eventPtr, or NoSymbol if no matching Keysym could be
1467 * found.
1468 *
1469 * Side effects:
1470 * In the first call for a given display, keycode-to-
1471 * KeySym maps get loaded.
1472 *
1473 *----------------------------------------------------------------------
1474 */
1475
1476 static KeySym
1477 GetKeySym(dispPtr, eventPtr)
1478 register TkDisplay *dispPtr; /* Display in which to
1479 * map keycode. */
1480 register XEvent *eventPtr; /* Description of X event. */
1481 {
1482 KeySym *symPtr;
1483 KeySym sym;
1484
1485 /*
1486 * Read the key mapping information from the server if
1487 * we don't have it already.
1488 */
1489
1490 if (dispPtr->symsPerCode == 0) {
1491 Display *dpy = dispPtr->display;
1492
1493 #ifdef IS_LINUX
1494 XDisplayKeycodes(dpy, &dispPtr->firstKeycode, &dispPtr->lastKeycode);
1495 #else
1496 dispPtr->firstKeycode =
1497 dpy->min_keycode;
1498 dispPtr->lastKeycode =
1499 dpy->max_keycode;
1500 #endif
1501 dispPtr->keySyms = XGetKeyboardMapping(dpy,
1502 dispPtr->firstKeycode, dispPtr->lastKeycode + 1
1503 - dispPtr->firstKeycode, &dispPtr->symsPerCode);
1504 }
1505
1506 /*
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.
1510 */
1511
1512 if ((eventPtr->xkey.keycode < dispPtr->firstKeycode)
1513 || (eventPtr->xkey.keycode > dispPtr->lastKeycode)) {
1514 return NoSymbol;
1515 }
1516 symPtr = &dispPtr->keySyms[(eventPtr->xkey.keycode
1517 - dispPtr->firstKeycode) * dispPtr->symsPerCode];
1518 sym = *symPtr;
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);
1526 }
1527 }
1528
1529 /*
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
1534 * keys).
1535 */
1536
1537 if (eventPtr->xkey.state & ShiftMask) {
1538 if ((dispPtr->symsPerCode > 1) && (symPtr[1] != NoSymbol)) {
1539 return symPtr[1];
1540 }
1541 shiftToUpper:
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);
1548 }
1549 return sym;
1550 }
1551 if (eventPtr->xkey.state & LockMask) {
1552 goto shiftToUpper;
1553 }
1554 return sym;
1555 }
1556 \f
1557 /*
1558 *----------------------------------------------------------------------
1559 *
1560 * MatchPatterns --
1561 *
1562 * Given a list of pattern sequences and a list of
1563 * recent events, return a pattern sequence that matches
1564 * the event list.
1565 *
1566 * Results:
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.
1571 *
1572 * Side effects:
1573 * None.
1574 *
1575 *----------------------------------------------------------------------
1576 */
1577
1578 static PatSeq *
1579 MatchPatterns(bindPtr, psPtr)
1580 BindingTable *bindPtr; /* Information about binding table, such
1581 * as ring of recent events. */
1582 register PatSeq *psPtr; /* List of pattern sequences. */
1583 {
1584 register PatSeq *bestPtr = NULL;
1585
1586 /*
1587 * Iterate over all the pattern sequences.
1588 */
1589
1590 for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1591 register XEvent *eventPtr;
1592 register Pattern *patPtr;
1593 Window window;
1594 int *detailPtr;
1595 int patCount, ringCount, flags, state;
1596
1597 /*
1598 * Iterate over all the patterns in a sequence to be
1599 * sure that they all match.
1600 */
1601
1602 eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1603 detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1604 window = eventPtr->xany.window;
1605 patPtr = psPtr->pats;
1606 patCount = psPtr->numPats;
1607 ringCount = EVENT_BUFFER_SIZE;
1608 while (patCount > 0) {
1609 if (ringCount <= 0) {
1610 goto nextSequence;
1611 }
1612 if (eventPtr->xany.window != window) {
1613 goto nextSequence;
1614 }
1615 if (eventPtr->xany.type != patPtr->eventType) {
1616 /*
1617 * If the event is a mouse motion, button release,
1618 * or key release event, and it didn't match
1619 * the pattern, then just skip the event and try
1620 * the next event against the same pattern.
1621 */
1622
1623 if ((eventPtr->xany.type == MotionNotify)
1624 || (eventPtr->xany.type == ButtonRelease)
1625 || (eventPtr->xany.type == KeyRelease)
1626 || (eventPtr->xany.type == NoExpose)
1627 || (eventPtr->xany.type == GraphicsExpose)) {
1628 goto nextEvent;
1629 }
1630 goto nextSequence;
1631 }
1632
1633 flags = flagArray[eventPtr->type];
1634 if (flags & KEY_BUTTON_MOTION) {
1635 state = eventPtr->xkey.state;
1636 } else if (flags & CROSSING) {
1637 state = eventPtr->xcrossing.state;
1638 } else {
1639 state = 0;
1640 }
1641 if ((state & patPtr->needMods)
1642 != patPtr->needMods) {
1643 goto nextSequence;
1644 }
1645 #if 0
1646 if ((state & patPtr->hateMods) != 0) {
1647 goto nextSequence;
1648 }
1649 #endif
1650 if ((patPtr->detail != 0)
1651 && (patPtr->detail != *detailPtr)) {
1652 goto nextSequence;
1653 }
1654 if (psPtr->flags & PAT_NEARBY) {
1655 register XEvent *firstPtr;
1656
1657 firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
1658 if ((firstPtr->xkey.x_root
1659 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
1660 || (firstPtr->xkey.x_root
1661 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
1662 || (firstPtr->xkey.y_root
1663 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
1664 || (firstPtr->xkey.y_root
1665 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
1666 || (firstPtr->xkey.time
1667 > (eventPtr->xkey.time + NEARBY_MS))) {
1668 goto nextSequence;
1669 }
1670 }
1671 patPtr++;
1672 patCount--;
1673 nextEvent:
1674 if (eventPtr == bindPtr->eventRing) {
1675 eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1676 detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1677 } else {
1678 eventPtr--;
1679 detailPtr--;
1680 }
1681 ringCount--;
1682 }
1683
1684 /*
1685 * This sequence matches. If we've already got another match,
1686 * pick whichever is most specific. Detail is most important,
1687 * then needMods, then hateMods.
1688 */
1689
1690 if (bestPtr != NULL) {
1691 register Pattern *patPtr2;
1692 int i;
1693
1694 if (psPtr->numPats != bestPtr->numPats) {
1695 if (bestPtr->numPats > psPtr->numPats) {
1696 goto nextSequence;
1697 } else {
1698 goto newBest;
1699 }
1700 }
1701 for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
1702 i < psPtr->numPats; i++,patPtr++, patPtr2++) {
1703 if (patPtr->detail != patPtr2->detail) {
1704 if (patPtr->detail == 0) {
1705 goto nextSequence;
1706 } else {
1707 goto newBest;
1708 }
1709 }
1710 if (patPtr->needMods != patPtr2->needMods) {
1711 if ((patPtr->needMods & patPtr2->needMods)
1712 == patPtr->needMods) {
1713 goto nextSequence;
1714 } else {
1715 goto newBest;
1716 }
1717 }
1718 if (patPtr->hateMods != patPtr2->hateMods) {
1719 if ((patPtr->hateMods & patPtr2->hateMods)
1720 == patPtr2->hateMods) {
1721 goto newBest;
1722 } else {
1723 goto nextSequence;
1724 }
1725 }
1726 }
1727 goto nextSequence; /* Tie goes to newest pattern. */
1728 }
1729 newBest:
1730 bestPtr = psPtr;
1731
1732 nextSequence: continue;
1733 }
1734 return bestPtr;
1735 }
1736 \f
1737 /*
1738 *--------------------------------------------------------------
1739 *
1740 * ExpandPercents --
1741 *
1742 * Given a command and an event, produce a new command
1743 * by replacing % constructs in the original command
1744 * with information from the X event.
1745 *
1746 * Results:
1747 * The return result is a pointer to the new %-substituted
1748 * command. If the command fits in the space at after, then
1749 * the return value is after. If the command is too large
1750 * to fit at after, then the return value is a pointer to
1751 * a malloc-ed buffer holding the command; in this case it
1752 * is the caller's responsibility to free up the buffer when
1753 * finished with it.
1754 *
1755 * Side effects:
1756 * None.
1757 *
1758 *--------------------------------------------------------------
1759 */
1760
1761 static char *
1762 ExpandPercents(before, eventPtr, keySym, after, afterSize)
1763 register char *before; /* Command containing percent
1764 * expressions to be replaced. */
1765 register XEvent *eventPtr; /* X event containing information
1766 * to be used in % replacements. */
1767 KeySym keySym; /* KeySym: only relevant for
1768 * KeyPress and KeyRelease events). */
1769 char *after; /* Place to generate new expanded
1770 * command. Must contain at least
1771 * "afterSize" bytes of space. */
1772 int afterSize; /* Number of bytes of space available at
1773 * after. */
1774 {
1775 register char *buffer; /* Pointer to buffer currently being used
1776 * as destination. */
1777 register char *dst; /* Pointer to next place to store character
1778 * in substituted string. */
1779 int spaceLeft; /* Indicates how many more non-null bytes
1780 * may be stored at *dst before space
1781 * runs out. */
1782 int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1783 * list element. */
1784 int number, flags;
1785 #define NUM_SIZE 40
1786 register char *string;
1787 char numStorage[NUM_SIZE+1];
1788
1789 if (eventPtr->type < LASTEvent) {
1790 flags = flagArray[eventPtr->type];
1791 } else {
1792 flags = 0;
1793 }
1794 dst = buffer = after;
1795 spaceLeft = afterSize - 1;
1796 while (*before != 0) {
1797 if (*before != '%') {
1798
1799 /*
1800 * Expand the destination string if necessary.
1801 */
1802
1803 if (spaceLeft <= 0) {
1804 char *newSpace;
1805
1806 newSpace = (char *) ckalloc((unsigned) (2*afterSize));
1807 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
1808 afterSize *= 2;
1809 dst = newSpace + (dst - buffer);
1810 if (buffer != after) {
1811 ckfree(buffer);
1812 }
1813 buffer = newSpace;
1814 spaceLeft = afterSize - (dst-buffer) - 1;
1815 }
1816 *dst = *before;
1817 dst++;
1818 before++;
1819 spaceLeft--;
1820 continue;
1821 }
1822
1823 number = 0;
1824 string = "??";
1825 switch (before[1]) {
1826 case '#':
1827 number = eventPtr->xany.serial;
1828 goto doNumber;
1829 case 'a':
1830 number = (int) eventPtr->xconfigure.above;
1831 goto doNumber;
1832 case 'b':
1833 number = eventPtr->xbutton.button;
1834 goto doNumber;
1835 case 'c':
1836 if (flags & EXPOSE) {
1837 number = eventPtr->xexpose.count;
1838 } else if (flags & MAPPING) {
1839 number = eventPtr->xmapping.count;
1840 }
1841 goto doNumber;
1842 case 'd':
1843 if (flags & (CROSSING|FOCUS)) {
1844 switch (eventPtr->xcrossing.detail) {
1845 case NotifyAncestor:
1846 string = "NotifyAncestor";
1847 break;
1848 case NotifyVirtual:
1849 string = "NotifyVirtual";
1850 break;
1851 case NotifyInferior:
1852 string = "NotifyInferior";
1853 break;
1854 case NotifyNonlinear:
1855 string = "NotifyNonlinear";
1856 break;
1857 case NotifyNonlinearVirtual:
1858 string = "NotifyNonlinearVirtual";
1859 break;
1860 case NotifyPointer:
1861 string = "NotifyPointer";
1862 break;
1863 case NotifyPointerRoot:
1864 string = "NotifyPointerRoot";
1865 break;
1866 case NotifyDetailNone:
1867 string = "NotifyDetailNone";
1868 break;
1869 }
1870 } else if (flags & CONFIG_REQ) {
1871 switch (eventPtr->xconfigurerequest.detail) {
1872 case Above:
1873 string = "Above";
1874 break;
1875 case Below:
1876 string = "Below";
1877 break;
1878 case TopIf:
1879 string = "TopIf";
1880 break;
1881 case BottomIf:
1882 string = "BottomIf";
1883 break;
1884 case Opposite:
1885 string = "Opposite";
1886 break;
1887 }
1888 }
1889 goto doString;
1890 case 'f':
1891 number = eventPtr->xcrossing.focus;
1892 goto doNumber;
1893 case 'h':
1894 if (flags & EXPOSE) {
1895 number = eventPtr->xexpose.height;
1896 } else if (flags & (CONFIG|CONFIG_REQ)) {
1897 number = eventPtr->xconfigure.height;
1898 } else if (flags & RESIZE_REQ) {
1899 number = eventPtr->xresizerequest.height;
1900 }
1901 goto doNumber;
1902 case 'k':
1903 number = eventPtr->xkey.keycode;
1904 goto doNumber;
1905 case 'm':
1906 if (flags & CROSSING) {
1907 number = eventPtr->xcrossing.mode;
1908 } else if (flags & FOCUS) {
1909 number = eventPtr->xfocus.mode;
1910 }
1911 switch (number) {
1912 case NotifyNormal:
1913 string = "NotifyNormal";
1914 break;
1915 case NotifyGrab:
1916 string = "NotifyGrab";
1917 break;
1918 case NotifyUngrab:
1919 string = "NotifyUngrab";
1920 break;
1921 case NotifyWhileGrabbed:
1922 string = "NotifyWhileGrabbed";
1923 break;
1924 }
1925 goto doString;
1926 case 'o':
1927 if (flags & CREATE) {
1928 number = eventPtr->xcreatewindow.override_redirect;
1929 } else if (flags & MAP) {
1930 number = eventPtr->xmap.override_redirect;
1931 } else if (flags & REPARENT) {
1932 number = eventPtr->xreparent.override_redirect;
1933 } else if (flags & CONFIG) {
1934 number = eventPtr->xconfigure.override_redirect;
1935 }
1936 goto doNumber;
1937 case 'p':
1938 switch (eventPtr->xcirculate.place) {
1939 case PlaceOnTop:
1940 string = "PlaceOnTop";
1941 break;
1942 case PlaceOnBottom:
1943 string = "PlaceOnBottom";
1944 break;
1945 }
1946 goto doString;
1947 case 's':
1948 if (flags & KEY_BUTTON_MOTION) {
1949 number = eventPtr->xkey.state;
1950 } else if (flags & CROSSING) {
1951 number = eventPtr->xcrossing.state;
1952 } else if (flags & VISIBILITY) {
1953 switch (eventPtr->xvisibility.state) {
1954 case VisibilityUnobscured:
1955 string = "VisibilityUnobscured";
1956 break;
1957 case VisibilityPartiallyObscured:
1958 string = "VisibilityPartiallyObscured";
1959 break;
1960 case VisibilityFullyObscured:
1961 string = "VisibilityFullyObscured";
1962 break;
1963 }
1964 goto doString;
1965 }
1966 goto doNumber;
1967 case 't':
1968 if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
1969 number = (int) eventPtr->xkey.time;
1970 } else if (flags & SEL_REQ) {
1971 number = (int) eventPtr->xselectionrequest.time;
1972 } else if (flags & SEL_NOTIFY) {
1973 number = (int) eventPtr->xselection.time;
1974 }
1975 goto doNumber;
1976 case 'v':
1977 number = eventPtr->xconfigurerequest.value_mask;
1978 goto doNumber;
1979 case 'w':
1980 if (flags & EXPOSE) {
1981 number = eventPtr->xexpose.width;
1982 } else if (flags & (CONFIG|CONFIG_REQ)) {
1983 number = eventPtr->xconfigure.width;
1984 } else if (flags & RESIZE_REQ) {
1985 number = eventPtr->xresizerequest.width;
1986 }
1987 goto doNumber;
1988 case 'x':
1989 if (flags & KEY_BUTTON_MOTION) {
1990 number = eventPtr->xkey.x;
1991 } else if (flags & EXPOSE) {
1992 number = eventPtr->xexpose.x;
1993 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
1994 number = eventPtr->xcreatewindow.x;
1995 } else if (flags & REPARENT) {
1996 number = eventPtr->xreparent.x;
1997 } else if (flags & CROSSING) {
1998 number = eventPtr->xcrossing.x;
1999 }
2000 goto doNumber;
2001 case 'y':
2002 if (flags & KEY_BUTTON_MOTION) {
2003 number = eventPtr->xkey.y;
2004 } else if (flags & EXPOSE) {
2005 number = eventPtr->xexpose.y;
2006 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
2007 number = eventPtr->xcreatewindow.y;
2008 } else if (flags & REPARENT) {
2009 number = eventPtr->xreparent.y;
2010 } else if (flags & CROSSING) {
2011 number = eventPtr->xcrossing.y;
2012
2013 }
2014 goto doNumber;
2015 case 'A':
2016 if ((eventPtr->type == KeyPress)
2017 || (eventPtr->type == KeyRelease)) {
2018 int numChars;
2019
2020 numChars = XLookupString(&eventPtr->xkey, numStorage,
2021 NUM_SIZE, (KeySym *) NULL,
2022 (XComposeStatus *) NULL);
2023 numStorage[numChars] = '\0';
2024 string = numStorage;
2025 }
2026 goto doString;
2027 case 'B':
2028 number = eventPtr->xcreatewindow.border_width;
2029 goto doNumber;
2030 case 'D':
2031 number = (int) eventPtr->xany.display;
2032 goto doNumber;
2033 case 'E':
2034 number = (int) eventPtr->xany.send_event;
2035 goto doNumber;
2036 case 'K':
2037 if ((eventPtr->type == KeyPress)
2038 || (eventPtr->type == KeyRelease)) {
2039 register KeySymInfo *kPtr;
2040
2041 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
2042 if (kPtr->value == keySym) {
2043 string = kPtr->name;
2044 break;
2045 }
2046 }
2047 }
2048 goto doString;
2049 case 'N':
2050 number = (int) keySym;
2051 goto doNumber;
2052 case 'R':
2053 number = (int) eventPtr->xkey.root;
2054 goto doNumber;
2055 case 'S':
2056 number = (int) eventPtr->xkey.subwindow;
2057 goto doNumber;
2058 case 'T':
2059 number = eventPtr->type;
2060 goto doNumber;
2061 case 'W': {
2062 TkWindow *winPtr;
2063
2064 if (XFindContext(eventPtr->xany.display, eventPtr->xany.window,
2065 tkWindowContext, (void *) &winPtr) == 0) {
2066 string = winPtr->pathName;
2067 } else {
2068 string = "??";
2069 }
2070 goto doString;
2071 }
2072 case 'X':
2073 number = eventPtr->xkey.x_root;
2074 goto doNumber;
2075 case 'Y':
2076 number = eventPtr->xkey.y_root;
2077 goto doNumber;
2078 default:
2079 numStorage[0] = before[1];
2080 numStorage[1] = '\0';
2081 string = numStorage;
2082 goto doString;
2083 }
2084
2085 doNumber:
2086 sprintf(numStorage, "%d", number);
2087 string = numStorage;
2088
2089 doString:
2090 spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2091 if (spaceNeeded >= spaceLeft) {
2092 char *newSpace;
2093
2094 newSpace = (char *) ckalloc((unsigned)
2095 (afterSize + spaceNeeded + 50));
2096 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
2097 afterSize += spaceNeeded + 50;
2098 dst = newSpace + (dst - buffer);
2099 if (buffer != after) {
2100 ckfree(buffer);
2101 }
2102 buffer = newSpace;
2103 spaceLeft = afterSize - (dst-buffer) - 1;
2104 }
2105 spaceNeeded = Tcl_ConvertElement(string, dst,
2106 cvtFlags | TCL_DONT_USE_BRACES);
2107 dst += spaceNeeded;
2108 spaceLeft -= spaceNeeded;
2109 before += 2;
2110 }
2111 *dst = '\0';
2112 return buffer;
2113 }
2114 \f
2115 /*
2116 *----------------------------------------------------------------------
2117 *
2118 * TkBindError --
2119 *
2120 * This procedure is invoked to handle errors that occur in Tcl
2121 * commands that are invoked in "background" (e.g. from event or
2122 * timer bindings).
2123 *
2124 * Results:
2125 * None.
2126 *
2127 * Side effects:
2128 * The command "tkerror" is invoked to process the error, passing
2129 * it the error message. If that fails, then an error message
2130 * is output on stderr.
2131 *
2132 *----------------------------------------------------------------------
2133 */
2134
2135 void
2136 TkBindError(interp)
2137 Tcl_Interp *interp; /* Interpreter in which an error has
2138 * occurred. */
2139 {
2140 char *argv[2];
2141 char *command;
2142 char *error;
2143 char *errorInfo, *tmp;
2144 int result;
2145
2146 error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1));
2147 strcpy(error, interp->result);
2148 tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2149 if (tmp == NULL) {
2150 errorInfo = error;
2151 } else {
2152 errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1));
2153 strcpy(errorInfo, tmp);
2154 }
2155 argv[0] = "tkerror";
2156 argv[1] = error;
2157 command = Tcl_Merge(2, argv);
2158 result = Tcl_GlobalEval(interp, command);
2159 if (result != TCL_OK) {
2160 if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2161 fprintf(stderr, "%s\n", errorInfo);
2162 } else {
2163 fprintf(stderr, "tkerror failed to handle background error.\n");
2164 fprintf(stderr, " Original error: %s\n", error);
2165 fprintf(stderr, " Error in tkerror: %s\n", interp->result);
2166 }
2167 }
2168 Tcl_ResetResult(interp);
2169 ckfree(command);
2170 ckfree(error);
2171 if (errorInfo != error) {
2172 ckfree(errorInfo);
2173 }
2174 }
Impressum, Datenschutz