]> cvs.zerfleddert.de Git - micropolis/blame - src/tk/tkbind.c
make double click work on OS X
[micropolis] / src / tk / tkbind.c
CommitLineData
6a5fa4e0
MG
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
18static 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
38typedef 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
65typedef 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
83typedef 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
110typedef 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
173typedef struct {
174 char *name; /* Name of keysym. */
175 KeySym value; /* Numeric identifier for keysym. */
176} KeySymInfo;
177KeySymInfo keyArray[] = {
178#ifndef lint
179#include "ks_names.h"
180#endif
181 (char *) NULL, 0
182};
183static Tcl_HashTable keySymTable; /* Hashed form of above structure. */
184
185static 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
194typedef 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
216static ModInfo modArray[] = {
217 "Control", ControlMask, 0,
218 "Shift", ShiftMask, 0,
219 "Lock", LockMask, 0,
220 "B1", Button1Mask, 0,
221 "Button1", Button1Mask, 0,
222 "B2", Button2Mask, 0,
223 "Button2", Button2Mask, 0,
224 "B3", Button3Mask, 0,
225 "Button3", Button3Mask, 0,
226 "B4", Button4Mask, 0,
227 "Button4", Button4Mask, 0,
228 "B5", Button5Mask, 0,
229 "Button5", Button5Mask, 0,
230 "Mod1", Mod1Mask, 0,
231 "M1", Mod1Mask, 0,
232 "Meta", Mod1Mask, 0,
233 "M", Mod1Mask, 0,
234 "Mod2", Mod2Mask, 0,
235 "M2", Mod2Mask, 0,
236 "Alt", Mod2Mask, 0,
237 "Mod3", Mod3Mask, 0,
238 "M3", Mod3Mask, 0,
239 "Mod4", Mod4Mask, 0,
240 "M4", Mod4Mask, 0,
241 "Mod5", Mod5Mask, 0,
242 "M5", Mod5Mask, 0,
243 "Double", 0, DOUBLE,
244 "Triple", 0, TRIPLE,
245 "Any", 0, ANY,
246 NULL, 0, 0};
247static 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
256typedef 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
272static 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};
304static 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
333static 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
376static char * ExpandPercents _ANSI_ARGS_((char *before,
377 XEvent *eventPtr, KeySym keySym, char *after,
378 int afterSize));
379static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
380 BindingTable *bindPtr, ClientData object,
381 char *eventString, int create,
382 unsigned long *maskPtr));
383static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));
384static KeySym GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
385 XEvent *eventPtr));
386static PatSeq * MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr,
387 PatSeq *psPtr));
388\f
389/*
390 *--------------------------------------------------------------
391 *
392 * Tk_CreateBindingTable --
393 *
394 * Set up a new domain in which event bindings may be created.
395 *
396 * Results:
397 * The return value is a token for the new table, which must
398 * be passed to procedures like Tk_CreatBinding.
399 *
400 * Side effects:
401 * Memory is allocated for the new table.
402 *
403 *--------------------------------------------------------------
404 */
405
406Tk_BindingTable
407Tk_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
482void
483Tk_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
542unsigned long
543Tk_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
611int
612Tk_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
699char *
700Tk_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
740void
741Tk_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
895void
896Tk_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
966void
967Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
968 Tk_BindingTable bindingTable; /* Table in which to look for
969 * bindings. */
970 XEvent *eventPtr; /* What actually happened. */
971 Tk_Window tkwin; /* Window on display where event
972 * occurred (needed in order to
973 * locate display information). */
974 int numObjects; /* Number of objects at *objectPtr. */
975 ClientData *objectPtr; /* Array of one or more objects
976 * to check for a matching binding. */
977{
978 BindingTable *bindPtr = (BindingTable *) bindingTable;
979 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
980 XEvent *ringPtr;
981 PatSeq *matchPtr;
982 PatternTableKey key;
983 Tcl_HashEntry *hPtr;
984 int detail;
985
986 /*
987 * Add the new event to the ring of saved events for the
988 * binding table. Consecutive MotionNotify events get combined:
989 * if both the new event and the previous event are MotionNotify,
990 * then put the new event *on top* of the previous event.
991 */
992
993 if ((eventPtr->type != MotionNotify)
994 || (bindPtr->eventRing[bindPtr->curEvent].type != MotionNotify)) {
995 bindPtr->curEvent++;
996 if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
997 bindPtr->curEvent = 0;
998 }
999 }
1000 ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1001 memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
1002 detail = 0;
1003 bindPtr->detailRing[bindPtr->curEvent] = 0;
1004 if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
1005 detail = (int) GetKeySym(dispPtr, ringPtr);
1006 if (detail == NoSymbol) {
1007 detail = 0;
1008 }
1009 } else if ((ringPtr->type == ButtonPress)
1010 || (ringPtr->type == ButtonRelease)) {
1011 detail = ringPtr->xbutton.button;
1012 }
1013 bindPtr->detailRing[bindPtr->curEvent] = detail;
1014
1015 /*
1016 * Loop over all the objects, matching the new event against
1017 * each in turn.
1018 */
1019
1020 for ( ; numObjects > 0; numObjects--, objectPtr++) {
1021
1022 /*
1023 * Match the new event against those recorded in the
1024 * pattern table, saving the longest matching pattern.
1025 * For events with details (button and key events) first
1026 * look for a binding for the specific key or button.
1027 * If none is found, then look for a binding for all
1028 * keys or buttons (detail of 0).
1029 */
1030
1031 matchPtr = NULL;
1032 key.object = *objectPtr;
1033 key.type = ringPtr->type;
1034 key.detail = detail;
1035 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1036 if (hPtr != NULL) {
1037 matchPtr = MatchPatterns(bindPtr,
1038 (PatSeq *) Tcl_GetHashValue(hPtr));
1039 }
1040 if ((detail != 0) && (matchPtr == NULL)) {
1041 key.detail = 0;
1042 hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1043 if (hPtr != NULL) {
1044 matchPtr = MatchPatterns(bindPtr,
1045 (PatSeq *) Tcl_GetHashValue(hPtr));
1046 }
1047 }
1048
1049 if (matchPtr != NULL) {
1050
1051 /*
1052 * %-substitution can increase the length of the command.
1053 * This code handles three cases: (a) no substitution;
1054 * (b) substitution results in short command (use space
1055 * on stack); and (c) substitution results in long
1056 * command (malloc it).
1057 */
1058
1059#define STATIC_SPACE 200
1060 char shortSpace[STATIC_SPACE];
1061 int result;
1062
1063 if (matchPtr->flags & PAT_PERCENTS) {
1064 char *p;
1065 p = ExpandPercents(matchPtr->command, eventPtr,
1066 (KeySym) detail, shortSpace, STATIC_SPACE);
1067 result = Tcl_GlobalEval(bindPtr->interp, p);
1068 if (p != shortSpace) {
1069 ckfree(p);
1070 }
1071 } else {
1072 /*
1073 * The code below is tricky in order allow the binding to
1074 * be modified or deleted as part of the command that the
1075 * binding invokes. Must make sure that the actual command
1076 * string isn't freed until the command completes, and must
1077 * copy the address of this string into a local variable
1078 * in case it's modified by the command.
1079 */
1080
1081 char *cmd = matchPtr->command;
1082
1083 Tk_Preserve((ClientData) cmd);
1084 result = Tcl_GlobalEval(bindPtr->interp, cmd);
1085 Tk_Release((ClientData) cmd);
1086 }
1087 if (result != TCL_OK) {
1088 Tcl_AddErrorInfo(bindPtr->interp,
1089 "\n (command bound to event)");
1090 TkBindError(bindPtr->interp);
1091 }
1092 return;
1093 }
1094 }
1095}
1096\f
1097/*
1098 *----------------------------------------------------------------------
1099 *
1100 * FindSequence --
1101 *
1102 * Find the entry in a binding table that corresponds to a
1103 * particular pattern string, and return a pointer to that
1104 * entry.
1105 *
1106 * Results:
1107 * The return value is normally a pointer to the PatSeq
1108 * in patternTable that corresponds to eventString. If an error
1109 * was found while parsing eventString, or if "create" is 0 and
1110 * no pattern sequence previously existed, then NULL is returned
1111 * and interp->result contains a message describing the problem.
1112 * If no pattern sequence previously existed for eventString, then
1113 * a new one is created with a NULL command field. In a successful
1114 * return, *maskPtr is filled in with a mask of the event types
1115 * on which the pattern sequence depends.
1116 *
1117 * Side effects:
1118 * A new pattern sequence may be created.
1119 *
1120 *----------------------------------------------------------------------
1121 */
1122
1123static PatSeq *
1124FindSequence(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
1438static char *
1439GetField(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
1476static KeySym
1477GetKeySym(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
1578static PatSeq *
1579MatchPatterns(bindPtr, psPtr)
1580 BindingTable *bindPtr; /* Information about binding table, such
1581 * as ring of recent events. */
1582 register PatSeq *psPtr; /* List of pattern sequences. */
1583{
1584 register PatSeq *bestPtr = NULL;
1585
1586 /*
1587 * Iterate over all the pattern sequences.
1588 */
1589
1590 for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1591 register XEvent *eventPtr;
1592 register Pattern *patPtr;
1593 Window window;
1594 int *detailPtr;
1595 int patCount, ringCount, flags, state;
1596
1597 /*
1598 * Iterate over all the patterns in a sequence to be
1599 * sure that they all match.
1600 */
1601
1602 eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1603 detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1604 window = eventPtr->xany.window;
1605 patPtr = psPtr->pats;
1606 patCount = psPtr->numPats;
1607 ringCount = EVENT_BUFFER_SIZE;
1608 while (patCount > 0) {
1609 if (ringCount <= 0) {
1610 goto nextSequence;
1611 }
6a5fa4e0
MG
1612 if (eventPtr->xany.type != patPtr->eventType) {
1613 /*
1614 * If the event is a mouse motion, button release,
1615 * or key release event, and it didn't match
1616 * the pattern, then just skip the event and try
1617 * the next event against the same pattern.
1618 */
1619
1620 if ((eventPtr->xany.type == MotionNotify)
1621 || (eventPtr->xany.type == ButtonRelease)
1622 || (eventPtr->xany.type == KeyRelease)
1623 || (eventPtr->xany.type == NoExpose)
4f9f8b18
MG
1624 || (eventPtr->xany.type == EnterNotify)
1625 || (eventPtr->xany.type == LeaveNotify)
6a5fa4e0
MG
1626 || (eventPtr->xany.type == GraphicsExpose)) {
1627 goto nextEvent;
1628 }
1629 goto nextSequence;
1630 }
4f9f8b18
MG
1631 if (eventPtr->xany.window != window) {
1632 goto nextSequence;
1633 }
6a5fa4e0
MG
1634
1635 flags = flagArray[eventPtr->type];
1636 if (flags & KEY_BUTTON_MOTION) {
1637 state = eventPtr->xkey.state;
1638 } else if (flags & CROSSING) {
1639 state = eventPtr->xcrossing.state;
1640 } else {
1641 state = 0;
1642 }
1643 if ((state & patPtr->needMods)
1644 != patPtr->needMods) {
1645 goto nextSequence;
1646 }
965e2fe7 1647#if 0
6a5fa4e0
MG
1648 if ((state & patPtr->hateMods) != 0) {
1649 goto nextSequence;
1650 }
965e2fe7 1651#endif
6a5fa4e0
MG
1652 if ((patPtr->detail != 0)
1653 && (patPtr->detail != *detailPtr)) {
1654 goto nextSequence;
1655 }
1656 if (psPtr->flags & PAT_NEARBY) {
1657 register XEvent *firstPtr;
1658
1659 firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
1660 if ((firstPtr->xkey.x_root
1661 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
1662 || (firstPtr->xkey.x_root
1663 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
1664 || (firstPtr->xkey.y_root
1665 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
1666 || (firstPtr->xkey.y_root
1667 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
1668 || (firstPtr->xkey.time
1669 > (eventPtr->xkey.time + NEARBY_MS))) {
1670 goto nextSequence;
1671 }
1672 }
1673 patPtr++;
1674 patCount--;
1675 nextEvent:
1676 if (eventPtr == bindPtr->eventRing) {
1677 eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1678 detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1679 } else {
1680 eventPtr--;
1681 detailPtr--;
1682 }
1683 ringCount--;
1684 }
1685
1686 /*
1687 * This sequence matches. If we've already got another match,
1688 * pick whichever is most specific. Detail is most important,
1689 * then needMods, then hateMods.
1690 */
1691
1692 if (bestPtr != NULL) {
1693 register Pattern *patPtr2;
1694 int i;
1695
1696 if (psPtr->numPats != bestPtr->numPats) {
1697 if (bestPtr->numPats > psPtr->numPats) {
1698 goto nextSequence;
1699 } else {
1700 goto newBest;
1701 }
1702 }
1703 for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
1704 i < psPtr->numPats; i++,patPtr++, patPtr2++) {
1705 if (patPtr->detail != patPtr2->detail) {
1706 if (patPtr->detail == 0) {
1707 goto nextSequence;
1708 } else {
1709 goto newBest;
1710 }
1711 }
1712 if (patPtr->needMods != patPtr2->needMods) {
1713 if ((patPtr->needMods & patPtr2->needMods)
1714 == patPtr->needMods) {
1715 goto nextSequence;
1716 } else {
1717 goto newBest;
1718 }
1719 }
1720 if (patPtr->hateMods != patPtr2->hateMods) {
1721 if ((patPtr->hateMods & patPtr2->hateMods)
1722 == patPtr2->hateMods) {
1723 goto newBest;
1724 } else {
1725 goto nextSequence;
1726 }
1727 }
1728 }
1729 goto nextSequence; /* Tie goes to newest pattern. */
1730 }
1731 newBest:
1732 bestPtr = psPtr;
1733
1734 nextSequence: continue;
1735 }
1736 return bestPtr;
1737}
1738\f
1739/*
1740 *--------------------------------------------------------------
1741 *
1742 * ExpandPercents --
1743 *
1744 * Given a command and an event, produce a new command
1745 * by replacing % constructs in the original command
1746 * with information from the X event.
1747 *
1748 * Results:
1749 * The return result is a pointer to the new %-substituted
1750 * command. If the command fits in the space at after, then
1751 * the return value is after. If the command is too large
1752 * to fit at after, then the return value is a pointer to
1753 * a malloc-ed buffer holding the command; in this case it
1754 * is the caller's responsibility to free up the buffer when
1755 * finished with it.
1756 *
1757 * Side effects:
1758 * None.
1759 *
1760 *--------------------------------------------------------------
1761 */
1762
1763static char *
1764ExpandPercents(before, eventPtr, keySym, after, afterSize)
1765 register char *before; /* Command containing percent
1766 * expressions to be replaced. */
1767 register XEvent *eventPtr; /* X event containing information
1768 * to be used in % replacements. */
1769 KeySym keySym; /* KeySym: only relevant for
1770 * KeyPress and KeyRelease events). */
1771 char *after; /* Place to generate new expanded
1772 * command. Must contain at least
1773 * "afterSize" bytes of space. */
1774 int afterSize; /* Number of bytes of space available at
1775 * after. */
1776{
1777 register char *buffer; /* Pointer to buffer currently being used
1778 * as destination. */
1779 register char *dst; /* Pointer to next place to store character
1780 * in substituted string. */
1781 int spaceLeft; /* Indicates how many more non-null bytes
1782 * may be stored at *dst before space
1783 * runs out. */
1784 int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1785 * list element. */
1786 int number, flags;
1787#define NUM_SIZE 40
1788 register char *string;
1789 char numStorage[NUM_SIZE+1];
1790
1791 if (eventPtr->type < LASTEvent) {
1792 flags = flagArray[eventPtr->type];
1793 } else {
1794 flags = 0;
1795 }
1796 dst = buffer = after;
1797 spaceLeft = afterSize - 1;
1798 while (*before != 0) {
1799 if (*before != '%') {
1800
1801 /*
1802 * Expand the destination string if necessary.
1803 */
1804
1805 if (spaceLeft <= 0) {
1806 char *newSpace;
1807
1808 newSpace = (char *) ckalloc((unsigned) (2*afterSize));
1809 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
1810 afterSize *= 2;
1811 dst = newSpace + (dst - buffer);
1812 if (buffer != after) {
1813 ckfree(buffer);
1814 }
1815 buffer = newSpace;
1816 spaceLeft = afterSize - (dst-buffer) - 1;
1817 }
1818 *dst = *before;
1819 dst++;
1820 before++;
1821 spaceLeft--;
1822 continue;
1823 }
1824
1825 number = 0;
1826 string = "??";
1827 switch (before[1]) {
1828 case '#':
1829 number = eventPtr->xany.serial;
1830 goto doNumber;
1831 case 'a':
1832 number = (int) eventPtr->xconfigure.above;
1833 goto doNumber;
1834 case 'b':
1835 number = eventPtr->xbutton.button;
1836 goto doNumber;
1837 case 'c':
1838 if (flags & EXPOSE) {
1839 number = eventPtr->xexpose.count;
1840 } else if (flags & MAPPING) {
1841 number = eventPtr->xmapping.count;
1842 }
1843 goto doNumber;
1844 case 'd':
1845 if (flags & (CROSSING|FOCUS)) {
1846 switch (eventPtr->xcrossing.detail) {
1847 case NotifyAncestor:
1848 string = "NotifyAncestor";
1849 break;
1850 case NotifyVirtual:
1851 string = "NotifyVirtual";
1852 break;
1853 case NotifyInferior:
1854 string = "NotifyInferior";
1855 break;
1856 case NotifyNonlinear:
1857 string = "NotifyNonlinear";
1858 break;
1859 case NotifyNonlinearVirtual:
1860 string = "NotifyNonlinearVirtual";
1861 break;
1862 case NotifyPointer:
1863 string = "NotifyPointer";
1864 break;
1865 case NotifyPointerRoot:
1866 string = "NotifyPointerRoot";
1867 break;
1868 case NotifyDetailNone:
1869 string = "NotifyDetailNone";
1870 break;
1871 }
1872 } else if (flags & CONFIG_REQ) {
1873 switch (eventPtr->xconfigurerequest.detail) {
1874 case Above:
1875 string = "Above";
1876 break;
1877 case Below:
1878 string = "Below";
1879 break;
1880 case TopIf:
1881 string = "TopIf";
1882 break;
1883 case BottomIf:
1884 string = "BottomIf";
1885 break;
1886 case Opposite:
1887 string = "Opposite";
1888 break;
1889 }
1890 }
1891 goto doString;
1892 case 'f':
1893 number = eventPtr->xcrossing.focus;
1894 goto doNumber;
1895 case 'h':
1896 if (flags & EXPOSE) {
1897 number = eventPtr->xexpose.height;
1898 } else if (flags & (CONFIG|CONFIG_REQ)) {
1899 number = eventPtr->xconfigure.height;
1900 } else if (flags & RESIZE_REQ) {
1901 number = eventPtr->xresizerequest.height;
1902 }
1903 goto doNumber;
1904 case 'k':
1905 number = eventPtr->xkey.keycode;
1906 goto doNumber;
1907 case 'm':
1908 if (flags & CROSSING) {
1909 number = eventPtr->xcrossing.mode;
1910 } else if (flags & FOCUS) {
1911 number = eventPtr->xfocus.mode;
1912 }
1913 switch (number) {
1914 case NotifyNormal:
1915 string = "NotifyNormal";
1916 break;
1917 case NotifyGrab:
1918 string = "NotifyGrab";
1919 break;
1920 case NotifyUngrab:
1921 string = "NotifyUngrab";
1922 break;
1923 case NotifyWhileGrabbed:
1924 string = "NotifyWhileGrabbed";
1925 break;
1926 }
1927 goto doString;
1928 case 'o':
1929 if (flags & CREATE) {
1930 number = eventPtr->xcreatewindow.override_redirect;
1931 } else if (flags & MAP) {
1932 number = eventPtr->xmap.override_redirect;
1933 } else if (flags & REPARENT) {
1934 number = eventPtr->xreparent.override_redirect;
1935 } else if (flags & CONFIG) {
1936 number = eventPtr->xconfigure.override_redirect;
1937 }
1938 goto doNumber;
1939 case 'p':
1940 switch (eventPtr->xcirculate.place) {
1941 case PlaceOnTop:
1942 string = "PlaceOnTop";
1943 break;
1944 case PlaceOnBottom:
1945 string = "PlaceOnBottom";
1946 break;
1947 }
1948 goto doString;
1949 case 's':
1950 if (flags & KEY_BUTTON_MOTION) {
1951 number = eventPtr->xkey.state;
1952 } else if (flags & CROSSING) {
1953 number = eventPtr->xcrossing.state;
1954 } else if (flags & VISIBILITY) {
1955 switch (eventPtr->xvisibility.state) {
1956 case VisibilityUnobscured:
1957 string = "VisibilityUnobscured";
1958 break;
1959 case VisibilityPartiallyObscured:
1960 string = "VisibilityPartiallyObscured";
1961 break;
1962 case VisibilityFullyObscured:
1963 string = "VisibilityFullyObscured";
1964 break;
1965 }
1966 goto doString;
1967 }
1968 goto doNumber;
1969 case 't':
1970 if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
1971 number = (int) eventPtr->xkey.time;
1972 } else if (flags & SEL_REQ) {
1973 number = (int) eventPtr->xselectionrequest.time;
1974 } else if (flags & SEL_NOTIFY) {
1975 number = (int) eventPtr->xselection.time;
1976 }
1977 goto doNumber;
1978 case 'v':
1979 number = eventPtr->xconfigurerequest.value_mask;
1980 goto doNumber;
1981 case 'w':
1982 if (flags & EXPOSE) {
1983 number = eventPtr->xexpose.width;
1984 } else if (flags & (CONFIG|CONFIG_REQ)) {
1985 number = eventPtr->xconfigure.width;
1986 } else if (flags & RESIZE_REQ) {
1987 number = eventPtr->xresizerequest.width;
1988 }
1989 goto doNumber;
1990 case 'x':
1991 if (flags & KEY_BUTTON_MOTION) {
1992 number = eventPtr->xkey.x;
1993 } else if (flags & EXPOSE) {
1994 number = eventPtr->xexpose.x;
1995 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
1996 number = eventPtr->xcreatewindow.x;
1997 } else if (flags & REPARENT) {
1998 number = eventPtr->xreparent.x;
1999 } else if (flags & CROSSING) {
2000 number = eventPtr->xcrossing.x;
2001 }
2002 goto doNumber;
2003 case 'y':
2004 if (flags & KEY_BUTTON_MOTION) {
2005 number = eventPtr->xkey.y;
2006 } else if (flags & EXPOSE) {
2007 number = eventPtr->xexpose.y;
2008 } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
2009 number = eventPtr->xcreatewindow.y;
2010 } else if (flags & REPARENT) {
2011 number = eventPtr->xreparent.y;
2012 } else if (flags & CROSSING) {
2013 number = eventPtr->xcrossing.y;
2014
2015 }
2016 goto doNumber;
2017 case 'A':
2018 if ((eventPtr->type == KeyPress)
2019 || (eventPtr->type == KeyRelease)) {
2020 int numChars;
2021
2022 numChars = XLookupString(&eventPtr->xkey, numStorage,
2023 NUM_SIZE, (KeySym *) NULL,
2024 (XComposeStatus *) NULL);
2025 numStorage[numChars] = '\0';
2026 string = numStorage;
2027 }
2028 goto doString;
2029 case 'B':
2030 number = eventPtr->xcreatewindow.border_width;
2031 goto doNumber;
2032 case 'D':
2033 number = (int) eventPtr->xany.display;
2034 goto doNumber;
2035 case 'E':
2036 number = (int) eventPtr->xany.send_event;
2037 goto doNumber;
2038 case 'K':
2039 if ((eventPtr->type == KeyPress)
2040 || (eventPtr->type == KeyRelease)) {
2041 register KeySymInfo *kPtr;
2042
2043 for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
2044 if (kPtr->value == keySym) {
2045 string = kPtr->name;
2046 break;
2047 }
2048 }
2049 }
2050 goto doString;
2051 case 'N':
2052 number = (int) keySym;
2053 goto doNumber;
2054 case 'R':
2055 number = (int) eventPtr->xkey.root;
2056 goto doNumber;
2057 case 'S':
2058 number = (int) eventPtr->xkey.subwindow;
2059 goto doNumber;
2060 case 'T':
2061 number = eventPtr->type;
2062 goto doNumber;
2063 case 'W': {
2064 TkWindow *winPtr;
2065
2066 if (XFindContext(eventPtr->xany.display, eventPtr->xany.window,
2067 tkWindowContext, (void *) &winPtr) == 0) {
2068 string = winPtr->pathName;
2069 } else {
2070 string = "??";
2071 }
2072 goto doString;
2073 }
2074 case 'X':
2075 number = eventPtr->xkey.x_root;
2076 goto doNumber;
2077 case 'Y':
2078 number = eventPtr->xkey.y_root;
2079 goto doNumber;
2080 default:
2081 numStorage[0] = before[1];
2082 numStorage[1] = '\0';
2083 string = numStorage;
2084 goto doString;
2085 }
2086
2087 doNumber:
2088 sprintf(numStorage, "%d", number);
2089 string = numStorage;
2090
2091 doString:
2092 spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2093 if (spaceNeeded >= spaceLeft) {
2094 char *newSpace;
2095
2096 newSpace = (char *) ckalloc((unsigned)
2097 (afterSize + spaceNeeded + 50));
2098 memcpy((VOID *) newSpace, (VOID *) buffer, afterSize);
2099 afterSize += spaceNeeded + 50;
2100 dst = newSpace + (dst - buffer);
2101 if (buffer != after) {
2102 ckfree(buffer);
2103 }
2104 buffer = newSpace;
2105 spaceLeft = afterSize - (dst-buffer) - 1;
2106 }
2107 spaceNeeded = Tcl_ConvertElement(string, dst,
2108 cvtFlags | TCL_DONT_USE_BRACES);
2109 dst += spaceNeeded;
2110 spaceLeft -= spaceNeeded;
2111 before += 2;
2112 }
2113 *dst = '\0';
2114 return buffer;
2115}
2116\f
2117/*
2118 *----------------------------------------------------------------------
2119 *
2120 * TkBindError --
2121 *
2122 * This procedure is invoked to handle errors that occur in Tcl
2123 * commands that are invoked in "background" (e.g. from event or
2124 * timer bindings).
2125 *
2126 * Results:
2127 * None.
2128 *
2129 * Side effects:
2130 * The command "tkerror" is invoked to process the error, passing
2131 * it the error message. If that fails, then an error message
2132 * is output on stderr.
2133 *
2134 *----------------------------------------------------------------------
2135 */
2136
2137void
2138TkBindError(interp)
2139 Tcl_Interp *interp; /* Interpreter in which an error has
2140 * occurred. */
2141{
2142 char *argv[2];
2143 char *command;
2144 char *error;
2145 char *errorInfo, *tmp;
2146 int result;
2147
2148 error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1));
2149 strcpy(error, interp->result);
2150 tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2151 if (tmp == NULL) {
2152 errorInfo = error;
2153 } else {
2154 errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1));
2155 strcpy(errorInfo, tmp);
2156 }
2157 argv[0] = "tkerror";
2158 argv[1] = error;
2159 command = Tcl_Merge(2, argv);
2160 result = Tcl_GlobalEval(interp, command);
2161 if (result != TCL_OK) {
2162 if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
2163 fprintf(stderr, "%s\n", errorInfo);
2164 } else {
2165 fprintf(stderr, "tkerror failed to handle background error.\n");
2166 fprintf(stderr, " Original error: %s\n", error);
2167 fprintf(stderr, " Error in tkerror: %s\n", interp->result);
2168 }
2169 }
2170 Tcl_ResetResult(interp);
2171 ckfree(command);
2172 ckfree(error);
2173 if (errorInfo != error) {
2174 ckfree(errorInfo);
2175 }
2176}
Impressum, Datenschutz