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