/*1* tkBind.c --2*3* This file provides procedures that associate Tcl commands4* with X events or sequences of X events.5*6* Copyright (c) 1989-1994 The Regents of the University of California.7* Copyright (c) 1994-1996 Sun Microsystems, Inc.8*9* See the file "license.terms" for information on usage and redistribution10* of this file, and for a DISCLAIMER OF ALL WARRANTIES.11*12* SCCS: @(#) tkBind.c 1.124 96/12/05 14:47:4013*/1415#include "tkInt.h"1617/*18* File structure:19*20* Structure definitions and static variables.21*22* Init/Free this package.23*24* Tcl "bind" command (actually located in tkCmds.c).25* "bind" command implementation.26* "bind" implementation helpers.27*28* Tcl "event" command.29* "event" command implementation.30* "event" implementation helpers.31*32* Package-specific common helpers.33*34* Non-package-specific helpers.35*/363738/*39* The following union is used to hold the detail information from an40* XEvent (including Tk's XVirtualEvent extension).41*/42typedef union {43KeySym keySym; /* KeySym that corresponds to xkey.keycode. */44int button; /* Button that was pressed (xbutton.button). */45Tk_Uid name; /* Tk_Uid of virtual event. */46ClientData clientData; /* Used when type of Detail is unknown, and to47* ensure that all bytes of Detail are initialized48* when this structure is used in a hash key. */49} Detail;5051/*52* The structure below represents a binding table. A binding table53* represents a domain in which event bindings may occur. It includes54* a space of objects relative to which events occur (usually windows,55* but not always), a history of recent events in the domain, and56* a set of mappings that associate particular Tcl commands with sequences57* of events in the domain. Multiple binding tables may exist at once,58* either because there are multiple applications open, or because there59* are multiple domains within an application with separate event60* bindings for each (for example, each canvas widget has a separate61* binding table for associating events with the items in the canvas).62*63* Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much64* below 30. To see this, consider a triple mouse button click while65* the Shift key is down (and auto-repeating). There may be as many66* as 3 auto-repeat events after each mouse button press or release67* (see the first large comment block within Tk_BindEvent for more on68* this), for a total of 20 events to cover the three button presses69* and two intervening releases. If you reduce EVENT_BUFFER_SIZE too70* much, shift multi-clicks will be lost.71*72*/7374#define EVENT_BUFFER_SIZE 3075typedef struct BindingTable {76XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events77* (higher indices are for more recent78* events). */79Detail detailRing[EVENT_BUFFER_SIZE];/* "Detail" information (keySym,80* button, Tk_Uid, or 0) for each81* entry in eventRing. */82int curEvent; /* Index in eventRing of most recent83* event. Newer events have higher84* indices. */85Tcl_HashTable patternTable; /* Used to map from an event to a86* list of patterns that may match that87* event. Keys are PatternTableKey88* structs, values are (PatSeq *). */89Tcl_HashTable objectTable; /* Used to map from an object to a90* list of patterns associated with91* that object. Keys are ClientData,92* values are (PatSeq *). */93Tcl_Interp *interp; /* Interpreter in which commands are94* executed. */95} BindingTable;9697/*98* The following structure represents virtual event table. A virtual event99* table provides a way to map from platform-specific physical events such100* as button clicks or key presses to virtual events such as <<Paste>>,101* <<Close>>, or <<ScrollWindow>>.102*103* A virtual event is usually never part of the event stream, but instead is104* synthesized inline by matching low-level events. However, a virtual105* event may be generated by platform-specific code or by Tcl scripts. In106* that case, no lookup of the virtual event will need to be done using107* this table, because the virtual event is actually in the event stream.108*/109110typedef struct TkVirtualEventTable {111Tcl_HashTable patternTable; /* Used to map from a physical event to112* a list of patterns that may match that113* event. Keys PatternTableKey structs,114* values are (PatSeq *). */115Tcl_HashTable virtualTable; /* Used to map a virtual event to the116* array of physical events that can117* trigger it. Keys are the Tk_Uid names118* of the virtual events, values are119* PhysicalsOwned structs. */120} TkVirtualEventTable;121122/*123* The following structure is used as a key in a patternTable for both124* binding tables and a virtual event tables.125*126* In a binding table, the object field corresponds to the binding tag127* for the widget whose bindings are being accessed.128*129* In a virtual event table, the object field is always NULL. Virtual130* events are a global definiton and are not tied to a particular131* binding tag.132*133* The same key is used for both types of pattern tables so that the134* helper functions that traverse and match patterns will work for both135* binding tables and virtual event tables.136*/137typedef struct PatternTableKey {138ClientData object; /* For binding table, identifies the binding139* tag of the object (or class of objects)140* relative to which the event occurred.141* For virtual event table, always NULL. */142int type; /* Type of event (from X). */143Detail detail; /* Additional information, such as keysym,144* button, Tk_Uid, or 0 if nothing145* additional. */146} PatternTableKey;147148/*149* The following structure defines a pattern, which is matched against X150* events as part of the process of converting X events into Tcl commands.151*/152153typedef struct Pattern {154int eventType; /* Type of X event, e.g. ButtonPress. */155int needMods; /* Mask of modifiers that must be156* present (0 means no modifiers are157* required). */158Detail detail; /* Additional information that must159* match event. Normally this is 0,160* meaning no additional information161* must match. For KeyPress and162* KeyRelease events, a keySym may163* be specified to select a164* particular keystroke (0 means any165* keystrokes). For button events,166* specifies a particular button (0167* means any buttons are OK). For virtual168* events, specifies the Tk_Uid of the169* virtual event name (never 0). */170} Pattern;171172/*173* The following structure defines a pattern sequence, which consists of one174* or more patterns. In order to trigger, a pattern sequence must match175* the most recent X events (first pattern to most recent event, next176* pattern to next event, and so on). It is used as the hash value in a177* patternTable for both binding tables and virtual event tables.178*179* In a binding table, it is the sequence of physical events that make up180* a binding for an object.181*182* In a virtual event table, it is the sequence of physical events that183* define a virtual event.184*185* The same structure is used for both types of pattern tables so that the186* helper functions that traverse and match patterns will work for both187* binding tables and virtual event tables.188*/189190typedef struct PatSeq {191int numPats; /* Number of patterns in sequence (usually192* 1). */193char *command; /* Command to invoke when this pattern194* sequence matches (malloc-ed). */195int flags; /* Miscellaneous flag values; see below for196* definitions. */197struct PatSeq *nextSeqPtr; /* Next in list of all pattern sequences198* that have the same initial pattern. NULL199* means end of list. */200Tcl_HashEntry *hPtr; /* Pointer to hash table entry for the201* initial pattern. This is the head of the202* list of which nextSeqPtr forms a part. */203struct VirtualOwners *voPtr;/* In a binding table, always NULL. In a204* virtual event table, identifies the array205* of virtual events that can be triggered by206* this event. */207struct PatSeq *nextObjPtr; /* In a binding table, next in list of all208* pattern sequences for the same object (NULL209* for end of list). Needed to implement210* Tk_DeleteAllBindings. In a virtual event211* table, always NULL. */212Pattern pats[1]; /* Array of "numPats" patterns. Only one213* element is declared here but in actuality214* enough space will be allocated for "numPats"215* patterns. To match, pats[0] must match216* event n, pats[1] must match event n-1, etc.217*/218} PatSeq;219220/*221* Flag values for PatSeq structures:222*223* PAT_NEARBY 1 means that all of the events matching224* this sequence must occur with nearby X225* and Y mouse coordinates and close in time.226* This is typically used to restrict multiple227* button presses.228*/229230#define PAT_NEARBY 1231232/*233* Constants that define how close together two events must be234* in milliseconds or pixels to meet the PAT_NEARBY constraint:235*/236237#define NEARBY_PIXELS 5238#define NEARBY_MS 500239240241/*242* The following structure keeps track of all the virtual events that are243* associated with a particular physical event. It is pointed to by the244* voPtr field in a PatSeq in the patternTable of a virtual event table.245*/246247typedef struct VirtualOwners {248int numOwners; /* Number of virtual events to trigger. */249Tcl_HashEntry *owners[1]; /* Array of pointers to entries in250* virtualTable. Enough space will251* actually be allocated for numOwners252* hash entries. */253} VirtualOwners;254255/*256* The following structure is used in the virtualTable of a virtual event257* table to associate a virtual event with all the physical events that can258* trigger it.259*/260typedef struct PhysicalsOwned {261int numOwned; /* Number of physical events owned. */262PatSeq *patSeqs[1]; /* Array of pointers to physical event263* patterns. Enough space will actually264* be allocated to hold numOwned. */265} PhysicalsOwned;266267268/*269* One of the following structures exists for each interpreter,270* associated with the key "tkBind". This structure keeps track271* of the current display and screen in the interpreter, so that272* a script can be invoked whenever the display/screen changes273* (the script does things like point tkPriv at a display-specific274* structure).275*/276277typedef struct ScreenInfo {278TkDisplay *curDispPtr; /* Display for last binding command invoked279* in this application. */280int curScreenIndex; /* Index of screen for last binding command. */281int bindingDepth; /* Number of active instances of Tk_BindEvent282* in this application. */283} ScreenInfo;284285/*286* In X11R4 and earlier versions, XStringToKeysym is ridiculously287* slow. The data structure and hash table below, along with the288* code that uses them, implement a fast mapping from strings to289* keysyms. In X11R5 and later releases XStringToKeysym is plenty290* fast so this stuff isn't needed. The #define REDO_KEYSYM_LOOKUP291* is normally undefined, so that XStringToKeysym gets used. It292* can be set in the Makefile to enable the use of the hash table293* below.294*/295296#ifdef REDO_KEYSYM_LOOKUP297typedef struct {298char *name; /* Name of keysym. */299KeySym value; /* Numeric identifier for keysym. */300} KeySymInfo;301static KeySymInfo keyArray[] = {302#ifndef lint303#include "tkNames.h"304#endif305{(char *) NULL, 0}306};307static Tcl_HashTable keySymTable; /* keyArray hashed by keysym value. */308static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */309#endif /* REDO_KEYSYM_LOOKUP */310311static int initialized = 0;312313/*314* A hash table is kept to map from the string names of event315* modifiers to information about those modifiers. The structure316* for storing this information, and the hash table built at317* initialization time, are defined below.318*/319320typedef struct {321char *name; /* Name of modifier. */322int mask; /* Button/modifier mask value, * such as Button1Mask. */323int flags; /* Various flags; see below for324* definitions. */325} ModInfo;326327/*328* Flags for ModInfo structures:329*330* DOUBLE - Non-zero means duplicate this event,331* e.g. for double-clicks.332* TRIPLE - Non-zero means triplicate this event,333* e.g. for triple-clicks.334*/335336#define DOUBLE 1337#define TRIPLE 2338339/*340* The following special modifier mask bits are defined, to indicate341* logical modifiers such as Meta and Alt that may float among the342* actual modifier bits.343*/344345#define META_MASK (AnyModifier<<1)346#define ALT_MASK (AnyModifier<<2)347348static ModInfo modArray[] = {349{"Control", ControlMask, 0},350{"Shift", ShiftMask, 0},351{"Lock", LockMask, 0},352{"Meta", META_MASK, 0},353{"M", META_MASK, 0},354{"Alt", ALT_MASK, 0},355{"B1", Button1Mask, 0},356{"Button1", Button1Mask, 0},357{"B2", Button2Mask, 0},358{"Button2", Button2Mask, 0},359{"B3", Button3Mask, 0},360{"Button3", Button3Mask, 0},361{"B4", Button4Mask, 0},362{"Button4", Button4Mask, 0},363{"B5", Button5Mask, 0},364{"Button5", Button5Mask, 0},365{"Mod1", Mod1Mask, 0},366{"M1", Mod1Mask, 0},367{"Command", Mod1Mask, 0},368{"Mod2", Mod2Mask, 0},369{"M2", Mod2Mask, 0},370{"Option", Mod2Mask, 0},371{"Mod3", Mod3Mask, 0},372{"M3", Mod3Mask, 0},373{"Mod4", Mod4Mask, 0},374{"M4", Mod4Mask, 0},375{"Mod5", Mod5Mask, 0},376{"M5", Mod5Mask, 0},377{"Double", 0, DOUBLE},378{"Triple", 0, TRIPLE},379{"Any", 0, 0}, /* Ignored: historical relic. */380{NULL, 0, 0}381};382static Tcl_HashTable modTable;383384/*385* This module also keeps a hash table mapping from event names386* to information about those events. The structure, an array387* to use to initialize the hash table, and the hash table are388* all defined below.389*/390391typedef struct {392char *name; /* Name of event. */393int type; /* Event type for X, such as394* ButtonPress. */395int eventMask; /* Mask bits (for XSelectInput)396* for this event type. */397} EventInfo;398399/*400* Note: some of the masks below are an OR-ed combination of401* several masks. This is necessary because X doesn't report402* up events unless you also ask for down events. Also, X403* doesn't report button state in motion events unless you've404* asked about button events.405*/406407static EventInfo eventArray[] = {408{"Key", KeyPress, KeyPressMask},409{"KeyPress", KeyPress, KeyPressMask},410{"KeyRelease", KeyRelease, KeyPressMask|KeyReleaseMask},411{"Button", ButtonPress, ButtonPressMask},412{"ButtonPress", ButtonPress, ButtonPressMask},413{"ButtonRelease", ButtonRelease,414ButtonPressMask|ButtonReleaseMask},415{"Motion", MotionNotify,416ButtonPressMask|PointerMotionMask},417{"Enter", EnterNotify, EnterWindowMask},418{"Leave", LeaveNotify, LeaveWindowMask},419{"FocusIn", FocusIn, FocusChangeMask},420{"FocusOut", FocusOut, FocusChangeMask},421{"Expose", Expose, ExposureMask},422{"Visibility", VisibilityNotify, VisibilityChangeMask},423{"Destroy", DestroyNotify, StructureNotifyMask},424{"Unmap", UnmapNotify, StructureNotifyMask},425{"Map", MapNotify, StructureNotifyMask},426{"Reparent", ReparentNotify, StructureNotifyMask},427{"Configure", ConfigureNotify, StructureNotifyMask},428{"Gravity", GravityNotify, StructureNotifyMask},429{"Circulate", CirculateNotify, StructureNotifyMask},430{"Property", PropertyNotify, PropertyChangeMask},431{"Colormap", ColormapNotify, ColormapChangeMask},432{"Activate", ActivateNotify, ActivateMask},433{"Deactivate", DeactivateNotify, ActivateMask},434{(char *) NULL, 0, 0}435};436static Tcl_HashTable eventTable;437438/*439* The defines and table below are used to classify events into440* various groups. The reason for this is that logically identical441* fields (e.g. "state") appear at different places in different442* types of events. The classification masks can be used to figure443* out quickly where to extract information from events.444*/445446#define KEY 0x1447#define BUTTON 0x2448#define MOTION 0x4449#define CROSSING 0x8450#define FOCUS 0x10451#define EXPOSE 0x20452#define VISIBILITY 0x40453#define CREATE 0x80454#define DESTROY 0x100455#define UNMAP 0x200456#define MAP 0x400457#define REPARENT 0x800458#define CONFIG 0x1000459#define GRAVITY 0x2000460#define CIRC 0x4000461#define PROP 0x8000462#define COLORMAP 0x10000463#define VIRTUAL 0x20000464#define ACTIVATE 0x40000465466#define KEY_BUTTON_MOTION_VIRTUAL (KEY|BUTTON|MOTION|VIRTUAL)467468static int flagArray[TK_LASTEVENT] = {469/* Not used */ 0,470/* Not used */ 0,471/* KeyPress */ KEY,472/* KeyRelease */ KEY,473/* ButtonPress */ BUTTON,474/* ButtonRelease */ BUTTON,475/* MotionNotify */ MOTION,476/* EnterNotify */ CROSSING,477/* LeaveNotify */ CROSSING,478/* FocusIn */ FOCUS,479/* FocusOut */ FOCUS,480/* KeymapNotify */ 0,481/* Expose */ EXPOSE,482/* GraphicsExpose */ EXPOSE,483/* NoExpose */ 0,484/* VisibilityNotify */ VISIBILITY,485/* CreateNotify */ CREATE,486/* DestroyNotify */ DESTROY,487/* UnmapNotify */ UNMAP,488/* MapNotify */ MAP,489/* MapRequest */ 0,490/* ReparentNotify */ REPARENT,491/* ConfigureNotify */ CONFIG,492/* ConfigureRequest */ 0,493/* GravityNotify */ GRAVITY,494/* ResizeRequest */ 0,495/* CirculateNotify */ CIRC,496/* CirculateRequest */ 0,497/* PropertyNotify */ PROP,498/* SelectionClear */ 0,499/* SelectionRequest */ 0,500/* SelectionNotify */ 0,501/* ColormapNotify */ COLORMAP,502/* ClientMessage */ 0,503/* MappingNotify */ 0,504/* VirtualEvent */ VIRTUAL,505/* Activate */ ACTIVATE,506/* Deactivate */ ACTIVATE507};508509/*510* The following tables are used as a two-way map between X's internal511* numeric values for fields in an XEvent and the strings used in Tcl. The512* tables are used both when constructing an XEvent from user input and513* when providing data from an XEvent to the user.514*/515516static TkStateMap notifyMode[] = {517{NotifyNormal, "NotifyNormal"},518{NotifyGrab, "NotifyGrab"},519{NotifyUngrab, "NotifyUngrab"},520{NotifyWhileGrabbed, "NotifyWhileGrabbed"},521{-1, NULL}522};523524static TkStateMap notifyDetail[] = {525{NotifyAncestor, "NotifyAncestor"},526{NotifyVirtual, "NotifyVirtual"},527{NotifyInferior, "NotifyInferior"},528{NotifyNonlinear, "NotifyNonlinear"},529{NotifyNonlinearVirtual,"NotifyNonlinearVirtual"},530{NotifyPointer, "NotifyPointer"},531{NotifyPointerRoot, "NotifyPointerRoot"},532{NotifyDetailNone, "NotifyDetailNone"},533{-1, NULL}534};535536static TkStateMap circPlace[] = {537{PlaceOnTop, "PlaceOnTop"},538{PlaceOnBottom, "PlaceOnBottom"},539{-1, NULL}540};541542static TkStateMap visNotify[] = {543{VisibilityUnobscured, "VisibilityUnobscured"},544{VisibilityPartiallyObscured, "VisibilityPartiallyObscured"},545{VisibilityFullyObscured, "VisibilityFullyObscured"},546{-1, NULL}547};548549/*550* Prototypes for local procedures defined in this file:551*/552553static void ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,554char *dispName, int screenIndex));555static int CreateVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,556TkVirtualEventTable *vetPtr, char *virtString,557char *eventString));558static TkVirtualEventTable *CreateVirtualEventTable _ANSI_ARGS_((void));559static int DeleteVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,560TkVirtualEventTable *vetPtr, char *virtString,561char *eventString));562static void DeleteVirtualEventTable _ANSI_ARGS_((563TkVirtualEventTable *vetPtr));564static void ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,565char *before, XEvent *eventPtr, KeySym keySym,566Tcl_DString *dsPtr));567static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,568Tcl_HashTable *patternTablePtr, ClientData object,569char *eventString, int create, int allowVirtual,570unsigned long *maskPtr));571static void FreeScreenInfo _ANSI_ARGS_((ClientData clientData,572Tcl_Interp *interp));573static void GetAllVirtualEvents _ANSI_ARGS_((Tcl_Interp *interp,574TkVirtualEventTable *vetPtr));575static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));576static KeySym GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,577XEvent *eventPtr));578static void GetPatternString _ANSI_ARGS_((PatSeq *psPtr,579Tcl_DString *dsPtr));580static int GetVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,581TkVirtualEventTable *vetPtr, char *virtString));582static Tk_Uid GetVirtualEventUid _ANSI_ARGS_((Tcl_Interp *interp,583char *virtString));584static int HandleEventGenerate _ANSI_ARGS_((Tcl_Interp *interp,585Tk_Window tkwin, int argc, char **argv));586static void InitKeymapInfo _ANSI_ARGS_((TkDisplay *dispPtr));587static PatSeq * MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,588BindingTable *bindPtr, PatSeq *psPtr,589PatSeq *bestPtr, ClientData object,590char **bestCommandPtr));591static int ParseEventDescription _ANSI_ARGS_((Tcl_Interp *interp,592char **eventStringPtr, Pattern *patPtr,593unsigned long *eventMaskPtr));594595596597/*598*---------------------------------------------------------------------------599*600* TkBindInit --601*602* This procedure is called when an application is created. It603* initializes all the structures used by bindings and virtual604* events.605*606* Results:607* None.608*609* Side effects:610* Memory allocated.611*612*---------------------------------------------------------------------------613*/614615void616TkBindInit(mainPtr)617TkMainInfo *mainPtr; /* The newly created application. */618{619if (sizeof(XEvent) < sizeof(XVirtualEvent)) {620panic("TkBindInit: virtual events can't be supported");621}622mainPtr->bindingTable = Tk_CreateBindingTable(mainPtr->interp);623mainPtr->vetPtr = CreateVirtualEventTable();624}625626/*627*---------------------------------------------------------------------------628*629* TkBindFree --630*631* This procedure is called when an application is deleted. It632* deletes all the structures used by bindings and virtual events.633*634* Results:635* None.636*637* Side effects:638* Memory freed.639*640*---------------------------------------------------------------------------641*/642643void644TkBindFree(mainPtr)645TkMainInfo *mainPtr; /* The newly created application. */646{647Tk_DeleteBindingTable(mainPtr->bindingTable);648mainPtr->bindingTable = NULL;649650DeleteVirtualEventTable(mainPtr->vetPtr);651mainPtr->vetPtr = NULL;652}653654/*655*--------------------------------------------------------------656*657* Tk_CreateBindingTable --658*659* Set up a new domain in which event bindings may be created.660*661* Results:662* The return value is a token for the new table, which must663* be passed to procedures like Tk_CreatBinding.664*665* Side effects:666* Memory is allocated for the new table.667*668*--------------------------------------------------------------669*/670671Tk_BindingTable672Tk_CreateBindingTable(interp)673Tcl_Interp *interp; /* Interpreter to associate with the binding674* table: commands are executed in this675* interpreter. */676{677BindingTable *bindPtr;678int i;679680/*681* If this is the first time a binding table has been created,682* initialize the global data structures.683*/684685if (!initialized) {686Tcl_HashEntry *hPtr;687ModInfo *modPtr;688EventInfo *eiPtr;689int dummy;690691#ifdef REDO_KEYSYM_LOOKUP692KeySymInfo *kPtr;693694Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);695Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS);696for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {697hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);698Tcl_SetHashValue(hPtr, kPtr->value);699hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value,700&dummy);701Tcl_SetHashValue(hPtr, kPtr->name);702}703#endif /* REDO_KEYSYM_LOOKUP */704705initialized = 1;706707Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);708for (modPtr = modArray; modPtr->name != NULL; modPtr++) {709hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);710Tcl_SetHashValue(hPtr, modPtr);711}712713Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);714for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {715hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);716Tcl_SetHashValue(hPtr, eiPtr);717}718}719720/*721* Create and initialize a new binding table.722*/723724bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));725for (i = 0; i < EVENT_BUFFER_SIZE; i++) {726bindPtr->eventRing[i].type = -1;727}728bindPtr->curEvent = 0;729Tcl_InitHashTable(&bindPtr->patternTable,730sizeof(PatternTableKey)/sizeof(int));731Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);732bindPtr->interp = interp;733return (Tk_BindingTable) bindPtr;734}735736/*737*--------------------------------------------------------------738*739* Tk_DeleteBindingTable --740*741* Destroy a binding table and free up all its memory.742* The caller should not use bindingTable again after743* this procedure returns.744*745* Results:746* None.747*748* Side effects:749* Memory is freed.750*751*--------------------------------------------------------------752*/753754void755Tk_DeleteBindingTable(bindingTable)756Tk_BindingTable bindingTable; /* Token for the binding table to757* destroy. */758{759BindingTable *bindPtr = (BindingTable *) bindingTable;760PatSeq *psPtr, *nextPtr;761Tcl_HashEntry *hPtr;762Tcl_HashSearch search;763764/*765* Find and delete all of the patterns associated with the binding766* table.767*/768769for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);770hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {771for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);772psPtr != NULL; psPtr = nextPtr) {773nextPtr = psPtr->nextSeqPtr;774ckfree((char *) psPtr->command);775ckfree((char *) psPtr);776}777}778779/*780* Clean up the rest of the information associated with the781* binding table.782*/783784Tcl_DeleteHashTable(&bindPtr->patternTable);785Tcl_DeleteHashTable(&bindPtr->objectTable);786ckfree((char *) bindPtr);787}788789/*790*--------------------------------------------------------------791*792* Tk_CreateBinding --793*794* Add a binding to a binding table, so that future calls to795* Tk_BindEvent may execute the command in the binding.796*797* Results:798* The return value is 0 if an error occurred while setting799* up the binding. In this case, an error message will be800* left in interp->result. If all went well then the return801* value is a mask of the event types that must be made802* available to Tk_BindEvent in order to properly detect when803* this binding triggers. This value can be used to determine804* what events to select for in a window, for example.805*806* Side effects:807* The new binding may cause future calls to Tk_BindEvent to808* behave differently than they did previously.809*810*--------------------------------------------------------------811*/812813unsigned long814Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)815Tcl_Interp *interp; /* Used for error reporting. */816Tk_BindingTable bindingTable; /* Table in which to create binding. */817ClientData object; /* Token for object with which binding818* is associated. */819char *eventString; /* String describing event sequence820* that triggers binding. */821char *command; /* Contains Tcl command to execute822* when binding triggers. */823int append; /* 0 means replace any existing824* binding for eventString; 1 means825* append to that binding. */826{827BindingTable *bindPtr = (BindingTable *) bindingTable;828PatSeq *psPtr;829unsigned long eventMask;830831psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,8321, 1, &eventMask);833if (psPtr == NULL) {834return 0;835}836if (psPtr->command == NULL) {837int new;838Tcl_HashEntry *hPtr;839840/*841* This pattern sequence was just created.842* Link the pattern into the list associated with the object.843*/844845hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,846&new);847if (new) {848psPtr->nextObjPtr = NULL;849} else {850psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);851}852Tcl_SetHashValue(hPtr, psPtr);853}854855if (append && (psPtr->command != NULL)) {856int length;857char *new;858859length = strlen(psPtr->command) + strlen(command) + 2;860new = (char *) ckalloc((unsigned) length);861sprintf(new, "%s\n%s", psPtr->command, command);862ckfree((char *) psPtr->command);863psPtr->command = new;864} else {865if (psPtr->command != NULL) {866ckfree((char *) psPtr->command);867}868psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));869strcpy(psPtr->command, command);870}871return eventMask;872}873874/*875*--------------------------------------------------------------876*877* Tk_DeleteBinding --878*879* Remove an event binding from a binding table.880*881* Results:882* The result is a standard Tcl return value. If an error883* occurs then interp->result will contain an error message.884*885* Side effects:886* The binding given by object and eventString is removed887* from bindingTable.888*889*--------------------------------------------------------------890*/891892int893Tk_DeleteBinding(interp, bindingTable, object, eventString)894Tcl_Interp *interp; /* Used for error reporting. */895Tk_BindingTable bindingTable; /* Table in which to delete binding. */896ClientData object; /* Token for object with which binding897* is associated. */898char *eventString; /* String describing event sequence899* that triggers binding. */900{901BindingTable *bindPtr = (BindingTable *) bindingTable;902PatSeq *psPtr, *prevPtr;903unsigned long eventMask;904Tcl_HashEntry *hPtr;905906psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,9070, 1, &eventMask);908if (psPtr == NULL) {909Tcl_ResetResult(interp);910return TCL_OK;911}912913/*914* Unlink the binding from the list for its object, then from the915* list for its pattern.916*/917918hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);919if (hPtr == NULL) {920panic("Tk_DeleteBinding couldn't find object table entry");921}922prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);923if (prevPtr == psPtr) {924Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);925} else {926for ( ; ; prevPtr = prevPtr->nextObjPtr) {927if (prevPtr == NULL) {928panic("Tk_DeleteBinding couldn't find on object list");929}930if (prevPtr->nextObjPtr == psPtr) {931prevPtr->nextObjPtr = psPtr->nextObjPtr;932break;933}934}935}936prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);937if (prevPtr == psPtr) {938if (psPtr->nextSeqPtr == NULL) {939Tcl_DeleteHashEntry(psPtr->hPtr);940} else {941Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);942}943} else {944for ( ; ; prevPtr = prevPtr->nextSeqPtr) {945if (prevPtr == NULL) {946panic("Tk_DeleteBinding couldn't find on hash chain");947}948if (prevPtr->nextSeqPtr == psPtr) {949prevPtr->nextSeqPtr = psPtr->nextSeqPtr;950break;951}952}953}954ckfree((char *) psPtr->command);955ckfree((char *) psPtr);956return TCL_OK;957}958959/*960*--------------------------------------------------------------961*962* Tk_GetBinding --963*964* Return the command associated with a given event string.965*966* Results:967* The return value is a pointer to the command string968* associated with eventString for object in the domain969* given by bindingTable. If there is no binding for970* eventString, or if eventString is improperly formed,971* then NULL is returned and an error message is left in972* interp->result. The return value is semi-static: it973* will persist until the binding is changed or deleted.974*975* Side effects:976* None.977*978*--------------------------------------------------------------979*/980981char *982Tk_GetBinding(interp, bindingTable, object, eventString)983Tcl_Interp *interp; /* Interpreter for error reporting. */984Tk_BindingTable bindingTable; /* Table in which to look for985* binding. */986ClientData object; /* Token for object with which binding987* is associated. */988char *eventString; /* String describing event sequence989* that triggers binding. */990{991BindingTable *bindPtr = (BindingTable *) bindingTable;992PatSeq *psPtr;993unsigned long eventMask;994995psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,9960, 1, &eventMask);997if (psPtr == NULL) {998return NULL;999}1000return psPtr->command;1001}10021003/*1004*--------------------------------------------------------------1005*1006* Tk_GetAllBindings --1007*1008* Return a list of event strings for all the bindings1009* associated with a given object.1010*1011* Results:1012* There is no return value. Interp->result is modified to1013* hold a Tcl list with one entry for each binding associated1014* with object in bindingTable. Each entry in the list1015* contains the event string associated with one binding.1016*1017* Side effects:1018* None.1019*1020*--------------------------------------------------------------1021*/10221023void1024Tk_GetAllBindings(interp, bindingTable, object)1025Tcl_Interp *interp; /* Interpreter returning result or1026* error. */1027Tk_BindingTable bindingTable; /* Table in which to look for1028* bindings. */1029ClientData object; /* Token for object. */10301031{1032BindingTable *bindPtr = (BindingTable *) bindingTable;1033PatSeq *psPtr;1034Tcl_HashEntry *hPtr;1035Tcl_DString ds;10361037hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);1038if (hPtr == NULL) {1039return;1040}1041Tcl_DStringInit(&ds);1042for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;1043psPtr = psPtr->nextObjPtr) {1044/*1045* For each binding, output information about each of the1046* patterns in its sequence.1047*/10481049Tcl_DStringSetLength(&ds, 0);1050GetPatternString(psPtr, &ds);1051Tcl_AppendElement(interp, Tcl_DStringValue(&ds));1052}1053Tcl_DStringFree(&ds);1054}10551056/*1057*--------------------------------------------------------------1058*1059* Tk_DeleteAllBindings --1060*1061* Remove all bindings associated with a given object in a1062* given binding table.1063*1064* Results:1065* All bindings associated with object are removed from1066* bindingTable.1067*1068* Side effects:1069* None.1070*1071*--------------------------------------------------------------1072*/10731074void1075Tk_DeleteAllBindings(bindingTable, object)1076Tk_BindingTable bindingTable; /* Table in which to delete1077* bindings. */1078ClientData object; /* Token for object. */1079{1080BindingTable *bindPtr = (BindingTable *) bindingTable;1081PatSeq *psPtr, *prevPtr;1082PatSeq *nextPtr;1083Tcl_HashEntry *hPtr;10841085hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);1086if (hPtr == NULL) {1087return;1088}1089for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;1090psPtr = nextPtr) {1091nextPtr = psPtr->nextObjPtr;10921093/*1094* Be sure to remove each binding from its hash chain in the1095* pattern table. If this is the last pattern in the chain,1096* then delete the hash entry too.1097*/10981099prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);1100if (prevPtr == psPtr) {1101if (psPtr->nextSeqPtr == NULL) {1102Tcl_DeleteHashEntry(psPtr->hPtr);1103} else {1104Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);1105}1106} else {1107for ( ; ; prevPtr = prevPtr->nextSeqPtr) {1108if (prevPtr == NULL) {1109panic("Tk_DeleteAllBindings couldn't find on hash chain");1110}1111if (prevPtr->nextSeqPtr == psPtr) {1112prevPtr->nextSeqPtr = psPtr->nextSeqPtr;1113break;1114}1115}1116}1117ckfree((char *) psPtr->command);1118ckfree((char *) psPtr);1119}1120Tcl_DeleteHashEntry(hPtr);1121}11221123/*1124*--------------------------------------------------------------1125*1126* Tk_BindEvent --1127*1128* This procedure is invoked to process an X event. The1129* event is added to those recorded for the binding table.1130* Then each of the objects at *objectPtr is checked in1131* order to see if it has a binding that matches the recent1132* events. If so, the most specific binding is invoked for1133* each object.1134*1135* Results:1136* None.1137*1138* Side effects:1139* Depends on the command associated with the matching1140* binding.1141*1142*--------------------------------------------------------------1143*/11441145void1146Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)1147Tk_BindingTable bindingTable; /* Table in which to look for1148* bindings. */1149XEvent *eventPtr; /* What actually happened. */1150Tk_Window tkwin; /* Window on display where event1151* occurred (needed in order to1152* locate display information). */1153int numObjects; /* Number of objects at *objectPtr. */1154ClientData *objectPtr; /* Array of one or more objects1155* to check for a matching binding. */1156{1157BindingTable *bindPtr = (BindingTable *) bindingTable;1158TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;1159TkDisplay *oldDispPtr;1160ScreenInfo *screenPtr;1161XEvent *ringPtr;1162PatSeq *vMatchDetailList, *vMatchNoDetailList;1163PatternTableKey key;1164Tcl_HashEntry *hPtr;1165int flags, code, oldScreen;1166Tcl_Interp *interp;1167Tcl_DString scripts, savedResult;1168char *p, *end;1169Detail detail;11701171/*1172* Ignore the event completely if it is an Enter, Leave, FocusIn,1173* or FocusOut event with detail NotifyInferior. The reason for1174* ignoring these events is that we don't want transitions between1175* a window and its children to visible to bindings on the parent:1176* this would cause problems for mega-widgets, since the internal1177* structure of a mega-widget isn't supposed to be visible to1178* people watching the parent.1179*/11801181if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {1182if (eventPtr->xcrossing.detail == NotifyInferior) {1183return;1184}1185}1186if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {1187if (eventPtr->xfocus.detail == NotifyInferior) {1188return;1189}1190}11911192/*1193* Add the new event to the ring of saved events for the1194* binding table. Two tricky points:1195*1196* 1. Combine consecutive MotionNotify events. Do this by putting1197* the new event *on top* of the previous event.1198* 2. If a modifier key is held down, it auto-repeats to generate1199* continuous KeyPress and KeyRelease events. These can flush1200* the event ring so that valuable information is lost (such1201* as repeated button clicks). To handle this, check for the1202* special case of a modifier KeyPress arriving when the previous1203* two events are a KeyRelease and KeyPress of the same key.1204* If this happens, mark the most recent event (the KeyRelease)1205* invalid and put the new event on top of the event before that1206* (the KeyPress).1207*/12081209if ((eventPtr->type == MotionNotify)1210&& (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {1211/*1212* Don't advance the ring pointer.1213*/1214} else if (eventPtr->type == KeyPress) {1215int i;1216for (i = 0; ; i++) {1217if (i >= dispPtr->numModKeyCodes) {1218goto advanceRingPointer;1219}1220if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {1221break;1222}1223}1224ringPtr = &bindPtr->eventRing[bindPtr->curEvent];1225if ((ringPtr->type != KeyRelease)1226|| (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {1227goto advanceRingPointer;1228}1229if (bindPtr->curEvent <= 0) {1230i = EVENT_BUFFER_SIZE - 1;1231} else {1232i = bindPtr->curEvent - 1;1233}1234ringPtr = &bindPtr->eventRing[i];1235if ((ringPtr->type != KeyPress)1236|| (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {1237goto advanceRingPointer;1238}1239bindPtr->eventRing[bindPtr->curEvent].type = -1;1240bindPtr->curEvent = i;1241} else {1242advanceRingPointer:1243bindPtr->curEvent++;1244if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {1245bindPtr->curEvent = 0;1246}1247}1248ringPtr = &bindPtr->eventRing[bindPtr->curEvent];1249memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));1250detail.clientData = 0;1251flags = flagArray[ringPtr->type];1252if (flags & KEY) {1253detail.keySym = GetKeySym(dispPtr, ringPtr);1254if (detail.keySym == NoSymbol) {1255detail.keySym = 0;1256}1257} else if (flags & BUTTON) {1258detail.button = ringPtr->xbutton.button;1259} else if (flags & VIRTUAL) {1260detail.name = ((XVirtualEvent *) ringPtr)->name;1261}1262bindPtr->detailRing[bindPtr->curEvent] = detail;12631264/*1265* Find out if there are any virtual events that correspond to this1266* physical event (or sequence of physical events).1267*/12681269vMatchDetailList = NULL;1270vMatchNoDetailList = NULL;1271memset(&key, 0, sizeof(key));12721273if (ringPtr->type != VirtualEvent) {1274TkWindow *winPtr = (TkWindow *) tkwin;1275Tcl_HashTable *veptPtr = &winPtr->mainPtr->vetPtr->patternTable;12761277key.object = NULL;1278key.type = ringPtr->type;1279key.detail = detail;12801281hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);1282if (hPtr != NULL) {1283vMatchDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);1284}12851286if (key.detail.clientData != 0) {1287key.detail.clientData = 0;1288hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);1289if (hPtr != NULL) {1290vMatchNoDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);1291}1292}1293}12941295/*1296* Loop over all the objects, finding the binding script for each1297* one. Append all of the binding scripts, with %-sequences expanded,1298* to "scripts", with null characters separating the scripts for1299* each object.1300*/13011302Tcl_DStringInit(&scripts);1303for ( ; numObjects > 0; numObjects--, objectPtr++) {1304PatSeq *matchPtr;1305char *command;13061307matchPtr = NULL;1308command = NULL;13091310/*1311* Match the new event against those recorded in the pattern table,1312* saving the longest matching pattern. For events with details1313* (button and key events), look for a binding for the specific1314* key or button. First see if the event matches a physical event1315* that the object is interested in, then look for a virtual event.1316*/13171318key.object = *objectPtr;1319key.type = ringPtr->type;1320key.detail = detail;1321hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);1322if (hPtr != NULL) {1323matchPtr = MatchPatterns(dispPtr, bindPtr,1324(PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,1325&command);1326}13271328if (vMatchDetailList != NULL) {1329matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList,1330matchPtr, *objectPtr, &command);1331}133213331334/*1335* If no match was found, look for a binding for all keys or buttons1336* (detail of 0). Again, first match on a virtual event.1337*/13381339if ((detail.clientData != 0) && (matchPtr == NULL)) {1340key.detail.clientData = 0;1341hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);1342if (hPtr != NULL) {1343matchPtr = MatchPatterns(dispPtr, bindPtr,1344(PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,1345&command);1346}13471348if (vMatchNoDetailList != NULL) {1349matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList,1350matchPtr, *objectPtr, &command);1351}13521353}13541355if (matchPtr != NULL) {1356if (command == NULL) {1357panic("Tk_BindEvent: missing command");1358}1359ExpandPercents((TkWindow *) tkwin, command, eventPtr,1360detail.keySym, &scripts);1361Tcl_DStringAppend(&scripts, "", 1);1362}1363}1364if (Tcl_DStringLength(&scripts) == 0) {1365return;1366}13671368/*1369* Now go back through and evaluate the script for each object,1370* in order, dealing with "break" and "continue" exceptions1371* appropriately.1372*1373* There are two tricks here:1374* 1. Bindings can be invoked from in the middle of Tcl commands,1375* where interp->result is significant (for example, a widget1376* might be deleted because of an error in creating it, so the1377* result contains an error message that is eventually going to1378* be returned by the creating command). To preserve the result,1379* we save it in a dynamic string.1380* 2. The binding's action can potentially delete the binding,1381* so bindPtr may not point to anything valid once the action1382* completes. Thus we have to save bindPtr->interp in a1383* local variable in order to restore the result.1384*/13851386interp = bindPtr->interp;1387Tcl_DStringInit(&savedResult);13881389/*1390* Save information about the current screen, then invoke a script1391* if the screen has changed.1392*/13931394Tcl_DStringGetResult(interp, &savedResult);1395screenPtr = (ScreenInfo *) Tcl_GetAssocData(interp, "tkBind",1396(Tcl_InterpDeleteProc **) NULL);1397if (screenPtr == NULL) {1398screenPtr = (ScreenInfo *) ckalloc(sizeof(ScreenInfo));1399screenPtr->curDispPtr = NULL;1400screenPtr->curScreenIndex = -1;1401screenPtr->bindingDepth = 0;1402Tcl_SetAssocData(interp, "tkBind", FreeScreenInfo,1403(ClientData) screenPtr);1404}1405oldDispPtr = screenPtr->curDispPtr;1406oldScreen = screenPtr->curScreenIndex;1407if ((dispPtr != screenPtr->curDispPtr)1408|| (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) {1409screenPtr->curDispPtr = dispPtr;1410screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin);1411ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex);1412}14131414p = Tcl_DStringValue(&scripts);1415end = p + Tcl_DStringLength(&scripts);1416while (p != end) {1417screenPtr->bindingDepth += 1;1418Tcl_AllowExceptions(interp);1419code = Tcl_GlobalEval(interp, p);1420screenPtr->bindingDepth -= 1;1421if (code != TCL_OK) {1422if (code == TCL_CONTINUE) {1423/*1424* Do nothing: just go on to the next script.1425*/1426} else if (code == TCL_BREAK) {1427break;1428} else {1429Tcl_AddErrorInfo(interp, "\n (command bound to event)");1430Tcl_BackgroundError(interp);1431break;1432}1433}14341435/*1436* Skip over the current script and its terminating null character.1437*/14381439while (*p != 0) {1440p++;1441}1442p++;1443}1444if ((screenPtr->bindingDepth != 0) &&1445((oldDispPtr != screenPtr->curDispPtr)1446|| (oldScreen != screenPtr->curScreenIndex))) {14471448/*1449* Some other binding script is currently executing, but its1450* screen is no longer current. Change the current display1451* back again.1452*/14531454screenPtr->curDispPtr = oldDispPtr;1455screenPtr->curScreenIndex = oldScreen;1456ChangeScreen(interp, oldDispPtr->name, oldScreen);1457}1458Tcl_DStringResult(interp, &savedResult);1459Tcl_DStringFree(&scripts);1460}14611462/*1463*----------------------------------------------------------------------1464*1465* MatchPatterns --1466*1467* Given a list of pattern sequences and a list of recent events,1468* return the pattern sequence that best matches the event list,1469* if there is one.1470*1471* This procedure is used in two different ways. In the simplest1472* use, "object" is NULL and psPtr is a list of pattern sequences,1473* each of which corresponds to a binding. In this case, the1474* procedure finds the pattern sequences that match the event list1475* and returns the most specify of those, if there is more than one.1476*1477* In the second case, psPtr is a list of pattern sequences, each1478* of which corresponds to a definition for a virtual binding.1479* In order for one of these sequences to "match", it must match1480* the events (as above) but in addition there must be a binding1481* for its associated virtual event on the current object. The1482* "object" argument indicates which object the binding must be for.1483*1484* Results:1485* The return value is NULL if bestPtr is NULL and no pattern matches1486* the recent events from bindPtr. Otherwise the return value is1487* the most specific pattern sequence among bestPtr and all those1488* at psPtr that match the event list and object. If a pattern1489* sequence other than bestPtr is returned, then *bestCommandPtr1490* is filled in with a pointer to the command from the best sequence.1491*1492* Side effects:1493* None.1494*1495*----------------------------------------------------------------------1496*/1497static PatSeq *1498MatchPatterns(dispPtr, bindPtr, psPtr, bestPtr, object, bestCommandPtr)1499TkDisplay *dispPtr; /* Display from which the event came. */1500BindingTable *bindPtr; /* Information about binding table, such as1501* ring of recent events. */1502PatSeq *psPtr; /* List of pattern sequences. */1503PatSeq *bestPtr; /* The best match seen so far, from a1504* previous call to this procedure. NULL1505* means no prior best match. */1506ClientData object; /* If NULL, the sequences at psPtr1507* correspond to "normal" bindings. If1508* non-NULL, the sequences at psPtr correspond1509* to virtual bindings; in order to match each1510* sequence must correspond to a virtual1511* binding for which a binding exists for1512* object in bindPtr. */1513char **bestCommandPtr; /* Returns the command associated with the1514* best match. Not modified unless a result1515* other than bestPtr is returned. */1516{1517PatSeq *matchPtr;1518char *bestCommand, *command;15191520bestCommand = *bestCommandPtr;15211522/*1523* Iterate over all the pattern sequences.1524*/15251526for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {1527XEvent *eventPtr;1528Pattern *patPtr;1529Window window;1530Detail *detailPtr;1531int patCount, ringCount, flags, state;1532int modMask;15331534/*1535* Iterate over all the patterns in a sequence to be1536* sure that they all match.1537*/15381539eventPtr = &bindPtr->eventRing[bindPtr->curEvent];1540detailPtr = &bindPtr->detailRing[bindPtr->curEvent];1541window = eventPtr->xany.window;1542patPtr = psPtr->pats;1543patCount = psPtr->numPats;1544ringCount = EVENT_BUFFER_SIZE;1545while (patCount > 0) {1546if (ringCount <= 0) {1547goto nextSequence;1548}1549if (eventPtr->xany.type != patPtr->eventType) {1550/*1551* Most of the event types are considered superfluous1552* in that they are ignored if they occur in the middle1553* of a pattern sequence and have mismatching types. The1554* only ones that cannot be ignored are ButtonPress and1555* ButtonRelease events (if the next event in the pattern1556* is a KeyPress or KeyRelease) and KeyPress and KeyRelease1557* events (if the next pattern event is a ButtonPress or1558* ButtonRelease). Here are some tricky cases to consider:1559* 1. Double-Button or Double-Key events.1560* 2. Double-ButtonRelease or Double-KeyRelease events.1561* 3. The arrival of various events like Enter and Leave1562* and FocusIn and GraphicsExpose between two button1563* presses or key presses.1564* 4. Modifier keys like Shift and Control shouldn't1565* generate conflicts with button events.1566*/15671568if ((patPtr->eventType == KeyPress)1569|| (patPtr->eventType == KeyRelease)) {1570if ((eventPtr->xany.type == ButtonPress)1571|| (eventPtr->xany.type == ButtonRelease)) {1572goto nextSequence;1573}1574} else if ((patPtr->eventType == ButtonPress)1575|| (patPtr->eventType == ButtonRelease)) {1576if ((eventPtr->xany.type == KeyPress)1577|| (eventPtr->xany.type == KeyRelease)) {1578int i;15791580/*1581* Ignore key events if they are modifier keys.1582*/15831584for (i = 0; i < dispPtr->numModKeyCodes; i++) {1585if (dispPtr->modKeyCodes[i]1586== eventPtr->xkey.keycode) {1587/*1588* This key is a modifier key, so ignore it.1589*/1590goto nextEvent;1591}1592}1593goto nextSequence;1594}1595}1596goto nextEvent;1597}1598if (eventPtr->xany.window != window) {1599goto nextSequence;1600}16011602/*1603* Note: it's important for the keysym check to go before1604* the modifier check, so we can ignore unwanted modifier1605* keys before choking on the modifier check.1606*/16071608if ((patPtr->detail.clientData != 0)1609&& (patPtr->detail.clientData != detailPtr->clientData)) {1610/*1611* The detail appears not to match. However, if the event1612* is a KeyPress for a modifier key then just ignore the1613* event. Otherwise event sequences like "aD" never match1614* because the shift key goes down between the "a" and the1615* "D".1616*/16171618if (eventPtr->xany.type == KeyPress) {1619int i;16201621for (i = 0; i < dispPtr->numModKeyCodes; i++) {1622if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {1623goto nextEvent;1624}1625}1626}1627goto nextSequence;1628}1629flags = flagArray[eventPtr->type];1630if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {1631state = eventPtr->xkey.state;1632} else if (flags & CROSSING) {1633state = eventPtr->xcrossing.state;1634} else {1635state = 0;1636}1637if (patPtr->needMods != 0) {1638modMask = patPtr->needMods;1639if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {1640modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;1641}1642if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {1643modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;1644}1645if ((state & modMask) != modMask) {1646goto nextSequence;1647}1648}1649if (psPtr->flags & PAT_NEARBY) {1650XEvent *firstPtr;1651int timeDiff;16521653firstPtr = &bindPtr->eventRing[bindPtr->curEvent];1654timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;1655if ((firstPtr->xkey.x_root1656< (eventPtr->xkey.x_root - NEARBY_PIXELS))1657|| (firstPtr->xkey.x_root1658> (eventPtr->xkey.x_root + NEARBY_PIXELS))1659|| (firstPtr->xkey.y_root1660< (eventPtr->xkey.y_root - NEARBY_PIXELS))1661|| (firstPtr->xkey.y_root1662> (eventPtr->xkey.y_root + NEARBY_PIXELS))1663|| (timeDiff > NEARBY_MS)) {1664goto nextSequence;1665}1666}1667patPtr++;1668patCount--;1669nextEvent:1670if (eventPtr == bindPtr->eventRing) {1671eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];1672detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];1673} else {1674eventPtr--;1675detailPtr--;1676}1677ringCount--;1678}16791680matchPtr = psPtr;1681command = matchPtr->command;16821683if (object != NULL) {1684int iVirt;1685VirtualOwners *voPtr;1686PatternTableKey key;16871688/*1689* The sequence matches the physical constraints.1690* Is this object interested in any of the virtual events1691* that correspond to this sequence?1692*/16931694voPtr = psPtr->voPtr;16951696memset(&key, 0, sizeof(key));1697key.object = object;1698key.type = VirtualEvent;1699key.detail.clientData = 0;17001701for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {1702Tcl_HashEntry *hPtr = voPtr->owners[iVirt];17031704key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr,1705hPtr);1706hPtr = Tcl_FindHashEntry(&bindPtr->patternTable,1707(char *) &key);1708if (hPtr != NULL) {17091710/*1711* This tag is interested in this virtual event and its1712* corresponding physical event is a good match with the1713* virtual event's definition.1714*/17151716PatSeq *virtMatchPtr;17171718virtMatchPtr = (PatSeq *) Tcl_GetHashValue(hPtr);1719if ((virtMatchPtr->numPats != 1)1720|| (virtMatchPtr->nextSeqPtr != NULL)) {1721panic("MatchPattern: badly constructed virtual event");1722}1723command = virtMatchPtr->command;17241725goto match;1726}1727}17281729/*1730* The physical event matches a virtual event's definition, but1731* the tag isn't interested in it.1732*/1733goto nextSequence;1734}1735match:17361737/*1738* This sequence matches. If we've already got another match,1739* pick whichever is most specific. Detail is most important,1740* then needMods.1741*/17421743if (bestPtr != NULL) {1744Pattern *patPtr2;1745int i;17461747if (matchPtr->numPats != bestPtr->numPats) {1748if (bestPtr->numPats > matchPtr->numPats) {1749goto nextSequence;1750} else {1751goto newBest;1752}1753}1754for (i = 0, patPtr = matchPtr->pats, patPtr2 = bestPtr->pats;1755i < matchPtr->numPats; i++, patPtr++, patPtr2++) {1756if (patPtr->detail.clientData != patPtr2->detail.clientData) {1757if (patPtr->detail.clientData == 0) {1758goto nextSequence;1759} else {1760goto newBest;1761}1762}1763if (patPtr->needMods != patPtr2->needMods) {1764if ((patPtr->needMods & patPtr2->needMods)1765== patPtr->needMods) {1766goto nextSequence;1767} else if ((patPtr->needMods & patPtr2->needMods)1768== patPtr2->needMods) {1769goto newBest;1770}1771}1772}1773/*1774* Tie goes to current best pattern.1775*1776* (1) For virtual vs. virtual, the least recently defined1777* virtual wins, because virtuals are examined in order of1778* definition. This order is _not_ guaranteed in the1779* documentation.1780*1781* (2) For virtual vs. physical, the physical wins because all1782* the physicals are examined before the virtuals. This order1783* is guaranteed in the documentation.1784*1785* (3) For physical vs. physical pattern, the most recently1786* defined physical wins, because physicals are examined in1787* reverse order of definition. This order is guaranteed in1788* the documentation.1789*/17901791goto nextSequence;1792}1793newBest:1794bestPtr = matchPtr;1795bestCommand = command;17961797nextSequence: continue;1798}17991800*bestCommandPtr = bestCommand;1801return bestPtr;1802}18031804/*1805*--------------------------------------------------------------1806*1807* ExpandPercents --1808*1809* Given a command and an event, produce a new command1810* by replacing % constructs in the original command1811* with information from the X event.1812*1813* Results:1814* The new expanded command is appended to the dynamic string1815* given by dsPtr.1816*1817* Side effects:1818* None.1819*1820*--------------------------------------------------------------1821*/18221823static void1824ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)1825TkWindow *winPtr; /* Window where event occurred: needed to1826* get input context. */1827char *before; /* Command containing percent expressions1828* to be replaced. */1829XEvent *eventPtr; /* X event containing information to be1830* used in % replacements. */1831KeySym keySym; /* KeySym: only relevant for KeyPress and1832* KeyRelease events). */1833Tcl_DString *dsPtr; /* Dynamic string in which to append new1834* command. */1835{1836int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl1837* list element. */1838int number, flags, length;1839#define NUM_SIZE 401840char *string;1841char numStorage[NUM_SIZE+1];18421843if (eventPtr->type < TK_LASTEVENT) {1844flags = flagArray[eventPtr->type];1845} else {1846flags = 0;1847}1848while (1) {1849/*1850* Find everything up to the next % character and append it1851* to the result string.1852*/18531854for (string = before; (*string != 0) && (*string != '%'); string++) {1855/* Empty loop body. */1856}1857if (string != before) {1858Tcl_DStringAppend(dsPtr, before, string-before);1859before = string;1860}1861if (*before == 0) {1862break;1863}18641865/*1866* There's a percent sequence here. Process it.1867*/18681869number = 0;1870string = "??";1871switch (before[1]) {1872case '#':1873number = eventPtr->xany.serial;1874goto doNumber;1875case 'a':1876sprintf(numStorage, "0x%x", (int) eventPtr->xconfigure.above);1877string = numStorage;1878goto doString;1879case 'b':1880number = eventPtr->xbutton.button;1881goto doNumber;1882case 'c':1883if (flags & EXPOSE) {1884number = eventPtr->xexpose.count;1885}1886goto doNumber;1887case 'd':1888if (flags & (CROSSING|FOCUS)) {1889if (flags & FOCUS) {1890number = eventPtr->xfocus.detail;1891} else {1892number = eventPtr->xcrossing.detail;1893}1894string = TkFindStateString(notifyDetail, number);1895}1896goto doString;1897case 'f':1898number = eventPtr->xcrossing.focus;1899goto doNumber;1900case 'h':1901if (flags & EXPOSE) {1902number = eventPtr->xexpose.height;1903} else if (flags & (CONFIG)) {1904number = eventPtr->xconfigure.height;1905}1906goto doNumber;1907case 'k':1908number = eventPtr->xkey.keycode;1909goto doNumber;1910case 'm':1911if (flags & CROSSING) {1912number = eventPtr->xcrossing.mode;1913} else if (flags & FOCUS) {1914number = eventPtr->xfocus.mode;1915}1916string = TkFindStateString(notifyMode, number);1917goto doString;1918case 'o':1919if (flags & CREATE) {1920number = eventPtr->xcreatewindow.override_redirect;1921} else if (flags & MAP) {1922number = eventPtr->xmap.override_redirect;1923} else if (flags & REPARENT) {1924number = eventPtr->xreparent.override_redirect;1925} else if (flags & CONFIG) {1926number = eventPtr->xconfigure.override_redirect;1927}1928goto doNumber;1929case 'p':1930string = TkFindStateString(circPlace, eventPtr->xcirculate.place);1931goto doString;1932case 's':1933if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {1934number = eventPtr->xkey.state;1935} else if (flags & CROSSING) {1936number = eventPtr->xcrossing.state;1937} else if (flags & VISIBILITY) {1938string = TkFindStateString(visNotify,1939eventPtr->xvisibility.state);1940goto doString;1941}1942goto doNumber;1943case 't':1944if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {1945number = (int) eventPtr->xkey.time;1946} else if (flags & CROSSING) {1947number = (int) eventPtr->xcrossing.time;1948} else if (flags & PROP) {1949number = (int) eventPtr->xproperty.time;1950}1951goto doNumber;1952case 'v':1953number = eventPtr->xconfigurerequest.value_mask;1954goto doNumber;1955case 'w':1956if (flags & EXPOSE) {1957number = eventPtr->xexpose.width;1958} else if (flags & CONFIG) {1959number = eventPtr->xconfigure.width;1960}1961goto doNumber;1962case 'x':1963if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {1964number = eventPtr->xkey.x;1965} else if (flags & CROSSING) {1966number = eventPtr->xcrossing.x;1967} else if (flags & EXPOSE) {1968number = eventPtr->xexpose.x;1969} else if (flags & (CREATE|CONFIG|GRAVITY)) {1970number = eventPtr->xcreatewindow.x;1971} else if (flags & REPARENT) {1972number = eventPtr->xreparent.x;1973}1974goto doNumber;1975case 'y':1976if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {1977number = eventPtr->xkey.y;1978} else if (flags & EXPOSE) {1979number = eventPtr->xexpose.y;1980} else if (flags & (CREATE|CONFIG|GRAVITY)) {1981number = eventPtr->xcreatewindow.y;1982} else if (flags & REPARENT) {1983number = eventPtr->xreparent.y;1984} else if (flags & CROSSING) {1985number = eventPtr->xcrossing.y;19861987}1988goto doNumber;1989case 'A':1990if (flags & KEY) {1991int numChars;19921993/*1994* If we're using input methods and this is a keypress1995* event, invoke XmbTkFindStateString. Otherwise just use1996* the older XTkFindStateString.1997*/19981999#ifdef TK_USE_INPUT_METHODS2000Status status;2001if ((winPtr->inputContext != NULL)2002&& (eventPtr->type == KeyPress)) {2003numChars = XmbLookupString(winPtr->inputContext,2004&eventPtr->xkey, numStorage, NUM_SIZE,2005(KeySym *) NULL, &status);2006if ((status != XLookupChars)2007&& (status != XLookupBoth)) {2008numChars = 0;2009}2010} else {2011numChars = XLookupString(&eventPtr->xkey, numStorage,2012NUM_SIZE, (KeySym *) NULL,2013(XComposeStatus *) NULL);2014}2015#else /* TK_USE_INPUT_METHODS */2016numChars = XLookupString(&eventPtr->xkey, numStorage,2017NUM_SIZE, (KeySym *) NULL,2018(XComposeStatus *) NULL);2019#endif /* TK_USE_INPUT_METHODS */2020numStorage[numChars] = '\0';2021string = numStorage;2022}2023goto doString;2024case 'B':2025number = eventPtr->xcreatewindow.border_width;2026goto doNumber;2027case 'E':2028number = (int) eventPtr->xany.send_event;2029goto doNumber;2030case 'K':2031if (flags & KEY) {2032char *name;20332034name = TkKeysymToString(keySym);2035if (name != NULL) {2036string = name;2037}2038}2039goto doString;2040case 'N':2041number = (int) keySym;2042goto doNumber;2043case 'R':2044number = (int) eventPtr->xkey.root;2045goto doNumber;2046case 'S':2047sprintf(numStorage, "0x%x", (int) eventPtr->xkey.subwindow);2048string = numStorage;2049goto doString;2050case 'T':2051number = eventPtr->type;2052goto doNumber;2053case 'W': {2054Tk_Window tkwin;20552056tkwin = Tk_IdToWindow(eventPtr->xany.display,2057eventPtr->xany.window);2058if (tkwin != NULL) {2059string = Tk_PathName(tkwin);2060} else {2061string = "??";2062}2063goto doString;2064}2065case 'X': {2066Tk_Window tkwin;2067int x, y;2068int width, height;20692070number = eventPtr->xkey.x_root;2071tkwin = Tk_IdToWindow(eventPtr->xany.display,2072eventPtr->xany.window);2073if (tkwin != NULL) {2074Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);2075number -= x;2076}2077goto doNumber;2078}2079case 'Y': {2080Tk_Window tkwin;2081int x, y;2082int width, height;20832084number = eventPtr->xkey.y_root;2085tkwin = Tk_IdToWindow(eventPtr->xany.display,2086eventPtr->xany.window);2087if (tkwin != NULL) {2088Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);2089number -= y;2090}2091goto doNumber;2092}2093default:2094numStorage[0] = before[1];2095numStorage[1] = '\0';2096string = numStorage;2097goto doString;2098}20992100doNumber:2101sprintf(numStorage, "%d", number);2102string = numStorage;21032104doString:2105spaceNeeded = Tcl_ScanElement(string, &cvtFlags);2106length = Tcl_DStringLength(dsPtr);2107Tcl_DStringSetLength(dsPtr, length + spaceNeeded);2108spaceNeeded = Tcl_ConvertElement(string,2109Tcl_DStringValue(dsPtr) + length,2110cvtFlags | TCL_DONT_USE_BRACES);2111Tcl_DStringSetLength(dsPtr, length + spaceNeeded);2112before += 2;2113}2114}21152116/*2117*----------------------------------------------------------------------2118*2119* FreeScreenInfo --2120*2121* This procedure is invoked when an interpreter is deleted in2122* order to free the ScreenInfo structure associated with the2123* "tkBind" AssocData.2124*2125* Results:2126* None.2127*2128* Side effects:2129* Storage is freed.2130*2131*----------------------------------------------------------------------2132*/21332134static void2135FreeScreenInfo(clientData, interp)2136ClientData clientData; /* Pointer to ScreenInfo structure. */2137Tcl_Interp *interp; /* Interpreter that is being deleted. */2138{2139ckfree((char *) clientData);2140}21412142/*2143*----------------------------------------------------------------------2144*2145* ChangeScreen --2146*2147* This procedure is invoked whenever the current screen changes2148* in an application. It invokes a Tcl procedure named2149* "tkScreenChanged", passing it the screen name as argument.2150* tkScreenChanged does things like making the tkPriv variable2151* point to an array for the current display.2152*2153* Results:2154* None.2155*2156* Side effects:2157* Depends on what tkScreenChanged does. If an error occurs2158* them tkError will be invoked.2159*2160*----------------------------------------------------------------------2161*/21622163static void2164ChangeScreen(interp, dispName, screenIndex)2165Tcl_Interp *interp; /* Interpreter in which to invoke2166* command. */2167char *dispName; /* Name of new display. */2168int screenIndex; /* Index of new screen. */2169{2170Tcl_DString cmd;2171int code;2172char screen[30];21732174Tcl_DStringInit(&cmd);2175Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);2176Tcl_DStringAppend(&cmd, dispName, -1);2177sprintf(screen, ".%d", screenIndex);2178Tcl_DStringAppend(&cmd, screen, -1);2179code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));2180if (code != TCL_OK) {2181Tcl_AddErrorInfo(interp,2182"\n (changing screen in event binding)");2183Tcl_BackgroundError(interp);2184}2185}218621872188/*2189*----------------------------------------------------------------------2190*2191* Tk_EventCmd --2192*2193* This procedure is invoked to process the "event" Tcl command.2194* It is used to define and generate events.2195*2196* Results:2197* A standard Tcl result.2198*2199* Side effects:2200* See the user documentation.2201*2202*----------------------------------------------------------------------2203*/22042205int2206Tk_EventCmd(clientData, interp, argc, argv)2207ClientData clientData; /* Main window associated with2208* interpreter. */2209Tcl_Interp *interp; /* Current interpreter. */2210int argc; /* Number of arguments. */2211char **argv; /* Argument strings. */2212{2213int i;2214size_t length;2215char *option;2216TkWindow *winPtr;2217TkVirtualEventTable *vetPtr;22182219if (argc < 2) {2220Tcl_AppendResult(interp, "wrong # args: should be \"",2221argv[0], " option ?arg1?\"", (char *) NULL);2222return TCL_ERROR;2223}22242225option = argv[1];2226length = strlen(option);2227if (length == 0) {2228goto badopt;2229}22302231winPtr = (TkWindow *) clientData;2232vetPtr = winPtr->mainPtr->vetPtr;22332234if (strncmp(option, "add", length) == 0) {2235if (argc < 4) {2236Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],2237" add virtual sequence ?sequence ...?\"", (char *) NULL);2238return TCL_ERROR;2239}2240for (i = 3; i < argc; i++) {2241if (CreateVirtualEvent(interp, vetPtr, argv[2], argv[i])2242!= TCL_OK) {2243return TCL_ERROR;2244}2245}2246} else if (strncmp(option, "delete", length) == 0) {2247if (argc < 3) {2248Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],2249" delete virtual ?sequence sequence ...?\"",2250(char *) NULL);2251return TCL_ERROR;2252}2253if (argc == 3) {2254return DeleteVirtualEvent(interp, vetPtr, argv[2], NULL);2255}2256for (i = 3; i < argc; i++) {2257if (DeleteVirtualEvent(interp, vetPtr, argv[2], argv[i])2258!= TCL_OK) {2259return TCL_ERROR;2260}2261}2262} else if (strncmp(option, "generate", length) == 0) {2263if (argc < 4) {2264Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],2265" generate window event ?options?\"", (char *) NULL);2266return TCL_ERROR;2267}2268return HandleEventGenerate(interp, (Tk_Window) winPtr,2269argc - 2, argv + 2);2270} else if (strncmp(option, "info", length) == 0) {2271if (argc == 2) {2272GetAllVirtualEvents(interp, vetPtr);2273return TCL_OK;2274} else if (argc == 3) {2275return GetVirtualEvent(interp, vetPtr, argv[2]);2276} else {2277Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],2278" info ?virtual?\"", (char *) NULL);2279return TCL_ERROR;2280}2281} else {2282badopt:2283Tcl_AppendResult(interp, "bad option \"", argv[1],2284"\": should be add, delete, generate, info", (char *) NULL);2285return TCL_ERROR;2286}2287return TCL_OK;2288}22892290/*2291*--------------------------------------------------------------2292*2293* CreateVirtualEventTable --2294*2295* Set up a new domain in which virtual events may be defined.2296*2297* Results:2298* The return value is a token for the new table, which must2299* be passed to procedures like Tk_CreateVirtualEvent().2300*2301* Side effects:2302* The caller must have already called Tk_CreateBindingTable() to2303* properly set up memory used by the entire event-handling subsystem.2304* Memory is allocated for the new table.2305*2306*--------------------------------------------------------------2307*/2308static TkVirtualEventTable *2309CreateVirtualEventTable()2310{2311TkVirtualEventTable *vetPtr;23122313if (!initialized) {2314panic("CreateVirtualEvent: Tk_CreateBindingTable never called");2315}2316vetPtr = (TkVirtualEventTable *) ckalloc(sizeof(TkVirtualEventTable));2317Tcl_InitHashTable(&vetPtr->patternTable,2318sizeof(PatternTableKey)/sizeof(int));2319Tcl_InitHashTable(&vetPtr->virtualTable, TCL_ONE_WORD_KEYS);23202321return vetPtr;2322}23232324/*2325*--------------------------------------------------------------2326*2327* DeleteVirtualEventTable --2328*2329* Destroy a virtual event table and free up all its memory.2330* The caller should not use virtualEventTable again after2331* this procedure returns.2332*2333* Results:2334* None.2335*2336* Side effects:2337* Memory is freed.2338*2339*--------------------------------------------------------------2340*/23412342static void2343DeleteVirtualEventTable(vetPtr)2344TkVirtualEventTable *vetPtr;/* The virtual event table to be destroyed. */2345{2346Tcl_HashEntry *hPtr;2347Tcl_HashSearch search;2348PatSeq *psPtr, *nextPtr;23492350hPtr = Tcl_FirstHashEntry(&vetPtr->patternTable, &search);2351for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {2352psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);2353for ( ; psPtr != NULL; psPtr = nextPtr) {2354nextPtr = psPtr->nextSeqPtr;2355ckfree((char *) psPtr->voPtr);2356ckfree((char *) psPtr);2357}2358}2359Tcl_DeleteHashTable(&vetPtr->patternTable);23602361hPtr = Tcl_FirstHashEntry(&vetPtr->virtualTable, &search);2362for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {2363ckfree((char *) Tcl_GetHashValue(hPtr));2364}2365Tcl_DeleteHashTable(&vetPtr->virtualTable);23662367ckfree((char *) vetPtr);2368}23692370/*2371*----------------------------------------------------------------------2372*2373* CreateVirtualEvent --2374*2375* Add a new definition for a virtual event. If the virtual event2376* is already defined, the new definition augments those that2377* already exist.2378*2379* Results:2380* The return value is TCL_ERROR if an error occured while2381* creating the virtual binding. In this case, an error message2382* will be left in interp->result. If all went well then the return2383* value is TCL_OK.2384*2385* Side effects:2386* The virtual event may cause future calls to Tk_BindEvent to2387* behave differently than they did previously.2388*2389*----------------------------------------------------------------------2390*/23912392static int2393CreateVirtualEvent(interp, vetPtr, virtString, eventString)2394Tcl_Interp *interp; /* Used for error reporting. */2395TkVirtualEventTable *vetPtr;/* Table in which to augment virtual event. */2396char *virtString; /* Name of new virtual event. */2397char *eventString; /* String describing physical event that2398* triggers virtual event. */2399{2400PatSeq *psPtr;2401int dummy;2402Tcl_HashEntry *vhPtr;2403unsigned long eventMask;2404PhysicalsOwned *poPtr;2405VirtualOwners *voPtr;2406Tk_Uid virtUid;24072408virtUid = GetVirtualEventUid(interp, virtString);2409if (virtUid == NULL) {2410return TCL_ERROR;2411}24122413/*2414* Find/create physical event2415*/24162417psPtr = FindSequence(interp, &vetPtr->patternTable, NULL, eventString,24181, 0, &eventMask);2419if (psPtr == NULL) {2420return TCL_ERROR;2421}24222423/*2424* Find/create virtual event.2425*/24262427vhPtr = Tcl_CreateHashEntry(&vetPtr->virtualTable, virtUid, &dummy);24282429/*2430* Make virtual event own the physical event.2431*/24322433poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);2434if (poPtr == NULL) {2435poPtr = (PhysicalsOwned *) ckalloc(sizeof(PhysicalsOwned));2436poPtr->numOwned = 0;2437} else {2438/*2439* See if this virtual event is already defined for this physical2440* event and just return if it is.2441*/24422443int i;2444for (i = 0; i < poPtr->numOwned; i++) {2445if (poPtr->patSeqs[i] == psPtr) {2446return TCL_OK;2447}2448}2449poPtr = (PhysicalsOwned *) ckrealloc((char *) poPtr,2450sizeof(PhysicalsOwned) + poPtr->numOwned * sizeof(PatSeq *));2451}2452Tcl_SetHashValue(vhPtr, (ClientData) poPtr);2453poPtr->patSeqs[poPtr->numOwned] = psPtr;2454poPtr->numOwned++;24552456/*2457* Make physical event so it can trigger the virtual event.2458*/24592460voPtr = psPtr->voPtr;2461if (voPtr == NULL) {2462voPtr = (VirtualOwners *) ckalloc(sizeof(VirtualOwners));2463voPtr->numOwners = 0;2464} else {2465voPtr = (VirtualOwners *) ckrealloc((char *) voPtr,2466sizeof(VirtualOwners)2467+ voPtr->numOwners * sizeof(Tcl_HashEntry *));2468}2469psPtr->voPtr = voPtr;2470voPtr->owners[voPtr->numOwners] = vhPtr;2471voPtr->numOwners++;24722473return TCL_OK;2474}24752476/*2477*--------------------------------------------------------------2478*2479* DeleteVirtualEvent --2480*2481* Remove the definition of a given virtual event. If the2482* event string is NULL, all definitions of the virtual event2483* will be removed. Otherwise, just the specified definition2484* of the virtual event will be removed.2485*2486* Results:2487* The result is a standard Tcl return value. If an error2488* occurs then interp->result will contain an error message.2489* It is not an error to attempt to delete a virtual event that2490* does not exist or a definition that does not exist.2491*2492* Side effects:2493* The virtual event given by virtString may be removed from the2494* virtual event table.2495*2496*--------------------------------------------------------------2497*/24982499static int2500DeleteVirtualEvent(interp, vetPtr, virtString, eventString)2501Tcl_Interp *interp; /* Used for error reporting. */2502TkVirtualEventTable *vetPtr;/* Table in which to delete event. */2503char *virtString; /* String describing event sequence that2504* triggers binding. */2505char *eventString; /* The event sequence that should be deleted,2506* or NULL to delete all event sequences for2507* the entire virtual event. */2508{2509int iPhys;2510Tk_Uid virtUid;2511Tcl_HashEntry *vhPtr;2512PhysicalsOwned *poPtr;2513PatSeq *eventPSPtr;25142515virtUid = GetVirtualEventUid(interp, virtString);2516if (virtUid == NULL) {2517return TCL_ERROR;2518}25192520vhPtr = Tcl_FindHashEntry(&vetPtr->virtualTable, virtUid);2521if (vhPtr == NULL) {2522return TCL_OK;2523}2524poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);25252526eventPSPtr = NULL;2527if (eventString != NULL) {2528unsigned long eventMask;25292530/*2531* Delete only the specific physical event associated with the2532* virtual event. If the physical event doesn't already exist, or2533* the virtual event doesn't own that physical event, return w/o2534* doing anything.2535*/25362537eventPSPtr = FindSequence(interp, &vetPtr->patternTable, NULL,2538eventString, 0, 0, &eventMask);2539if (eventPSPtr == NULL) {2540return (interp->result[0] != '\0') ? TCL_ERROR : TCL_OK;2541}2542}25432544for (iPhys = poPtr->numOwned; --iPhys >= 0; ) {2545PatSeq *psPtr = poPtr->patSeqs[iPhys];2546if ((eventPSPtr == NULL) || (psPtr == eventPSPtr)) {2547int iVirt;2548VirtualOwners *voPtr;25492550/*2551* Remove association between this physical event and the given2552* virtual event that it triggers.2553*/25542555voPtr = psPtr->voPtr;2556for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {2557if (voPtr->owners[iVirt] == vhPtr) {2558break;2559}2560}2561if (iVirt == voPtr->numOwners) {2562panic("DeleteVirtualEvent: couldn't find owner");2563}2564voPtr->numOwners--;2565if (voPtr->numOwners == 0) {2566/*2567* Removed last reference to this physical event, so2568* remove it from physical->virtual map.2569*/2570PatSeq *prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);2571if (prevPtr == psPtr) {2572if (psPtr->nextSeqPtr == NULL) {2573Tcl_DeleteHashEntry(psPtr->hPtr);2574} else {2575Tcl_SetHashValue(psPtr->hPtr,2576psPtr->nextSeqPtr);2577}2578} else {2579for ( ; ; prevPtr = prevPtr->nextSeqPtr) {2580if (prevPtr == NULL) {2581panic("Tk_DeleteVirtualEvent couldn't find on hash chain");2582}2583if (prevPtr->nextSeqPtr == psPtr) {2584prevPtr->nextSeqPtr = psPtr->nextSeqPtr;2585break;2586}2587}2588}2589ckfree((char *) psPtr->voPtr);2590ckfree((char *) psPtr);2591} else {2592/*2593* This physical event still triggers some other virtual2594* event(s). Consolidate the list of virtual owners for2595* this physical event so it no longer triggers the2596* given virtual event.2597*/2598voPtr->owners[iVirt] = voPtr->owners[voPtr->numOwners];2599}26002601/*2602* Now delete the virtual event's reference to the physical2603* event.2604*/26052606poPtr->numOwned--;2607if (eventPSPtr != NULL && poPtr->numOwned != 0) {2608/*2609* Just deleting this one physical event. Consolidate list2610* of owned physical events and return.2611*/26122613poPtr->patSeqs[iPhys] = poPtr->patSeqs[poPtr->numOwned];2614return TCL_OK;2615}2616}2617}26182619if (poPtr->numOwned == 0) {2620/*2621* All the physical events for this virtual event were deleted,2622* either because there was only one associated physical event or2623* because the caller was deleting the entire virtual event. Now2624* the virtual event itself should be deleted.2625*/26262627ckfree((char *) poPtr);2628Tcl_DeleteHashEntry(vhPtr);2629}2630return TCL_OK;2631}26322633/*2634*---------------------------------------------------------------------------2635*2636* GetVirtualEvent --2637*2638* Return the list of physical events that can invoke the2639* given virtual event.2640*2641* Results:2642* The return value is TCL_OK and interp->result is filled with the2643* string representation of the physical events associated with the2644* virtual event; if there are no physical events for the given virtual2645* event, interp->result is filled with and empty string. If the2646* virtual event string is improperly formed, then TCL_ERROR is2647* returned and an error message is left in interp->result.2648*2649* Side effects:2650* None.2651*2652*---------------------------------------------------------------------------2653*/26542655static int2656GetVirtualEvent(interp, vetPtr, virtString)2657Tcl_Interp *interp; /* Interpreter for reporting. */2658TkVirtualEventTable *vetPtr;/* Table in which to look for event. */2659char *virtString; /* String describing virtual event. */2660{2661Tcl_HashEntry *vhPtr;2662Tcl_DString ds;2663int iPhys;2664PhysicalsOwned *poPtr;2665Tk_Uid virtUid;26662667virtUid = GetVirtualEventUid(interp, virtString);2668if (virtUid == NULL) {2669return TCL_ERROR;2670}26712672vhPtr = Tcl_FindHashEntry(&vetPtr->virtualTable, virtUid);2673if (vhPtr == NULL) {2674return TCL_OK;2675}26762677Tcl_DStringInit(&ds);26782679poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);2680for (iPhys = 0; iPhys < poPtr->numOwned; iPhys++) {2681Tcl_DStringSetLength(&ds, 0);2682GetPatternString(poPtr->patSeqs[iPhys], &ds);2683Tcl_AppendElement(interp, Tcl_DStringValue(&ds));2684}2685Tcl_DStringFree(&ds);26862687return TCL_OK;2688}26892690/*2691*--------------------------------------------------------------2692*2693* GetAllVirtualEvents --2694*2695* Return a list that contains the names of all the virtual2696* event defined.2697*2698* Results:2699* There is no return value. Interp->result is modified to2700* hold a Tcl list with one entry for each virtual event in2701* virtualTable.2702*2703* Side effects:2704* None.2705*2706*--------------------------------------------------------------2707*/27082709static void2710GetAllVirtualEvents(interp, vetPtr)2711Tcl_Interp *interp; /* Interpreter returning result. */2712TkVirtualEventTable *vetPtr;/* Table containing events. */2713{2714Tcl_HashEntry *hPtr;2715Tcl_HashSearch search;2716Tcl_DString ds;27172718Tcl_DStringInit(&ds);27192720hPtr = Tcl_FirstHashEntry(&vetPtr->virtualTable, &search);2721for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {2722Tcl_DStringSetLength(&ds, 0);2723Tcl_DStringAppend(&ds, "<<", 2);2724Tcl_DStringAppend(&ds, Tcl_GetHashKey(hPtr->tablePtr, hPtr), -1);2725Tcl_DStringAppend(&ds, ">>", 2);2726Tcl_AppendElement(interp, Tcl_DStringValue(&ds));2727}27282729Tcl_DStringFree(&ds);2730}27312732/*2733*---------------------------------------------------------------------------2734*2735* HandleEventGenerate --2736*2737* Helper function for the "event generate" command. Generate and2738* process an XEvent, constructed from information parsed from the2739* event description string and its optional arguments.2740*2741* argv[0] contains name of the target window.2742* argv[1] contains pattern string for one event (e.g, <Control-v>).2743* argv[2..argc-1] contains -field/option pairs for specifying2744* additional detail in the generated event.2745*2746* Either virtual or physical events can be generated this way.2747* The event description string must contain the specification2748* for only one event.2749*2750* Results:2751* None.2752*2753* Side effects:2754* When constructing the event,2755* event.xany.serial is filled with the current X serial number.2756* event.xany.window is filled with the target window.2757* event.xany.display is filled with the target window's display.2758* Any other fields in eventPtr which are not specified by the pattern2759* string or the optional arguments, are set to 0.2760*2761* The event may be handled sychronously or asynchronously, depending2762* on the value specified by the optional "-when" option. The2763* default setting is synchronous.2764*2765*---------------------------------------------------------------------------2766*/2767static int2768HandleEventGenerate(interp, tkwin, argc, argv)2769Tcl_Interp *interp; /* Interp for error messages and name lookup. */2770Tk_Window tkwin; /* Main window of this application. */2771int argc; /* Number of arguments. */2772char **argv; /* Argument strings. */2773{2774Pattern pat;2775char *p;2776unsigned long eventMask;2777int count, i, state, flags, synch;2778Tcl_QueuePosition pos;2779union2780{2781XEvent E;2782XVirtualEvent V;2783} event;27842785tkwin = Tk_NameToWindow(interp, argv[0], tkwin);2786if (tkwin == NULL) {2787return TCL_ERROR;2788}27892790p = argv[1];2791count = ParseEventDescription(interp, &p, &pat, &eventMask);2792if (count == 0) {2793return TCL_ERROR;2794}2795if (count != 1) {2796interp->result = "Double or Triple modifier not allowed";2797return TCL_ERROR;2798}2799if (*p != '\0') {2800interp->result = "only one event specification allowed";2801return TCL_ERROR;2802}2803if (argc & 1) {2804Tcl_AppendResult(interp, "value for \"", argv[argc - 1],2805"\" missing", (char *) NULL);2806return TCL_ERROR;2807}28082809memset((VOID *) &event, 0, sizeof(event));2810event.E.xany.type = pat.eventType;2811event.E.xany.serial = NextRequest(Tk_Display(tkwin));2812event.E.xany.send_event = False;2813event.E.xany.window = Tk_WindowId(tkwin);2814event.E.xany.display = Tk_Display(tkwin);28152816flags = flagArray[event.E.xany.type];2817if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {2818event.E.xkey.state = pat.needMods;2819if (flags & KEY) {2820/*2821* When mapping from a keysym to a keycode, need information about2822* the modifier state that should be used so that when they call2823* XKeycodeToKeysym taking into account the xkey.state, they will2824* get back the original keysym.2825*/28262827if (pat.detail.keySym == NoSymbol) {2828event.E.xkey.keycode = 0;2829} else {2830event.E.xkey.keycode = XKeysymToKeycode(event.E.xany.display,2831pat.detail.keySym);2832}2833if (event.E.xkey.keycode != 0) {2834for (state = 0; state < 4; state++) {2835if (XKeycodeToKeysym(event.E.xany.display,2836event.E.xkey.keycode, state) == pat.detail.keySym) {2837if (state & 1) {2838event.E.xkey.state |= ShiftMask;2839}2840if (state & 2) {2841TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;2842event.E.xkey.state |= dispPtr->modeModMask;2843}2844break;2845}2846}2847}2848} else if (flags & BUTTON) {2849event.E.xbutton.button = pat.detail.button;2850} else if (flags & VIRTUAL) {2851event.V.name = pat.detail.name;2852}2853}2854if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) {2855event.E.xcreatewindow.window = event.E.xany.window;2856}28572858/*2859* Process the remaining arguments to fill in additional fields2860* of the event.2861*/28622863synch = 1;2864pos = TCL_QUEUE_TAIL;2865for (i = 2; i < argc; i += 2) {2866char *field, *value;2867Tk_Window tkwin2;2868int number;2869KeySym keysym;28702871field = argv[i];2872value = argv[i+1];28732874if (strcmp(field, "-when") == 0) {2875if (strcmp(value, "now") == 0) {2876synch = 1;2877} else if (strcmp(value, "head") == 0) {2878pos = TCL_QUEUE_HEAD;2879synch = 0;2880} else if (strcmp(value, "mark") == 0) {2881pos = TCL_QUEUE_MARK;2882synch = 0;2883} else if (strcmp(value, "tail") == 0) {2884pos = TCL_QUEUE_TAIL;2885synch = 0;2886} else {2887Tcl_AppendResult(interp, "bad position \"", value,2888"\": should be now, head, mark, tail", (char *) NULL);2889return TCL_ERROR;2890}2891} else if (strcmp(field, "-above") == 0) {2892if (value[0] == '.') {2893tkwin2 = Tk_NameToWindow(interp, value, tkwin);2894if (tkwin2 == NULL) {2895return TCL_ERROR;2896}2897number = Tk_WindowId(tkwin2);2898} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {2899return TCL_ERROR;2900}2901if (flags & CONFIG) {2902event.E.xconfigure.above = number;2903} else {2904goto badopt;2905}2906} else if (strcmp(field, "-borderwidth") == 0) {2907if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {2908return TCL_ERROR;2909}2910if (flags & (CREATE|CONFIG)) {2911event.E.xcreatewindow.border_width = number;2912} else {2913goto badopt;2914}2915} else if (strcmp(field, "-button") == 0) {2916if (Tcl_GetInt(interp, value, &number) != TCL_OK) {2917return TCL_ERROR;2918}2919if (flags & BUTTON) {2920event.E.xbutton.button = number;2921} else {2922goto badopt;2923}2924} else if (strcmp(field, "-count") == 0) {2925if (Tcl_GetInt(interp, value, &number) != TCL_OK) {2926return TCL_ERROR;2927}2928if (flags & EXPOSE) {2929event.E.xexpose.count = number;2930} else {2931goto badopt;2932}2933} else if (strcmp(field, "-detail") == 0) {2934number = TkFindStateNum(interp, field, notifyDetail, value);2935if (number < 0) {2936return TCL_ERROR;2937}2938if (flags & FOCUS) {2939event.E.xfocus.detail = number;2940} else if (flags & CROSSING) {2941event.E.xcrossing.detail = number;2942} else {2943goto badopt;2944}2945} else if (strcmp(field, "-focus") == 0) {2946if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {2947return TCL_ERROR;2948}2949if (flags & CROSSING) {2950event.E.xcrossing.focus = number;2951} else {2952goto badopt;2953}2954} else if (strcmp(field, "-height") == 0) {2955if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {2956return TCL_ERROR;2957}2958if (flags & EXPOSE) {2959event.E.xexpose.height = number;2960} else if (flags & CONFIG) {2961event.E.xconfigure.height = number;2962} else {2963goto badopt;2964}2965} else if (strcmp(field, "-keycode") == 0) {2966if (Tcl_GetInt(interp, value, &number) != TCL_OK) {2967return TCL_ERROR;2968}2969if (flags & KEY) {2970event.E.xkey.keycode = number;2971} else {2972goto badopt;2973}2974} else if (strcmp(field, "-keysym") == 0) {2975keysym = TkStringToKeysym(value);2976if (keysym == NoSymbol) {2977Tcl_AppendResult(interp, "unknown keysym \"", value,2978"\"", (char *) NULL);2979return TCL_ERROR;2980}2981/*2982* When mapping from a keysym to a keycode, need information about2983* the modifier state that should be used so that when they call2984* XKeycodeToKeysym taking into account the xkey.state, they will2985* get back the original keysym.2986*/29872988number = XKeysymToKeycode(event.E.xany.display, keysym);2989if (number == 0) {2990Tcl_AppendResult(interp, "no keycode for keysym \"", value,2991"\"", (char *) NULL);2992return TCL_ERROR;2993}2994for (state = 0; state < 4; state++) {2995if (XKeycodeToKeysym(event.E.xany.display, (unsigned) number,2996state) == keysym) {2997if (state & 1) {2998event.E.xkey.state |= ShiftMask;2999}3000if (state & 2) {3001TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;3002event.E.xkey.state |= dispPtr->modeModMask;3003}3004break;3005}3006}3007if (flags & KEY) {3008event.E.xkey.keycode = number;3009} else {3010goto badopt;3011}3012} else if (strcmp(field, "-mode") == 0) {3013number = TkFindStateNum(interp, field, notifyMode, value);3014if (number < 0) {3015return TCL_ERROR;3016}3017if (flags & CROSSING) {3018event.E.xcrossing.mode = number;3019} else if (flags & FOCUS) {3020event.E.xfocus.mode = number;3021} else {3022goto badopt;3023}3024} else if (strcmp(field, "-override") == 0) {3025if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {3026return TCL_ERROR;3027}3028if (flags & CREATE) {3029event.E.xcreatewindow.override_redirect = number;3030} else if (flags & MAP) {3031event.E.xmap.override_redirect = number;3032} else if (flags & REPARENT) {3033event.E.xreparent.override_redirect = number;3034} else if (flags & CONFIG) {3035event.E.xconfigure.override_redirect = number;3036} else {3037goto badopt;3038}3039} else if (strcmp(field, "-place") == 0) {3040number = TkFindStateNum(interp, field, circPlace, value);3041if (number < 0) {3042return TCL_ERROR;3043}3044if (flags & CIRC) {3045event.E.xcirculate.place = number;3046} else {3047goto badopt;3048}3049} else if (strcmp(field, "-root") == 0) {3050if (value[0] == '.') {3051tkwin2 = Tk_NameToWindow(interp, value, tkwin);3052if (tkwin2 == NULL) {3053return TCL_ERROR;3054}3055number = Tk_WindowId(tkwin2);3056} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3057return TCL_ERROR;3058}3059if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3060event.E.xkey.root = number;3061} else {3062goto badopt;3063}3064} else if (strcmp(field, "-rootx") == 0) {3065if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {3066return TCL_ERROR;3067}3068if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3069event.E.xkey.x_root = number;3070} else {3071goto badopt;3072}3073} else if (strcmp(field, "-rooty") == 0) {3074if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {3075return TCL_ERROR;3076}3077if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3078event.E.xkey.y_root = number;3079} else {3080goto badopt;3081}3082} else if (strcmp(field, "-sendevent") == 0) {3083if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {3084return TCL_ERROR;3085}3086event.E.xany.send_event = number;3087} else if (strcmp(field, "-serial") == 0) {3088if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3089return TCL_ERROR;3090}3091event.E.xany.serial = number;3092} else if (strcmp(field, "-state") == 0) {3093if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3094if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3095return TCL_ERROR;3096}3097if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {3098event.E.xkey.state = number;3099} else {3100event.E.xcrossing.state = number;3101}3102} else if (flags & VISIBILITY) {3103number = TkFindStateNum(interp, field, visNotify, value);3104if (number < 0) {3105return TCL_ERROR;3106}3107event.E.xvisibility.state = number;3108} else {3109goto badopt;3110}3111} else if (strcmp(field, "-subwindow") == 0) {3112if (value[0] == '.') {3113tkwin2 = Tk_NameToWindow(interp, value, tkwin);3114if (tkwin2 == NULL) {3115return TCL_ERROR;3116}3117number = Tk_WindowId(tkwin2);3118} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3119return TCL_ERROR;3120}3121if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3122event.E.xkey.subwindow = number;3123} else {3124goto badopt;3125}3126} else if (strcmp(field, "-time") == 0) {3127if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3128return TCL_ERROR;3129}3130if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3131event.E.xkey.time = (Time) number;3132} else if (flags & PROP) {3133event.E.xproperty.time = (Time) number;3134} else {3135goto badopt;3136}3137} else if (strcmp(field, "-width") == 0) {3138if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {3139return TCL_ERROR;3140}3141if (flags & EXPOSE) {3142event.E.xexpose.width = number;3143} else if (flags & (CREATE|CONFIG)) {3144event.E.xcreatewindow.width = number;3145} else {3146goto badopt;3147}3148} else if (strcmp(field, "-window") == 0) {3149if (value[0] == '.') {3150tkwin2 = Tk_NameToWindow(interp, value, tkwin);3151if (tkwin2 == NULL) {3152return TCL_ERROR;3153}3154number = Tk_WindowId(tkwin2);3155} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {3156return TCL_ERROR;3157}3158if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG3159|GRAVITY|CIRC)) {3160event.E.xcreatewindow.window = number;3161} else {3162goto badopt;3163}3164} else if (strcmp(field, "-x") == 0) {3165int rootX, rootY;3166if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {3167return TCL_ERROR;3168}3169Tk_GetRootCoords(tkwin, &rootX, &rootY);3170rootX += number;3171if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3172event.E.xkey.x = number;3173event.E.xkey.x_root = rootX;3174} else if (flags & EXPOSE) {3175event.E.xexpose.x = number;3176} else if (flags & (CREATE|CONFIG|GRAVITY)) {3177event.E.xcreatewindow.x = number;3178} else if (flags & REPARENT) {3179event.E.xreparent.x = number;3180} else {3181goto badopt;3182}3183} else if (strcmp(field, "-y") == 0) {3184int rootX, rootY;3185if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {3186return TCL_ERROR;3187}3188Tk_GetRootCoords(tkwin, &rootX, &rootY);3189rootY += number;3190if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {3191event.E.xkey.y = number;3192event.E.xkey.y_root = rootY;3193} else if (flags & EXPOSE) {3194event.E.xexpose.y = number;3195} else if (flags & (CREATE|CONFIG|GRAVITY)) {3196event.E.xcreatewindow.y = number;3197} else if (flags & REPARENT) {3198event.E.xreparent.y = number;3199} else {3200goto badopt;3201}3202} else {3203badopt:3204Tcl_AppendResult(interp, "bad option to ", argv[1],3205" event: \"", field, "\"", (char *) NULL);3206return TCL_ERROR;3207}3208}32093210if (synch != 0) {3211Tk_HandleEvent(&event.E);3212} else {3213Tk_QueueWindowEvent(&event.E, pos);3214}3215return TCL_OK;3216}32173218/*3219*-------------------------------------------------------------------------3220*3221* GetVirtualEventUid --3222*3223* Determine if the given string is in the proper format for a3224* virtual event.3225*3226* Results:3227* The return value is NULL if the virtual event string was3228* not in the proper format. In this case, an error message3229* will be left in interp->result. Otherwise the return3230* value is a Tk_Uid that represents the virtual event.3231*3232* Side effects:3233* None.3234*3235*-------------------------------------------------------------------------3236*/3237static Tk_Uid3238GetVirtualEventUid(interp, virtString)3239Tcl_Interp *interp;3240char *virtString;3241{3242Tk_Uid uid;3243int length;32443245length = strlen(virtString);32463247if (length < 5 || virtString[0] != '<' || virtString[1] != '<' ||3248virtString[length - 2] != '>' || virtString[length - 1] != '>') {3249Tcl_AppendResult(interp, "virtual event \"", virtString,3250"\" is badly formed", (char *) NULL);3251return NULL;3252}3253virtString[length - 2] = '\0';3254uid = Tk_GetUid(virtString + 2);3255virtString[length - 2] = '>';32563257return uid;3258}325932603261/*3262*----------------------------------------------------------------------3263*3264* FindSequence --3265*3266* Find the entry in the pattern table that corresponds to a3267* particular pattern string, and return a pointer to that3268* entry.3269*3270* Results:3271* The return value is normally a pointer to the PatSeq3272* in patternTable that corresponds to eventString. If an error3273* was found while parsing eventString, or if "create" is 0 and3274* no pattern sequence previously existed, then NULL is returned3275* and interp->result contains a message describing the problem.3276* If no pattern sequence previously existed for eventString, then3277* a new one is created with a NULL command field. In a successful3278* return, *maskPtr is filled in with a mask of the event types3279* on which the pattern sequence depends.3280*3281* Side effects:3282* A new pattern sequence may be allocated.3283*3284*----------------------------------------------------------------------3285*/32863287static PatSeq *3288FindSequence(interp, patternTablePtr, object, eventString, create,3289allowVirtual, maskPtr)3290Tcl_Interp *interp; /* Interpreter to use for error3291* reporting. */3292Tcl_HashTable *patternTablePtr; /* Table to use for lookup. */3293ClientData object; /* For binding table, token for object with3294* which binding is associated.3295* For virtual event table, NULL. */3296char *eventString; /* String description of pattern to3297* match on. See user documentation3298* for details. */3299int create; /* 0 means don't create the entry if3300* it doesn't already exist. Non-zero3301* means create. */3302int allowVirtual; /* 0 means that virtual events are not3303* allowed in the sequence. Non-zero3304* otherwise. */3305unsigned long *maskPtr; /* *maskPtr is filled in with the event3306* types on which this pattern sequence3307* depends. */3308{33093310Pattern pats[EVENT_BUFFER_SIZE];3311int numPats, virtualFound;3312char *p;3313Pattern *patPtr;3314PatSeq *psPtr;3315Tcl_HashEntry *hPtr;3316int flags, count, new;3317size_t sequenceSize;3318unsigned long eventMask;3319PatternTableKey key;33203321/*3322*-------------------------------------------------------------3323* Step 1: parse the pattern string to produce an array3324* of Patterns. The array is generated backwards, so3325* that the lowest-indexed pattern corresponds to the last3326* event that must occur.3327*-------------------------------------------------------------3328*/33293330p = eventString;3331flags = 0;3332eventMask = 0;3333virtualFound = 0;33343335patPtr = &pats[EVENT_BUFFER_SIZE-1];3336for (numPats = 0; numPats < EVENT_BUFFER_SIZE; numPats++, patPtr--) {3337while (isspace(UCHAR(*p))) {3338p++;3339}3340if (*p == '\0') {3341break;3342}33433344count = ParseEventDescription(interp, &p, patPtr, &eventMask);3345if (count == 0) {3346return NULL;3347}33483349if (eventMask & VirtualEventMask) {3350if (allowVirtual == 0) {3351interp->result =3352"virtual event not allowed in definition of another virtual event";3353return NULL;3354}3355virtualFound = 1;3356}33573358/*3359* Replicate events for DOUBLE and TRIPLE.3360*/33613362if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {3363flags |= PAT_NEARBY;3364patPtr[-1] = patPtr[0];3365patPtr--;3366numPats++;3367if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {3368patPtr[-1] = patPtr[0];3369patPtr--;3370numPats++;3371}3372}3373}33743375/*3376*-------------------------------------------------------------3377* Step 2: find the sequence in the binding table if it exists,3378* and add a new sequence to the table if it doesn't.3379*-------------------------------------------------------------3380*/33813382if (numPats == 0) {3383interp->result = "no events specified in binding";3384return NULL;3385}3386if ((numPats > 1) && (virtualFound != 0)) {3387interp->result = "virtual events may not be composed";3388return NULL;3389}33903391patPtr = &pats[EVENT_BUFFER_SIZE-numPats];3392memset(&key, 0, sizeof(key));3393key.object = object;3394key.type = patPtr->eventType;3395key.detail = patPtr->detail;3396hPtr = Tcl_CreateHashEntry(patternTablePtr, (char *) &key, &new);3397sequenceSize = numPats*sizeof(Pattern);3398if (!new) {3399for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;3400psPtr = psPtr->nextSeqPtr) {3401if ((numPats == psPtr->numPats)3402&& ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))3403&& (memcmp((char *) patPtr, (char *) psPtr->pats,3404sequenceSize) == 0)) {3405goto done;3406}3407}3408}3409if (!create) {3410if (new) {3411Tcl_DeleteHashEntry(hPtr);3412}3413/* Tcl_AppendResult(interp, "no binding exists for \"",3414eventString, "\"", (char *) NULL);*/3415return NULL;3416}3417psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)3418+ (numPats-1)*sizeof(Pattern)));3419psPtr->numPats = numPats;3420psPtr->command = NULL;3421psPtr->flags = flags;3422psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);3423psPtr->hPtr = hPtr;3424psPtr->voPtr = NULL;3425psPtr->nextObjPtr = NULL;3426Tcl_SetHashValue(hPtr, psPtr);34273428memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);34293430done:3431*maskPtr = eventMask;3432return psPtr;3433}34343435/*3436*---------------------------------------------------------------------------3437*3438* ParseEventDescription --3439*3440* Fill Pattern buffer with information about event from3441* event string.3442*3443* Results:3444* Leaves error message in interp and returns 0 if there was an3445* error due to a badly formed event string. Returns 1 if proper3446* event was specified, 2 if Double modifier was used in event3447* string, or 3 if Triple was used.3448*3449* Side effects:3450* On exit, eventStringPtr points to rest of event string (after the3451* closing '>', so that this procedure can be called repeatedly to3452* parse all the events in the entire sequence.3453*3454*---------------------------------------------------------------------------3455*/34563457static int3458ParseEventDescription(interp, eventStringPtr, patPtr,3459eventMaskPtr)3460Tcl_Interp *interp; /* For error messages. */3461char **eventStringPtr; /* On input, holds a pointer to start of3462* event string. On exit, gets pointer to3463* rest of string after parsed event. */3464Pattern *patPtr; /* Filled with the pattern parsed from the3465* event string. */3466unsigned long *eventMaskPtr;/* Filled with event mask of matched event. */34673468{3469char *p;3470unsigned long eventMask;3471int count, eventFlags;3472#define FIELD_SIZE 483473char field[FIELD_SIZE];3474Tcl_HashEntry *hPtr;34753476p = *eventStringPtr;34773478patPtr->eventType = -1;3479patPtr->needMods = 0;3480patPtr->detail.clientData = 0;34813482eventMask = 0;3483count = 1;34843485/*3486* Handle simple ASCII characters.3487*/34883489if (*p != '<') {3490char string[2];34913492patPtr->eventType = KeyPress;3493eventMask = KeyPressMask;3494string[0] = *p;3495string[1] = 0;3496patPtr->detail.keySym = TkStringToKeysym(string);3497if (patPtr->detail.keySym == NoSymbol) {3498if (isprint(UCHAR(*p))) {3499patPtr->detail.keySym = *p;3500} else {3501sprintf(interp->result,3502"bad ASCII character 0x%x", (unsigned char) *p);3503return 0;3504}3505}3506p++;3507goto end;3508}35093510/*3511* A fancier event description. This can be either a virtual event3512* or a physical event.3513*3514* A virtual event description consists of:3515*3516* 1. double open angle brackets.3517* 2. virtual event name.3518* 3. double close angle brackets.3519*3520* A physical event description consists of:3521*3522* 1. open angle bracket.3523* 2. any number of modifiers, each followed by spaces3524* or dashes.3525* 3. an optional event name.3526* 4. an option button or keysym name. Either this or3527* item 3 *must* be present; if both are present3528* then they are separated by spaces or dashes.3529* 5. a close angle bracket.3530*/35313532p++;3533if (*p == '<') {3534/*3535* This is a virtual event: soak up all the characters up to3536* the next '>'.3537*/35383539char *field = p + 1;3540p = strchr(field, '>');3541if (p == field) {3542interp->result = "virtual event \"<<>>\" is badly formed";3543return 0;3544}3545if ((p == NULL) || (p[1] != '>')) {3546interp->result = "missing \">\" in virtual binding";3547return 0;3548}3549*p = '\0';3550patPtr->eventType = VirtualEvent;3551eventMask = VirtualEventMask;3552patPtr->detail.name = Tk_GetUid(field);3553*p = '>';35543555p += 2;3556goto end;3557}35583559while (1) {3560ModInfo *modPtr;3561p = GetField(p, field, FIELD_SIZE);3562hPtr = Tcl_FindHashEntry(&modTable, field);3563if (hPtr == NULL) {3564break;3565}3566modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);3567patPtr->needMods |= modPtr->mask;3568if (modPtr->flags & (DOUBLE|TRIPLE)) {3569if (modPtr->flags & DOUBLE) {3570count = 2;3571} else {3572count = 3;3573}3574}3575while ((*p == '-') || isspace(UCHAR(*p))) {3576p++;3577}3578}35793580eventFlags = 0;3581hPtr = Tcl_FindHashEntry(&eventTable, field);3582if (hPtr != NULL) {3583EventInfo *eiPtr;3584eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);35853586patPtr->eventType = eiPtr->type;3587eventFlags = flagArray[eiPtr->type];3588eventMask = eiPtr->eventMask;3589while ((*p == '-') || isspace(UCHAR(*p))) {3590p++;3591}3592p = GetField(p, field, FIELD_SIZE);3593}3594if (*field != '\0') {3595if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {3596if (eventFlags == 0) {3597patPtr->eventType = ButtonPress;3598eventMask = ButtonPressMask;3599} else if (eventFlags & KEY) {3600goto getKeysym;3601} else if ((eventFlags & BUTTON) == 0) {3602Tcl_AppendResult(interp, "specified button \"", field,3603"\" for non-button event", (char *) NULL);3604return 0;3605}3606patPtr->detail.button = (*field - '0');3607} else {3608getKeysym:3609patPtr->detail.keySym = TkStringToKeysym(field);3610if (patPtr->detail.keySym == NoSymbol) {3611Tcl_AppendResult(interp, "bad event type or keysym \"",3612field, "\"", (char *) NULL);3613return 0;3614}3615if (eventFlags == 0) {3616patPtr->eventType = KeyPress;3617eventMask = KeyPressMask;3618} else if ((eventFlags & KEY) == 0) {3619Tcl_AppendResult(interp, "specified keysym \"", field,3620"\" for non-key event", (char *) NULL);3621return 0;3622}3623}3624} else if (eventFlags == 0) {3625interp->result = "no event type or button # or keysym";3626return 0;3627}36283629while ((*p == '-') || isspace(UCHAR(*p))) {3630p++;3631}3632if (*p != '>') {3633while (*p != '\0') {3634p++;3635if (*p == '>') {3636interp->result = "extra characters after detail in binding";3637return 0;3638}3639}3640interp->result = "missing \">\" in binding";3641return 0;3642}3643p++;36443645end:3646*eventStringPtr = p;3647*eventMaskPtr |= eventMask;3648return count;3649}36503651/*3652*----------------------------------------------------------------------3653*3654* GetField --3655*3656* Used to parse pattern descriptions. Copies up to3657* size characters from p to copy, stopping at end of3658* string, space, "-", ">", or whenever size is3659* exceeded.3660*3661* Results:3662* The return value is a pointer to the character just3663* after the last one copied (usually "-" or space or3664* ">", but could be anything if size was exceeded).3665* Also places NULL-terminated string (up to size3666* character, including NULL), at copy.3667*3668* Side effects:3669* None.3670*3671*----------------------------------------------------------------------3672*/36733674static char *3675GetField(p, copy, size)3676char *p; /* Pointer to part of pattern. */3677char *copy; /* Place to copy field. */3678int size; /* Maximum number of characters to3679* copy. */3680{3681while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')3682&& (*p != '-') && (size > 1)) {3683*copy = *p;3684p++;3685copy++;3686size--;3687}3688*copy = '\0';3689return p;3690}36913692/*3693*---------------------------------------------------------------------------3694*3695* GetPatternString --3696*3697* Produce a string version of the given event, for displaying to3698* the user.3699*3700* Results:3701* The string is left in dsPtr.3702*3703* Side effects:3704* It is the caller's responsibility to initialize the DString before3705* and to free it after calling this procedure.3706*3707*---------------------------------------------------------------------------3708*/3709static void3710GetPatternString(psPtr, dsPtr)3711PatSeq *psPtr;3712Tcl_DString *dsPtr;3713{3714Pattern *patPtr;3715char c, buffer[10];3716int patsLeft, needMods;3717ModInfo *modPtr;3718EventInfo *eiPtr;37193720/*3721* The order of the patterns in the sequence is backwards from the order3722* in which they must be output.3723*/37243725for (patsLeft = psPtr->numPats, patPtr = &psPtr->pats[psPtr->numPats - 1];3726patsLeft > 0; patsLeft--, patPtr--) {37273728/*3729* Check for simple case of an ASCII character.3730*/37313732if ((patPtr->eventType == KeyPress)3733&& ((psPtr->flags & PAT_NEARBY) == 0)3734&& (patPtr->needMods == 0)3735&& (patPtr->detail.keySym < 128)3736&& isprint(UCHAR(patPtr->detail.keySym))3737&& (patPtr->detail.keySym != '<')3738&& (patPtr->detail.keySym != ' ')) {37393740c = (char) patPtr->detail.keySym;3741Tcl_DStringAppend(dsPtr, &c, 1);3742continue;3743}37443745/*3746* Check for virtual event.3747*/37483749if (patPtr->eventType == VirtualEvent) {3750Tcl_DStringAppend(dsPtr, "<<", 2);3751Tcl_DStringAppend(dsPtr, patPtr->detail.name, -1);3752Tcl_DStringAppend(dsPtr, ">>", 2);3753continue;3754}37553756/*3757* It's a more general event specification. First check3758* for "Double" or "Triple", then modifiers, then event type,3759* then keysym or button detail.3760*/37613762Tcl_DStringAppend(dsPtr, "<", 1);3763if ((psPtr->flags & PAT_NEARBY) && (patsLeft > 1)3764&& (memcmp((char *) patPtr, (char *) (patPtr-1),3765sizeof(Pattern)) == 0)) {3766patsLeft--;3767patPtr--;3768if ((patsLeft > 1) && (memcmp((char *) patPtr,3769(char *) (patPtr-1), sizeof(Pattern)) == 0)) {3770patsLeft--;3771patPtr--;3772Tcl_DStringAppend(dsPtr, "Triple-", 7);3773} else {3774Tcl_DStringAppend(dsPtr, "Double-", 7);3775}3776}3777for (needMods = patPtr->needMods, modPtr = modArray;3778needMods != 0; modPtr++) {3779if (modPtr->mask & needMods) {3780needMods &= ~modPtr->mask;3781Tcl_DStringAppend(dsPtr, modPtr->name, -1);3782Tcl_DStringAppend(dsPtr, "-", 1);3783}3784}3785for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {3786if (eiPtr->type == patPtr->eventType) {3787Tcl_DStringAppend(dsPtr, eiPtr->name, -1);3788if (patPtr->detail.clientData != 0) {3789Tcl_DStringAppend(dsPtr, "-", 1);3790}3791break;3792}3793}37943795if (patPtr->detail.clientData != 0) {3796if ((patPtr->eventType == KeyPress)3797|| (patPtr->eventType == KeyRelease)) {3798char *string;37993800string = TkKeysymToString(patPtr->detail.keySym);3801if (string != NULL) {3802Tcl_DStringAppend(dsPtr, string, -1);3803}3804} else {3805sprintf(buffer, "%d", patPtr->detail.button);3806Tcl_DStringAppend(dsPtr, buffer, -1);3807}3808}3809Tcl_DStringAppend(dsPtr, ">", 1);3810}3811}38123813/*3814*----------------------------------------------------------------------3815*3816* GetKeySym --3817*3818* Given an X KeyPress or KeyRelease event, map the3819* keycode in the event into a KeySym.3820*3821* Results:3822* The return value is the KeySym corresponding to3823* eventPtr, or NoSymbol if no matching Keysym could be3824* found.3825*3826* Side effects:3827* In the first call for a given display, keycode-to-3828* KeySym maps get loaded.3829*3830*----------------------------------------------------------------------3831*/38323833static KeySym3834GetKeySym(dispPtr, eventPtr)3835TkDisplay *dispPtr; /* Display in which to3836* map keycode. */3837XEvent *eventPtr; /* Description of X event. */3838{3839KeySym sym;3840int index;38413842/*3843* Refresh the mapping information if it's stale3844*/38453846if (dispPtr->bindInfoStale) {3847InitKeymapInfo(dispPtr);3848}38493850/*3851* Figure out which of the four slots in the keymap vector to3852* use for this key. Refer to Xlib documentation for more info3853* on how this computation works.3854*/38553856index = 0;3857if (eventPtr->xkey.state & dispPtr->modeModMask) {3858index = 2;3859}3860if ((eventPtr->xkey.state & ShiftMask)3861|| ((dispPtr->lockUsage != LU_IGNORE)3862&& (eventPtr->xkey.state & LockMask))) {3863index += 1;3864}3865sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index);38663867/*3868* Special handling: if the key was shifted because of Lock, but3869* lock is only caps lock, not shift lock, and the shifted keysym3870* isn't upper-case alphabetic, then switch back to the unshifted3871* keysym.3872*/38733874if ((index & 1) && !(eventPtr->xkey.state & ShiftMask)3875&& (dispPtr->lockUsage == LU_CAPS)) {3876if (!(((sym >= XK_A) && (sym <= XK_Z))3877|| ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))3878|| ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) {3879index &= ~1;3880sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,3881index);3882}3883}38843885/*3886* Another bit of special handling: if this is a shifted key and there3887* is no keysym defined, then use the keysym for the unshifted key.3888*/38893890if ((index & 1) && (sym == NoSymbol)) {3891sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,3892index & ~1);3893}3894return sym;3895}38963897/*3898*--------------------------------------------------------------3899*3900* InitKeymapInfo --3901*3902* This procedure is invoked to scan keymap information3903* to recompute stuff that's important for binding, such3904* as the modifier key (if any) that corresponds to "mode3905* switch".3906*3907* Results:3908* None.3909*3910* Side effects:3911* Keymap-related information in dispPtr is updated.3912*3913*--------------------------------------------------------------3914*/39153916static void3917InitKeymapInfo(dispPtr)3918TkDisplay *dispPtr; /* Display for which to recompute keymap3919* information. */3920{3921XModifierKeymap *modMapPtr;3922KeyCode *codePtr;3923KeySym keysym;3924int count, i, j, max, arraySize;3925#define KEYCODE_ARRAY_SIZE 2039263927dispPtr->bindInfoStale = 0;3928modMapPtr = XGetModifierMapping(dispPtr->display);39293930/*3931* Check the keycodes associated with the Lock modifier. If3932* any of them is associated with the XK_Shift_Lock modifier,3933* then Lock has to be interpreted as Shift Lock, not Caps Lock.3934*/39353936dispPtr->lockUsage = LU_IGNORE;3937codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;3938for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {3939if (*codePtr == 0) {3940continue;3941}3942keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);3943if (keysym == XK_Shift_Lock) {3944dispPtr->lockUsage = LU_SHIFT;3945break;3946}3947if (keysym == XK_Caps_Lock) {3948dispPtr->lockUsage = LU_CAPS;3949break;3950}3951}39523953/*3954* Look through the keycodes associated with modifiers to see if3955* the the "mode switch", "meta", or "alt" keysyms are associated3956* with any modifiers. If so, remember their modifier mask bits.3957*/39583959dispPtr->modeModMask = 0;3960dispPtr->metaModMask = 0;3961dispPtr->altModMask = 0;3962codePtr = modMapPtr->modifiermap;3963max = 8*modMapPtr->max_keypermod;3964for (i = 0; i < max; i++, codePtr++) {3965if (*codePtr == 0) {3966continue;3967}3968keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);3969if (keysym == XK_Mode_switch) {3970dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);3971}3972if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {3973dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);3974}3975if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {3976dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);3977}3978}39793980/*3981* Create an array of the keycodes for all modifier keys.3982*/39833984if (dispPtr->modKeyCodes != NULL) {3985ckfree((char *) dispPtr->modKeyCodes);3986}3987dispPtr->numModKeyCodes = 0;3988arraySize = KEYCODE_ARRAY_SIZE;3989dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)3990(KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));3991for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {3992if (*codePtr == 0) {3993continue;3994}39953996/*3997* Make sure that the keycode isn't already in the array.3998*/39994000for (j = 0; j < dispPtr->numModKeyCodes; j++) {4001if (dispPtr->modKeyCodes[j] == *codePtr) {4002goto nextModCode;4003}4004}4005if (dispPtr->numModKeyCodes >= arraySize) {4006KeyCode *new;40074008/*4009* Ran out of space in the array; grow it.4010*/40114012arraySize *= 2;4013new = (KeyCode *) ckalloc((unsigned)4014(arraySize * sizeof(KeyCode)));4015memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,4016(dispPtr->numModKeyCodes * sizeof(KeyCode)));4017ckfree((char *) dispPtr->modKeyCodes);4018dispPtr->modKeyCodes = new;4019}4020dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;4021dispPtr->numModKeyCodes++;4022nextModCode: continue;4023}4024XFreeModifiermap(modMapPtr);4025}402640274028/*4029*----------------------------------------------------------------------4030*4031* TkStringToKeysym --4032*4033* This procedure finds the keysym associated with a given keysym4034* name.4035*4036* Results:4037* The return value is the keysym that corresponds to name, or4038* NoSymbol if there is no such keysym.4039*4040* Side effects:4041* None.4042*4043*----------------------------------------------------------------------4044*/40454046KeySym4047TkStringToKeysym(name)4048char *name; /* Name of a keysym. */4049{4050#ifdef REDO_KEYSYM_LOOKUP4051Tcl_HashEntry *hPtr;4052KeySym keysym;40534054hPtr = Tcl_FindHashEntry(&keySymTable, name);4055if (hPtr != NULL) {4056return (KeySym) Tcl_GetHashValue(hPtr);4057}4058if (strlen(name) == 1) {4059keysym = (KeySym) (unsigned char) name[0];4060if (TkKeysymToString(keysym) != NULL) {4061return keysym;4062}4063}4064#endif /* REDO_KEYSYM_LOOKUP */4065return XStringToKeysym(name);4066}40674068/*4069*----------------------------------------------------------------------4070*4071* TkKeysymToString --4072*4073* This procedure finds the keysym name associated with a given4074* keysym.4075*4076* Results:4077* The return value is a pointer to a static string containing4078* the name of the given keysym, or NULL if there is no known name.4079*4080* Side effects:4081* None.4082*4083*----------------------------------------------------------------------4084*/40854086char *4087TkKeysymToString(keysym)4088KeySym keysym;4089{4090#ifdef REDO_KEYSYM_LOOKUP4091Tcl_HashEntry *hPtr;40924093hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym);4094if (hPtr != NULL) {4095return (char *) Tcl_GetHashValue(hPtr);4096}4097#endif /* REDO_KEYSYM_LOOKUP */4098return XKeysymToString(keysym);4099}41004101/*4102*----------------------------------------------------------------------4103*4104* TkCopyAndGlobalEval --4105*4106* This procedure makes a copy of a script then calls Tcl_GlobalEval4107* to evaluate it. It's used in situations where the execution of4108* a command may cause the original command string to be reallocated.4109*4110* Results:4111* Returns the result of evaluating script, including both a standard4112* Tcl completion code and a string in interp->result.4113*4114* Side effects:4115* None.4116*4117*----------------------------------------------------------------------4118*/41194120int4121TkCopyAndGlobalEval(interp, script)4122Tcl_Interp *interp; /* Interpreter in which to evaluate4123* script. */4124char *script; /* Script to evaluate. */4125{4126Tcl_DString buffer;4127int code;41284129Tcl_DStringInit(&buffer);4130Tcl_DStringAppend(&buffer, script, -1);4131code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));4132Tcl_DStringFree(&buffer);4133return code;4134}41354136413741384139