]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkbind.c
Fixes for compilation with gcc 15
[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 (
408 Tcl_Interp *interp /* Interpreter to associate with the binding
409 * table: commands are executed in this
410 * interpreter. */
411 )
412 {
413 register BindingTable *bindPtr;
414 int i;
415
416 /*
417 * If this is the first time a binding table has been created,
418 * initialize the global data structures.
419 */
420
421 if (!initialized) {
422 register KeySymInfo *kPtr;
423 register Tcl_HashEntry *hPtr;
424 register ModInfo *modPtr;
425 register EventInfo *eiPtr;
426 int dummy;
427
428 initialized = 1;
429
430 Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
431 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
432 hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
433 Tcl_SetHashValue(hPtr, kPtr->value);
434 }
435
436 Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
437 for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
438 hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
439 Tcl_SetHashValue(hPtr, modPtr);
440 }
441
442 Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
443 for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
444 hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
445 Tcl_SetHashValue(hPtr, eiPtr);
446 }
447 }
448
449 /*
450 * Create and initialize a new binding table.
451 */
452
453 bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
454 for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
455 bindPtr->eventRing[i].type = -1;
456 }
457 bindPtr->curEvent = 0;
458 Tcl_InitHashTable(&bindPtr->patternTable,
459 sizeof(PatternTableKey)/sizeof(int));
460 Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
461 bindPtr->interp = interp;
462 return (Tk_BindingTable) bindPtr;
463 }
464 \f
465 /*
466 *--------------------------------------------------------------
467 *
468 * Tk_DeleteBindingTable --
469 *
470 * Destroy a binding table and free up all its memory.
471 * The caller should not use bindingTable again after
472 * this procedure returns.
473 *
474 * Results:
475 * None.
476 *
477 * Side effects:
478 * Memory is freed.
479 *
480 *--------------------------------------------------------------
481 */
482
483 void
484 Tk_DeleteBindingTable (
485 Tk_BindingTable bindingTable /* Token for the binding table to
486 * destroy. */
487 )
488 {
489 BindingTable *bindPtr = (BindingTable *) bindingTable;
490 PatSeq *psPtr, *nextPtr;
491 Tcl_HashEntry *hPtr;
492 Tcl_HashSearch search;
493
494 /*
495 * Find and delete all of the patterns associated with the binding
496 * table.
497 */
498
499 for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
500 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
501 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
502 psPtr != NULL; psPtr = nextPtr) {
503 nextPtr = psPtr->nextSeqPtr;
504 Tk_EventuallyFree((ClientData) psPtr->command,
505 (Tk_FreeProc *) free);
506 ckfree((char *) psPtr);
507 }
508 }
509
510 /*
511 * Clean up the rest of the information associated with the
512 * binding table.
513 */
514
515 Tcl_DeleteHashTable(&bindPtr->patternTable);
516 Tcl_DeleteHashTable(&bindPtr->objectTable);
517 ckfree((char *) bindPtr);
518 }
519 \f
520 /*
521 *--------------------------------------------------------------
522 *
523 * Tk_CreateBinding --
524 *
525 * Add a binding to a binding table, so that future calls to
526 * Tk_BindEvent may execute the command in the binding.
527 *
528 * Results:
529 * The return value is 0 if an error occurred while setting
530 * up the binding. In this case, an error message will be
531 * left in interp->result. If all went well then the return
532 * value is a mask of the event types that must be made
533 * available to Tk_BindEvent in order to properly detect when
534 * this binding triggers. This value can be used to determine
535 * what events to select for in a window, for example.
536 *
537 * Side effects:
538 * The new binding may cause future calls to Tk_BindEvent to
539 * behave differently than they did previously.
540 *
541 *--------------------------------------------------------------
542 */
543
544 unsigned long
545 Tk_CreateBinding (
546 Tcl_Interp *interp, /* Used for error reporting. */
547 Tk_BindingTable bindingTable, /* Table in which to create binding. */
548 ClientData object, /* Token for object with which binding
549 * is associated. */
550 char *eventString, /* String describing event sequence
551 * that triggers binding. */
552 char *command, /* Contains Tcl command to execute
553 * when binding triggers. */
554 int append /* 0 means replace any existing
555 * binding for eventString; 1 means
556 * append to that binding. */
557 )
558 {
559 BindingTable *bindPtr = (BindingTable *) bindingTable;
560 register PatSeq *psPtr;
561 unsigned long eventMask;
562
563 psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask);
564 if (psPtr == NULL) {
565 return 0;
566 }
567 if (append && (psPtr->command != NULL)) {
568 int length;
569 char *new;
570
571 length = strlen(psPtr->command) + strlen(command) + 3;
572 new = (char *) ckalloc((unsigned) length);
573 sprintf(new, "%s; %s", psPtr->command, command);
574 Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
575 psPtr->command = new;
576 } else {
577 if (psPtr->command != NULL) {
578 Tk_EventuallyFree((ClientData) psPtr->command,
579 (Tk_FreeProc *) free);
580 }
581 psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
582 strcpy(psPtr->command, command);
583 }
584
585 /*
586 * See if the command contains percents and thereby requires
587 * percent substitution.
588 */
589
590 if (strchr(psPtr->command, '%') != NULL) {
591 psPtr->flags |= PAT_PERCENTS;
592 }
593 return eventMask;
594 }
595 \f
596 /*
597 *--------------------------------------------------------------
598 *
599 * Tk_DeleteBinding --
600 *
601 * Remove an event binding from a binding table.
602 *
603 * Results:
604 * The result is a standard Tcl return value. If an error
605 * occurs then interp->result will contain an error message.
606 *
607 * Side effects:
608 * The binding given by object and eventString is removed
609 * from bindingTable.
610 *
611 *--------------------------------------------------------------
612 */
613
614 int
615 Tk_DeleteBinding (
616 Tcl_Interp *interp, /* Used for error reporting. */
617 Tk_BindingTable bindingTable, /* Table in which to delete binding. */
618 ClientData object, /* Token for object with which binding
619 * is associated. */
620 char *eventString /* String describing event sequence
621 * that triggers binding. */
622 )
623 {
624 BindingTable *bindPtr = (BindingTable *) bindingTable;
625 register PatSeq *psPtr, *prevPtr;
626 unsigned long eventMask;
627 Tcl_HashEntry *hPtr;
628
629 psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
630 if (psPtr == NULL) {
631 Tcl_ResetResult(interp);
632 return TCL_OK;
633 }
634
635 /*
636 * Unlink the binding from the list for its object, then from the
637 * list for its pattern.
638 */
639
640 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
641 if (hPtr == NULL) {
642 panic("Tk_DeleteBinding couldn't find object table entry");
643 }
644 prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
645 if (prevPtr == psPtr) {
646 Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
647 } else {
648 for ( ; ; prevPtr = prevPtr->nextObjPtr) {
649 if (prevPtr == NULL) {
650 panic("Tk_DeleteBinding couldn't find on object list");
651 }
652 if (prevPtr->nextObjPtr == psPtr) {
653 prevPtr->nextObjPtr = psPtr->nextObjPtr;
654 break;
655 }
656 }
657 }
658 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
659 if (prevPtr == psPtr) {
660 if (psPtr->nextSeqPtr == NULL) {
661 Tcl_DeleteHashEntry(psPtr->hPtr);
662 } else {
663 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
664 }
665 } else {
666 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
667 if (prevPtr == NULL) {
668 panic("Tk_DeleteBinding couldn't find on hash chain");
669 }
670 if (prevPtr->nextSeqPtr == psPtr) {
671 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
672 break;
673 }
674 }
675 }
676 Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
677 ckfree((char *) psPtr);
678 return TCL_OK;
679 }
680 \f
681 /*
682 *--------------------------------------------------------------
683 *
684 * Tk_GetBinding --
685 *
686 * Return the command associated with a given event string.
687 *
688 * Results:
689 * The return value is a pointer to the command string
690 * associated with eventString for object in the domain
691 * given by bindingTable. If there is no binding for
692 * eventString, or if eventString is improperly formed,
693 * then NULL is returned and an error message is left in
694 * interp->result. The return value is semi-static: it
695 * will persist until the binding is changed or deleted.
696 *
697 * Side effects:
698 * None.
699 *
700 *--------------------------------------------------------------
701 */
702
703 char *
704 Tk_GetBinding (
705 Tcl_Interp *interp, /* Interpreter for error reporting. */
706 Tk_BindingTable bindingTable, /* Table in which to look for
707 * binding. */
708 ClientData object, /* Token for object with which binding
709 * is associated. */
710 char *eventString /* String describing event sequence
711 * that triggers binding. */
712 )
713 {
714 BindingTable *bindPtr = (BindingTable *) bindingTable;
715 register PatSeq *psPtr;
716 unsigned long eventMask;
717
718 psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
719 if (psPtr == NULL) {
720 return NULL;
721 }
722 return psPtr->command;
723 }
724 \f
725 /*
726 *--------------------------------------------------------------
727 *
728 * Tk_GetAllBindings --
729 *
730 * Return a list of event strings for all the bindings
731 * associated with a given object.
732 *
733 * Results:
734 * There is no return value. Interp->result is modified to
735 * hold a Tcl list with one entry for each binding associated
736 * with object in bindingTable. Each entry in the list
737 * contains the event string associated with one binding.
738 *
739 * Side effects:
740 * None.
741 *
742 *--------------------------------------------------------------
743 */
744
745 void
746 Tk_GetAllBindings (
747 Tcl_Interp *interp, /* Interpreter for error reporting. */
748 Tk_BindingTable bindingTable, /* Table in which to look for
749 * bindings. */
750 ClientData object /* Token for object. */
751 )
752
753 {
754 BindingTable *bindPtr = (BindingTable *) bindingTable;
755 register PatSeq *psPtr;
756 register Pattern *patPtr;
757 Tcl_HashEntry *hPtr;
758 char string[200*EVENT_BUFFER_SIZE];
759 register char *p;
760 int patsLeft, needMods;
761 register ModInfo *modPtr;
762
763 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
764 if (hPtr == NULL) {
765 return;
766 }
767 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
768 psPtr = psPtr->nextObjPtr) {
769
770 p = string;
771
772 /*
773 * For each binding, output information about each of the
774 * patterns in its sequence. The order of the patterns in
775 * the sequence is backwards from the order in which they
776 * must be output.
777 */
778
779 for (patsLeft = psPtr->numPats,
780 patPtr = &psPtr->pats[psPtr->numPats - 1];
781 patsLeft > 0; patsLeft--, patPtr--) {
782
783 /*
784 * Check for simple case of an ASCII character.
785 */
786
787 if ((patPtr->eventType == KeyPress)
788 && (patPtr->needMods == 0)
789 && (patPtr->hateMods == ~ShiftMask)
790 && isascii(patPtr->detail) && isprint(patPtr->detail)
791 && (patPtr->detail != '<')
792 && (patPtr->detail != ' ')) {
793
794 *p = patPtr->detail;
795 p++;
796 continue;
797 }
798
799 /*
800 * It's a more general event specification. First check
801 * for "Double" or "Triple", then "Any", then modifiers,
802 * the event type, then keysym or button detail.
803 */
804
805 *p = '<';
806 p++;
807 if ((patsLeft > 1) && (memcmp((char *) patPtr,
808 (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
809 patsLeft--;
810 patPtr--;
811 if ((patsLeft > 1) && (memcmp((char *) patPtr,
812 (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
813 patsLeft--;
814 patPtr--;
815 strcpy(p, "Triple-");
816 } else {
817 strcpy(p, "Double-");
818 }
819 p += strlen(p);
820 }
821
822 if (patPtr->hateMods == 0) {
823 strcpy(p, "Any-");
824 p += strlen(p);
825 }
826
827 for (needMods = patPtr->needMods, modPtr = modArray;
828 needMods != 0; modPtr++) {
829 if (modPtr->mask & needMods) {
830 needMods &= ~modPtr->mask;
831 strcpy(p, modPtr->name);
832 p += strlen(p);
833 *p = '-';
834 p++;
835 }
836 }
837
838 if ((patPtr->eventType != KeyPress)
839 || (patPtr->detail == 0)) {
840 register EventInfo *eiPtr;
841
842 for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
843 if (eiPtr->type == patPtr->eventType) {
844 strcpy(p, eiPtr->name);
845 p += strlen(p);
846 if (patPtr->detail != 0) {
847 *p = '-';
848 p++;
849 }
850 break;
851 }
852 }
853 }
854
855 if (patPtr->detail != 0) {
856 if ((patPtr->eventType == KeyPress)
857 || (patPtr->eventType == KeyRelease)) {
858 register KeySymInfo *kPtr;
859
860 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
861 if (patPtr->detail == (int) kPtr->value) {
862 sprintf(p, "%.100s", kPtr->name);
863 p += strlen(p);
864 break;
865 }
866 }
867 } else {
868 sprintf(p, "%d", patPtr->detail);
869 p += strlen(p);
870 }
871 }
872 *p = '>';
873 p++;
874 }
875 *p = 0;
876 if ((p - string) >= sizeof(string)) {
877 panic("Tk_GetAllBindings overflowed buffer");
878 }
879 Tcl_AppendElement(interp, string, 0);
880 }
881 }
882 \f
883 /*
884 *--------------------------------------------------------------
885 *
886 * Tk_DeleteAllBindings --
887 *
888 * Remove all bindings associated with a given object in a
889 * given binding table.
890 *
891 * Results:
892 * All bindings associated with object are removed from
893 * bindingTable.
894 *
895 * Side effects:
896 * None.
897 *
898 *--------------------------------------------------------------
899 */
900
901 void
902 Tk_DeleteAllBindings (
903 Tk_BindingTable bindingTable, /* Table in which to delete
904 * bindings. */
905 ClientData object /* Token for object. */
906 )
907 {
908 BindingTable *bindPtr = (BindingTable *) bindingTable;
909 register PatSeq *psPtr, *prevPtr;
910 PatSeq *nextPtr;
911 Tcl_HashEntry *hPtr;
912
913 hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
914 if (hPtr == NULL) {
915 return;
916 }
917 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
918 psPtr = nextPtr) {
919 nextPtr = psPtr->nextObjPtr;
920
921 /*
922 * Be sure to remove each binding from its hash chain in the
923 * pattern table. If this is the last pattern in the chain,
924 * then delete the hash entry too.
925 */
926
927 prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
928 if (prevPtr == psPtr) {
929 if (psPtr->nextSeqPtr == NULL) {
930 Tcl_DeleteHashEntry(psPtr->hPtr);
931 } else {
932 Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
933 }
934 } else {
935 for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
936 if (prevPtr == NULL) {
937 panic("Tk_DeleteAllBindings couldn't find on hash chain");
938 }
939 if (prevPtr->nextSeqPtr == psPtr) {
940 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
941 break;
942 }
943 }
944 }
945 Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free);
946 ckfree((char *) psPtr);
947 }
948 Tcl_DeleteHashEntry(hPtr);
949 }
950 \f
951 /*
952 *--------------------------------------------------------------
953 *
954 * Tk_BindEvent --
955 *
956 * This procedure is invoked to process an X event. The
957 * event is added to those recorded for the binding table.
958 * Then each of the objects at *objectPtr is checked in
959 * order to see if it has a binding that matches the recent
960 * events. If so, that binding is invoked and the rest of
961 * objects are skipped.
962 *
963 * Results:
964 * None.
965 *
966 * Side effects:
967 * Depends on the command associated with the matching
968 * binding.
969 *
970 *--------------------------------------------------------------
971 */
972
973 void
974 Tk_BindEvent (
975 Tk_BindingTable bindingTable, /* Table in which to look for
976 * bindings. */
977 XEvent *eventPtr, /* What actually happened. */
978 Tk_Window tkwin, /* Window on display where event
979 * occurred (needed in order to
980 * locate display information). */
981 int numObjects, /* Number of objects at *objectPtr. */
982 ClientData *objectPtr /* Array of one or more objects
983 * to check for a matching binding. */
984 )
985 {
986 BindingTable *bindPtr = (BindingTable *) bindingTable;
987 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
988 XEvent *ringPtr;
989 PatSeq *matchPtr;
990 PatternTableKey key;
991 Tcl_HashEntry *hPtr;
992 int detail;
993
994 /*
995 * Add the new event to the ring of saved events for the
996 * binding table. Consecutive MotionNotify events get combined:
997 * if both the new event and the previous event are MotionNotify,
998 * then put the new event *on top* of the previous event.
999 */
1000
1001 if ((eventPtr->type != MotionNotify)
1002 || (bindPtr->eventRing[bindPtr->curEvent].type != MotionNotify)) {
1003 bindPtr->curEvent++;
1004 if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
1005 bindPtr->curEvent = 0;
1006 }
1007 }
1008 ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1009 memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
1010 detail = 0;
1011 bindPtr->detailRing[bindPtr->curEvent] = 0;
1012 if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
1013 detail = (int) GetKeySym(dispPtr, ringPtr);
1014 if (detail == NoSymbol) {
1015 detail = 0;
1016 }
1017 } else if ((ringPtr->type == ButtonPress)
1018 || (ringPtr->type == ButtonRelease)) {
1019 detail = ringPtr->xbutton.button;
1020 }
1021 bindPtr->detailRing[bindPtr->curEvent] = detail;
1022
1023 /*
1024 * Loop over all the objects, matching the new event against
1025 * each in turn.
1026 */
1027
1028 for ( ; numObjects > 0; numObjects--, objectPtr++) {
1029
1030 /*
1031 * Match the new event against those recorded in the
1032 * pattern table, saving the longest matching pattern.
1033 * For events with details (button and key events) first
1034 * look for a binding for the specific key or button.
1035 * If none is found, then look for a binding for all
1036 * keys or buttons (detail of 0).
1037 */
1038
1039 matchPtr = NULL;
1040 key.object = *objectPtr;
1041 key.type = ringPtr->type;
1042 key.detail = detail;
1043 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1044 if (hPtr != NULL) {
1045 matchPtr = MatchPatterns(dispPtr, bindPtr,
1046 (PatSeq *) Tcl_GetHashValue(hPtr));
1047 }
1048 if ((detail != 0) && (matchPtr == NULL)) {
1049 key.detail = 0;
1050 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1051 if (hPtr != NULL) {
1052 matchPtr = MatchPatterns(dispPtr, bindPtr,
1053 (PatSeq *) Tcl_GetHashValue(hPtr));
1054 }
1055 }
1056
1057 if (matchPtr != NULL) {
1058
1059 /*
1060 * %-substitution can increase the length of the command.
1061 * This code handles three cases: (a) no substitution;
1062 * (b) substitution results in short command (use space
1063 * on stack); and (c) substitution results in long
1064 * command (malloc it).
1065 */
1066
1067 #define STATIC_SPACE 200
1068 char shortSpace[STATIC_SPACE];
1069 int result;
1070
1071 if (matchPtr->flags & PAT_PERCENTS) {
1072 char *p;
1073 p = ExpandPercents(matchPtr->command, eventPtr,
1074 (KeySym) detail, shortSpace, STATIC_SPACE);
1075 result = Tcl_GlobalEval(bindPtr->interp, p);
1076 if (p != shortSpace) {
1077 ckfree(p);
1078 }
1079 } else {
1080 /*
1081 * The code below is tricky in order allow the binding to
1082 * be modified or deleted as part of the command that the
1083 * binding invokes. Must make sure that the actual command
1084 * string isn't freed until the command completes, and must
1085 * copy the address of this string into a local variable
1086 * in case it's modified by the command.
1087 */
1088
1089 char *cmd = matchPtr->command;
1090
1091 Tk_Preserve((ClientData) cmd);
1092 result = Tcl_GlobalEval(bindPtr->interp, cmd);
1093 Tk_Release((ClientData) cmd);
1094 }
1095 if (result != TCL_OK) {
1096 Tcl_AddErrorInfo(bindPtr->interp,
1097 "\n (command bound to event)");
1098 TkBindError(bindPtr->interp);
1099 }
1100 return;
1101 }
1102 }
1103 }
1104 \f
1105 /*
1106 *----------------------------------------------------------------------
1107 *
1108 * FindSequence --
1109 *
1110 * Find the entry in a binding table that corresponds to a
1111 * particular pattern string, and return a pointer to that
1112 * entry.
1113 *
1114 * Results:
1115 * The return value is normally a pointer to the PatSeq
1116 * in patternTable that corresponds to eventString. If an error
1117 * was found while parsing eventString, or if "create" is 0 and
1118 * no pattern sequence previously existed, then NULL is returned
1119 * and interp->result contains a message describing the problem.
1120 * If no pattern sequence previously existed for eventString, then
1121 * a new one is created with a NULL command field. In a successful
1122 * return, *maskPtr is filled in with a mask of the event types
1123 * on which the pattern sequence depends.
1124 *
1125 * Side effects:
1126 * A new pattern sequence may be created.
1127 *
1128 *----------------------------------------------------------------------
1129 */
1130
1131 static PatSeq *
1132 FindSequence (
1133 Tcl_Interp *interp, /* Interpreter to use for error
1134 * reporting. */
1135 BindingTable *bindPtr, /* Table to use for lookup. */
1136 ClientData object, /* Token for object(s) with which binding
1137 * is associated. */
1138 char *eventString, /* String description of pattern to
1139 * match on. See user documentation
1140 * for details. */
1141 int create, /* 0 means don't create the entry if
1142 * it doesn't already exist. Non-zero
1143 * means create. */
1144 unsigned long *maskPtr /* *maskPtr is filled in with the event
1145 * types on which this pattern sequence
1146 * depends. */
1147 )
1148
1149 {
1150 Pattern pats[EVENT_BUFFER_SIZE];
1151 int numPats;
1152 register char *p;
1153 register Pattern *patPtr;
1154 register PatSeq *psPtr;
1155 register Tcl_HashEntry *hPtr;
1156 #define FIELD_SIZE 20
1157 char field[FIELD_SIZE];
1158 int flags, any, count, new, sequenceSize;
1159 unsigned long eventMask;
1160 PatternTableKey key;
1161
1162 /*
1163 *-------------------------------------------------------------
1164 * Step 1: parse the pattern string to produce an array
1165 * of Patterns. The array is generated backwards, so
1166 * that the lowest-indexed pattern corresponds to the last
1167 * event that must occur.
1168 *-------------------------------------------------------------
1169 */
1170
1171 p = eventString;
1172 flags = 0;
1173 eventMask = 0;
1174 for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
1175 numPats < EVENT_BUFFER_SIZE;
1176 numPats++, patPtr--) {
1177 patPtr->eventType = -1;
1178 patPtr->needMods = 0;
1179 patPtr->hateMods = ~0;
1180 patPtr->detail = 0;
1181 while (isspace(*p)) {
1182 p++;
1183 }
1184 if (*p == '\0') {
1185 break;
1186 }
1187
1188 /*
1189 * Handle simple ASCII characters. Note: the shift
1190 * modifier is ignored in this case (it's really part
1191 * of the character, rather than a "modifier").
1192 */
1193
1194 if (*p != '<') {
1195 char string[2];
1196
1197 patPtr->eventType = KeyPress;
1198 eventMask |= KeyPressMask;
1199 string[0] = *p;
1200 string[1] = 0;
1201 hPtr = Tcl_FindHashEntry(&keySymTable, string);
1202 if (hPtr != NULL) {
1203 patPtr->detail = (int) Tcl_GetHashValue(hPtr);
1204 } else {
1205 if (isprint(*p)) {
1206 patPtr->detail = *p;
1207 } else {
1208 sprintf(interp->result,
1209 "bad ASCII character 0x%x", *p);
1210 return NULL;
1211 }
1212 }
1213 patPtr->hateMods = ~ShiftMask;
1214 p++;
1215 continue;
1216 }
1217
1218 /*
1219 * A fancier event description. Must consist of
1220 * 1. open angle bracket.
1221 * 2. any number of modifiers, each followed by spaces
1222 * or dashes.
1223 * 3. an optional event name.
1224 * 4. an option button or keysym name. Either this or
1225 * item 3 *must* be present; if both are present
1226 * then they are separated by spaces or dashes.
1227 * 5. a close angle bracket.
1228 */
1229
1230 any = 0;
1231 count = 1;
1232 p++;
1233 while (1) {
1234 register ModInfo *modPtr;
1235 p = GetField(p, field, FIELD_SIZE);
1236 hPtr = Tcl_FindHashEntry(&modTable, field);
1237 if (hPtr == NULL) {
1238 break;
1239 }
1240 modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
1241 patPtr->needMods |= modPtr->mask;
1242 if (modPtr->flags & (DOUBLE|TRIPLE)) {
1243 flags |= PAT_NEARBY;
1244 if (modPtr->flags & DOUBLE) {
1245 count = 2;
1246 } else {
1247 count = 3;
1248 }
1249 }
1250 if (modPtr->flags & ANY) {
1251 any = 1;
1252 }
1253 while ((*p == '-') || isspace(*p)) {
1254 p++;
1255 }
1256 }
1257 if (any) {
1258 patPtr->hateMods = 0;
1259 } else {
1260 patPtr->hateMods = ~patPtr->needMods;
1261 }
1262 hPtr = Tcl_FindHashEntry(&eventTable, field);
1263 if (hPtr != NULL) {
1264 register EventInfo *eiPtr;
1265 eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
1266 patPtr->eventType = eiPtr->type;
1267 eventMask |= eiPtr->eventMask;
1268 while ((*p == '-') || isspace(*p)) {
1269 p++;
1270 }
1271 p = GetField(p, field, FIELD_SIZE);
1272 }
1273 if (*field != '\0') {
1274 if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
1275 static int masks[] = {~0, ~Button1Mask, ~Button2Mask,
1276 ~Button3Mask, ~Button4Mask, ~Button5Mask};
1277
1278 if (patPtr->eventType == -1) {
1279 patPtr->eventType = ButtonPress;
1280 eventMask |= ButtonPressMask;
1281 } else if ((patPtr->eventType == KeyPress)
1282 || (patPtr->eventType == KeyRelease)) {
1283 goto getKeysym;
1284 } else if ((patPtr->eventType != ButtonPress)
1285 && (patPtr->eventType != ButtonRelease)) {
1286 Tcl_AppendResult(interp, "specified button \"", field,
1287 "\" for non-button event", (char *) NULL);
1288 return NULL;
1289 }
1290 patPtr->detail = (*field - '0');
1291
1292 /*
1293 * Ignore this button as a modifier: its state is already
1294 * fixed.
1295 */
1296
1297 patPtr->needMods &= masks[patPtr->detail];
1298 patPtr->hateMods &= masks[patPtr->detail];
1299 } else {
1300 getKeysym:
1301 hPtr = Tcl_FindHashEntry(&keySymTable, (char *) field);
1302 if (hPtr == NULL) {
1303 Tcl_AppendResult(interp, "bad event type or keysym \"",
1304 field, "\"", (char *) NULL);
1305 return NULL;
1306 }
1307 if (patPtr->eventType == -1) {
1308 patPtr->eventType = KeyPress;
1309 eventMask |= KeyPressMask;
1310 } else if ((patPtr->eventType != KeyPress)
1311 && (patPtr->eventType != KeyRelease)) {
1312 Tcl_AppendResult(interp, "specified keysym \"", field,
1313 "\" for non-key event", (char *) NULL);
1314 return NULL;
1315 }
1316 patPtr->detail = (int) Tcl_GetHashValue(hPtr);
1317
1318 /*
1319 * Don't get upset about the shift modifier with keys:
1320 * if the key doesn't permit the shift modifier then
1321 * that will already be factored in when translating
1322 * from keycode to keysym in Tk_BindEvent. If the keysym
1323 * has both a shifted and unshifted form, we want to allow
1324 * the shifted form to be specified explicitly, though.
1325 */
1326
1327 patPtr->hateMods &= ~ShiftMask;
1328 }
1329 } else if (patPtr->eventType == -1) {
1330 interp->result = "no event type or button # or keysym";
1331 return NULL;
1332 }
1333 while ((*p == '-') || isspace(*p)) {
1334 p++;
1335 }
1336 if (*p != '>') {
1337 interp->result = "missing \">\" in binding";
1338 return NULL;
1339 }
1340 p++;
1341
1342 /*
1343 * Replicate events for DOUBLE and TRIPLE.
1344 */
1345
1346 if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
1347 patPtr[-1] = patPtr[0];
1348 patPtr--;
1349 numPats++;
1350 if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
1351 patPtr[-1] = patPtr[0];
1352 patPtr--;
1353 numPats++;
1354 }
1355 }
1356 }
1357
1358 /*
1359 *-------------------------------------------------------------
1360 * Step 2: find the sequence in the binding table if it exists,
1361 * and add a new sequence to the table if it doesn't.
1362 *-------------------------------------------------------------
1363 */
1364
1365 if (numPats == 0) {
1366 interp->result = "no events specified in binding";
1367 return NULL;
1368 }
1369 patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
1370 key.object = object;
1371 key.type = patPtr->eventType;
1372 key.detail = patPtr->detail;
1373 hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
1374 sequenceSize = numPats*sizeof(Pattern);
1375 if (!new) {
1376 for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1377 psPtr = psPtr->nextSeqPtr) {
1378 if ((numPats == psPtr->numPats)
1379 && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
1380 && (memcmp((char *) patPtr, (char *) psPtr->pats,
1381 sequenceSize) == 0)) {
1382 *maskPtr = eventMask; /*don't forget to pass back the mask*/
1383 goto done;
1384 }
1385 }
1386 }
1387 if (!create) {
1388 if (new) {
1389 Tcl_DeleteHashEntry(hPtr);
1390 }
1391 Tcl_AppendResult(interp, "no binding exists for \"",
1392 eventString, "\"", (char *) NULL);
1393 return NULL;
1394 }
1395 psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
1396 + (numPats-1)*sizeof(Pattern)));
1397 psPtr->numPats = numPats;
1398 psPtr->command = NULL;
1399 psPtr->flags = flags;
1400 psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1401 psPtr->hPtr = hPtr;
1402 Tcl_SetHashValue(hPtr, psPtr);
1403
1404 /*
1405 * Link the pattern into the list associated with the object.
1406 */
1407
1408 psPtr->object = object;
1409 hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
1410 if (new) {
1411 psPtr->nextObjPtr = NULL;
1412 } else {
1413 psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1414 }
1415 Tcl_SetHashValue(hPtr, psPtr);
1416
1417 memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
1418
1419 done:
1420 *maskPtr = eventMask;
1421 return psPtr;
1422 }
1423 \f
1424 /*
1425 *----------------------------------------------------------------------
1426 *
1427 * GetField --
1428 *
1429 * Used to parse pattern descriptions. Copies up to
1430 * size characters from p to copy, stopping at end of
1431 * string, space, "-", ">", or whenever size is
1432 * exceeded.
1433 *
1434 * Results:
1435 * The return value is a pointer to the character just
1436 * after the last one copied (usually "-" or space or
1437 * ">", but could be anything if size was exceeded).
1438 * Also places NULL-terminated string (up to size
1439 * character, including NULL), at copy.
1440 *
1441 * Side effects:
1442 * None.
1443 *
1444 *----------------------------------------------------------------------
1445 */
1446
1447 static char *
1448 GetField (
1449 register char *p, /* Pointer to part of pattern. */
1450 register char *copy, /* Place to copy field. */
1451 int size /* Maximum number of characters to
1452 * copy. */
1453 )
1454 {
1455 while ((*p != '\0') && !isspace(*p) && (*p != '>')
1456 && (*p != '-') && (size > 1)) {
1457 *copy = *p;
1458 p++;
1459 copy++;
1460 size--;
1461 }
1462 *copy = '\0';
1463 return p;
1464 }
1465 \f
1466 /*
1467 *----------------------------------------------------------------------
1468 *
1469 * GetKeySym --
1470 *
1471 * Given an X KeyPress or KeyRelease event, map the
1472 * keycode in the event into a KeySym.
1473 *
1474 * Results:
1475 * The return value is the KeySym corresponding to
1476 * eventPtr, or NoSymbol if no matching Keysym could be
1477 * found.
1478 *
1479 * Side effects:
1480 * In the first call for a given display, keycode-to-
1481 * KeySym maps get loaded.
1482 *
1483 *----------------------------------------------------------------------
1484 */
1485
1486 static KeySym
1487 GetKeySym (
1488 register TkDisplay *dispPtr, /* Display in which to
1489 * map keycode. */
1490 register XEvent *eventPtr /* Description of X event. */
1491 )
1492 {
1493 KeySym *symPtr;
1494 KeySym sym;
1495
1496 /*
1497 * Read the key mapping information from the server if
1498 * we don't have it already.
1499 */
1500
1501 if (dispPtr->symsPerCode == 0) {
1502 Display *dpy = dispPtr->display;
1503
1504 #ifdef IS_LINUX
1505 XDisplayKeycodes(dpy, &dispPtr->firstKeycode, &dispPtr->lastKeycode);
1506 #else
1507 dispPtr->firstKeycode =
1508 dpy->min_keycode;
1509 dispPtr->lastKeycode =
1510 dpy->max_keycode;
1511 #endif
1512 dispPtr->keySyms = XGetKeyboardMapping(dpy,
1513 dispPtr->firstKeycode, dispPtr->lastKeycode + 1
1514 - dispPtr->firstKeycode, &dispPtr->symsPerCode);
1515 }
1516
1517 /*
1518 * Compute the lower-case KeySym for this keycode. May
1519 * have to convert an upper-case KeySym to a lower-case
1520 * one if the list only has a single element.
1521 */
1522
1523 if ((eventPtr->xkey.keycode < dispPtr->firstKeycode)
1524 || (eventPtr->xkey.keycode > dispPtr->lastKeycode)) {
1525 return NoSymbol;
1526 }
1527 symPtr = &dispPtr->keySyms[(eventPtr->xkey.keycode
1528 - dispPtr->firstKeycode) * dispPtr->symsPerCode];
1529 sym = *symPtr;
1530 if ((dispPtr->symsPerCode == 1) || (symPtr[1] == NoSymbol)) {
1531 if ((sym >= XK_A) && (sym <= XK_Z)) {
1532 sym += (XK_a - XK_A);
1533 } else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) {
1534 sym += (XK_agrave - XK_Agrave);
1535 } else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) {
1536 sym += (XK_oslash - XK_Ooblique);
1537 }
1538 }
1539
1540 /*
1541 * See whether the key is shifted or caps-locked. If so,
1542 * use an upper-case equivalent if provided, or compute
1543 * one (for caps-lock, just compute upper-case: don't
1544 * use shifted KeySym since that would shift non-alphabetic
1545 * keys).
1546 */
1547
1548 if (eventPtr->xkey.state & ShiftMask) {
1549 if ((dispPtr->symsPerCode > 1) && (symPtr[1] != NoSymbol)) {
1550 return symPtr[1];
1551 }
1552 shiftToUpper:
1553 if ((sym >= XK_a) && (sym <= XK_z)) {
1554 sym += (XK_A - XK_a);
1555 } else if ((sym >= XK_agrave) && (sym <= XK_adiaeresis)) {
1556 sym += (XK_Agrave - XK_agrave);
1557 } else if ((sym >= XK_oslash) && (sym <= XK_thorn)) {
1558 sym += (XK_Ooblique - XK_oslash);
1559 }
1560 return sym;
1561 }
1562 if (eventPtr->xkey.state & LockMask) {
1563 goto shiftToUpper;
1564 }
1565 return sym;
1566 }
1567 \f
1568 /*
1569 *----------------------------------------------------------------------
1570 *
1571 * MatchPatterns --
1572 *
1573 * Given a list of pattern sequences and a list of
1574 * recent events, return a pattern sequence that matches
1575 * the event list.
1576 *
1577 * Results:
1578 * The return value is NULL if no pattern matches the
1579 * recent events from bindPtr. If one or more patterns
1580 * matches, then the longest (or most specific) matching
1581 * pattern is returned.
1582 *
1583 * Side effects:
1584 * None.
1585 *
1586 *----------------------------------------------------------------------
1587 */
1588
1589 static PatSeq *
1590 MatchPatterns (
1591 TkDisplay *dispPtr,
1592 BindingTable *bindPtr, /* Information about binding table, such
1593 * as ring of recent events. */
1594 register PatSeq *psPtr /* List of pattern sequences. */
1595 )
1596 {
1597 register PatSeq *bestPtr = NULL;
1598
1599 /*
1600 * Iterate over all the pattern sequences.
1601 */
1602
1603 for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1604 register XEvent *eventPtr;
1605 register Pattern *patPtr;
1606 Window window;
1607 int *detailPtr;
1608 int patCount, ringCount, flags, state;
1609
1610 /*
1611 * Iterate over all the patterns in a sequence to be
1612 * sure that they all match.
1613 */
1614
1615 eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1616 detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1617 window = eventPtr->xany.window;
1618 patPtr = psPtr->pats;
1619 patCount = psPtr->numPats;
1620 ringCount = EVENT_BUFFER_SIZE;
1621 while (patCount > 0) {
1622 if (ringCount <= 0) {
1623 goto nextSequence;
1624 }
1625 if (eventPtr->xany.type != patPtr->eventType) {
1626 /*
1627 * If the event is a mouse motion, button release,
1628 * or key release event, and it didn't match
1629 * the pattern, then just skip the event and try
1630 * the next event against the same pattern.
1631 */
1632
1633 if ((eventPtr->xany.type == MotionNotify)
1634 || (eventPtr->xany.type == ButtonRelease)
1635 || (eventPtr->xany.type == KeyRelease)
1636 || (eventPtr->xany.type == NoExpose)
1637 || (eventPtr->xany.type == EnterNotify)
1638 || (eventPtr->xany.type == LeaveNotify)
1639 || (eventPtr->xany.type == GraphicsExpose)) {
1640 goto nextEvent;
1641 }
1642 goto nextSequence;
1643 }
1644 if (eventPtr->xany.window != window) {
1645 goto nextSequence;
1646 }
1647
1648 flags = flagArray[eventPtr->type];
1649 if (flags & KEY_BUTTON_MOTION) {
1650 state = eventPtr->xkey.state;
1651 } else if (flags & CROSSING) {
1652 state = eventPtr->xcrossing.state;
1653 } else {
1654 state = 0;
1655 }
1656 if (patPtr->needMods != 0) {
1657 int modMask = patPtr->needMods;
1658
1659 if (!dispPtr->metaModMask && !dispPtr->altModMask && !dispPtr->modeModMask) {
1660 int i, max;
1661 XModifierKeymap *modMapPtr;
1662 KeyCode *codePtr;
1663 KeySym keysym;
1664
1665 modMapPtr = XGetModifierMapping(dispPtr->display);
1666 codePtr = modMapPtr->modifiermap;
1667 max = 8*modMapPtr->max_keypermod;
1668
1669 for (i = 0; i < max; i++, codePtr++) {
1670 if (*codePtr == 0) {
1671 continue;
1672 }
1673 keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
1674 if (keysym == XK_Mode_switch) {
1675 dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
1676 }
1677 if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
1678 dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
1679 }
1680 if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
1681 dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
1682 }
1683 }
1684 }
1685 if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
1686 modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
1687 }
1688 if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
1689 modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
1690 }
1691
1692 if ((state & META_MASK) && (dispPtr->metaModMask != 0)) {
1693 state = (state & ~META_MASK) | dispPtr->metaModMask;
1694 }
1695 if ((state & ALT_MASK) && (dispPtr->altModMask != 0)) {
1696 state = (state & ~ALT_MASK) | dispPtr->altModMask;
1697 }
1698
1699 if ((state & modMask) != modMask) {
1700 goto nextSequence;
1701 }
1702 }
1703 #if 0
1704 if ((state & patPtr->hateMods) != 0) {
1705 goto nextSequence;
1706 }
1707 #endif
1708 if ((patPtr->detail != 0)
1709 && (patPtr->detail != *detailPtr)) {
1710 goto nextSequence;
1711 }
1712 if (psPtr->flags & PAT_NEARBY) {
1713 register XEvent *firstPtr;
1714
1715 firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
1716 if ((firstPtr->xkey.x_root
1717 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
1718 || (firstPtr->xkey.x_root
1719 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
1720 || (firstPtr->xkey.y_root
1721 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
1722 || (firstPtr->xkey.y_root
1723 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
1724 || (firstPtr->xkey.time
1725 > (eventPtr->xkey.time + NEARBY_MS))) {
1726 goto nextSequence;
1727 }
1728 }
1729 patPtr++;
1730 patCount--;
1731 nextEvent:
1732 if (eventPtr == bindPtr->eventRing) {
1733 eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1734 detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1735 } else {
1736 eventPtr--;
1737 detailPtr--;
1738 }
1739 ringCount--;
1740 }
1741
1742 /*
1743 * This sequence matches. If we've already got another match,
1744 * pick whichever is most specific. Detail is most important,
1745 * then needMods, then hateMods.
1746 */
1747
1748 if (bestPtr != NULL) {
1749 register Pattern *patPtr2;
1750 int i;
1751
1752 if (psPtr->numPats != bestPtr->numPats) {
1753 if (bestPtr->numPats > psPtr->numPats) {
1754 goto nextSequence;
1755 } else {
1756 goto newBest;
1757 }
1758 }
1759 for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
1760 i < psPtr->numPats; i++,patPtr++, patPtr2++) {
1761 if (patPtr->detail != patPtr2->detail) {
1762 if (patPtr->detail == 0) {
1763 goto nextSequence;
1764 } else {
1765 goto newBest;
1766 }
1767 }
1768 if (patPtr->needMods != patPtr2->needMods) {
1769 if ((patPtr->needMods & patPtr2->needMods)
1770 == patPtr->needMods) {
1771 goto nextSequence;
1772 } else {
1773 goto newBest;
1774 }
1775 }
1776 if (patPtr->hateMods != patPtr2->hateMods) {
1777 if ((patPtr->hateMods & patPtr2->hateMods)
1778 == patPtr2->hateMods) {
1779 goto newBest;
1780 } else {
1781 goto nextSequence;
1782 }
1783 }
1784 }
1785 goto nextSequence; /* Tie goes to newest pattern. */
1786 }
1787 newBest:
1788 bestPtr = psPtr;
1789
1790 nextSequence: continue;
1791 }
1792 return bestPtr;
1793 }
1794 \f
1795 /*
1796 *--------------------------------------------------------------
1797 *
1798 * ExpandPercents --
1799 *
1800 * Given a command and an event, produce a new command
1801 * by replacing % constructs in the original command
1802 * with information from the X event.
1803 *
1804 * Results:
1805 * The return result is a pointer to the new %-substituted
1806 * command. If the command fits in the space at after, then
1807 * the return value is after. If the command is too large
1808 * to fit at after, then the return value is a pointer to
1809 * a malloc-ed buffer holding the command; in this case it
1810 * is the caller's responsibility to free up the buffer when
1811 * finished with it.
1812 *
1813 * Side effects:
1814 * None.
1815 *
1816 *--------------------------------------------------------------
1817 */
1818
1819 static char *
1820 ExpandPercents (
1821 register char *before, /* Command containing percent
1822 * expressions to be replaced. */
1823 register XEvent *eventPtr, /* X event containing information
1824 * to be used in % replacements. */
1825 KeySym keySym, /* KeySym: only relevant for
1826 * KeyPress and KeyRelease events). */
1827 char *after, /* Place to generate new expanded
1828 * command. Must contain at least
1829 * "afterSize" bytes of space. */
1830 int afterSize /* Number of bytes of space available at
1831 * after. */
1832 )
1833 {
1834 register char *buffer; /* Pointer to buffer currently being used
1835 * as destination. */
1836 register char *dst; /* Pointer to next place to store character
1837 * in substituted string. */
1838 int spaceLeft; /* Indicates how many more non-null bytes
1839 * may be stored at *dst before space
1840 * runs out. */
1841 int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1842 * list element. */
1843 int number, flags;
1844 #define NUM_SIZE 40
1845 register char *string;
1846 char numStorage[NUM_SIZE+1];
1847
1848 if (eventPtr->type < LASTEvent) {
1849 flags = flagArray[eventPtr->type];
1850 } else {
1851 flags = 0;
1852 }
1853 dst = buffer = after;
1854 spaceLeft = afterSize - 1;
1855 while (*before != 0) {
1856 if (*before != '%') {
1857
1858 /*
1859 * Expand the destination string if necessary.
1860 */
1861
1862 if (spaceLeft <= 0) {
1863 char *newSpace;
1864
1865 newSpace = (char *) ckalloc((unsigned) (2*afterSize));
1866 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
1867 afterSize *= 2;
1868 dst = newSpace + (dst - buffer);
1869 if (buffer != after) {
1870 ckfree(buffer);
1871 }
1872 buffer = newSpace;
1873 spaceLeft = afterSize - (dst-buffer) - 1;
1874 }
1875 *dst = *before;
1876 dst++;
1877 before++;
1878 spaceLeft--;
1879 continue;
1880 }
1881
1882 number = 0;
1883 string = "??";
1884 switch (before[1]) {
1885 case '#':
1886 number = eventPtr->xany.serial;
1887 goto doNumber;
1888 case 'a':
1889 number = (int) eventPtr->xconfigure.above;
1890 goto doNumber;
1891 case 'b':
1892 number = eventPtr->xbutton.button;
1893 goto doNumber;
1894 case 'c':
1895 if (flags & EXPOSE) {
1896 number = eventPtr->xexpose.count;
1897 } else if (flags & MAPPING) {
1898 number = eventPtr->xmapping.count;
1899 }
1900 goto doNumber;
1901 case 'd':
1902 if (flags & (CROSSING|FOCUS)) {
1903 switch (eventPtr->xcrossing.detail) {
1904 case NotifyAncestor:
1905 string = "NotifyAncestor";
1906 break;
1907 case NotifyVirtual:
1908 string = "NotifyVirtual";
1909 break;
1910 case NotifyInferior:
1911 string = "NotifyInferior";
1912 break;
1913 case NotifyNonlinear:
1914 string = "NotifyNonlinear";
1915 break;
1916 case NotifyNonlinearVirtual:
1917 string = "NotifyNonlinearVirtual";
1918 break;
1919 case NotifyPointer:
1920 string = "NotifyPointer";
1921 break;
1922 case NotifyPointerRoot:
1923 string = "NotifyPointerRoot";
1924 break;
1925 case NotifyDetailNone:
1926 string = "NotifyDetailNone";
1927 break;
1928 }
1929 } else if (flags & CONFIG_REQ) {
1930 switch (eventPtr->xconfigurerequest.detail) {
1931 case Above:
1932 string = "Above";
1933 break;
1934 case Below:
1935 string = "Below";
1936 break;
1937 case TopIf:
1938 string = "TopIf";
1939 break;
1940 case BottomIf:
1941 string = "BottomIf";
1942 break;
1943 case Opposite:
1944 string = "Opposite";
1945 break;
1946 }
1947 }
1948 goto doString;
1949 case 'f':
1950 number = eventPtr->xcrossing.focus;
1951 goto doNumber;
1952 case 'h':
1953 if (flags & EXPOSE) {
1954 number = eventPtr->xexpose.height;
1955 } else if (flags & (CONFIG|CONFIG_REQ)) {
1956 number = eventPtr->xconfigure.height;
1957 } else if (flags & RESIZE_REQ) {
1958 number = eventPtr->xresizerequest.height;
1959 }
1960 goto doNumber;
1961 case 'k':
1962 number = eventPtr->xkey.keycode;
1963 goto doNumber;
1964 case 'm':
1965 if (flags & CROSSING) {
1966 number = eventPtr->xcrossing.mode;
1967 } else if (flags & FOCUS) {
1968 number = eventPtr->xfocus.mode;
1969 }
1970 switch (number) {
1971 case NotifyNormal:
1972 string = "NotifyNormal";
1973 break;
1974 case NotifyGrab:
1975 string = "NotifyGrab";
1976 break;
1977 case NotifyUngrab:
1978 string = "NotifyUngrab";
1979 break;
1980 case NotifyWhileGrabbed:
1981 string = "NotifyWhileGrabbed";
1982 break;
1983 }
1984 goto doString;
1985 case 'o':
1986 if (flags & CREATE) {
1987 number = eventPtr->xcreatewindow.override_redirect;
1988 } else if (flags & MAP) {
1989 number = eventPtr->xmap.override_redirect;
1990 } else if (flags & REPARENT) {
1991 number = eventPtr->xreparent.override_redirect;
1992 } else if (flags & CONFIG) {
1993 number = eventPtr->xconfigure.override_redirect;
1994 }
1995 goto doNumber;
1996 case 'p':
1997 switch (eventPtr->xcirculate.place) {
1998 case PlaceOnTop:
1999 string = "PlaceOnTop";
2000 break;
2001 case PlaceOnBottom:
2002 string = "PlaceOnBottom";
2003 break;
2004 }
2005 goto doString;
2006 case 's':
2007 if (flags & KEY_BUTTON_MOTION) {
2008 number = eventPtr->xkey.state;
2009 } else if (flags & CROSSING) {
2010 number = eventPtr->xcrossing.state;
2011 } else if (flags & VISIBILITY) {
2012 switch (eventPtr->xvisibility.state) {
2013 case VisibilityUnobscured:
2014 string = "VisibilityUnobscured";
2015 break;
2016 case VisibilityPartiallyObscured:
2017 string = "VisibilityPartiallyObscured";
2018 break;
2019 case VisibilityFullyObscured:
2020 string = "VisibilityFullyObscured";
2021 break;
2022 }
2023 goto doString;
2024 }
2025 goto doNumber;
2026 case 't':
2027 if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
2028 number = (int) eventPtr->xkey.time;
2029 } else if (flags & SEL_REQ) {
2030 number = (int) eventPtr->xselectionrequest.time;
2031 } else if (flags & SEL_NOTIFY) {
2032 number = (int) eventPtr->xselection.time;
2033 }
2034 goto doNumber;
2035 case 'v':
2036 number = eventPtr->xconfigurerequest.value_mask;
2037 goto doNumber;
2038 case 'w':
2039 if (flags & EXPOSE) {
2040 number = eventPtr->xexpose.width;
2041 } else if (flags & (CONFIG|CONFIG_REQ)) {
2042 number = eventPtr->xconfigure.width;
2043 } else if (flags & RESIZE_REQ) {
2044 number = eventPtr->xresizerequest.width;
2045 }
2046 goto doNumber;
2047 case 'x':
2048 if (flags & KEY_BUTTON_MOTION) {
2049 number = eventPtr->xkey.x;
2050 } else if (flags & EXPOSE) {
2051 number = eventPtr->xexpose.x;
2052 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
2053 number = eventPtr->xcreatewindow.x;
2054 } else if (flags & REPARENT) {
2055 number = eventPtr->xreparent.x;
2056 } else if (flags & CROSSING) {
2057 number = eventPtr->xcrossing.x;
2058 }
2059 goto doNumber;
2060 case 'y':
2061 if (flags & KEY_BUTTON_MOTION) {
2062 number = eventPtr->xkey.y;
2063 } else if (flags & EXPOSE) {
2064 number = eventPtr->xexpose.y;
2065 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
2066 number = eventPtr->xcreatewindow.y;
2067 } else if (flags & REPARENT) {
2068 number = eventPtr->xreparent.y;
2069 } else if (flags & CROSSING) {
2070 number = eventPtr->xcrossing.y;
2071
2072 }
2073 goto doNumber;
2074 case 'A':
2075 if ((eventPtr->type == KeyPress)
2076 || (eventPtr->type == KeyRelease)) {
2077 int numChars;
2078
2079 numChars = XLookupString(&eventPtr->xkey, numStorage,
2080 NUM_SIZE, (KeySym *) NULL,
2081 (XComposeStatus *) NULL);
2082 numStorage[numChars] = '\0';
2083 string = numStorage;
2084 }
2085 goto doString;
2086 case 'B':
2087 number = eventPtr->xcreatewindow.border_width;
2088 goto doNumber;
2089 case 'D':
2090 number = (int) eventPtr->xany.display;
2091 goto doNumber;
2092 case 'E':
2093 number = (int) eventPtr->xany.send_event;
2094 goto doNumber;
2095 case 'K':
2096 if ((eventPtr->type == KeyPress)
2097 || (eventPtr->type == KeyRelease)) {
2098 register KeySymInfo *kPtr;
2099
2100 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
2101 if (kPtr->value == keySym) {
2102 string = kPtr->name;
2103 break;
2104 }
2105 }
2106 }
2107 goto doString;
2108 case 'N':
2109 number = (int) keySym;
2110 goto doNumber;
2111 case 'R':
2112 number = (int) eventPtr->xkey.root;
2113 goto doNumber;
2114 case 'S':
2115 number = (int) eventPtr->xkey.subwindow;
2116 goto doNumber;
2117 case 'T':
2118 number = eventPtr->type;
2119 goto doNumber;
2120 case 'W': {
2121 TkWindow *winPtr;
2122
2123 if (XFindContext(eventPtr->xany.display, eventPtr->xany.window,
2124 tkWindowContext, (void *) &winPtr) == 0) {
2125 string = winPtr->pathName;
2126 } else {
2127 string = "??";
2128 }
2129 goto doString;
2130 }
2131 case 'X':
2132 number = eventPtr->xkey.x_root;
2133 goto doNumber;
2134 case 'Y':
2135 number = eventPtr->xkey.y_root;
2136 goto doNumber;
2137 default:
2138 numStorage[0] = before[1];
2139 numStorage[1] = '\0';
2140 string = numStorage;
2141 goto doString;
2142 }
2143
2144 doNumber:
2145 sprintf(numStorage, "%d", number);
2146 string = numStorage;
2147
2148 doString:
2149 spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2150 if (spaceNeeded >= spaceLeft) {
2151 char *newSpace;
2152
2153 newSpace = (char *) ckalloc((unsigned)
2154 (afterSize + spaceNeeded + 50));
2155 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
2156 afterSize += spaceNeeded + 50;
2157 dst = newSpace + (dst - buffer);
2158 if (buffer != after) {
2159 ckfree(buffer);
2160 }
2161 buffer = newSpace;
2162 spaceLeft = afterSize - (dst-buffer) - 1;
2163 }
2164 spaceNeeded = Tcl_ConvertElement(string, dst,
2165 cvtFlags | TCL_DONT_USE_BRACES);
2166 dst += spaceNeeded;
2167 spaceLeft -= spaceNeeded;
2168 before += 2;
2169 }
2170 *dst = '\0';
2171 return buffer;
2172 }
2173 \f
2174 /*
2175 *----------------------------------------------------------------------
2176 *
2177 * TkBindError --
2178 *
2179 * This procedure is invoked to handle errors that occur in Tcl
2180 * commands that are invoked in "background" (e.g. from event or
2181 * timer bindings).
2182 *
2183 * Results:
2184 * None.
2185 *
2186 * Side effects:
2187 * The command "tkerror" is invoked to process the error, passing
2188 * it the error message. If that fails, then an error message
2189 * is output on stderr.
2190 *
2191 *----------------------------------------------------------------------
2192 */
2193
2194 void
2195 TkBindError (
2196 Tcl_Interp *interp /* Interpreter in which an error has
2197 * occurred. */
2198 )
2199 {
2200 char *argv[2];
2201 char *command;
2202 char *error;
2203 char *errorInfo, *tmp;
2204 int result;
2205
2206 error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1));
2207 strcpy(error, interp->result);
2208 tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2209 if (tmp == NULL) {
2210 errorInfo = error;
2211 } else {
2212 errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1));
2213 strcpy(errorInfo, tmp);
2214 }
2215 argv[0] = "tkerror";
2216 argv[1] = error;
2217 command = Tcl_Merge(2, argv);
2218 result = Tcl_GlobalEval(interp, command);
2219 if (result != TCL_OK) {
2220 if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2221 fprintf(stderr, "%s\n", errorInfo);
2222 } else {
2223 fprintf(stderr, "tkerror failed to handle background error.\n");
2224 fprintf(stderr, " Original error: %s\n", error);
2225 fprintf(stderr, " Error in tkerror: %s\n", interp->result);
2226 }
2227 }
2228 Tcl_ResetResult(interp);
2229 ckfree(command);
2230 ckfree(error);
2231 if (errorInfo != error) {
2232 ckfree(errorInfo);
2233 }
2234 }
Impressum, Datenschutz