|
|
1.1 ! root 1: /* ! 2: * tkBind.c -- ! 3: * ! 4: * This file provides procedures that associate Tcl commands ! 5: * with X events or sequences of X events. ! 6: * ! 7: * Copyright 1989-1991 Regents of the University of California ! 8: * Permission to use, copy, modify, and distribute this ! 9: * software and its documentation for any purpose and without ! 10: * fee is hereby granted, provided that the above copyright ! 11: * notice appear in all copies. The University of California ! 12: * makes no representations about the suitability of this ! 13: * software for any purpose. It is provided "as is" without ! 14: * express or implied warranty. ! 15: */ ! 16: ! 17: #ifndef lint ! 18: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkBind.c,v 1.48 92/08/10 16:55:24 ouster Exp $ SPRITE (Berkeley)"; ! 19: #endif /* not lint */ ! 20: ! 21: #include "tkconfig.h" ! 22: #include "tkint.h" ! 23: ! 24: /* ! 25: * The structure below represents a binding table. A binding table ! 26: * represents a domain in which event bindings may occur. It includes ! 27: * a space of objects relative to which events occur (usually windows, ! 28: * but not always), a history of recent events in the domain, and ! 29: * a set of mappings that associate particular Tcl commands with sequences ! 30: * of events in the domain. Multiple binding tables may exist at once, ! 31: * either because there are multiple applications open, or because there ! 32: * are multiple domains within an application with separate event ! 33: * bindings for each (for example, each canvas widget has a separate ! 34: * binding table for associating events with the items in the canvas). ! 35: */ ! 36: ! 37: #define EVENT_BUFFER_SIZE 10 ! 38: typedef struct BindingTable { ! 39: XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events ! 40: * (higher indices are for more recent ! 41: * events). */ ! 42: int detailRing[EVENT_BUFFER_SIZE]; /* "Detail" information (keySym or ! 43: * button or 0) for each entry in ! 44: * eventRing. */ ! 45: int curEvent; /* Index in eventRing of most recent ! 46: * event. Newer events have higher ! 47: * indices. */ ! 48: Tcl_HashTable patternTable; /* Used to map from an event to a list ! 49: * of patterns that may match that ! 50: * event. Keys are PatternTableKey ! 51: * structs, values are (PatSeq *). */ ! 52: Tcl_HashTable objectTable; /* Used to map from an object to a list ! 53: * of patterns associated with that ! 54: * object. Keys are ClientData, ! 55: * values are (PatSeq *). */ ! 56: Tcl_Interp *interp; /* Interpreter in which commands are ! 57: * executed. */ ! 58: } BindingTable; ! 59: ! 60: /* ! 61: * Structures of the following form are used as keys in the patternTable ! 62: * for a binding table: ! 63: */ ! 64: ! 65: typedef struct PatternTableKey { ! 66: ClientData object; /* Identifies object (or class of objects) ! 67: * relative to which event occurred. For ! 68: * example, in the widget binding table for ! 69: * an application this is the path name of ! 70: * a widget, or a widget class, or "all". */ ! 71: int type; /* Type of event (from X). */ ! 72: int detail; /* Additional information, such as ! 73: * keysym or button, or 0 if nothing ! 74: * additional.*/ ! 75: } PatternTableKey; ! 76: ! 77: /* ! 78: * The following structure defines a pattern, which is matched ! 79: * against X events as part of the process of converting X events ! 80: * into Tcl commands. ! 81: */ ! 82: ! 83: typedef struct Pattern { ! 84: int eventType; /* Type of X event, e.g. ButtonPress. */ ! 85: int needMods; /* Mask of modifiers that must be ! 86: * present (0 means no modifiers are ! 87: * required). */ ! 88: int hateMods; /* Mask of modifiers that must not be ! 89: * present (0 means any modifiers are ! 90: * OK). */ ! 91: int detail; /* Additional information that must ! 92: * match event. Normally this is 0, ! 93: * meaning no additional information ! 94: * must match. For KeyPress and ! 95: * KeyRelease events, a keySym may ! 96: * be specified to select a ! 97: * particular keystroke (0 means any ! 98: * keystrokes). For button events, ! 99: * specifies a particular button (0 ! 100: * means any buttons are OK). */ ! 101: } Pattern; ! 102: ! 103: /* ! 104: * The structure below defines a pattern sequence, which consists ! 105: * of one or more patterns. In order to trigger, a pattern ! 106: * sequence must match the most recent X events (first pattern ! 107: * to most recent event, next pattern to next event, and so on). ! 108: */ ! 109: ! 110: typedef struct PatSeq { ! 111: int numPats; /* Number of patterns in sequence ! 112: * (usually 1). */ ! 113: char *command; /* Command to invoke when this ! 114: * pattern sequence matches (malloc-ed). */ ! 115: int flags; /* Miscellaneous flag values; see ! 116: * below for definitions. */ ! 117: struct PatSeq *nextSeqPtr; ! 118: /* Next in list of all pattern ! 119: * sequences that have the same ! 120: * initial pattern. NULL means ! 121: * end of list. */ ! 122: Tcl_HashEntry *hPtr; /* Pointer to hash table entry for ! 123: * the initial pattern. This is the ! 124: * head of the list of which nextSeqPtr ! 125: * forms a part. */ ! 126: ClientData object; /* Identifies object with which event is ! 127: * associated (e.g. window). */ ! 128: struct PatSeq *nextObjPtr; ! 129: /* Next in list of all pattern ! 130: * sequences for the same object ! 131: * (NULL for end of list). Needed to ! 132: * implement Tk_DeleteAllBindings. */ ! 133: Pattern pats[1]; /* Array of "numPats" patterns. Only ! 134: * one element is declared here but ! 135: * in actuality enough space will be ! 136: * allocated for "numPats" patterns. ! 137: * To match, pats[0] must match event ! 138: * n, pats[1] must match event n-1, ! 139: * etc. */ ! 140: } PatSeq; ! 141: ! 142: /* ! 143: * Flag values for PatSeq structures: ! 144: * ! 145: * PAT_NEARBY 1 means that all of the events matching ! 146: * this sequence must occur with nearby X ! 147: * and Y mouse coordinates and close in time. ! 148: * This is typically used to restrict multiple ! 149: * button presses. ! 150: * PAT_PERCENTS 1 means that the command for this pattern ! 151: * requires percent substitution. 0 means there ! 152: * are no percents in the command. ! 153: */ ! 154: ! 155: #define PAT_NEARBY 1 ! 156: #define PAT_PERCENTS 2 ! 157: ! 158: /* ! 159: * Constants that define how close together two events must be ! 160: * in milliseconds or pixels to meet the PAT_NEARBY constraint: ! 161: */ ! 162: ! 163: #define NEARBY_PIXELS 5 ! 164: #define NEARBY_MS 500 ! 165: ! 166: /* ! 167: * The data structure and hash table below are used to map from ! 168: * textual keysym names to keysym numbers. This structure is ! 169: * present here because the corresponding X procedures are ! 170: * ridiculously slow. ! 171: */ ! 172: ! 173: typedef struct { ! 174: char *name; /* Name of keysym. */ ! 175: KeySym value; /* Numeric identifier for keysym. */ ! 176: } KeySymInfo; ! 177: KeySymInfo keyArray[] = { ! 178: #ifndef lint ! 179: #include "ks_names.h" ! 180: #endif ! 181: (char *) NULL, 0 ! 182: }; ! 183: static Tcl_HashTable keySymTable; /* Hashed form of above structure. */ ! 184: ! 185: static int initialized = 0; ! 186: ! 187: /* ! 188: * A hash table is kept to map from the string names of event ! 189: * modifiers to information about those modifiers. The structure ! 190: * for storing this information, and the hash table built at ! 191: * initialization time, are defined below. ! 192: */ ! 193: ! 194: typedef struct { ! 195: char *name; /* Name of modifier. */ ! 196: int mask; /* Button/modifier mask value, * such as Button1Mask. */ ! 197: int flags; /* Various flags; see below for ! 198: * definitions. */ ! 199: } ModInfo; ! 200: ! 201: /* ! 202: * Flags for ModInfo structures: ! 203: * ! 204: * DOUBLE - Non-zero means duplicate this event, ! 205: * e.g. for double-clicks. ! 206: * TRIPLE - Non-zero means triplicate this event, ! 207: * e.g. for triple-clicks. ! 208: * ANY - Non-zero means that this event allows ! 209: * any unspecified modifiers. ! 210: */ ! 211: ! 212: #define DOUBLE 1 ! 213: #define TRIPLE 2 ! 214: #define ANY 4 ! 215: ! 216: static ModInfo modArray[] = { ! 217: "Control", ControlMask, 0, ! 218: "Shift", ShiftMask, 0, ! 219: "Lock", LockMask, 0, ! 220: "B1", Button1Mask, 0, ! 221: "Button1", Button1Mask, 0, ! 222: "B2", Button2Mask, 0, ! 223: "Button2", Button2Mask, 0, ! 224: "B3", Button3Mask, 0, ! 225: "Button3", Button3Mask, 0, ! 226: "B4", Button4Mask, 0, ! 227: "Button4", Button4Mask, 0, ! 228: "B5", Button5Mask, 0, ! 229: "Button5", Button5Mask, 0, ! 230: "Mod1", Mod1Mask, 0, ! 231: "M1", Mod1Mask, 0, ! 232: "Meta", Mod1Mask, 0, ! 233: "M", Mod1Mask, 0, ! 234: "Mod2", Mod2Mask, 0, ! 235: "M2", Mod2Mask, 0, ! 236: "Alt", Mod2Mask, 0, ! 237: "Mod3", Mod3Mask, 0, ! 238: "M3", Mod3Mask, 0, ! 239: "Mod4", Mod4Mask, 0, ! 240: "M4", Mod4Mask, 0, ! 241: "Mod5", Mod5Mask, 0, ! 242: "M5", Mod5Mask, 0, ! 243: "Double", 0, DOUBLE, ! 244: "Triple", 0, TRIPLE, ! 245: "Any", 0, ANY, ! 246: NULL, 0, 0}; ! 247: static Tcl_HashTable modTable; ! 248: ! 249: /* ! 250: * This module also keeps a hash table mapping from event names ! 251: * to information about those events. The structure, an array ! 252: * to use to initialize the hash table, and the hash table are ! 253: * all defined below. ! 254: */ ! 255: ! 256: typedef struct { ! 257: char *name; /* Name of event. */ ! 258: int type; /* Event type for X, such as ! 259: * ButtonPress. */ ! 260: int eventMask; /* Mask bits (for XSelectInput) ! 261: * for this event type. */ ! 262: } EventInfo; ! 263: ! 264: /* ! 265: * Note: some of the masks below are an OR-ed combination of ! 266: * several masks. This is necessary because X doesn't report ! 267: * up events unless you also ask for down events. Also, X ! 268: * doesn't report button state in motion events unless you've ! 269: * asked about button events. ! 270: */ ! 271: ! 272: static EventInfo eventArray[] = { ! 273: "Motion", MotionNotify, ! 274: ButtonPressMask|PointerMotionMask, ! 275: "Button", ButtonPress, ButtonPressMask, ! 276: "ButtonPress", ButtonPress, ButtonPressMask, ! 277: "ButtonRelease", ButtonRelease, ! 278: ButtonPressMask|ButtonReleaseMask, ! 279: "Colormap", ColormapNotify, ColormapChangeMask, ! 280: "Enter", EnterNotify, EnterWindowMask, ! 281: "Leave", LeaveNotify, LeaveWindowMask, ! 282: "Expose", Expose, ExposureMask, ! 283: "FocusIn", FocusIn, FocusChangeMask, ! 284: "FocusOut", FocusOut, FocusChangeMask, ! 285: "Keymap", KeymapNotify, KeymapStateMask, ! 286: "Key", KeyPress, KeyPressMask, ! 287: "KeyPress", KeyPress, KeyPressMask, ! 288: "KeyRelease", KeyRelease, ! 289: KeyPressMask|KeyReleaseMask, ! 290: "Property", PropertyNotify, PropertyChangeMask, ! 291: "ResizeRequest", ResizeRequest, ResizeRedirectMask, ! 292: "Circulate", CirculateNotify, StructureNotifyMask, ! 293: "Configure", ConfigureNotify, StructureNotifyMask, ! 294: "Destroy", DestroyNotify, StructureNotifyMask, ! 295: "Gravity", GravityNotify, StructureNotifyMask, ! 296: "Map", MapNotify, StructureNotifyMask, ! 297: "Reparent", ReparentNotify, StructureNotifyMask, ! 298: "Unmap", UnmapNotify, StructureNotifyMask, ! 299: "Visibility", VisibilityNotify, VisibilityChangeMask, ! 300: "CirculateRequest", CirculateRequest, SubstructureRedirectMask, ! 301: "ConfigureRequest", ConfigureRequest, SubstructureRedirectMask, ! 302: "MapRequest", MapRequest, SubstructureRedirectMask, ! 303: (char *) NULL, 0, 0}; ! 304: static Tcl_HashTable eventTable; ! 305: ! 306: /* ! 307: * The defines and table below are used to classify events into ! 308: * various groups. The reason for this is that logically identical ! 309: * fields (e.g. "state") appear at different places in different ! 310: * types of events. The classification masks can be used to figure ! 311: * out quickly where to extract information from events. ! 312: */ ! 313: ! 314: #define KEY_BUTTON_MOTION 0x1 ! 315: #define CROSSING 0x2 ! 316: #define FOCUS 0x4 ! 317: #define EXPOSE 0x8 ! 318: #define VISIBILITY 0x10 ! 319: #define CREATE 0x20 ! 320: #define MAP 0x40 ! 321: #define REPARENT 0x80 ! 322: #define CONFIG 0x100 ! 323: #define CONFIG_REQ 0x200 ! 324: #define RESIZE_REQ 0x400 ! 325: #define GRAVITY 0x800 ! 326: #define PROP 0x0100 ! 327: #define SEL_CLEAR 0x2000 ! 328: #define SEL_REQ 0x4000 ! 329: #define SEL_NOTIFY 0x8000 ! 330: #define COLORMAP 0x10000 ! 331: #define MAPPING 0x20000 ! 332: ! 333: static int flagArray[LASTEvent] = { ! 334: /* Not used */ 0, ! 335: /* Not used */ 0, ! 336: /* KeyPress */ KEY_BUTTON_MOTION, ! 337: /* KeyRelease */ KEY_BUTTON_MOTION, ! 338: /* ButtonPress */ KEY_BUTTON_MOTION, ! 339: /* ButtonRelease */ KEY_BUTTON_MOTION, ! 340: /* MotionNotify */ KEY_BUTTON_MOTION, ! 341: /* EnterNotify */ CROSSING, ! 342: /* LeaveNotify */ CROSSING, ! 343: /* FocusIn */ FOCUS, ! 344: /* FocusOut */ FOCUS, ! 345: /* KeymapNotify */ 0, ! 346: /* Expose */ EXPOSE, ! 347: /* GraphicsExpose */ EXPOSE, ! 348: /* NoExpose */ 0, ! 349: /* VisibilityNotify */ VISIBILITY, ! 350: /* CreateNotify */ CREATE, ! 351: /* DestroyNotify */ 0, ! 352: /* UnmapNotify */ 0, ! 353: /* MapNotify */ MAP, ! 354: /* MapRequest */ 0, ! 355: /* ReparentNotify */ REPARENT, ! 356: /* ConfigureNotify */ CONFIG, ! 357: /* ConfigureRequest */ CONFIG_REQ, ! 358: /* GravityNotify */ 0, ! 359: /* ResizeRequest */ RESIZE_REQ, ! 360: /* CirculateNotify */ 0, ! 361: /* CirculateRequest */ 0, ! 362: /* PropertyNotify */ PROP, ! 363: /* SelectionClear */ SEL_CLEAR, ! 364: /* SelectionRequest */ SEL_REQ, ! 365: /* SelectionNotify */ SEL_NOTIFY, ! 366: /* ColormapNotify */ COLORMAP, ! 367: /* ClientMessage */ 0, ! 368: /* MappingNotify */ MAPPING ! 369: }; ! 370: ! 371: /* ! 372: * Forward declarations for procedures defined later in this ! 373: * file: ! 374: */ ! 375: ! 376: static char * ExpandPercents _ANSI_ARGS_((char *before, ! 377: XEvent *eventPtr, KeySym keySym, char *after, ! 378: int afterSize)); ! 379: static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp, ! 380: BindingTable *bindPtr, ClientData object, ! 381: char *eventString, int create, ! 382: unsigned long *maskPtr)); ! 383: static char * GetField _ANSI_ARGS_((char *p, char *copy, int size)); ! 384: static KeySym GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr, ! 385: XEvent *eventPtr)); ! 386: static PatSeq * MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr, ! 387: PatSeq *psPtr)); ! 388: ! 389: /* ! 390: *-------------------------------------------------------------- ! 391: * ! 392: * Tk_CreateBindingTable -- ! 393: * ! 394: * Set up a new domain in which event bindings may be created. ! 395: * ! 396: * Results: ! 397: * The return value is a token for the new table, which must ! 398: * be passed to procedures like Tk_CreatBinding. ! 399: * ! 400: * Side effects: ! 401: * Memory is allocated for the new table. ! 402: * ! 403: *-------------------------------------------------------------- ! 404: */ ! 405: ! 406: Tk_BindingTable ! 407: Tk_CreateBindingTable(interp) ! 408: Tcl_Interp *interp; /* Interpreter to associate with the binding ! 409: * table: commands are executed in this ! 410: * interpreter. */ ! 411: { ! 412: register BindingTable *bindPtr; ! 413: int i; ! 414: ! 415: /* ! 416: * If this is the first time a binding table has been created, ! 417: * initialize the global data structures. ! 418: */ ! 419: ! 420: if (!initialized) { ! 421: register KeySymInfo *kPtr; ! 422: register Tcl_HashEntry *hPtr; ! 423: register ModInfo *modPtr; ! 424: register EventInfo *eiPtr; ! 425: int dummy; ! 426: ! 427: initialized = 1; ! 428: ! 429: Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS); ! 430: for (kPtr = keyArray; kPtr->name != NULL; kPtr++) { ! 431: hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy); ! 432: Tcl_SetHashValue(hPtr, kPtr->value); ! 433: } ! 434: ! 435: Tcl_InitHashTable(&modTable, TCL_STRING_KEYS); ! 436: for (modPtr = modArray; modPtr->name != NULL; modPtr++) { ! 437: hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy); ! 438: Tcl_SetHashValue(hPtr, modPtr); ! 439: } ! 440: ! 441: Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS); ! 442: for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) { ! 443: hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy); ! 444: Tcl_SetHashValue(hPtr, eiPtr); ! 445: } ! 446: } ! 447: ! 448: /* ! 449: * Create and initialize a new binding table. ! 450: */ ! 451: ! 452: bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable)); ! 453: for (i = 0; i < EVENT_BUFFER_SIZE; i++) { ! 454: bindPtr->eventRing[i].type = -1; ! 455: } ! 456: bindPtr->curEvent = 0; ! 457: Tcl_InitHashTable(&bindPtr->patternTable, ! 458: sizeof(PatternTableKey)/sizeof(int)); ! 459: Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS); ! 460: bindPtr->interp = interp; ! 461: return (Tk_BindingTable) bindPtr; ! 462: } ! 463: ! 464: /* ! 465: *-------------------------------------------------------------- ! 466: * ! 467: * Tk_DeleteBindingTable -- ! 468: * ! 469: * Destroy a binding table and free up all its memory. ! 470: * The caller should not use bindingTable again after ! 471: * this procedure returns. ! 472: * ! 473: * Results: ! 474: * None. ! 475: * ! 476: * Side effects: ! 477: * Memory is freed. ! 478: * ! 479: *-------------------------------------------------------------- ! 480: */ ! 481: ! 482: void ! 483: Tk_DeleteBindingTable(bindingTable) ! 484: Tk_BindingTable bindingTable; /* Token for the binding table to ! 485: * destroy. */ ! 486: { ! 487: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 488: PatSeq *psPtr, *nextPtr; ! 489: Tcl_HashEntry *hPtr; ! 490: Tcl_HashSearch search; ! 491: ! 492: /* ! 493: * Find and delete all of the patterns associated with the binding ! 494: * table. ! 495: */ ! 496: ! 497: for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); ! 498: hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { ! 499: for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); ! 500: psPtr != NULL; psPtr = nextPtr) { ! 501: nextPtr = psPtr->nextSeqPtr; ! 502: Tk_EventuallyFree((ClientData) psPtr->command, ! 503: (Tk_FreeProc *) free); ! 504: ckfree((char *) psPtr); ! 505: } ! 506: } ! 507: ! 508: /* ! 509: * Clean up the rest of the information associated with the ! 510: * binding table. ! 511: */ ! 512: ! 513: Tcl_DeleteHashTable(&bindPtr->patternTable); ! 514: Tcl_DeleteHashTable(&bindPtr->objectTable); ! 515: ckfree((char *) bindPtr); ! 516: } ! 517: ! 518: /* ! 519: *-------------------------------------------------------------- ! 520: * ! 521: * Tk_CreateBinding -- ! 522: * ! 523: * Add a binding to a binding table, so that future calls to ! 524: * Tk_BindEvent may execute the command in the binding. ! 525: * ! 526: * Results: ! 527: * The return value is 0 if an error occurred while setting ! 528: * up the binding. In this case, an error message will be ! 529: * left in interp->result. If all went well then the return ! 530: * value is a mask of the event types that must be made ! 531: * available to Tk_BindEvent in order to properly detect when ! 532: * this binding triggers. This value can be used to determine ! 533: * what events to select for in a window, for example. ! 534: * ! 535: * Side effects: ! 536: * The new binding may cause future calls to Tk_BindEvent to ! 537: * behave differently than they did previously. ! 538: * ! 539: *-------------------------------------------------------------- ! 540: */ ! 541: ! 542: unsigned long ! 543: Tk_CreateBinding(interp, bindingTable, object, eventString, command, append) ! 544: Tcl_Interp *interp; /* Used for error reporting. */ ! 545: Tk_BindingTable bindingTable; /* Table in which to create binding. */ ! 546: ClientData object; /* Token for object with which binding ! 547: * is associated. */ ! 548: char *eventString; /* String describing event sequence ! 549: * that triggers binding. */ ! 550: char *command; /* Contains Tcl command to execute ! 551: * when binding triggers. */ ! 552: int append; /* 0 means replace any existing ! 553: * binding for eventString; 1 means ! 554: * append to that binding. */ ! 555: { ! 556: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 557: register PatSeq *psPtr; ! 558: unsigned long eventMask; ! 559: ! 560: psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask); ! 561: if (psPtr == NULL) { ! 562: return 0; ! 563: } ! 564: if (append && (psPtr->command != NULL)) { ! 565: int length; ! 566: char *new; ! 567: ! 568: length = strlen(psPtr->command) + strlen(command) + 3; ! 569: new = (char *) ckalloc((unsigned) length); ! 570: sprintf(new, "%s; %s", psPtr->command, command); ! 571: Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free); ! 572: psPtr->command = new; ! 573: } else { ! 574: if (psPtr->command != NULL) { ! 575: Tk_EventuallyFree((ClientData) psPtr->command, ! 576: (Tk_FreeProc *) free); ! 577: } ! 578: psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1)); ! 579: strcpy(psPtr->command, command); ! 580: } ! 581: ! 582: /* ! 583: * See if the command contains percents and thereby requires ! 584: * percent substitution. ! 585: */ ! 586: ! 587: if (strchr(psPtr->command, '%') != NULL) { ! 588: psPtr->flags |= PAT_PERCENTS; ! 589: } ! 590: return eventMask; ! 591: } ! 592: ! 593: /* ! 594: *-------------------------------------------------------------- ! 595: * ! 596: * Tk_DeleteBinding -- ! 597: * ! 598: * Remove an event binding from a binding table. ! 599: * ! 600: * Results: ! 601: * The result is a standard Tcl return value. If an error ! 602: * occurs then interp->result will contain an error message. ! 603: * ! 604: * Side effects: ! 605: * The binding given by object and eventString is removed ! 606: * from bindingTable. ! 607: * ! 608: *-------------------------------------------------------------- ! 609: */ ! 610: ! 611: int ! 612: Tk_DeleteBinding(interp, bindingTable, object, eventString) ! 613: Tcl_Interp *interp; /* Used for error reporting. */ ! 614: Tk_BindingTable bindingTable; /* Table in which to delete binding. */ ! 615: ClientData object; /* Token for object with which binding ! 616: * is associated. */ ! 617: char *eventString; /* String describing event sequence ! 618: * that triggers binding. */ ! 619: { ! 620: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 621: register PatSeq *psPtr, *prevPtr; ! 622: unsigned long eventMask; ! 623: Tcl_HashEntry *hPtr; ! 624: ! 625: psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask); ! 626: if (psPtr == NULL) { ! 627: Tcl_ResetResult(interp); ! 628: return TCL_OK; ! 629: } ! 630: ! 631: /* ! 632: * Unlink the binding from the list for its object, then from the ! 633: * list for its pattern. ! 634: */ ! 635: ! 636: hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object); ! 637: if (hPtr == NULL) { ! 638: panic("Tk_DeleteBinding couldn't find object table entry"); ! 639: } ! 640: prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr); ! 641: if (prevPtr == psPtr) { ! 642: Tcl_SetHashValue(hPtr, psPtr->nextObjPtr); ! 643: } else { ! 644: for ( ; ; prevPtr = prevPtr->nextObjPtr) { ! 645: if (prevPtr == NULL) { ! 646: panic("Tk_DeleteBinding couldn't find on object list"); ! 647: } ! 648: if (prevPtr->nextObjPtr == psPtr) { ! 649: prevPtr->nextObjPtr = psPtr->nextObjPtr; ! 650: break; ! 651: } ! 652: } ! 653: } ! 654: prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr); ! 655: if (prevPtr == psPtr) { ! 656: if (psPtr->nextSeqPtr == NULL) { ! 657: Tcl_DeleteHashEntry(psPtr->hPtr); ! 658: } else { ! 659: Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); ! 660: } ! 661: } else { ! 662: for ( ; ; prevPtr = prevPtr->nextSeqPtr) { ! 663: if (prevPtr == NULL) { ! 664: panic("Tk_DeleteBinding couldn't find on hash chain"); ! 665: } ! 666: if (prevPtr->nextSeqPtr == psPtr) { ! 667: prevPtr->nextSeqPtr = psPtr->nextSeqPtr; ! 668: break; ! 669: } ! 670: } ! 671: } ! 672: Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free); ! 673: ckfree((char *) psPtr); ! 674: return TCL_OK; ! 675: } ! 676: ! 677: /* ! 678: *-------------------------------------------------------------- ! 679: * ! 680: * Tk_GetBinding -- ! 681: * ! 682: * Return the command associated with a given event string. ! 683: * ! 684: * Results: ! 685: * The return value is a pointer to the command string ! 686: * associated with eventString for object in the domain ! 687: * given by bindingTable. If there is no binding for ! 688: * eventString, or if eventString is improperly formed, ! 689: * then NULL is returned and an error message is left in ! 690: * interp->result. The return value is semi-static: it ! 691: * will persist until the binding is changed or deleted. ! 692: * ! 693: * Side effects: ! 694: * None. ! 695: * ! 696: *-------------------------------------------------------------- ! 697: */ ! 698: ! 699: char * ! 700: Tk_GetBinding(interp, bindingTable, object, eventString) ! 701: Tcl_Interp *interp; /* Interpreter for error reporting. */ ! 702: Tk_BindingTable bindingTable; /* Table in which to look for ! 703: * binding. */ ! 704: ClientData object; /* Token for object with which binding ! 705: * is associated. */ ! 706: char *eventString; /* String describing event sequence ! 707: * that triggers binding. */ ! 708: { ! 709: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 710: register PatSeq *psPtr; ! 711: unsigned long eventMask; ! 712: ! 713: psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask); ! 714: if (psPtr == NULL) { ! 715: return NULL; ! 716: } ! 717: return psPtr->command; ! 718: } ! 719: ! 720: /* ! 721: *-------------------------------------------------------------- ! 722: * ! 723: * Tk_GetAllBindings -- ! 724: * ! 725: * Return a list of event strings for all the bindings ! 726: * associated with a given object. ! 727: * ! 728: * Results: ! 729: * There is no return value. Interp->result is modified to ! 730: * hold a Tcl list with one entry for each binding associated ! 731: * with object in bindingTable. Each entry in the list ! 732: * contains the event string associated with one binding. ! 733: * ! 734: * Side effects: ! 735: * None. ! 736: * ! 737: *-------------------------------------------------------------- ! 738: */ ! 739: ! 740: void ! 741: Tk_GetAllBindings(interp, bindingTable, object) ! 742: Tcl_Interp *interp; /* Interpreter for error reporting. */ ! 743: Tk_BindingTable bindingTable; /* Table in which to look for ! 744: * bindings. */ ! 745: ClientData object; /* Token for object. */ ! 746: ! 747: { ! 748: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 749: register PatSeq *psPtr; ! 750: register Pattern *patPtr; ! 751: Tcl_HashEntry *hPtr; ! 752: char string[200*EVENT_BUFFER_SIZE]; ! 753: register char *p; ! 754: int patsLeft, needMods; ! 755: register ModInfo *modPtr; ! 756: ! 757: hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object); ! 758: if (hPtr == NULL) { ! 759: return; ! 760: } ! 761: for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL; ! 762: psPtr = psPtr->nextObjPtr) { ! 763: ! 764: p = string; ! 765: ! 766: /* ! 767: * For each binding, output information about each of the ! 768: * patterns in its sequence. The order of the patterns in ! 769: * the sequence is backwards from the order in which they ! 770: * must be output. ! 771: */ ! 772: ! 773: for (patsLeft = psPtr->numPats, ! 774: patPtr = &psPtr->pats[psPtr->numPats - 1]; ! 775: patsLeft > 0; patsLeft--, patPtr--) { ! 776: ! 777: /* ! 778: * Check for simple case of an ASCII character. ! 779: */ ! 780: ! 781: if ((patPtr->eventType == KeyPress) ! 782: && (patPtr->needMods == 0) ! 783: && (patPtr->hateMods == ~ShiftMask) ! 784: && isascii(patPtr->detail) && isprint(patPtr->detail) ! 785: && (patPtr->detail != '<') ! 786: && (patPtr->detail != ' ')) { ! 787: ! 788: *p = patPtr->detail; ! 789: p++; ! 790: continue; ! 791: } ! 792: ! 793: /* ! 794: * It's a more general event specification. First check ! 795: * for "Double" or "Triple", then "Any", then modifiers, ! 796: * the event type, then keysym or button detail. ! 797: */ ! 798: ! 799: *p = '<'; ! 800: p++; ! 801: if ((patsLeft > 1) && (memcmp((char *) patPtr, ! 802: (char *) (patPtr-1), sizeof(Pattern)) == 0)) { ! 803: patsLeft--; ! 804: patPtr--; ! 805: if ((patsLeft > 1) && (memcmp((char *) patPtr, ! 806: (char *) (patPtr-1), sizeof(Pattern)) == 0)) { ! 807: patsLeft--; ! 808: patPtr--; ! 809: strcpy(p, "Triple-"); ! 810: } else { ! 811: strcpy(p, "Double-"); ! 812: } ! 813: p += strlen(p); ! 814: } ! 815: ! 816: if (patPtr->hateMods == 0) { ! 817: strcpy(p, "Any-"); ! 818: p += strlen(p); ! 819: } ! 820: ! 821: for (needMods = patPtr->needMods, modPtr = modArray; ! 822: needMods != 0; modPtr++) { ! 823: if (modPtr->mask & needMods) { ! 824: needMods &= ~modPtr->mask; ! 825: strcpy(p, modPtr->name); ! 826: p += strlen(p); ! 827: *p = '-'; ! 828: p++; ! 829: } ! 830: } ! 831: ! 832: if ((patPtr->eventType != KeyPress) ! 833: || (patPtr->detail == 0)) { ! 834: register EventInfo *eiPtr; ! 835: ! 836: for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) { ! 837: if (eiPtr->type == patPtr->eventType) { ! 838: strcpy(p, eiPtr->name); ! 839: p += strlen(p); ! 840: if (patPtr->detail != 0) { ! 841: *p = '-'; ! 842: p++; ! 843: } ! 844: break; ! 845: } ! 846: } ! 847: } ! 848: ! 849: if (patPtr->detail != 0) { ! 850: if ((patPtr->eventType == KeyPress) ! 851: || (patPtr->eventType == KeyRelease)) { ! 852: register KeySymInfo *kPtr; ! 853: ! 854: for (kPtr = keyArray; kPtr->name != NULL; kPtr++) { ! 855: if (patPtr->detail == (int) kPtr->value) { ! 856: sprintf(p, "%.100s", kPtr->name); ! 857: p += strlen(p); ! 858: break; ! 859: } ! 860: } ! 861: } else { ! 862: sprintf(p, "%d", patPtr->detail); ! 863: p += strlen(p); ! 864: } ! 865: } ! 866: *p = '>'; ! 867: p++; ! 868: } ! 869: *p = 0; ! 870: if ((p - string) >= sizeof(string)) { ! 871: panic("Tk_GetAllBindings overflowed buffer"); ! 872: } ! 873: Tcl_AppendElement(interp, string, 0); ! 874: } ! 875: } ! 876: ! 877: /* ! 878: *-------------------------------------------------------------- ! 879: * ! 880: * Tk_DeleteAllBindings -- ! 881: * ! 882: * Remove all bindings associated with a given object in a ! 883: * given binding table. ! 884: * ! 885: * Results: ! 886: * All bindings associated with object are removed from ! 887: * bindingTable. ! 888: * ! 889: * Side effects: ! 890: * None. ! 891: * ! 892: *-------------------------------------------------------------- ! 893: */ ! 894: ! 895: void ! 896: Tk_DeleteAllBindings(bindingTable, object) ! 897: Tk_BindingTable bindingTable; /* Table in which to delete ! 898: * bindings. */ ! 899: ClientData object; /* Token for object. */ ! 900: { ! 901: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 902: register PatSeq *psPtr, *prevPtr; ! 903: PatSeq *nextPtr; ! 904: Tcl_HashEntry *hPtr; ! 905: ! 906: hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object); ! 907: if (hPtr == NULL) { ! 908: return; ! 909: } ! 910: for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL; ! 911: psPtr = nextPtr) { ! 912: nextPtr = psPtr->nextObjPtr; ! 913: ! 914: /* ! 915: * Be sure to remove each binding from its hash chain in the ! 916: * pattern table. If this is the last pattern in the chain, ! 917: * then delete the hash entry too. ! 918: */ ! 919: ! 920: prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr); ! 921: if (prevPtr == psPtr) { ! 922: if (psPtr->nextSeqPtr == NULL) { ! 923: Tcl_DeleteHashEntry(psPtr->hPtr); ! 924: } else { ! 925: Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); ! 926: } ! 927: } else { ! 928: for ( ; ; prevPtr = prevPtr->nextSeqPtr) { ! 929: if (prevPtr == NULL) { ! 930: panic("Tk_DeleteAllBindings couldn't find on hash chain"); ! 931: } ! 932: if (prevPtr->nextSeqPtr == psPtr) { ! 933: prevPtr->nextSeqPtr = psPtr->nextSeqPtr; ! 934: break; ! 935: } ! 936: } ! 937: } ! 938: Tk_EventuallyFree((ClientData) psPtr->command, (Tk_FreeProc *) free); ! 939: ckfree((char *) psPtr); ! 940: } ! 941: Tcl_DeleteHashEntry(hPtr); ! 942: } ! 943: ! 944: /* ! 945: *-------------------------------------------------------------- ! 946: * ! 947: * Tk_BindEvent -- ! 948: * ! 949: * This procedure is invoked to process an X event. The ! 950: * event is added to those recorded for the binding table. ! 951: * Then each of the objects at *objectPtr is checked in ! 952: * order to see if it has a binding that matches the recent ! 953: * events. If so, that binding is invoked and the rest of ! 954: * objects are skipped. ! 955: * ! 956: * Results: ! 957: * None. ! 958: * ! 959: * Side effects: ! 960: * Depends on the command associated with the matching ! 961: * binding. ! 962: * ! 963: *-------------------------------------------------------------- ! 964: */ ! 965: ! 966: void ! 967: Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr) ! 968: Tk_BindingTable bindingTable; /* Table in which to look for ! 969: * bindings. */ ! 970: XEvent *eventPtr; /* What actually happened. */ ! 971: Tk_Window tkwin; /* Window on display where event ! 972: * occurred (needed in order to ! 973: * locate display information). */ ! 974: int numObjects; /* Number of objects at *objectPtr. */ ! 975: ClientData *objectPtr; /* Array of one or more objects ! 976: * to check for a matching binding. */ ! 977: { ! 978: BindingTable *bindPtr = (BindingTable *) bindingTable; ! 979: TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; ! 980: XEvent *ringPtr; ! 981: PatSeq *matchPtr; ! 982: PatternTableKey key; ! 983: Tcl_HashEntry *hPtr; ! 984: int detail; ! 985: ! 986: /* ! 987: * Add the new event to the ring of saved events for the ! 988: * binding table. Consecutive MotionNotify events get combined: ! 989: * if both the new event and the previous event are MotionNotify, ! 990: * then put the new event *on top* of the previous event. ! 991: */ ! 992: ! 993: if ((eventPtr->type != MotionNotify) ! 994: || (bindPtr->eventRing[bindPtr->curEvent].type != MotionNotify)) { ! 995: bindPtr->curEvent++; ! 996: if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) { ! 997: bindPtr->curEvent = 0; ! 998: } ! 999: } ! 1000: ringPtr = &bindPtr->eventRing[bindPtr->curEvent]; ! 1001: memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent)); ! 1002: detail = 0; ! 1003: bindPtr->detailRing[bindPtr->curEvent] = 0; ! 1004: if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) { ! 1005: detail = (int) GetKeySym(dispPtr, ringPtr); ! 1006: if (detail == NoSymbol) { ! 1007: detail = 0; ! 1008: } ! 1009: } else if ((ringPtr->type == ButtonPress) ! 1010: || (ringPtr->type == ButtonRelease)) { ! 1011: detail = ringPtr->xbutton.button; ! 1012: } ! 1013: bindPtr->detailRing[bindPtr->curEvent] = detail; ! 1014: ! 1015: /* ! 1016: * Loop over all the objects, matching the new event against ! 1017: * each in turn. ! 1018: */ ! 1019: ! 1020: for ( ; numObjects > 0; numObjects--, objectPtr++) { ! 1021: ! 1022: /* ! 1023: * Match the new event against those recorded in the ! 1024: * pattern table, saving the longest matching pattern. ! 1025: * For events with details (button and key events) first ! 1026: * look for a binding for the specific key or button. ! 1027: * If none is found, then look for a binding for all ! 1028: * keys or buttons (detail of 0). ! 1029: */ ! 1030: ! 1031: matchPtr = NULL; ! 1032: key.object = *objectPtr; ! 1033: key.type = ringPtr->type; ! 1034: key.detail = detail; ! 1035: hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); ! 1036: if (hPtr != NULL) { ! 1037: matchPtr = MatchPatterns(bindPtr, ! 1038: (PatSeq *) Tcl_GetHashValue(hPtr)); ! 1039: } ! 1040: if ((detail != 0) && (matchPtr == NULL)) { ! 1041: key.detail = 0; ! 1042: hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); ! 1043: if (hPtr != NULL) { ! 1044: matchPtr = MatchPatterns(bindPtr, ! 1045: (PatSeq *) Tcl_GetHashValue(hPtr)); ! 1046: } ! 1047: } ! 1048: ! 1049: if (matchPtr != NULL) { ! 1050: ! 1051: /* ! 1052: * %-substitution can increase the length of the command. ! 1053: * This code handles three cases: (a) no substitution; ! 1054: * (b) substitution results in short command (use space ! 1055: * on stack); and (c) substitution results in long ! 1056: * command (malloc it). ! 1057: */ ! 1058: ! 1059: #define STATIC_SPACE 200 ! 1060: char shortSpace[STATIC_SPACE]; ! 1061: int result; ! 1062: ! 1063: if (matchPtr->flags & PAT_PERCENTS) { ! 1064: char *p; ! 1065: p = ExpandPercents(matchPtr->command, eventPtr, ! 1066: (KeySym) detail, shortSpace, STATIC_SPACE); ! 1067: result = Tcl_GlobalEval(bindPtr->interp, p); ! 1068: if (p != shortSpace) { ! 1069: ckfree(p); ! 1070: } ! 1071: } else { ! 1072: /* ! 1073: * The code below is tricky in order allow the binding to ! 1074: * be modified or deleted as part of the command that the ! 1075: * binding invokes. Must make sure that the actual command ! 1076: * string isn't freed until the command completes, and must ! 1077: * copy the address of this string into a local variable ! 1078: * in case it's modified by the command. ! 1079: */ ! 1080: ! 1081: char *cmd = matchPtr->command; ! 1082: ! 1083: Tk_Preserve((ClientData) cmd); ! 1084: result = Tcl_GlobalEval(bindPtr->interp, cmd); ! 1085: Tk_Release((ClientData) cmd); ! 1086: } ! 1087: if (result != TCL_OK) { ! 1088: Tcl_AddErrorInfo(bindPtr->interp, ! 1089: "\n (command bound to event)"); ! 1090: TkBindError(bindPtr->interp); ! 1091: } ! 1092: return; ! 1093: } ! 1094: } ! 1095: } ! 1096: ! 1097: /* ! 1098: *---------------------------------------------------------------------- ! 1099: * ! 1100: * FindSequence -- ! 1101: * ! 1102: * Find the entry in a binding table that corresponds to a ! 1103: * particular pattern string, and return a pointer to that ! 1104: * entry. ! 1105: * ! 1106: * Results: ! 1107: * The return value is normally a pointer to the PatSeq ! 1108: * in patternTable that corresponds to eventString. If an error ! 1109: * was found while parsing eventString, or if "create" is 0 and ! 1110: * no pattern sequence previously existed, then NULL is returned ! 1111: * and interp->result contains a message describing the problem. ! 1112: * If no pattern sequence previously existed for eventString, then ! 1113: * a new one is created with a NULL command field. In a successful ! 1114: * return, *maskPtr is filled in with a mask of the event types ! 1115: * on which the pattern sequence depends. ! 1116: * ! 1117: * Side effects: ! 1118: * A new pattern sequence may be created. ! 1119: * ! 1120: *---------------------------------------------------------------------- ! 1121: */ ! 1122: ! 1123: static PatSeq * ! 1124: FindSequence(interp, bindPtr, object, eventString, create, maskPtr) ! 1125: Tcl_Interp *interp; /* Interpreter to use for error ! 1126: * reporting. */ ! 1127: BindingTable *bindPtr; /* Table to use for lookup. */ ! 1128: ClientData object; /* Token for object(s) with which binding ! 1129: * is associated. */ ! 1130: char *eventString; /* String description of pattern to ! 1131: * match on. See user documentation ! 1132: * for details. */ ! 1133: int create; /* 0 means don't create the entry if ! 1134: * it doesn't already exist. Non-zero ! 1135: * means create. */ ! 1136: unsigned long *maskPtr; /* *maskPtr is filled in with the event ! 1137: * types on which this pattern sequence ! 1138: * depends. */ ! 1139: ! 1140: { ! 1141: Pattern pats[EVENT_BUFFER_SIZE]; ! 1142: int numPats; ! 1143: register char *p; ! 1144: register Pattern *patPtr; ! 1145: register PatSeq *psPtr; ! 1146: register Tcl_HashEntry *hPtr; ! 1147: #define FIELD_SIZE 20 ! 1148: char field[FIELD_SIZE]; ! 1149: int flags, any, count, new, sequenceSize; ! 1150: unsigned long eventMask; ! 1151: PatternTableKey key; ! 1152: ! 1153: /* ! 1154: *------------------------------------------------------------- ! 1155: * Step 1: parse the pattern string to produce an array ! 1156: * of Patterns. The array is generated backwards, so ! 1157: * that the lowest-indexed pattern corresponds to the last ! 1158: * event that must occur. ! 1159: *------------------------------------------------------------- ! 1160: */ ! 1161: ! 1162: p = eventString; ! 1163: flags = 0; ! 1164: eventMask = 0; ! 1165: for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1]; ! 1166: numPats < EVENT_BUFFER_SIZE; ! 1167: numPats++, patPtr--) { ! 1168: patPtr->eventType = -1; ! 1169: patPtr->needMods = 0; ! 1170: patPtr->hateMods = ~0; ! 1171: patPtr->detail = 0; ! 1172: while (isspace(*p)) { ! 1173: p++; ! 1174: } ! 1175: if (*p == '\0') { ! 1176: break; ! 1177: } ! 1178: ! 1179: /* ! 1180: * Handle simple ASCII characters. Note: the shift ! 1181: * modifier is ignored in this case (it's really part ! 1182: * of the character, rather than a "modifier"). ! 1183: */ ! 1184: ! 1185: if (*p != '<') { ! 1186: char string[2]; ! 1187: ! 1188: patPtr->eventType = KeyPress; ! 1189: eventMask |= KeyPressMask; ! 1190: string[0] = *p; ! 1191: string[1] = 0; ! 1192: hPtr = Tcl_FindHashEntry(&keySymTable, string); ! 1193: if (hPtr != NULL) { ! 1194: patPtr->detail = (int) Tcl_GetHashValue(hPtr); ! 1195: } else { ! 1196: if (isprint(*p)) { ! 1197: patPtr->detail = *p; ! 1198: } else { ! 1199: sprintf(interp->result, ! 1200: "bad ASCII character 0x%x", *p); ! 1201: return NULL; ! 1202: } ! 1203: } ! 1204: patPtr->hateMods = ~ShiftMask; ! 1205: p++; ! 1206: continue; ! 1207: } ! 1208: ! 1209: /* ! 1210: * A fancier event description. Must consist of ! 1211: * 1. open angle bracket. ! 1212: * 2. any number of modifiers, each followed by spaces ! 1213: * or dashes. ! 1214: * 3. an optional event name. ! 1215: * 4. an option button or keysym name. Either this or ! 1216: * item 3 *must* be present; if both are present ! 1217: * then they are separated by spaces or dashes. ! 1218: * 5. a close angle bracket. ! 1219: */ ! 1220: ! 1221: any = 0; ! 1222: count = 1; ! 1223: p++; ! 1224: while (1) { ! 1225: register ModInfo *modPtr; ! 1226: p = GetField(p, field, FIELD_SIZE); ! 1227: hPtr = Tcl_FindHashEntry(&modTable, field); ! 1228: if (hPtr == NULL) { ! 1229: break; ! 1230: } ! 1231: modPtr = (ModInfo *) Tcl_GetHashValue(hPtr); ! 1232: patPtr->needMods |= modPtr->mask; ! 1233: if (modPtr->flags & (DOUBLE|TRIPLE)) { ! 1234: flags |= PAT_NEARBY; ! 1235: if (modPtr->flags & DOUBLE) { ! 1236: count = 2; ! 1237: } else { ! 1238: count = 3; ! 1239: } ! 1240: } ! 1241: if (modPtr->flags & ANY) { ! 1242: any = 1; ! 1243: } ! 1244: while ((*p == '-') || isspace(*p)) { ! 1245: p++; ! 1246: } ! 1247: } ! 1248: if (any) { ! 1249: patPtr->hateMods = 0; ! 1250: } else { ! 1251: patPtr->hateMods = ~patPtr->needMods; ! 1252: } ! 1253: hPtr = Tcl_FindHashEntry(&eventTable, field); ! 1254: if (hPtr != NULL) { ! 1255: register EventInfo *eiPtr; ! 1256: eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); ! 1257: patPtr->eventType = eiPtr->type; ! 1258: eventMask |= eiPtr->eventMask; ! 1259: while ((*p == '-') || isspace(*p)) { ! 1260: p++; ! 1261: } ! 1262: p = GetField(p, field, FIELD_SIZE); ! 1263: } ! 1264: if (*field != '\0') { ! 1265: if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) { ! 1266: static int masks[] = {~0, ~Button1Mask, ~Button2Mask, ! 1267: ~Button3Mask, ~Button4Mask, ~Button5Mask}; ! 1268: ! 1269: if (patPtr->eventType == -1) { ! 1270: patPtr->eventType = ButtonPress; ! 1271: eventMask |= ButtonPressMask; ! 1272: } else if ((patPtr->eventType == KeyPress) ! 1273: || (patPtr->eventType == KeyRelease)) { ! 1274: goto getKeysym; ! 1275: } else if ((patPtr->eventType != ButtonPress) ! 1276: && (patPtr->eventType != ButtonRelease)) { ! 1277: Tcl_AppendResult(interp, "specified button \"", field, ! 1278: "\" for non-button event", (char *) NULL); ! 1279: return NULL; ! 1280: } ! 1281: patPtr->detail = (*field - '0'); ! 1282: ! 1283: /* ! 1284: * Ignore this button as a modifier: its state is already ! 1285: * fixed. ! 1286: */ ! 1287: ! 1288: patPtr->needMods &= masks[patPtr->detail]; ! 1289: patPtr->hateMods &= masks[patPtr->detail]; ! 1290: } else { ! 1291: getKeysym: ! 1292: hPtr = Tcl_FindHashEntry(&keySymTable, (char *) field); ! 1293: if (hPtr == NULL) { ! 1294: Tcl_AppendResult(interp, "bad event type or keysym \"", ! 1295: field, "\"", (char *) NULL); ! 1296: return NULL; ! 1297: } ! 1298: if (patPtr->eventType == -1) { ! 1299: patPtr->eventType = KeyPress; ! 1300: eventMask |= KeyPressMask; ! 1301: } else if ((patPtr->eventType != KeyPress) ! 1302: && (patPtr->eventType != KeyRelease)) { ! 1303: Tcl_AppendResult(interp, "specified keysym \"", field, ! 1304: "\" for non-key event", (char *) NULL); ! 1305: return NULL; ! 1306: } ! 1307: patPtr->detail = (int) Tcl_GetHashValue(hPtr); ! 1308: ! 1309: /* ! 1310: * Don't get upset about the shift modifier with keys: ! 1311: * if the key doesn't permit the shift modifier then ! 1312: * that will already be factored in when translating ! 1313: * from keycode to keysym in Tk_BindEvent. If the keysym ! 1314: * has both a shifted and unshifted form, we want to allow ! 1315: * the shifted form to be specified explicitly, though. ! 1316: */ ! 1317: ! 1318: patPtr->hateMods &= ~ShiftMask; ! 1319: } ! 1320: } else if (patPtr->eventType == -1) { ! 1321: interp->result = "no event type or button # or keysym"; ! 1322: return NULL; ! 1323: } ! 1324: while ((*p == '-') || isspace(*p)) { ! 1325: p++; ! 1326: } ! 1327: if (*p != '>') { ! 1328: interp->result = "missing \">\" in binding"; ! 1329: return NULL; ! 1330: } ! 1331: p++; ! 1332: ! 1333: /* ! 1334: * Replicate events for DOUBLE and TRIPLE. ! 1335: */ ! 1336: ! 1337: if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) { ! 1338: patPtr[-1] = patPtr[0]; ! 1339: patPtr--; ! 1340: numPats++; ! 1341: if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) { ! 1342: patPtr[-1] = patPtr[0]; ! 1343: patPtr--; ! 1344: numPats++; ! 1345: } ! 1346: } ! 1347: } ! 1348: ! 1349: /* ! 1350: *------------------------------------------------------------- ! 1351: * Step 2: find the sequence in the binding table if it exists, ! 1352: * and add a new sequence to the table if it doesn't. ! 1353: *------------------------------------------------------------- ! 1354: */ ! 1355: ! 1356: if (numPats == 0) { ! 1357: interp->result = "no events specified in binding"; ! 1358: return NULL; ! 1359: } ! 1360: patPtr = &pats[EVENT_BUFFER_SIZE-numPats]; ! 1361: key.object = object; ! 1362: key.type = patPtr->eventType; ! 1363: key.detail = patPtr->detail; ! 1364: hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new); ! 1365: sequenceSize = numPats*sizeof(Pattern); ! 1366: if (!new) { ! 1367: for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL; ! 1368: psPtr = psPtr->nextSeqPtr) { ! 1369: if ((numPats == psPtr->numPats) ! 1370: && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY)) ! 1371: && (memcmp((char *) patPtr, (char *) psPtr->pats, ! 1372: sequenceSize) == 0)) { ! 1373: *maskPtr = eventMask; /*don't forget to pass back the mask*/ ! 1374: goto done; ! 1375: } ! 1376: } ! 1377: } ! 1378: if (!create) { ! 1379: if (new) { ! 1380: Tcl_DeleteHashEntry(hPtr); ! 1381: } ! 1382: Tcl_AppendResult(interp, "no binding exists for \"", ! 1383: eventString, "\"", (char *) NULL); ! 1384: return NULL; ! 1385: } ! 1386: psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq) ! 1387: + (numPats-1)*sizeof(Pattern))); ! 1388: psPtr->numPats = numPats; ! 1389: psPtr->command = NULL; ! 1390: psPtr->flags = flags; ! 1391: psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr); ! 1392: psPtr->hPtr = hPtr; ! 1393: Tcl_SetHashValue(hPtr, psPtr); ! 1394: ! 1395: /* ! 1396: * Link the pattern into the list associated with the object. ! 1397: */ ! 1398: ! 1399: psPtr->object = object; ! 1400: hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new); ! 1401: if (new) { ! 1402: psPtr->nextObjPtr = NULL; ! 1403: } else { ! 1404: psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr); ! 1405: } ! 1406: Tcl_SetHashValue(hPtr, psPtr); ! 1407: ! 1408: memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize); ! 1409: ! 1410: done: ! 1411: *maskPtr = eventMask; ! 1412: return psPtr; ! 1413: } ! 1414: ! 1415: /* ! 1416: *---------------------------------------------------------------------- ! 1417: * ! 1418: * GetField -- ! 1419: * ! 1420: * Used to parse pattern descriptions. Copies up to ! 1421: * size characters from p to copy, stopping at end of ! 1422: * string, space, "-", ">", or whenever size is ! 1423: * exceeded. ! 1424: * ! 1425: * Results: ! 1426: * The return value is a pointer to the character just ! 1427: * after the last one copied (usually "-" or space or ! 1428: * ">", but could be anything if size was exceeded). ! 1429: * Also places NULL-terminated string (up to size ! 1430: * character, including NULL), at copy. ! 1431: * ! 1432: * Side effects: ! 1433: * None. ! 1434: * ! 1435: *---------------------------------------------------------------------- ! 1436: */ ! 1437: ! 1438: static char * ! 1439: GetField(p, copy, size) ! 1440: register char *p; /* Pointer to part of pattern. */ ! 1441: register char *copy; /* Place to copy field. */ ! 1442: int size; /* Maximum number of characters to ! 1443: * copy. */ ! 1444: { ! 1445: while ((*p != '\0') && !isspace(*p) && (*p != '>') ! 1446: && (*p != '-') && (size > 1)) { ! 1447: *copy = *p; ! 1448: p++; ! 1449: copy++; ! 1450: size--; ! 1451: } ! 1452: *copy = '\0'; ! 1453: return p; ! 1454: } ! 1455: ! 1456: /* ! 1457: *---------------------------------------------------------------------- ! 1458: * ! 1459: * GetKeySym -- ! 1460: * ! 1461: * Given an X KeyPress or KeyRelease event, map the ! 1462: * keycode in the event into a KeySym. ! 1463: * ! 1464: * Results: ! 1465: * The return value is the KeySym corresponding to ! 1466: * eventPtr, or NoSymbol if no matching Keysym could be ! 1467: * found. ! 1468: * ! 1469: * Side effects: ! 1470: * In the first call for a given display, keycode-to- ! 1471: * KeySym maps get loaded. ! 1472: * ! 1473: *---------------------------------------------------------------------- ! 1474: */ ! 1475: ! 1476: static KeySym ! 1477: GetKeySym(dispPtr, eventPtr) ! 1478: register TkDisplay *dispPtr; /* Display in which to ! 1479: * map keycode. */ ! 1480: register XEvent *eventPtr; /* Description of X event. */ ! 1481: { ! 1482: KeySym *symPtr; ! 1483: KeySym sym; ! 1484: ! 1485: /* ! 1486: * Read the key mapping information from the server if ! 1487: * we don't have it already. ! 1488: */ ! 1489: ! 1490: if (dispPtr->symsPerCode == 0) { ! 1491: Display *dpy = dispPtr->display; ! 1492: ! 1493: #ifdef IS_LINUX ! 1494: XDisplayKeycodes(dpy, &dispPtr->firstKeycode, &dispPtr->lastKeycode); ! 1495: #else ! 1496: dispPtr->firstKeycode = ! 1497: dpy->min_keycode; ! 1498: dispPtr->lastKeycode = ! 1499: dpy->max_keycode; ! 1500: #endif ! 1501: dispPtr->keySyms = XGetKeyboardMapping(dpy, ! 1502: dispPtr->firstKeycode, dispPtr->lastKeycode + 1 ! 1503: - dispPtr->firstKeycode, &dispPtr->symsPerCode); ! 1504: } ! 1505: ! 1506: /* ! 1507: * Compute the lower-case KeySym for this keycode. May ! 1508: * have to convert an upper-case KeySym to a lower-case ! 1509: * one if the list only has a single element. ! 1510: */ ! 1511: ! 1512: if ((eventPtr->xkey.keycode < dispPtr->firstKeycode) ! 1513: || (eventPtr->xkey.keycode > dispPtr->lastKeycode)) { ! 1514: return NoSymbol; ! 1515: } ! 1516: symPtr = &dispPtr->keySyms[(eventPtr->xkey.keycode ! 1517: - dispPtr->firstKeycode) * dispPtr->symsPerCode]; ! 1518: sym = *symPtr; ! 1519: if ((dispPtr->symsPerCode == 1) || (symPtr[1] == NoSymbol)) { ! 1520: if ((sym >= XK_A) && (sym <= XK_Z)) { ! 1521: sym += (XK_a - XK_A); ! 1522: } else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) { ! 1523: sym += (XK_agrave - XK_Agrave); ! 1524: } else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) { ! 1525: sym += (XK_oslash - XK_Ooblique); ! 1526: } ! 1527: } ! 1528: ! 1529: /* ! 1530: * See whether the key is shifted or caps-locked. If so, ! 1531: * use an upper-case equivalent if provided, or compute ! 1532: * one (for caps-lock, just compute upper-case: don't ! 1533: * use shifted KeySym since that would shift non-alphabetic ! 1534: * keys). ! 1535: */ ! 1536: ! 1537: if (eventPtr->xkey.state & ShiftMask) { ! 1538: if ((dispPtr->symsPerCode > 1) && (symPtr[1] != NoSymbol)) { ! 1539: return symPtr[1]; ! 1540: } ! 1541: shiftToUpper: ! 1542: if ((sym >= XK_a) && (sym <= XK_z)) { ! 1543: sym += (XK_A - XK_a); ! 1544: } else if ((sym >= XK_agrave) && (sym <= XK_adiaeresis)) { ! 1545: sym += (XK_Agrave - XK_agrave); ! 1546: } else if ((sym >= XK_oslash) && (sym <= XK_thorn)) { ! 1547: sym += (XK_Ooblique - XK_oslash); ! 1548: } ! 1549: return sym; ! 1550: } ! 1551: if (eventPtr->xkey.state & LockMask) { ! 1552: goto shiftToUpper; ! 1553: } ! 1554: return sym; ! 1555: } ! 1556: ! 1557: /* ! 1558: *---------------------------------------------------------------------- ! 1559: * ! 1560: * MatchPatterns -- ! 1561: * ! 1562: * Given a list of pattern sequences and a list of ! 1563: * recent events, return a pattern sequence that matches ! 1564: * the event list. ! 1565: * ! 1566: * Results: ! 1567: * The return value is NULL if no pattern matches the ! 1568: * recent events from bindPtr. If one or more patterns ! 1569: * matches, then the longest (or most specific) matching ! 1570: * pattern is returned. ! 1571: * ! 1572: * Side effects: ! 1573: * None. ! 1574: * ! 1575: *---------------------------------------------------------------------- ! 1576: */ ! 1577: ! 1578: static PatSeq * ! 1579: MatchPatterns(bindPtr, psPtr) ! 1580: BindingTable *bindPtr; /* Information about binding table, such ! 1581: * as ring of recent events. */ ! 1582: register PatSeq *psPtr; /* List of pattern sequences. */ ! 1583: { ! 1584: register PatSeq *bestPtr = NULL; ! 1585: ! 1586: /* ! 1587: * Iterate over all the pattern sequences. ! 1588: */ ! 1589: ! 1590: for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) { ! 1591: register XEvent *eventPtr; ! 1592: register Pattern *patPtr; ! 1593: Window window; ! 1594: int *detailPtr; ! 1595: int patCount, ringCount, flags, state; ! 1596: ! 1597: /* ! 1598: * Iterate over all the patterns in a sequence to be ! 1599: * sure that they all match. ! 1600: */ ! 1601: ! 1602: eventPtr = &bindPtr->eventRing[bindPtr->curEvent]; ! 1603: detailPtr = &bindPtr->detailRing[bindPtr->curEvent]; ! 1604: window = eventPtr->xany.window; ! 1605: patPtr = psPtr->pats; ! 1606: patCount = psPtr->numPats; ! 1607: ringCount = EVENT_BUFFER_SIZE; ! 1608: while (patCount > 0) { ! 1609: if (ringCount <= 0) { ! 1610: goto nextSequence; ! 1611: } ! 1612: if (eventPtr->xany.window != window) { ! 1613: goto nextSequence; ! 1614: } ! 1615: if (eventPtr->xany.type != patPtr->eventType) { ! 1616: /* ! 1617: * If the event is a mouse motion, button release, ! 1618: * or key release event, and it didn't match ! 1619: * the pattern, then just skip the event and try ! 1620: * the next event against the same pattern. ! 1621: */ ! 1622: ! 1623: if ((eventPtr->xany.type == MotionNotify) ! 1624: || (eventPtr->xany.type == ButtonRelease) ! 1625: || (eventPtr->xany.type == KeyRelease) ! 1626: || (eventPtr->xany.type == NoExpose) ! 1627: || (eventPtr->xany.type == GraphicsExpose)) { ! 1628: goto nextEvent; ! 1629: } ! 1630: goto nextSequence; ! 1631: } ! 1632: ! 1633: flags = flagArray[eventPtr->type]; ! 1634: if (flags & KEY_BUTTON_MOTION) { ! 1635: state = eventPtr->xkey.state; ! 1636: } else if (flags & CROSSING) { ! 1637: state = eventPtr->xcrossing.state; ! 1638: } else { ! 1639: state = 0; ! 1640: } ! 1641: if ((state & patPtr->needMods) ! 1642: != patPtr->needMods) { ! 1643: goto nextSequence; ! 1644: } ! 1645: if ((state & patPtr->hateMods) != 0) { ! 1646: goto nextSequence; ! 1647: } ! 1648: if ((patPtr->detail != 0) ! 1649: && (patPtr->detail != *detailPtr)) { ! 1650: goto nextSequence; ! 1651: } ! 1652: if (psPtr->flags & PAT_NEARBY) { ! 1653: register XEvent *firstPtr; ! 1654: ! 1655: firstPtr = &bindPtr->eventRing[bindPtr->curEvent]; ! 1656: if ((firstPtr->xkey.x_root ! 1657: < (eventPtr->xkey.x_root - NEARBY_PIXELS)) ! 1658: || (firstPtr->xkey.x_root ! 1659: > (eventPtr->xkey.x_root + NEARBY_PIXELS)) ! 1660: || (firstPtr->xkey.y_root ! 1661: < (eventPtr->xkey.y_root - NEARBY_PIXELS)) ! 1662: || (firstPtr->xkey.y_root ! 1663: > (eventPtr->xkey.y_root + NEARBY_PIXELS)) ! 1664: || (firstPtr->xkey.time ! 1665: > (eventPtr->xkey.time + NEARBY_MS))) { ! 1666: goto nextSequence; ! 1667: } ! 1668: } ! 1669: patPtr++; ! 1670: patCount--; ! 1671: nextEvent: ! 1672: if (eventPtr == bindPtr->eventRing) { ! 1673: eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1]; ! 1674: detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1]; ! 1675: } else { ! 1676: eventPtr--; ! 1677: detailPtr--; ! 1678: } ! 1679: ringCount--; ! 1680: } ! 1681: ! 1682: /* ! 1683: * This sequence matches. If we've already got another match, ! 1684: * pick whichever is most specific. Detail is most important, ! 1685: * then needMods, then hateMods. ! 1686: */ ! 1687: ! 1688: if (bestPtr != NULL) { ! 1689: register Pattern *patPtr2; ! 1690: int i; ! 1691: ! 1692: if (psPtr->numPats != bestPtr->numPats) { ! 1693: if (bestPtr->numPats > psPtr->numPats) { ! 1694: goto nextSequence; ! 1695: } else { ! 1696: goto newBest; ! 1697: } ! 1698: } ! 1699: for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats; ! 1700: i < psPtr->numPats; i++,patPtr++, patPtr2++) { ! 1701: if (patPtr->detail != patPtr2->detail) { ! 1702: if (patPtr->detail == 0) { ! 1703: goto nextSequence; ! 1704: } else { ! 1705: goto newBest; ! 1706: } ! 1707: } ! 1708: if (patPtr->needMods != patPtr2->needMods) { ! 1709: if ((patPtr->needMods & patPtr2->needMods) ! 1710: == patPtr->needMods) { ! 1711: goto nextSequence; ! 1712: } else { ! 1713: goto newBest; ! 1714: } ! 1715: } ! 1716: if (patPtr->hateMods != patPtr2->hateMods) { ! 1717: if ((patPtr->hateMods & patPtr2->hateMods) ! 1718: == patPtr2->hateMods) { ! 1719: goto newBest; ! 1720: } else { ! 1721: goto nextSequence; ! 1722: } ! 1723: } ! 1724: } ! 1725: goto nextSequence; /* Tie goes to newest pattern. */ ! 1726: } ! 1727: newBest: ! 1728: bestPtr = psPtr; ! 1729: ! 1730: nextSequence: continue; ! 1731: } ! 1732: return bestPtr; ! 1733: } ! 1734: ! 1735: /* ! 1736: *-------------------------------------------------------------- ! 1737: * ! 1738: * ExpandPercents -- ! 1739: * ! 1740: * Given a command and an event, produce a new command ! 1741: * by replacing % constructs in the original command ! 1742: * with information from the X event. ! 1743: * ! 1744: * Results: ! 1745: * The return result is a pointer to the new %-substituted ! 1746: * command. If the command fits in the space at after, then ! 1747: * the return value is after. If the command is too large ! 1748: * to fit at after, then the return value is a pointer to ! 1749: * a malloc-ed buffer holding the command; in this case it ! 1750: * is the caller's responsibility to free up the buffer when ! 1751: * finished with it. ! 1752: * ! 1753: * Side effects: ! 1754: * None. ! 1755: * ! 1756: *-------------------------------------------------------------- ! 1757: */ ! 1758: ! 1759: static char * ! 1760: ExpandPercents(before, eventPtr, keySym, after, afterSize) ! 1761: register char *before; /* Command containing percent ! 1762: * expressions to be replaced. */ ! 1763: register XEvent *eventPtr; /* X event containing information ! 1764: * to be used in % replacements. */ ! 1765: KeySym keySym; /* KeySym: only relevant for ! 1766: * KeyPress and KeyRelease events). */ ! 1767: char *after; /* Place to generate new expanded ! 1768: * command. Must contain at least ! 1769: * "afterSize" bytes of space. */ ! 1770: int afterSize; /* Number of bytes of space available at ! 1771: * after. */ ! 1772: { ! 1773: register char *buffer; /* Pointer to buffer currently being used ! 1774: * as destination. */ ! 1775: register char *dst; /* Pointer to next place to store character ! 1776: * in substituted string. */ ! 1777: int spaceLeft; /* Indicates how many more non-null bytes ! 1778: * may be stored at *dst before space ! 1779: * runs out. */ ! 1780: int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl ! 1781: * list element. */ ! 1782: int number, flags; ! 1783: #define NUM_SIZE 40 ! 1784: register char *string; ! 1785: char numStorage[NUM_SIZE+1]; ! 1786: ! 1787: if (eventPtr->type < LASTEvent) { ! 1788: flags = flagArray[eventPtr->type]; ! 1789: } else { ! 1790: flags = 0; ! 1791: } ! 1792: dst = buffer = after; ! 1793: spaceLeft = afterSize - 1; ! 1794: while (*before != 0) { ! 1795: if (*before != '%') { ! 1796: ! 1797: /* ! 1798: * Expand the destination string if necessary. ! 1799: */ ! 1800: ! 1801: if (spaceLeft <= 0) { ! 1802: char *newSpace; ! 1803: ! 1804: newSpace = (char *) ckalloc((unsigned) (2*afterSize)); ! 1805: memcpy((VOID *) newSpace, (VOID *) buffer, afterSize); ! 1806: afterSize *= 2; ! 1807: dst = newSpace + (dst - buffer); ! 1808: if (buffer != after) { ! 1809: ckfree(buffer); ! 1810: } ! 1811: buffer = newSpace; ! 1812: spaceLeft = afterSize - (dst-buffer) - 1; ! 1813: } ! 1814: *dst = *before; ! 1815: dst++; ! 1816: before++; ! 1817: spaceLeft--; ! 1818: continue; ! 1819: } ! 1820: ! 1821: number = 0; ! 1822: string = "??"; ! 1823: switch (before[1]) { ! 1824: case '#': ! 1825: number = eventPtr->xany.serial; ! 1826: goto doNumber; ! 1827: case 'a': ! 1828: number = (int) eventPtr->xconfigure.above; ! 1829: goto doNumber; ! 1830: case 'b': ! 1831: number = eventPtr->xbutton.button; ! 1832: goto doNumber; ! 1833: case 'c': ! 1834: if (flags & EXPOSE) { ! 1835: number = eventPtr->xexpose.count; ! 1836: } else if (flags & MAPPING) { ! 1837: number = eventPtr->xmapping.count; ! 1838: } ! 1839: goto doNumber; ! 1840: case 'd': ! 1841: if (flags & (CROSSING|FOCUS)) { ! 1842: switch (eventPtr->xcrossing.detail) { ! 1843: case NotifyAncestor: ! 1844: string = "NotifyAncestor"; ! 1845: break; ! 1846: case NotifyVirtual: ! 1847: string = "NotifyVirtual"; ! 1848: break; ! 1849: case NotifyInferior: ! 1850: string = "NotifyInferior"; ! 1851: break; ! 1852: case NotifyNonlinear: ! 1853: string = "NotifyNonlinear"; ! 1854: break; ! 1855: case NotifyNonlinearVirtual: ! 1856: string = "NotifyNonlinearVirtual"; ! 1857: break; ! 1858: case NotifyPointer: ! 1859: string = "NotifyPointer"; ! 1860: break; ! 1861: case NotifyPointerRoot: ! 1862: string = "NotifyPointerRoot"; ! 1863: break; ! 1864: case NotifyDetailNone: ! 1865: string = "NotifyDetailNone"; ! 1866: break; ! 1867: } ! 1868: } else if (flags & CONFIG_REQ) { ! 1869: switch (eventPtr->xconfigurerequest.detail) { ! 1870: case Above: ! 1871: string = "Above"; ! 1872: break; ! 1873: case Below: ! 1874: string = "Below"; ! 1875: break; ! 1876: case TopIf: ! 1877: string = "TopIf"; ! 1878: break; ! 1879: case BottomIf: ! 1880: string = "BottomIf"; ! 1881: break; ! 1882: case Opposite: ! 1883: string = "Opposite"; ! 1884: break; ! 1885: } ! 1886: } ! 1887: goto doString; ! 1888: case 'f': ! 1889: number = eventPtr->xcrossing.focus; ! 1890: goto doNumber; ! 1891: case 'h': ! 1892: if (flags & EXPOSE) { ! 1893: number = eventPtr->xexpose.height; ! 1894: } else if (flags & (CONFIG|CONFIG_REQ)) { ! 1895: number = eventPtr->xconfigure.height; ! 1896: } else if (flags & RESIZE_REQ) { ! 1897: number = eventPtr->xresizerequest.height; ! 1898: } ! 1899: goto doNumber; ! 1900: case 'k': ! 1901: number = eventPtr->xkey.keycode; ! 1902: goto doNumber; ! 1903: case 'm': ! 1904: if (flags & CROSSING) { ! 1905: number = eventPtr->xcrossing.mode; ! 1906: } else if (flags & FOCUS) { ! 1907: number = eventPtr->xfocus.mode; ! 1908: } ! 1909: switch (number) { ! 1910: case NotifyNormal: ! 1911: string = "NotifyNormal"; ! 1912: break; ! 1913: case NotifyGrab: ! 1914: string = "NotifyGrab"; ! 1915: break; ! 1916: case NotifyUngrab: ! 1917: string = "NotifyUngrab"; ! 1918: break; ! 1919: case NotifyWhileGrabbed: ! 1920: string = "NotifyWhileGrabbed"; ! 1921: break; ! 1922: } ! 1923: goto doString; ! 1924: case 'o': ! 1925: if (flags & CREATE) { ! 1926: number = eventPtr->xcreatewindow.override_redirect; ! 1927: } else if (flags & MAP) { ! 1928: number = eventPtr->xmap.override_redirect; ! 1929: } else if (flags & REPARENT) { ! 1930: number = eventPtr->xreparent.override_redirect; ! 1931: } else if (flags & CONFIG) { ! 1932: number = eventPtr->xconfigure.override_redirect; ! 1933: } ! 1934: goto doNumber; ! 1935: case 'p': ! 1936: switch (eventPtr->xcirculate.place) { ! 1937: case PlaceOnTop: ! 1938: string = "PlaceOnTop"; ! 1939: break; ! 1940: case PlaceOnBottom: ! 1941: string = "PlaceOnBottom"; ! 1942: break; ! 1943: } ! 1944: goto doString; ! 1945: case 's': ! 1946: if (flags & KEY_BUTTON_MOTION) { ! 1947: number = eventPtr->xkey.state; ! 1948: } else if (flags & CROSSING) { ! 1949: number = eventPtr->xcrossing.state; ! 1950: } else if (flags & VISIBILITY) { ! 1951: switch (eventPtr->xvisibility.state) { ! 1952: case VisibilityUnobscured: ! 1953: string = "VisibilityUnobscured"; ! 1954: break; ! 1955: case VisibilityPartiallyObscured: ! 1956: string = "VisibilityPartiallyObscured"; ! 1957: break; ! 1958: case VisibilityFullyObscured: ! 1959: string = "VisibilityFullyObscured"; ! 1960: break; ! 1961: } ! 1962: goto doString; ! 1963: } ! 1964: goto doNumber; ! 1965: case 't': ! 1966: if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) { ! 1967: number = (int) eventPtr->xkey.time; ! 1968: } else if (flags & SEL_REQ) { ! 1969: number = (int) eventPtr->xselectionrequest.time; ! 1970: } else if (flags & SEL_NOTIFY) { ! 1971: number = (int) eventPtr->xselection.time; ! 1972: } ! 1973: goto doNumber; ! 1974: case 'v': ! 1975: number = eventPtr->xconfigurerequest.value_mask; ! 1976: goto doNumber; ! 1977: case 'w': ! 1978: if (flags & EXPOSE) { ! 1979: number = eventPtr->xexpose.width; ! 1980: } else if (flags & (CONFIG|CONFIG_REQ)) { ! 1981: number = eventPtr->xconfigure.width; ! 1982: } else if (flags & RESIZE_REQ) { ! 1983: number = eventPtr->xresizerequest.width; ! 1984: } ! 1985: goto doNumber; ! 1986: case 'x': ! 1987: if (flags & KEY_BUTTON_MOTION) { ! 1988: number = eventPtr->xkey.x; ! 1989: } else if (flags & EXPOSE) { ! 1990: number = eventPtr->xexpose.x; ! 1991: } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) { ! 1992: number = eventPtr->xcreatewindow.x; ! 1993: } else if (flags & REPARENT) { ! 1994: number = eventPtr->xreparent.x; ! 1995: } else if (flags & CROSSING) { ! 1996: number = eventPtr->xcrossing.x; ! 1997: } ! 1998: goto doNumber; ! 1999: case 'y': ! 2000: if (flags & KEY_BUTTON_MOTION) { ! 2001: number = eventPtr->xkey.y; ! 2002: } else if (flags & EXPOSE) { ! 2003: number = eventPtr->xexpose.y; ! 2004: } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) { ! 2005: number = eventPtr->xcreatewindow.y; ! 2006: } else if (flags & REPARENT) { ! 2007: number = eventPtr->xreparent.y; ! 2008: } else if (flags & CROSSING) { ! 2009: number = eventPtr->xcrossing.y; ! 2010: ! 2011: } ! 2012: goto doNumber; ! 2013: case 'A': ! 2014: if ((eventPtr->type == KeyPress) ! 2015: || (eventPtr->type == KeyRelease)) { ! 2016: int numChars; ! 2017: ! 2018: numChars = XLookupString(&eventPtr->xkey, numStorage, ! 2019: NUM_SIZE, (KeySym *) NULL, ! 2020: (XComposeStatus *) NULL); ! 2021: numStorage[numChars] = '\0'; ! 2022: string = numStorage; ! 2023: } ! 2024: goto doString; ! 2025: case 'B': ! 2026: number = eventPtr->xcreatewindow.border_width; ! 2027: goto doNumber; ! 2028: case 'D': ! 2029: number = (int) eventPtr->xany.display; ! 2030: goto doNumber; ! 2031: case 'E': ! 2032: number = (int) eventPtr->xany.send_event; ! 2033: goto doNumber; ! 2034: case 'K': ! 2035: if ((eventPtr->type == KeyPress) ! 2036: || (eventPtr->type == KeyRelease)) { ! 2037: register KeySymInfo *kPtr; ! 2038: ! 2039: for (kPtr = keyArray; kPtr->name != NULL; kPtr++) { ! 2040: if (kPtr->value == keySym) { ! 2041: string = kPtr->name; ! 2042: break; ! 2043: } ! 2044: } ! 2045: } ! 2046: goto doString; ! 2047: case 'N': ! 2048: number = (int) keySym; ! 2049: goto doNumber; ! 2050: case 'R': ! 2051: number = (int) eventPtr->xkey.root; ! 2052: goto doNumber; ! 2053: case 'S': ! 2054: number = (int) eventPtr->xkey.subwindow; ! 2055: goto doNumber; ! 2056: case 'T': ! 2057: number = eventPtr->type; ! 2058: goto doNumber; ! 2059: case 'W': { ! 2060: TkWindow *winPtr; ! 2061: ! 2062: if (XFindContext(eventPtr->xany.display, eventPtr->xany.window, ! 2063: tkWindowContext, (void *) &winPtr) == 0) { ! 2064: string = winPtr->pathName; ! 2065: } else { ! 2066: string = "??"; ! 2067: } ! 2068: goto doString; ! 2069: } ! 2070: case 'X': ! 2071: number = eventPtr->xkey.x_root; ! 2072: goto doNumber; ! 2073: case 'Y': ! 2074: number = eventPtr->xkey.y_root; ! 2075: goto doNumber; ! 2076: default: ! 2077: numStorage[0] = before[1]; ! 2078: numStorage[1] = '\0'; ! 2079: string = numStorage; ! 2080: goto doString; ! 2081: } ! 2082: ! 2083: doNumber: ! 2084: sprintf(numStorage, "%d", number); ! 2085: string = numStorage; ! 2086: ! 2087: doString: ! 2088: spaceNeeded = Tcl_ScanElement(string, &cvtFlags); ! 2089: if (spaceNeeded >= spaceLeft) { ! 2090: char *newSpace; ! 2091: ! 2092: newSpace = (char *) ckalloc((unsigned) ! 2093: (afterSize + spaceNeeded + 50)); ! 2094: memcpy((VOID *) newSpace, (VOID *) buffer, afterSize); ! 2095: afterSize += spaceNeeded + 50; ! 2096: dst = newSpace + (dst - buffer); ! 2097: if (buffer != after) { ! 2098: ckfree(buffer); ! 2099: } ! 2100: buffer = newSpace; ! 2101: spaceLeft = afterSize - (dst-buffer) - 1; ! 2102: } ! 2103: spaceNeeded = Tcl_ConvertElement(string, dst, ! 2104: cvtFlags | TCL_DONT_USE_BRACES); ! 2105: dst += spaceNeeded; ! 2106: spaceLeft -= spaceNeeded; ! 2107: before += 2; ! 2108: } ! 2109: *dst = '\0'; ! 2110: return buffer; ! 2111: } ! 2112: ! 2113: /* ! 2114: *---------------------------------------------------------------------- ! 2115: * ! 2116: * TkBindError -- ! 2117: * ! 2118: * This procedure is invoked to handle errors that occur in Tcl ! 2119: * commands that are invoked in "background" (e.g. from event or ! 2120: * timer bindings). ! 2121: * ! 2122: * Results: ! 2123: * None. ! 2124: * ! 2125: * Side effects: ! 2126: * The command "tkerror" is invoked to process the error, passing ! 2127: * it the error message. If that fails, then an error message ! 2128: * is output on stderr. ! 2129: * ! 2130: *---------------------------------------------------------------------- ! 2131: */ ! 2132: ! 2133: void ! 2134: TkBindError(interp) ! 2135: Tcl_Interp *interp; /* Interpreter in which an error has ! 2136: * occurred. */ ! 2137: { ! 2138: char *argv[2]; ! 2139: char *command; ! 2140: char *error; ! 2141: char *errorInfo, *tmp; ! 2142: int result; ! 2143: ! 2144: error = (char *) ckalloc((unsigned) (strlen(interp->result) + 1)); ! 2145: strcpy(error, interp->result); ! 2146: tmp = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); ! 2147: if (tmp == NULL) { ! 2148: errorInfo = error; ! 2149: } else { ! 2150: errorInfo = (char *) ckalloc((unsigned) (strlen(tmp) + 1)); ! 2151: strcpy(errorInfo, tmp); ! 2152: } ! 2153: argv[0] = "tkerror"; ! 2154: argv[1] = error; ! 2155: command = Tcl_Merge(2, argv); ! 2156: result = Tcl_GlobalEval(interp, command); ! 2157: if (result != TCL_OK) { ! 2158: if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) { ! 2159: fprintf(stderr, "%s\n", errorInfo); ! 2160: } else { ! 2161: fprintf(stderr, "tkerror failed to handle background error.\n"); ! 2162: fprintf(stderr, " Original error: %s\n", error); ! 2163: fprintf(stderr, " Error in tkerror: %s\n", interp->result); ! 2164: } ! 2165: } ! 2166: Tcl_ResetResult(interp); ! 2167: ckfree(command); ! 2168: ckfree(error); ! 2169: if (errorInfo != error) { ! 2170: ckfree(errorInfo); ! 2171: } ! 2172: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.