|
|
1.1 root 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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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 ((state & patPtr->hateMods) != 0) {
1646: goto nextSequence;
1647: }
1648: if ((patPtr->detail != 0)
1649: && (patPtr->detail != *detailPtr)) {
1650: goto nextSequence;
1651: }
1652: if (psPtr->flags & PAT_NEARBY) {
1653: register XEvent *firstPtr;
1654:
1655: firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
1656: if ((firstPtr->xkey.x_root
1657: < (eventPtr->xkey.x_root - NEARBY_PIXELS))
1658: || (firstPtr->xkey.x_root
1659: > (eventPtr->xkey.x_root + NEARBY_PIXELS))
1660: || (firstPtr->xkey.y_root
1661: < (eventPtr->xkey.y_root - NEARBY_PIXELS))
1662: || (firstPtr->xkey.y_root
1663: > (eventPtr->xkey.y_root + NEARBY_PIXELS))
1664: || (firstPtr->xkey.time
1665: > (eventPtr->xkey.time + NEARBY_MS))) {
1666: goto nextSequence;
1667: }
1668: }
1669: patPtr++;
1670: patCount--;
1671: nextEvent:
1672: if (eventPtr == bindPtr->eventRing) {
1673: eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1674: detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1675: } else {
1676: eventPtr--;
1677: detailPtr--;
1678: }
1679: ringCount--;
1680: }
1681:
1682: /*
1683: * This sequence matches. If we've already got another match,
1684: * pick whichever is most specific. Detail is most important,
1685: * then needMods, then hateMods.
1686: */
1687:
1688: if (bestPtr != NULL) {
1689: register Pattern *patPtr2;
1690: int i;
1691:
1692: if (psPtr->numPats != bestPtr->numPats) {
1693: if (bestPtr->numPats > psPtr->numPats) {
1694: goto nextSequence;
1695: } else {
1696: goto newBest;
1697: }
1698: }
1699: for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
1700: i < psPtr->numPats; i++,patPtr++, patPtr2++) {
1701: if (patPtr->detail != patPtr2->detail) {
1702: if (patPtr->detail == 0) {
1703: goto nextSequence;
1704: } else {
1705: goto newBest;
1706: }
1707: }
1708: if (patPtr->needMods != patPtr2->needMods) {
1709: if ((patPtr->needMods & patPtr2->needMods)
1710: == patPtr->needMods) {
1711: goto nextSequence;
1712: } else {
1713: goto newBest;
1714: }
1715: }
1716: if (patPtr->hateMods != patPtr2->hateMods) {
1717: if ((patPtr->hateMods & patPtr2->hateMods)
1718: == patPtr2->hateMods) {
1719: goto newBest;
1720: } else {
1721: goto nextSequence;
1722: }
1723: }
1724: }
1725: goto nextSequence; /* Tie goes to newest pattern. */
1726: }
1727: newBest:
1728: bestPtr = psPtr;
1729:
1730: nextSequence: continue;
1731: }
1732: return bestPtr;
1733: }
1734:
1735: /*
1736: *--------------------------------------------------------------
1737: *
1738: * ExpandPercents --
1739: *
1740: * Given a command and an event, produce a new command
1741: * by replacing % constructs in the original command
1742: * with information from the X event.
1743: *
1744: * Results:
1745: * The return result is a pointer to the new %-substituted
1746: * command. If the command fits in the space at after, then
1747: * the return value is after. If the command is too large
1748: * to fit at after, then the return value is a pointer to
1749: * a malloc-ed buffer holding the command; in this case it
1750: * is the caller's responsibility to free up the buffer when
1751: * finished with it.
1752: *
1753: * Side effects:
1754: * None.
1755: *
1756: *--------------------------------------------------------------
1757: */
1758:
1759: static char *
1760: ExpandPercents(before, eventPtr, keySym, after, afterSize)
1761: register char *before; /* Command containing percent
1762: * expressions to be replaced. */
1763: register XEvent *eventPtr; /* X event containing information
1764: * to be used in % replacements. */
1765: KeySym keySym; /* KeySym: only relevant for
1766: * KeyPress and KeyRelease events). */
1767: char *after; /* Place to generate new expanded
1768: * command. Must contain at least
1769: * "afterSize" bytes of space. */
1770: int afterSize; /* Number of bytes of space available at
1771: * after. */
1772: {
1773: register char *buffer; /* Pointer to buffer currently being used
1774: * as destination. */
1775: register char *dst; /* Pointer to next place to store character
1776: * in substituted string. */
1777: int spaceLeft; /* Indicates how many more non-null bytes
1778: * may be stored at *dst before space
1779: * runs out. */
1780: int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1781: * list element. */
1782: int number, flags;
1783: #define NUM_SIZE 40
1784: register char *string;
1785: char numStorage[NUM_SIZE+1];
1786:
1787: if (eventPtr->type < LASTEvent) {
1788: flags = flagArray[eventPtr->type];
1789: } else {
1790: flags = 0;
1791: }
1792: dst = buffer = after;
1793: spaceLeft = afterSize - 1;
1794: while (*before != 0) {
1795: if (*before != '%') {
1796:
1797: /*
1798: * Expand the destination string if necessary.
1799: */
1800:
1801: if (spaceLeft <= 0) {
1802: char *newSpace;
1803:
1804: newSpace = (char *) ckalloc((unsigned) (2*afterSize));
1805: memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
1806: afterSize *= 2;
1807: dst = newSpace + (dst - buffer);
1808: if (buffer != after) {
1809: ckfree(buffer);
1810: }
1811: buffer = newSpace;
1812: spaceLeft = afterSize - (dst-buffer) - 1;
1813: }
1814: *dst = *before;
1815: dst++;
1816: before++;
1817: spaceLeft--;
1818: continue;
1819: }
1820:
1821: number = 0;
1822: string = "??";
1823: switch (before[1]) {
1824: case '#':
1825: number = eventPtr->xany.serial;
1826: goto doNumber;
1827: case 'a':
1828: number = (int) eventPtr->xconfigure.above;
1829: goto doNumber;
1830: case 'b':
1831: number = eventPtr->xbutton.button;
1832: goto doNumber;
1833: case 'c':
1834: if (flags & EXPOSE) {
1835: number = eventPtr->xexpose.count;
1836: } else if (flags & MAPPING) {
1837: number = eventPtr->xmapping.count;
1838: }
1839: goto doNumber;
1840: case 'd':
1841: if (flags & (CROSSING|FOCUS)) {
1842: switch (eventPtr->xcrossing.detail) {
1843: case NotifyAncestor:
1844: string = "NotifyAncestor";
1845: break;
1846: case NotifyVirtual:
1847: string = "NotifyVirtual";
1848: break;
1849: case NotifyInferior:
1850: string = "NotifyInferior";
1851: break;
1852: case NotifyNonlinear:
1853: string = "NotifyNonlinear";
1854: break;
1855: case NotifyNonlinearVirtual:
1856: string = "NotifyNonlinearVirtual";
1857: break;
1858: case NotifyPointer:
1859: string = "NotifyPointer";
1860: break;
1861: case NotifyPointerRoot:
1862: string = "NotifyPointerRoot";
1863: break;
1864: case NotifyDetailNone:
1865: string = "NotifyDetailNone";
1866: break;
1867: }
1868: } else if (flags & CONFIG_REQ) {
1869: switch (eventPtr->xconfigurerequest.detail) {
1870: case Above:
1871: string = "Above";
1872: break;
1873: case Below:
1874: string = "Below";
1875: break;
1876: case TopIf:
1877: string = "TopIf";
1878: break;
1879: case BottomIf:
1880: string = "BottomIf";
1881: break;
1882: case Opposite:
1883: string = "Opposite";
1884: break;
1885: }
1886: }
1887: goto doString;
1888: case 'f':
1889: number = eventPtr->xcrossing.focus;
1890: goto doNumber;
1891: case 'h':
1892: if (flags & EXPOSE) {
1893: number = eventPtr->xexpose.height;
1894: } else if (flags & (CONFIG|CONFIG_REQ)) {
1895: number = eventPtr->xconfigure.height;
1896: } else if (flags & RESIZE_REQ) {
1897: number = eventPtr->xresizerequest.height;
1898: }
1899: goto doNumber;
1900: case 'k':
1901: number = eventPtr->xkey.keycode;
1902: goto doNumber;
1903: case 'm':
1904: if (flags & CROSSING) {
1905: number = eventPtr->xcrossing.mode;
1906: } else if (flags & FOCUS) {
1907: number = eventPtr->xfocus.mode;
1908: }
1909: switch (number) {
1910: case NotifyNormal:
1911: string = "NotifyNormal";
1912: break;
1913: case NotifyGrab:
1914: string = "NotifyGrab";
1915: break;
1916: case NotifyUngrab:
1917: string = "NotifyUngrab";
1918: break;
1919: case NotifyWhileGrabbed:
1920: string = "NotifyWhileGrabbed";
1921: break;
1922: }
1923: goto doString;
1924: case 'o':
1925: if (flags & CREATE) {
1926: number = eventPtr->xcreatewindow.override_redirect;
1927: } else if (flags & MAP) {
1928: number = eventPtr->xmap.override_redirect;
1929: } else if (flags & REPARENT) {
1930: number = eventPtr->xreparent.override_redirect;
1931: } else if (flags & CONFIG) {
1932: number = eventPtr->xconfigure.override_redirect;
1933: }
1934: goto doNumber;
1935: case 'p':
1936: switch (eventPtr->xcirculate.place) {
1937: case PlaceOnTop:
1938: string = "PlaceOnTop";
1939: break;
1940: case PlaceOnBottom:
1941: string = "PlaceOnBottom";
1942: break;
1943: }
1944: goto doString;
1945: case 's':
1946: if (flags & KEY_BUTTON_MOTION) {
1947: number = eventPtr->xkey.state;
1948: } else if (flags & CROSSING) {
1949: number = eventPtr->xcrossing.state;
1950: } else if (flags & VISIBILITY) {
1951: switch (eventPtr->xvisibility.state) {
1952: case VisibilityUnobscured:
1953: string = "VisibilityUnobscured";
1954: break;
1955: case VisibilityPartiallyObscured:
1956: string = "VisibilityPartiallyObscured";
1957: break;
1958: case VisibilityFullyObscured:
1959: string = "VisibilityFullyObscured";
1960: break;
1961: }
1962: goto doString;
1963: }
1964: goto doNumber;
1965: case 't':
1966: if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
1967: number = (int) eventPtr->xkey.time;
1968: } else if (flags & SEL_REQ) {
1969: number = (int) eventPtr->xselectionrequest.time;
1970: } else if (flags & SEL_NOTIFY) {
1971: number = (int) eventPtr->xselection.time;
1972: }
1973: goto doNumber;
1974: case 'v':
1975: number = eventPtr->xconfigurerequest.value_mask;
1976: goto doNumber;
1977: case 'w':
1978: if (flags & EXPOSE) {
1979: number = eventPtr->xexpose.width;
1980: } else if (flags & (CONFIG|CONFIG_REQ)) {
1981: number = eventPtr->xconfigure.width;
1982: } else if (flags & RESIZE_REQ) {
1983: number = eventPtr->xresizerequest.width;
1984: }
1985: goto doNumber;
1986: case 'x':
1987: if (flags & KEY_BUTTON_MOTION) {
1988: number = eventPtr->xkey.x;
1989: } else if (flags & EXPOSE) {
1990: number = eventPtr->xexpose.x;
1991: } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
1992: number = eventPtr->xcreatewindow.x;
1993: } else if (flags & REPARENT) {
1994: number = eventPtr->xreparent.x;
1995: } else if (flags & CROSSING) {
1996: number = eventPtr->xcrossing.x;
1997: }
1998: goto doNumber;
1999: case 'y':
2000: if (flags & KEY_BUTTON_MOTION) {
2001: number = eventPtr->xkey.y;
2002: } else if (flags & EXPOSE) {
2003: number = eventPtr->xexpose.y;
2004: } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
2005: number = eventPtr->xcreatewindow.y;
2006: } else if (flags & REPARENT) {
2007: number = eventPtr->xreparent.y;
2008: } else if (flags & CROSSING) {
2009: number = eventPtr->xcrossing.y;
2010:
2011: }
2012: goto doNumber;
2013: case 'A':
2014: if ((eventPtr->type == KeyPress)
2015: || (eventPtr->type == KeyRelease)) {
2016: int numChars;
2017:
2018: numChars = XLookupString(&eventPtr->xkey, numStorage,
2019: NUM_SIZE, (KeySym *) NULL,
2020: (XComposeStatus *) NULL);
2021: numStorage[numChars] = '\0';
2022: string = numStorage;
2023: }
2024: goto doString;
2025: case 'B':
2026: number = eventPtr->xcreatewindow.border_width;
2027: goto doNumber;
2028: case 'D':
2029: number = (int) eventPtr->xany.display;
2030: goto doNumber;
2031: case 'E':
2032: number = (int) eventPtr->xany.send_event;
2033: goto doNumber;
2034: case 'K':
2035: if ((eventPtr->type == KeyPress)
2036: || (eventPtr->type == KeyRelease)) {
2037: register KeySymInfo *kPtr;
2038:
2039: for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
2040: if (kPtr->value == keySym) {
2041: string = kPtr->name;
2042: break;
2043: }
2044: }
2045: }
2046: goto doString;
2047: case 'N':
2048: number = (int) keySym;
2049: goto doNumber;
2050: case 'R':
2051: number = (int) eventPtr->xkey.root;
2052: goto doNumber;
2053: case 'S':
2054: number = (int) eventPtr->xkey.subwindow;
2055: goto doNumber;
2056: case 'T':
2057: number = eventPtr->type;
2058: goto doNumber;
2059: case 'W': {
2060: TkWindow *winPtr;
2061:
2062: if (XFindContext(eventPtr->xany.display, eventPtr->xany.window,
2063: tkWindowContext, (void *) &winPtr) == 0) {
2064: string = winPtr->pathName;
2065: } else {
2066: string = "??";
2067: }
2068: goto doString;
2069: }
2070: case 'X':
2071: number = eventPtr->xkey.x_root;
2072: goto doNumber;
2073: case 'Y':
2074: number = eventPtr->xkey.y_root;
2075: goto doNumber;
2076: default:
2077: numStorage[0] = before[1];
2078: numStorage[1] = '\0';
2079: string = numStorage;
2080: goto doString;
2081: }
2082:
2083: doNumber:
2084: sprintf(numStorage, "%d", number);
2085: string = numStorage;
2086:
2087: doString:
2088: spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2089: if (spaceNeeded >= spaceLeft) {
2090: char *newSpace;
2091:
2092: newSpace = (char *) ckalloc((unsigned)
2093: (afterSize + spaceNeeded + 50));
2094: memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
2095: afterSize += spaceNeeded + 50;
2096: dst = newSpace + (dst - buffer);
2097: if (buffer != after) {
2098: ckfree(buffer);
2099: }
2100: buffer = newSpace;
2101: spaceLeft = afterSize - (dst-buffer) - 1;
2102: }
2103: spaceNeeded = Tcl_ConvertElement(string, dst,
2104: cvtFlags | TCL_DONT_USE_BRACES);
2105: dst += spaceNeeded;
2106: spaceLeft -= spaceNeeded;
2107: before += 2;
2108: }
2109: *dst = '\0';
2110: return buffer;
2111: }
2112:
2113: /*
2114: *----------------------------------------------------------------------
2115: *
2116: * TkBindError --
2117: *
2118: * This procedure is invoked to handle errors that occur in Tcl
2119: * commands that are invoked in "background" (e.g. from event or
2120: * timer bindings).
2121: *
2122: * Results:
2123: * None.
2124: *
2125: * Side effects:
2126: * The command "tkerror" is invoked to process the error, passing
2127: * it the error message. If that fails, then an error message
2128: * is output on stderr.
2129: *
2130: *----------------------------------------------------------------------
2131: */
2132:
2133: void
2134: TkBindError(interp)
2135: Tcl_Interp *interp; /* Interpreter in which an error has
2136: * occurred. */
2137: {
2138: char *argv[2];
2139: char *command;
2140: char *error;
2141: char *errorInfo, *tmp;
2142: int result;
2143:
2144: error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1));
2145: strcpy(error, interp->result);
2146: tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2147: if (tmp == NULL) {
2148: errorInfo = error;
2149: } else {
2150: errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1));
2151: strcpy(errorInfo, tmp);
2152: }
2153: argv[0] = "tkerror";
2154: argv[1] = error;
2155: command = Tcl_Merge(2, argv);
2156: result = Tcl_GlobalEval(interp, command);
2157: if (result != TCL_OK) {
2158: if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2159: fprintf(stderr, "%s\n", errorInfo);
2160: } else {
2161: fprintf(stderr, "tkerror failed to handle background error.\n");
2162: fprintf(stderr, " Original error: %s\n", error);
2163: fprintf(stderr, " Error in tkerror: %s\n", interp->result);
2164: }
2165: }
2166: Tcl_ResetResult(interp);
2167: ckfree(command);
2168: ckfree(error);
2169: if (errorInfo != error) {
2170: ckfree(errorInfo);
2171: }
2172: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.