Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkBind.c
1810 views
1
/*
2
* tkBind.c --
3
*
4
* This file provides procedures that associate Tcl commands
5
* with X events or sequences of X events.
6
*
7
* Copyright (c) 1989-1994 The Regents of the University of California.
8
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
9
*
10
* See the file "license.terms" for information on usage and redistribution
11
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
*
13
* SCCS: @(#) tkBind.c 1.124 96/12/05 14:47:40
14
*/
15
16
#include "tkInt.h"
17
18
/*
19
* File structure:
20
*
21
* Structure definitions and static variables.
22
*
23
* Init/Free this package.
24
*
25
* Tcl "bind" command (actually located in tkCmds.c).
26
* "bind" command implementation.
27
* "bind" implementation helpers.
28
*
29
* Tcl "event" command.
30
* "event" command implementation.
31
* "event" implementation helpers.
32
*
33
* Package-specific common helpers.
34
*
35
* Non-package-specific helpers.
36
*/
37
38
39
/*
40
* The following union is used to hold the detail information from an
41
* XEvent (including Tk's XVirtualEvent extension).
42
*/
43
typedef union {
44
KeySym keySym; /* KeySym that corresponds to xkey.keycode. */
45
int button; /* Button that was pressed (xbutton.button). */
46
Tk_Uid name; /* Tk_Uid of virtual event. */
47
ClientData clientData; /* Used when type of Detail is unknown, and to
48
* ensure that all bytes of Detail are initialized
49
* when this structure is used in a hash key. */
50
} Detail;
51
52
/*
53
* The structure below represents a binding table. A binding table
54
* represents a domain in which event bindings may occur. It includes
55
* a space of objects relative to which events occur (usually windows,
56
* but not always), a history of recent events in the domain, and
57
* a set of mappings that associate particular Tcl commands with sequences
58
* of events in the domain. Multiple binding tables may exist at once,
59
* either because there are multiple applications open, or because there
60
* are multiple domains within an application with separate event
61
* bindings for each (for example, each canvas widget has a separate
62
* binding table for associating events with the items in the canvas).
63
*
64
* Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
65
* below 30. To see this, consider a triple mouse button click while
66
* the Shift key is down (and auto-repeating). There may be as many
67
* as 3 auto-repeat events after each mouse button press or release
68
* (see the first large comment block within Tk_BindEvent for more on
69
* this), for a total of 20 events to cover the three button presses
70
* and two intervening releases. If you reduce EVENT_BUFFER_SIZE too
71
* much, shift multi-clicks will be lost.
72
*
73
*/
74
75
#define EVENT_BUFFER_SIZE 30
76
typedef struct BindingTable {
77
XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
78
* (higher indices are for more recent
79
* events). */
80
Detail detailRing[EVENT_BUFFER_SIZE];/* "Detail" information (keySym,
81
* button, Tk_Uid, or 0) for each
82
* entry in eventRing. */
83
int curEvent; /* Index in eventRing of most recent
84
* event. Newer events have higher
85
* indices. */
86
Tcl_HashTable patternTable; /* Used to map from an event to a
87
* list of patterns that may match that
88
* event. Keys are PatternTableKey
89
* structs, values are (PatSeq *). */
90
Tcl_HashTable objectTable; /* Used to map from an object to a
91
* list of patterns associated with
92
* that object. Keys are ClientData,
93
* values are (PatSeq *). */
94
Tcl_Interp *interp; /* Interpreter in which commands are
95
* executed. */
96
} BindingTable;
97
98
/*
99
* The following structure represents virtual event table. A virtual event
100
* table provides a way to map from platform-specific physical events such
101
* as button clicks or key presses to virtual events such as <<Paste>>,
102
* <<Close>>, or <<ScrollWindow>>.
103
*
104
* A virtual event is usually never part of the event stream, but instead is
105
* synthesized inline by matching low-level events. However, a virtual
106
* event may be generated by platform-specific code or by Tcl scripts. In
107
* that case, no lookup of the virtual event will need to be done using
108
* this table, because the virtual event is actually in the event stream.
109
*/
110
111
typedef struct TkVirtualEventTable {
112
Tcl_HashTable patternTable; /* Used to map from a physical event to
113
* a list of patterns that may match that
114
* event. Keys PatternTableKey structs,
115
* values are (PatSeq *). */
116
Tcl_HashTable virtualTable; /* Used to map a virtual event to the
117
* array of physical events that can
118
* trigger it. Keys are the Tk_Uid names
119
* of the virtual events, values are
120
* PhysicalsOwned structs. */
121
} TkVirtualEventTable;
122
123
/*
124
* The following structure is used as a key in a patternTable for both
125
* binding tables and a virtual event tables.
126
*
127
* In a binding table, the object field corresponds to the binding tag
128
* for the widget whose bindings are being accessed.
129
*
130
* In a virtual event table, the object field is always NULL. Virtual
131
* events are a global definiton and are not tied to a particular
132
* binding tag.
133
*
134
* The same key is used for both types of pattern tables so that the
135
* helper functions that traverse and match patterns will work for both
136
* binding tables and virtual event tables.
137
*/
138
typedef struct PatternTableKey {
139
ClientData object; /* For binding table, identifies the binding
140
* tag of the object (or class of objects)
141
* relative to which the event occurred.
142
* For virtual event table, always NULL. */
143
int type; /* Type of event (from X). */
144
Detail detail; /* Additional information, such as keysym,
145
* button, Tk_Uid, or 0 if nothing
146
* additional. */
147
} PatternTableKey;
148
149
/*
150
* The following structure defines a pattern, which is matched against X
151
* events as part of the process of converting X events into Tcl commands.
152
*/
153
154
typedef struct Pattern {
155
int eventType; /* Type of X event, e.g. ButtonPress. */
156
int needMods; /* Mask of modifiers that must be
157
* present (0 means no modifiers are
158
* required). */
159
Detail detail; /* Additional information that must
160
* match event. Normally this is 0,
161
* meaning no additional information
162
* must match. For KeyPress and
163
* KeyRelease events, a keySym may
164
* be specified to select a
165
* particular keystroke (0 means any
166
* keystrokes). For button events,
167
* specifies a particular button (0
168
* means any buttons are OK). For virtual
169
* events, specifies the Tk_Uid of the
170
* virtual event name (never 0). */
171
} Pattern;
172
173
/*
174
* The following structure defines a pattern sequence, which consists of one
175
* or more patterns. In order to trigger, a pattern sequence must match
176
* the most recent X events (first pattern to most recent event, next
177
* pattern to next event, and so on). It is used as the hash value in a
178
* patternTable for both binding tables and virtual event tables.
179
*
180
* In a binding table, it is the sequence of physical events that make up
181
* a binding for an object.
182
*
183
* In a virtual event table, it is the sequence of physical events that
184
* define a virtual event.
185
*
186
* The same structure is used for both types of pattern tables so that the
187
* helper functions that traverse and match patterns will work for both
188
* binding tables and virtual event tables.
189
*/
190
191
typedef struct PatSeq {
192
int numPats; /* Number of patterns in sequence (usually
193
* 1). */
194
char *command; /* Command to invoke when this pattern
195
* sequence matches (malloc-ed). */
196
int flags; /* Miscellaneous flag values; see below for
197
* definitions. */
198
struct PatSeq *nextSeqPtr; /* Next in list of all pattern sequences
199
* that have the same initial pattern. NULL
200
* means end of list. */
201
Tcl_HashEntry *hPtr; /* Pointer to hash table entry for the
202
* initial pattern. This is the head of the
203
* list of which nextSeqPtr forms a part. */
204
struct VirtualOwners *voPtr;/* In a binding table, always NULL. In a
205
* virtual event table, identifies the array
206
* of virtual events that can be triggered by
207
* this event. */
208
struct PatSeq *nextObjPtr; /* In a binding table, next in list of all
209
* pattern sequences for the same object (NULL
210
* for end of list). Needed to implement
211
* Tk_DeleteAllBindings. In a virtual event
212
* table, always NULL. */
213
Pattern pats[1]; /* Array of "numPats" patterns. Only one
214
* element is declared here but in actuality
215
* enough space will be allocated for "numPats"
216
* patterns. To match, pats[0] must match
217
* event n, pats[1] must match event n-1, etc.
218
*/
219
} PatSeq;
220
221
/*
222
* Flag values for PatSeq structures:
223
*
224
* PAT_NEARBY 1 means that all of the events matching
225
* this sequence must occur with nearby X
226
* and Y mouse coordinates and close in time.
227
* This is typically used to restrict multiple
228
* button presses.
229
*/
230
231
#define PAT_NEARBY 1
232
233
/*
234
* Constants that define how close together two events must be
235
* in milliseconds or pixels to meet the PAT_NEARBY constraint:
236
*/
237
238
#define NEARBY_PIXELS 5
239
#define NEARBY_MS 500
240
241
242
/*
243
* The following structure keeps track of all the virtual events that are
244
* associated with a particular physical event. It is pointed to by the
245
* voPtr field in a PatSeq in the patternTable of a virtual event table.
246
*/
247
248
typedef struct VirtualOwners {
249
int numOwners; /* Number of virtual events to trigger. */
250
Tcl_HashEntry *owners[1]; /* Array of pointers to entries in
251
* virtualTable. Enough space will
252
* actually be allocated for numOwners
253
* hash entries. */
254
} VirtualOwners;
255
256
/*
257
* The following structure is used in the virtualTable of a virtual event
258
* table to associate a virtual event with all the physical events that can
259
* trigger it.
260
*/
261
typedef struct PhysicalsOwned {
262
int numOwned; /* Number of physical events owned. */
263
PatSeq *patSeqs[1]; /* Array of pointers to physical event
264
* patterns. Enough space will actually
265
* be allocated to hold numOwned. */
266
} PhysicalsOwned;
267
268
269
/*
270
* One of the following structures exists for each interpreter,
271
* associated with the key "tkBind". This structure keeps track
272
* of the current display and screen in the interpreter, so that
273
* a script can be invoked whenever the display/screen changes
274
* (the script does things like point tkPriv at a display-specific
275
* structure).
276
*/
277
278
typedef struct ScreenInfo {
279
TkDisplay *curDispPtr; /* Display for last binding command invoked
280
* in this application. */
281
int curScreenIndex; /* Index of screen for last binding command. */
282
int bindingDepth; /* Number of active instances of Tk_BindEvent
283
* in this application. */
284
} ScreenInfo;
285
286
/*
287
* In X11R4 and earlier versions, XStringToKeysym is ridiculously
288
* slow. The data structure and hash table below, along with the
289
* code that uses them, implement a fast mapping from strings to
290
* keysyms. In X11R5 and later releases XStringToKeysym is plenty
291
* fast so this stuff isn't needed. The #define REDO_KEYSYM_LOOKUP
292
* is normally undefined, so that XStringToKeysym gets used. It
293
* can be set in the Makefile to enable the use of the hash table
294
* below.
295
*/
296
297
#ifdef REDO_KEYSYM_LOOKUP
298
typedef struct {
299
char *name; /* Name of keysym. */
300
KeySym value; /* Numeric identifier for keysym. */
301
} KeySymInfo;
302
static KeySymInfo keyArray[] = {
303
#ifndef lint
304
#include "tkNames.h"
305
#endif
306
{(char *) NULL, 0}
307
};
308
static Tcl_HashTable keySymTable; /* keyArray hashed by keysym value. */
309
static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */
310
#endif /* REDO_KEYSYM_LOOKUP */
311
312
static int initialized = 0;
313
314
/*
315
* A hash table is kept to map from the string names of event
316
* modifiers to information about those modifiers. The structure
317
* for storing this information, and the hash table built at
318
* initialization time, are defined below.
319
*/
320
321
typedef struct {
322
char *name; /* Name of modifier. */
323
int mask; /* Button/modifier mask value, * such as Button1Mask. */
324
int flags; /* Various flags; see below for
325
* definitions. */
326
} ModInfo;
327
328
/*
329
* Flags for ModInfo structures:
330
*
331
* DOUBLE - Non-zero means duplicate this event,
332
* e.g. for double-clicks.
333
* TRIPLE - Non-zero means triplicate this event,
334
* e.g. for triple-clicks.
335
*/
336
337
#define DOUBLE 1
338
#define TRIPLE 2
339
340
/*
341
* The following special modifier mask bits are defined, to indicate
342
* logical modifiers such as Meta and Alt that may float among the
343
* actual modifier bits.
344
*/
345
346
#define META_MASK (AnyModifier<<1)
347
#define ALT_MASK (AnyModifier<<2)
348
349
static ModInfo modArray[] = {
350
{"Control", ControlMask, 0},
351
{"Shift", ShiftMask, 0},
352
{"Lock", LockMask, 0},
353
{"Meta", META_MASK, 0},
354
{"M", META_MASK, 0},
355
{"Alt", ALT_MASK, 0},
356
{"B1", Button1Mask, 0},
357
{"Button1", Button1Mask, 0},
358
{"B2", Button2Mask, 0},
359
{"Button2", Button2Mask, 0},
360
{"B3", Button3Mask, 0},
361
{"Button3", Button3Mask, 0},
362
{"B4", Button4Mask, 0},
363
{"Button4", Button4Mask, 0},
364
{"B5", Button5Mask, 0},
365
{"Button5", Button5Mask, 0},
366
{"Mod1", Mod1Mask, 0},
367
{"M1", Mod1Mask, 0},
368
{"Command", Mod1Mask, 0},
369
{"Mod2", Mod2Mask, 0},
370
{"M2", Mod2Mask, 0},
371
{"Option", Mod2Mask, 0},
372
{"Mod3", Mod3Mask, 0},
373
{"M3", Mod3Mask, 0},
374
{"Mod4", Mod4Mask, 0},
375
{"M4", Mod4Mask, 0},
376
{"Mod5", Mod5Mask, 0},
377
{"M5", Mod5Mask, 0},
378
{"Double", 0, DOUBLE},
379
{"Triple", 0, TRIPLE},
380
{"Any", 0, 0}, /* Ignored: historical relic. */
381
{NULL, 0, 0}
382
};
383
static Tcl_HashTable modTable;
384
385
/*
386
* This module also keeps a hash table mapping from event names
387
* to information about those events. The structure, an array
388
* to use to initialize the hash table, and the hash table are
389
* all defined below.
390
*/
391
392
typedef struct {
393
char *name; /* Name of event. */
394
int type; /* Event type for X, such as
395
* ButtonPress. */
396
int eventMask; /* Mask bits (for XSelectInput)
397
* for this event type. */
398
} EventInfo;
399
400
/*
401
* Note: some of the masks below are an OR-ed combination of
402
* several masks. This is necessary because X doesn't report
403
* up events unless you also ask for down events. Also, X
404
* doesn't report button state in motion events unless you've
405
* asked about button events.
406
*/
407
408
static EventInfo eventArray[] = {
409
{"Key", KeyPress, KeyPressMask},
410
{"KeyPress", KeyPress, KeyPressMask},
411
{"KeyRelease", KeyRelease, KeyPressMask|KeyReleaseMask},
412
{"Button", ButtonPress, ButtonPressMask},
413
{"ButtonPress", ButtonPress, ButtonPressMask},
414
{"ButtonRelease", ButtonRelease,
415
ButtonPressMask|ButtonReleaseMask},
416
{"Motion", MotionNotify,
417
ButtonPressMask|PointerMotionMask},
418
{"Enter", EnterNotify, EnterWindowMask},
419
{"Leave", LeaveNotify, LeaveWindowMask},
420
{"FocusIn", FocusIn, FocusChangeMask},
421
{"FocusOut", FocusOut, FocusChangeMask},
422
{"Expose", Expose, ExposureMask},
423
{"Visibility", VisibilityNotify, VisibilityChangeMask},
424
{"Destroy", DestroyNotify, StructureNotifyMask},
425
{"Unmap", UnmapNotify, StructureNotifyMask},
426
{"Map", MapNotify, StructureNotifyMask},
427
{"Reparent", ReparentNotify, StructureNotifyMask},
428
{"Configure", ConfigureNotify, StructureNotifyMask},
429
{"Gravity", GravityNotify, StructureNotifyMask},
430
{"Circulate", CirculateNotify, StructureNotifyMask},
431
{"Property", PropertyNotify, PropertyChangeMask},
432
{"Colormap", ColormapNotify, ColormapChangeMask},
433
{"Activate", ActivateNotify, ActivateMask},
434
{"Deactivate", DeactivateNotify, ActivateMask},
435
{(char *) NULL, 0, 0}
436
};
437
static Tcl_HashTable eventTable;
438
439
/*
440
* The defines and table below are used to classify events into
441
* various groups. The reason for this is that logically identical
442
* fields (e.g. "state") appear at different places in different
443
* types of events. The classification masks can be used to figure
444
* out quickly where to extract information from events.
445
*/
446
447
#define KEY 0x1
448
#define BUTTON 0x2
449
#define MOTION 0x4
450
#define CROSSING 0x8
451
#define FOCUS 0x10
452
#define EXPOSE 0x20
453
#define VISIBILITY 0x40
454
#define CREATE 0x80
455
#define DESTROY 0x100
456
#define UNMAP 0x200
457
#define MAP 0x400
458
#define REPARENT 0x800
459
#define CONFIG 0x1000
460
#define GRAVITY 0x2000
461
#define CIRC 0x4000
462
#define PROP 0x8000
463
#define COLORMAP 0x10000
464
#define VIRTUAL 0x20000
465
#define ACTIVATE 0x40000
466
467
#define KEY_BUTTON_MOTION_VIRTUAL (KEY|BUTTON|MOTION|VIRTUAL)
468
469
static int flagArray[TK_LASTEVENT] = {
470
/* Not used */ 0,
471
/* Not used */ 0,
472
/* KeyPress */ KEY,
473
/* KeyRelease */ KEY,
474
/* ButtonPress */ BUTTON,
475
/* ButtonRelease */ BUTTON,
476
/* MotionNotify */ MOTION,
477
/* EnterNotify */ CROSSING,
478
/* LeaveNotify */ CROSSING,
479
/* FocusIn */ FOCUS,
480
/* FocusOut */ FOCUS,
481
/* KeymapNotify */ 0,
482
/* Expose */ EXPOSE,
483
/* GraphicsExpose */ EXPOSE,
484
/* NoExpose */ 0,
485
/* VisibilityNotify */ VISIBILITY,
486
/* CreateNotify */ CREATE,
487
/* DestroyNotify */ DESTROY,
488
/* UnmapNotify */ UNMAP,
489
/* MapNotify */ MAP,
490
/* MapRequest */ 0,
491
/* ReparentNotify */ REPARENT,
492
/* ConfigureNotify */ CONFIG,
493
/* ConfigureRequest */ 0,
494
/* GravityNotify */ GRAVITY,
495
/* ResizeRequest */ 0,
496
/* CirculateNotify */ CIRC,
497
/* CirculateRequest */ 0,
498
/* PropertyNotify */ PROP,
499
/* SelectionClear */ 0,
500
/* SelectionRequest */ 0,
501
/* SelectionNotify */ 0,
502
/* ColormapNotify */ COLORMAP,
503
/* ClientMessage */ 0,
504
/* MappingNotify */ 0,
505
/* VirtualEvent */ VIRTUAL,
506
/* Activate */ ACTIVATE,
507
/* Deactivate */ ACTIVATE
508
};
509
510
/*
511
* The following tables are used as a two-way map between X's internal
512
* numeric values for fields in an XEvent and the strings used in Tcl. The
513
* tables are used both when constructing an XEvent from user input and
514
* when providing data from an XEvent to the user.
515
*/
516
517
static TkStateMap notifyMode[] = {
518
{NotifyNormal, "NotifyNormal"},
519
{NotifyGrab, "NotifyGrab"},
520
{NotifyUngrab, "NotifyUngrab"},
521
{NotifyWhileGrabbed, "NotifyWhileGrabbed"},
522
{-1, NULL}
523
};
524
525
static TkStateMap notifyDetail[] = {
526
{NotifyAncestor, "NotifyAncestor"},
527
{NotifyVirtual, "NotifyVirtual"},
528
{NotifyInferior, "NotifyInferior"},
529
{NotifyNonlinear, "NotifyNonlinear"},
530
{NotifyNonlinearVirtual,"NotifyNonlinearVirtual"},
531
{NotifyPointer, "NotifyPointer"},
532
{NotifyPointerRoot, "NotifyPointerRoot"},
533
{NotifyDetailNone, "NotifyDetailNone"},
534
{-1, NULL}
535
};
536
537
static TkStateMap circPlace[] = {
538
{PlaceOnTop, "PlaceOnTop"},
539
{PlaceOnBottom, "PlaceOnBottom"},
540
{-1, NULL}
541
};
542
543
static TkStateMap visNotify[] = {
544
{VisibilityUnobscured, "VisibilityUnobscured"},
545
{VisibilityPartiallyObscured, "VisibilityPartiallyObscured"},
546
{VisibilityFullyObscured, "VisibilityFullyObscured"},
547
{-1, NULL}
548
};
549
550
/*
551
* Prototypes for local procedures defined in this file:
552
*/
553
554
static void ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,
555
char *dispName, int screenIndex));
556
static int CreateVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
557
TkVirtualEventTable *vetPtr, char *virtString,
558
char *eventString));
559
static TkVirtualEventTable *CreateVirtualEventTable _ANSI_ARGS_((void));
560
static int DeleteVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
561
TkVirtualEventTable *vetPtr, char *virtString,
562
char *eventString));
563
static void DeleteVirtualEventTable _ANSI_ARGS_((
564
TkVirtualEventTable *vetPtr));
565
static void ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,
566
char *before, XEvent *eventPtr, KeySym keySym,
567
Tcl_DString *dsPtr));
568
static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
569
Tcl_HashTable *patternTablePtr, ClientData object,
570
char *eventString, int create, int allowVirtual,
571
unsigned long *maskPtr));
572
static void FreeScreenInfo _ANSI_ARGS_((ClientData clientData,
573
Tcl_Interp *interp));
574
static void GetAllVirtualEvents _ANSI_ARGS_((Tcl_Interp *interp,
575
TkVirtualEventTable *vetPtr));
576
static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));
577
static KeySym GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
578
XEvent *eventPtr));
579
static void GetPatternString _ANSI_ARGS_((PatSeq *psPtr,
580
Tcl_DString *dsPtr));
581
static int GetVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
582
TkVirtualEventTable *vetPtr, char *virtString));
583
static Tk_Uid GetVirtualEventUid _ANSI_ARGS_((Tcl_Interp *interp,
584
char *virtString));
585
static int HandleEventGenerate _ANSI_ARGS_((Tcl_Interp *interp,
586
Tk_Window tkwin, int argc, char **argv));
587
static void InitKeymapInfo _ANSI_ARGS_((TkDisplay *dispPtr));
588
static PatSeq * MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,
589
BindingTable *bindPtr, PatSeq *psPtr,
590
PatSeq *bestPtr, ClientData object,
591
char **bestCommandPtr));
592
static int ParseEventDescription _ANSI_ARGS_((Tcl_Interp *interp,
593
char **eventStringPtr, Pattern *patPtr,
594
unsigned long *eventMaskPtr));
595
596
597
598
/*
599
*---------------------------------------------------------------------------
600
*
601
* TkBindInit --
602
*
603
* This procedure is called when an application is created. It
604
* initializes all the structures used by bindings and virtual
605
* events.
606
*
607
* Results:
608
* None.
609
*
610
* Side effects:
611
* Memory allocated.
612
*
613
*---------------------------------------------------------------------------
614
*/
615
616
void
617
TkBindInit(mainPtr)
618
TkMainInfo *mainPtr; /* The newly created application. */
619
{
620
if (sizeof(XEvent) < sizeof(XVirtualEvent)) {
621
panic("TkBindInit: virtual events can't be supported");
622
}
623
mainPtr->bindingTable = Tk_CreateBindingTable(mainPtr->interp);
624
mainPtr->vetPtr = CreateVirtualEventTable();
625
}
626
627
/*
628
*---------------------------------------------------------------------------
629
*
630
* TkBindFree --
631
*
632
* This procedure is called when an application is deleted. It
633
* deletes all the structures used by bindings and virtual events.
634
*
635
* Results:
636
* None.
637
*
638
* Side effects:
639
* Memory freed.
640
*
641
*---------------------------------------------------------------------------
642
*/
643
644
void
645
TkBindFree(mainPtr)
646
TkMainInfo *mainPtr; /* The newly created application. */
647
{
648
Tk_DeleteBindingTable(mainPtr->bindingTable);
649
mainPtr->bindingTable = NULL;
650
651
DeleteVirtualEventTable(mainPtr->vetPtr);
652
mainPtr->vetPtr = NULL;
653
}
654
655
/*
656
*--------------------------------------------------------------
657
*
658
* Tk_CreateBindingTable --
659
*
660
* Set up a new domain in which event bindings may be created.
661
*
662
* Results:
663
* The return value is a token for the new table, which must
664
* be passed to procedures like Tk_CreatBinding.
665
*
666
* Side effects:
667
* Memory is allocated for the new table.
668
*
669
*--------------------------------------------------------------
670
*/
671
672
Tk_BindingTable
673
Tk_CreateBindingTable(interp)
674
Tcl_Interp *interp; /* Interpreter to associate with the binding
675
* table: commands are executed in this
676
* interpreter. */
677
{
678
BindingTable *bindPtr;
679
int i;
680
681
/*
682
* If this is the first time a binding table has been created,
683
* initialize the global data structures.
684
*/
685
686
if (!initialized) {
687
Tcl_HashEntry *hPtr;
688
ModInfo *modPtr;
689
EventInfo *eiPtr;
690
int dummy;
691
692
#ifdef REDO_KEYSYM_LOOKUP
693
KeySymInfo *kPtr;
694
695
Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
696
Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS);
697
for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
698
hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
699
Tcl_SetHashValue(hPtr, kPtr->value);
700
hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value,
701
&dummy);
702
Tcl_SetHashValue(hPtr, kPtr->name);
703
}
704
#endif /* REDO_KEYSYM_LOOKUP */
705
706
initialized = 1;
707
708
Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
709
for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
710
hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
711
Tcl_SetHashValue(hPtr, modPtr);
712
}
713
714
Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
715
for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
716
hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
717
Tcl_SetHashValue(hPtr, eiPtr);
718
}
719
}
720
721
/*
722
* Create and initialize a new binding table.
723
*/
724
725
bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
726
for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
727
bindPtr->eventRing[i].type = -1;
728
}
729
bindPtr->curEvent = 0;
730
Tcl_InitHashTable(&bindPtr->patternTable,
731
sizeof(PatternTableKey)/sizeof(int));
732
Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
733
bindPtr->interp = interp;
734
return (Tk_BindingTable) bindPtr;
735
}
736
737
/*
738
*--------------------------------------------------------------
739
*
740
* Tk_DeleteBindingTable --
741
*
742
* Destroy a binding table and free up all its memory.
743
* The caller should not use bindingTable again after
744
* this procedure returns.
745
*
746
* Results:
747
* None.
748
*
749
* Side effects:
750
* Memory is freed.
751
*
752
*--------------------------------------------------------------
753
*/
754
755
void
756
Tk_DeleteBindingTable(bindingTable)
757
Tk_BindingTable bindingTable; /* Token for the binding table to
758
* destroy. */
759
{
760
BindingTable *bindPtr = (BindingTable *) bindingTable;
761
PatSeq *psPtr, *nextPtr;
762
Tcl_HashEntry *hPtr;
763
Tcl_HashSearch search;
764
765
/*
766
* Find and delete all of the patterns associated with the binding
767
* table.
768
*/
769
770
for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
771
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
772
for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
773
psPtr != NULL; psPtr = nextPtr) {
774
nextPtr = psPtr->nextSeqPtr;
775
ckfree((char *) psPtr->command);
776
ckfree((char *) psPtr);
777
}
778
}
779
780
/*
781
* Clean up the rest of the information associated with the
782
* binding table.
783
*/
784
785
Tcl_DeleteHashTable(&bindPtr->patternTable);
786
Tcl_DeleteHashTable(&bindPtr->objectTable);
787
ckfree((char *) bindPtr);
788
}
789
790
/*
791
*--------------------------------------------------------------
792
*
793
* Tk_CreateBinding --
794
*
795
* Add a binding to a binding table, so that future calls to
796
* Tk_BindEvent may execute the command in the binding.
797
*
798
* Results:
799
* The return value is 0 if an error occurred while setting
800
* up the binding. In this case, an error message will be
801
* left in interp->result. If all went well then the return
802
* value is a mask of the event types that must be made
803
* available to Tk_BindEvent in order to properly detect when
804
* this binding triggers. This value can be used to determine
805
* what events to select for in a window, for example.
806
*
807
* Side effects:
808
* The new binding may cause future calls to Tk_BindEvent to
809
* behave differently than they did previously.
810
*
811
*--------------------------------------------------------------
812
*/
813
814
unsigned long
815
Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
816
Tcl_Interp *interp; /* Used for error reporting. */
817
Tk_BindingTable bindingTable; /* Table in which to create binding. */
818
ClientData object; /* Token for object with which binding
819
* is associated. */
820
char *eventString; /* String describing event sequence
821
* that triggers binding. */
822
char *command; /* Contains Tcl command to execute
823
* when binding triggers. */
824
int append; /* 0 means replace any existing
825
* binding for eventString; 1 means
826
* append to that binding. */
827
{
828
BindingTable *bindPtr = (BindingTable *) bindingTable;
829
PatSeq *psPtr;
830
unsigned long eventMask;
831
832
psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
833
1, 1, &eventMask);
834
if (psPtr == NULL) {
835
return 0;
836
}
837
if (psPtr->command == NULL) {
838
int new;
839
Tcl_HashEntry *hPtr;
840
841
/*
842
* This pattern sequence was just created.
843
* Link the pattern into the list associated with the object.
844
*/
845
846
hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,
847
&new);
848
if (new) {
849
psPtr->nextObjPtr = NULL;
850
} else {
851
psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
852
}
853
Tcl_SetHashValue(hPtr, psPtr);
854
}
855
856
if (append && (psPtr->command != NULL)) {
857
int length;
858
char *new;
859
860
length = strlen(psPtr->command) + strlen(command) + 2;
861
new = (char *) ckalloc((unsigned) length);
862
sprintf(new, "%s\n%s", psPtr->command, command);
863
ckfree((char *) psPtr->command);
864
psPtr->command = new;
865
} else {
866
if (psPtr->command != NULL) {
867
ckfree((char *) psPtr->command);
868
}
869
psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
870
strcpy(psPtr->command, command);
871
}
872
return eventMask;
873
}
874
875
/*
876
*--------------------------------------------------------------
877
*
878
* Tk_DeleteBinding --
879
*
880
* Remove an event binding from a binding table.
881
*
882
* Results:
883
* The result is a standard Tcl return value. If an error
884
* occurs then interp->result will contain an error message.
885
*
886
* Side effects:
887
* The binding given by object and eventString is removed
888
* from bindingTable.
889
*
890
*--------------------------------------------------------------
891
*/
892
893
int
894
Tk_DeleteBinding(interp, bindingTable, object, eventString)
895
Tcl_Interp *interp; /* Used for error reporting. */
896
Tk_BindingTable bindingTable; /* Table in which to delete binding. */
897
ClientData object; /* Token for object with which binding
898
* is associated. */
899
char *eventString; /* String describing event sequence
900
* that triggers binding. */
901
{
902
BindingTable *bindPtr = (BindingTable *) bindingTable;
903
PatSeq *psPtr, *prevPtr;
904
unsigned long eventMask;
905
Tcl_HashEntry *hPtr;
906
907
psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
908
0, 1, &eventMask);
909
if (psPtr == NULL) {
910
Tcl_ResetResult(interp);
911
return TCL_OK;
912
}
913
914
/*
915
* Unlink the binding from the list for its object, then from the
916
* list for its pattern.
917
*/
918
919
hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
920
if (hPtr == NULL) {
921
panic("Tk_DeleteBinding couldn't find object table entry");
922
}
923
prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
924
if (prevPtr == psPtr) {
925
Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
926
} else {
927
for ( ; ; prevPtr = prevPtr->nextObjPtr) {
928
if (prevPtr == NULL) {
929
panic("Tk_DeleteBinding couldn't find on object list");
930
}
931
if (prevPtr->nextObjPtr == psPtr) {
932
prevPtr->nextObjPtr = psPtr->nextObjPtr;
933
break;
934
}
935
}
936
}
937
prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
938
if (prevPtr == psPtr) {
939
if (psPtr->nextSeqPtr == NULL) {
940
Tcl_DeleteHashEntry(psPtr->hPtr);
941
} else {
942
Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
943
}
944
} else {
945
for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
946
if (prevPtr == NULL) {
947
panic("Tk_DeleteBinding couldn't find on hash chain");
948
}
949
if (prevPtr->nextSeqPtr == psPtr) {
950
prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
951
break;
952
}
953
}
954
}
955
ckfree((char *) psPtr->command);
956
ckfree((char *) psPtr);
957
return TCL_OK;
958
}
959
960
/*
961
*--------------------------------------------------------------
962
*
963
* Tk_GetBinding --
964
*
965
* Return the command associated with a given event string.
966
*
967
* Results:
968
* The return value is a pointer to the command string
969
* associated with eventString for object in the domain
970
* given by bindingTable. If there is no binding for
971
* eventString, or if eventString is improperly formed,
972
* then NULL is returned and an error message is left in
973
* interp->result. The return value is semi-static: it
974
* will persist until the binding is changed or deleted.
975
*
976
* Side effects:
977
* None.
978
*
979
*--------------------------------------------------------------
980
*/
981
982
char *
983
Tk_GetBinding(interp, bindingTable, object, eventString)
984
Tcl_Interp *interp; /* Interpreter for error reporting. */
985
Tk_BindingTable bindingTable; /* Table in which to look for
986
* binding. */
987
ClientData object; /* Token for object with which binding
988
* is associated. */
989
char *eventString; /* String describing event sequence
990
* that triggers binding. */
991
{
992
BindingTable *bindPtr = (BindingTable *) bindingTable;
993
PatSeq *psPtr;
994
unsigned long eventMask;
995
996
psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
997
0, 1, &eventMask);
998
if (psPtr == NULL) {
999
return NULL;
1000
}
1001
return psPtr->command;
1002
}
1003
1004
/*
1005
*--------------------------------------------------------------
1006
*
1007
* Tk_GetAllBindings --
1008
*
1009
* Return a list of event strings for all the bindings
1010
* associated with a given object.
1011
*
1012
* Results:
1013
* There is no return value. Interp->result is modified to
1014
* hold a Tcl list with one entry for each binding associated
1015
* with object in bindingTable. Each entry in the list
1016
* contains the event string associated with one binding.
1017
*
1018
* Side effects:
1019
* None.
1020
*
1021
*--------------------------------------------------------------
1022
*/
1023
1024
void
1025
Tk_GetAllBindings(interp, bindingTable, object)
1026
Tcl_Interp *interp; /* Interpreter returning result or
1027
* error. */
1028
Tk_BindingTable bindingTable; /* Table in which to look for
1029
* bindings. */
1030
ClientData object; /* Token for object. */
1031
1032
{
1033
BindingTable *bindPtr = (BindingTable *) bindingTable;
1034
PatSeq *psPtr;
1035
Tcl_HashEntry *hPtr;
1036
Tcl_DString ds;
1037
1038
hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
1039
if (hPtr == NULL) {
1040
return;
1041
}
1042
Tcl_DStringInit(&ds);
1043
for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1044
psPtr = psPtr->nextObjPtr) {
1045
/*
1046
* For each binding, output information about each of the
1047
* patterns in its sequence.
1048
*/
1049
1050
Tcl_DStringSetLength(&ds, 0);
1051
GetPatternString(psPtr, &ds);
1052
Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
1053
}
1054
Tcl_DStringFree(&ds);
1055
}
1056
1057
/*
1058
*--------------------------------------------------------------
1059
*
1060
* Tk_DeleteAllBindings --
1061
*
1062
* Remove all bindings associated with a given object in a
1063
* given binding table.
1064
*
1065
* Results:
1066
* All bindings associated with object are removed from
1067
* bindingTable.
1068
*
1069
* Side effects:
1070
* None.
1071
*
1072
*--------------------------------------------------------------
1073
*/
1074
1075
void
1076
Tk_DeleteAllBindings(bindingTable, object)
1077
Tk_BindingTable bindingTable; /* Table in which to delete
1078
* bindings. */
1079
ClientData object; /* Token for object. */
1080
{
1081
BindingTable *bindPtr = (BindingTable *) bindingTable;
1082
PatSeq *psPtr, *prevPtr;
1083
PatSeq *nextPtr;
1084
Tcl_HashEntry *hPtr;
1085
1086
hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
1087
if (hPtr == NULL) {
1088
return;
1089
}
1090
for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
1091
psPtr = nextPtr) {
1092
nextPtr = psPtr->nextObjPtr;
1093
1094
/*
1095
* Be sure to remove each binding from its hash chain in the
1096
* pattern table. If this is the last pattern in the chain,
1097
* then delete the hash entry too.
1098
*/
1099
1100
prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
1101
if (prevPtr == psPtr) {
1102
if (psPtr->nextSeqPtr == NULL) {
1103
Tcl_DeleteHashEntry(psPtr->hPtr);
1104
} else {
1105
Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
1106
}
1107
} else {
1108
for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
1109
if (prevPtr == NULL) {
1110
panic("Tk_DeleteAllBindings couldn't find on hash chain");
1111
}
1112
if (prevPtr->nextSeqPtr == psPtr) {
1113
prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
1114
break;
1115
}
1116
}
1117
}
1118
ckfree((char *) psPtr->command);
1119
ckfree((char *) psPtr);
1120
}
1121
Tcl_DeleteHashEntry(hPtr);
1122
}
1123
1124
/*
1125
*--------------------------------------------------------------
1126
*
1127
* Tk_BindEvent --
1128
*
1129
* This procedure is invoked to process an X event. The
1130
* event is added to those recorded for the binding table.
1131
* Then each of the objects at *objectPtr is checked in
1132
* order to see if it has a binding that matches the recent
1133
* events. If so, the most specific binding is invoked for
1134
* each object.
1135
*
1136
* Results:
1137
* None.
1138
*
1139
* Side effects:
1140
* Depends on the command associated with the matching
1141
* binding.
1142
*
1143
*--------------------------------------------------------------
1144
*/
1145
1146
void
1147
Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
1148
Tk_BindingTable bindingTable; /* Table in which to look for
1149
* bindings. */
1150
XEvent *eventPtr; /* What actually happened. */
1151
Tk_Window tkwin; /* Window on display where event
1152
* occurred (needed in order to
1153
* locate display information). */
1154
int numObjects; /* Number of objects at *objectPtr. */
1155
ClientData *objectPtr; /* Array of one or more objects
1156
* to check for a matching binding. */
1157
{
1158
BindingTable *bindPtr = (BindingTable *) bindingTable;
1159
TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1160
TkDisplay *oldDispPtr;
1161
ScreenInfo *screenPtr;
1162
XEvent *ringPtr;
1163
PatSeq *vMatchDetailList, *vMatchNoDetailList;
1164
PatternTableKey key;
1165
Tcl_HashEntry *hPtr;
1166
int flags, code, oldScreen;
1167
Tcl_Interp *interp;
1168
Tcl_DString scripts, savedResult;
1169
char *p, *end;
1170
Detail detail;
1171
1172
/*
1173
* Ignore the event completely if it is an Enter, Leave, FocusIn,
1174
* or FocusOut event with detail NotifyInferior. The reason for
1175
* ignoring these events is that we don't want transitions between
1176
* a window and its children to visible to bindings on the parent:
1177
* this would cause problems for mega-widgets, since the internal
1178
* structure of a mega-widget isn't supposed to be visible to
1179
* people watching the parent.
1180
*/
1181
1182
if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
1183
if (eventPtr->xcrossing.detail == NotifyInferior) {
1184
return;
1185
}
1186
}
1187
if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
1188
if (eventPtr->xfocus.detail == NotifyInferior) {
1189
return;
1190
}
1191
}
1192
1193
/*
1194
* Add the new event to the ring of saved events for the
1195
* binding table. Two tricky points:
1196
*
1197
* 1. Combine consecutive MotionNotify events. Do this by putting
1198
* the new event *on top* of the previous event.
1199
* 2. If a modifier key is held down, it auto-repeats to generate
1200
* continuous KeyPress and KeyRelease events. These can flush
1201
* the event ring so that valuable information is lost (such
1202
* as repeated button clicks). To handle this, check for the
1203
* special case of a modifier KeyPress arriving when the previous
1204
* two events are a KeyRelease and KeyPress of the same key.
1205
* If this happens, mark the most recent event (the KeyRelease)
1206
* invalid and put the new event on top of the event before that
1207
* (the KeyPress).
1208
*/
1209
1210
if ((eventPtr->type == MotionNotify)
1211
&& (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {
1212
/*
1213
* Don't advance the ring pointer.
1214
*/
1215
} else if (eventPtr->type == KeyPress) {
1216
int i;
1217
for (i = 0; ; i++) {
1218
if (i >= dispPtr->numModKeyCodes) {
1219
goto advanceRingPointer;
1220
}
1221
if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
1222
break;
1223
}
1224
}
1225
ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1226
if ((ringPtr->type != KeyRelease)
1227
|| (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
1228
goto advanceRingPointer;
1229
}
1230
if (bindPtr->curEvent <= 0) {
1231
i = EVENT_BUFFER_SIZE - 1;
1232
} else {
1233
i = bindPtr->curEvent - 1;
1234
}
1235
ringPtr = &bindPtr->eventRing[i];
1236
if ((ringPtr->type != KeyPress)
1237
|| (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
1238
goto advanceRingPointer;
1239
}
1240
bindPtr->eventRing[bindPtr->curEvent].type = -1;
1241
bindPtr->curEvent = i;
1242
} else {
1243
advanceRingPointer:
1244
bindPtr->curEvent++;
1245
if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
1246
bindPtr->curEvent = 0;
1247
}
1248
}
1249
ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
1250
memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
1251
detail.clientData = 0;
1252
flags = flagArray[ringPtr->type];
1253
if (flags & KEY) {
1254
detail.keySym = GetKeySym(dispPtr, ringPtr);
1255
if (detail.keySym == NoSymbol) {
1256
detail.keySym = 0;
1257
}
1258
} else if (flags & BUTTON) {
1259
detail.button = ringPtr->xbutton.button;
1260
} else if (flags & VIRTUAL) {
1261
detail.name = ((XVirtualEvent *) ringPtr)->name;
1262
}
1263
bindPtr->detailRing[bindPtr->curEvent] = detail;
1264
1265
/*
1266
* Find out if there are any virtual events that correspond to this
1267
* physical event (or sequence of physical events).
1268
*/
1269
1270
vMatchDetailList = NULL;
1271
vMatchNoDetailList = NULL;
1272
memset(&key, 0, sizeof(key));
1273
1274
if (ringPtr->type != VirtualEvent) {
1275
TkWindow *winPtr = (TkWindow *) tkwin;
1276
Tcl_HashTable *veptPtr = &winPtr->mainPtr->vetPtr->patternTable;
1277
1278
key.object = NULL;
1279
key.type = ringPtr->type;
1280
key.detail = detail;
1281
1282
hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
1283
if (hPtr != NULL) {
1284
vMatchDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
1285
}
1286
1287
if (key.detail.clientData != 0) {
1288
key.detail.clientData = 0;
1289
hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
1290
if (hPtr != NULL) {
1291
vMatchNoDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
1292
}
1293
}
1294
}
1295
1296
/*
1297
* Loop over all the objects, finding the binding script for each
1298
* one. Append all of the binding scripts, with %-sequences expanded,
1299
* to "scripts", with null characters separating the scripts for
1300
* each object.
1301
*/
1302
1303
Tcl_DStringInit(&scripts);
1304
for ( ; numObjects > 0; numObjects--, objectPtr++) {
1305
PatSeq *matchPtr;
1306
char *command;
1307
1308
matchPtr = NULL;
1309
command = NULL;
1310
1311
/*
1312
* Match the new event against those recorded in the pattern table,
1313
* saving the longest matching pattern. For events with details
1314
* (button and key events), look for a binding for the specific
1315
* key or button. First see if the event matches a physical event
1316
* that the object is interested in, then look for a virtual event.
1317
*/
1318
1319
key.object = *objectPtr;
1320
key.type = ringPtr->type;
1321
key.detail = detail;
1322
hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1323
if (hPtr != NULL) {
1324
matchPtr = MatchPatterns(dispPtr, bindPtr,
1325
(PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
1326
&command);
1327
}
1328
1329
if (vMatchDetailList != NULL) {
1330
matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList,
1331
matchPtr, *objectPtr, &command);
1332
}
1333
1334
1335
/*
1336
* If no match was found, look for a binding for all keys or buttons
1337
* (detail of 0). Again, first match on a virtual event.
1338
*/
1339
1340
if ((detail.clientData != 0) && (matchPtr == NULL)) {
1341
key.detail.clientData = 0;
1342
hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
1343
if (hPtr != NULL) {
1344
matchPtr = MatchPatterns(dispPtr, bindPtr,
1345
(PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
1346
&command);
1347
}
1348
1349
if (vMatchNoDetailList != NULL) {
1350
matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList,
1351
matchPtr, *objectPtr, &command);
1352
}
1353
1354
}
1355
1356
if (matchPtr != NULL) {
1357
if (command == NULL) {
1358
panic("Tk_BindEvent: missing command");
1359
}
1360
ExpandPercents((TkWindow *) tkwin, command, eventPtr,
1361
detail.keySym, &scripts);
1362
Tcl_DStringAppend(&scripts, "", 1);
1363
}
1364
}
1365
if (Tcl_DStringLength(&scripts) == 0) {
1366
return;
1367
}
1368
1369
/*
1370
* Now go back through and evaluate the script for each object,
1371
* in order, dealing with "break" and "continue" exceptions
1372
* appropriately.
1373
*
1374
* There are two tricks here:
1375
* 1. Bindings can be invoked from in the middle of Tcl commands,
1376
* where interp->result is significant (for example, a widget
1377
* might be deleted because of an error in creating it, so the
1378
* result contains an error message that is eventually going to
1379
* be returned by the creating command). To preserve the result,
1380
* we save it in a dynamic string.
1381
* 2. The binding's action can potentially delete the binding,
1382
* so bindPtr may not point to anything valid once the action
1383
* completes. Thus we have to save bindPtr->interp in a
1384
* local variable in order to restore the result.
1385
*/
1386
1387
interp = bindPtr->interp;
1388
Tcl_DStringInit(&savedResult);
1389
1390
/*
1391
* Save information about the current screen, then invoke a script
1392
* if the screen has changed.
1393
*/
1394
1395
Tcl_DStringGetResult(interp, &savedResult);
1396
screenPtr = (ScreenInfo *) Tcl_GetAssocData(interp, "tkBind",
1397
(Tcl_InterpDeleteProc **) NULL);
1398
if (screenPtr == NULL) {
1399
screenPtr = (ScreenInfo *) ckalloc(sizeof(ScreenInfo));
1400
screenPtr->curDispPtr = NULL;
1401
screenPtr->curScreenIndex = -1;
1402
screenPtr->bindingDepth = 0;
1403
Tcl_SetAssocData(interp, "tkBind", FreeScreenInfo,
1404
(ClientData) screenPtr);
1405
}
1406
oldDispPtr = screenPtr->curDispPtr;
1407
oldScreen = screenPtr->curScreenIndex;
1408
if ((dispPtr != screenPtr->curDispPtr)
1409
|| (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) {
1410
screenPtr->curDispPtr = dispPtr;
1411
screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin);
1412
ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex);
1413
}
1414
1415
p = Tcl_DStringValue(&scripts);
1416
end = p + Tcl_DStringLength(&scripts);
1417
while (p != end) {
1418
screenPtr->bindingDepth += 1;
1419
Tcl_AllowExceptions(interp);
1420
code = Tcl_GlobalEval(interp, p);
1421
screenPtr->bindingDepth -= 1;
1422
if (code != TCL_OK) {
1423
if (code == TCL_CONTINUE) {
1424
/*
1425
* Do nothing: just go on to the next script.
1426
*/
1427
} else if (code == TCL_BREAK) {
1428
break;
1429
} else {
1430
Tcl_AddErrorInfo(interp, "\n (command bound to event)");
1431
Tcl_BackgroundError(interp);
1432
break;
1433
}
1434
}
1435
1436
/*
1437
* Skip over the current script and its terminating null character.
1438
*/
1439
1440
while (*p != 0) {
1441
p++;
1442
}
1443
p++;
1444
}
1445
if ((screenPtr->bindingDepth != 0) &&
1446
((oldDispPtr != screenPtr->curDispPtr)
1447
|| (oldScreen != screenPtr->curScreenIndex))) {
1448
1449
/*
1450
* Some other binding script is currently executing, but its
1451
* screen is no longer current. Change the current display
1452
* back again.
1453
*/
1454
1455
screenPtr->curDispPtr = oldDispPtr;
1456
screenPtr->curScreenIndex = oldScreen;
1457
ChangeScreen(interp, oldDispPtr->name, oldScreen);
1458
}
1459
Tcl_DStringResult(interp, &savedResult);
1460
Tcl_DStringFree(&scripts);
1461
}
1462
1463
/*
1464
*----------------------------------------------------------------------
1465
*
1466
* MatchPatterns --
1467
*
1468
* Given a list of pattern sequences and a list of recent events,
1469
* return the pattern sequence that best matches the event list,
1470
* if there is one.
1471
*
1472
* This procedure is used in two different ways. In the simplest
1473
* use, "object" is NULL and psPtr is a list of pattern sequences,
1474
* each of which corresponds to a binding. In this case, the
1475
* procedure finds the pattern sequences that match the event list
1476
* and returns the most specify of those, if there is more than one.
1477
*
1478
* In the second case, psPtr is a list of pattern sequences, each
1479
* of which corresponds to a definition for a virtual binding.
1480
* In order for one of these sequences to "match", it must match
1481
* the events (as above) but in addition there must be a binding
1482
* for its associated virtual event on the current object. The
1483
* "object" argument indicates which object the binding must be for.
1484
*
1485
* Results:
1486
* The return value is NULL if bestPtr is NULL and no pattern matches
1487
* the recent events from bindPtr. Otherwise the return value is
1488
* the most specific pattern sequence among bestPtr and all those
1489
* at psPtr that match the event list and object. If a pattern
1490
* sequence other than bestPtr is returned, then *bestCommandPtr
1491
* is filled in with a pointer to the command from the best sequence.
1492
*
1493
* Side effects:
1494
* None.
1495
*
1496
*----------------------------------------------------------------------
1497
*/
1498
static PatSeq *
1499
MatchPatterns(dispPtr, bindPtr, psPtr, bestPtr, object, bestCommandPtr)
1500
TkDisplay *dispPtr; /* Display from which the event came. */
1501
BindingTable *bindPtr; /* Information about binding table, such as
1502
* ring of recent events. */
1503
PatSeq *psPtr; /* List of pattern sequences. */
1504
PatSeq *bestPtr; /* The best match seen so far, from a
1505
* previous call to this procedure. NULL
1506
* means no prior best match. */
1507
ClientData object; /* If NULL, the sequences at psPtr
1508
* correspond to "normal" bindings. If
1509
* non-NULL, the sequences at psPtr correspond
1510
* to virtual bindings; in order to match each
1511
* sequence must correspond to a virtual
1512
* binding for which a binding exists for
1513
* object in bindPtr. */
1514
char **bestCommandPtr; /* Returns the command associated with the
1515
* best match. Not modified unless a result
1516
* other than bestPtr is returned. */
1517
{
1518
PatSeq *matchPtr;
1519
char *bestCommand, *command;
1520
1521
bestCommand = *bestCommandPtr;
1522
1523
/*
1524
* Iterate over all the pattern sequences.
1525
*/
1526
1527
for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
1528
XEvent *eventPtr;
1529
Pattern *patPtr;
1530
Window window;
1531
Detail *detailPtr;
1532
int patCount, ringCount, flags, state;
1533
int modMask;
1534
1535
/*
1536
* Iterate over all the patterns in a sequence to be
1537
* sure that they all match.
1538
*/
1539
1540
eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
1541
detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
1542
window = eventPtr->xany.window;
1543
patPtr = psPtr->pats;
1544
patCount = psPtr->numPats;
1545
ringCount = EVENT_BUFFER_SIZE;
1546
while (patCount > 0) {
1547
if (ringCount <= 0) {
1548
goto nextSequence;
1549
}
1550
if (eventPtr->xany.type != patPtr->eventType) {
1551
/*
1552
* Most of the event types are considered superfluous
1553
* in that they are ignored if they occur in the middle
1554
* of a pattern sequence and have mismatching types. The
1555
* only ones that cannot be ignored are ButtonPress and
1556
* ButtonRelease events (if the next event in the pattern
1557
* is a KeyPress or KeyRelease) and KeyPress and KeyRelease
1558
* events (if the next pattern event is a ButtonPress or
1559
* ButtonRelease). Here are some tricky cases to consider:
1560
* 1. Double-Button or Double-Key events.
1561
* 2. Double-ButtonRelease or Double-KeyRelease events.
1562
* 3. The arrival of various events like Enter and Leave
1563
* and FocusIn and GraphicsExpose between two button
1564
* presses or key presses.
1565
* 4. Modifier keys like Shift and Control shouldn't
1566
* generate conflicts with button events.
1567
*/
1568
1569
if ((patPtr->eventType == KeyPress)
1570
|| (patPtr->eventType == KeyRelease)) {
1571
if ((eventPtr->xany.type == ButtonPress)
1572
|| (eventPtr->xany.type == ButtonRelease)) {
1573
goto nextSequence;
1574
}
1575
} else if ((patPtr->eventType == ButtonPress)
1576
|| (patPtr->eventType == ButtonRelease)) {
1577
if ((eventPtr->xany.type == KeyPress)
1578
|| (eventPtr->xany.type == KeyRelease)) {
1579
int i;
1580
1581
/*
1582
* Ignore key events if they are modifier keys.
1583
*/
1584
1585
for (i = 0; i < dispPtr->numModKeyCodes; i++) {
1586
if (dispPtr->modKeyCodes[i]
1587
== eventPtr->xkey.keycode) {
1588
/*
1589
* This key is a modifier key, so ignore it.
1590
*/
1591
goto nextEvent;
1592
}
1593
}
1594
goto nextSequence;
1595
}
1596
}
1597
goto nextEvent;
1598
}
1599
if (eventPtr->xany.window != window) {
1600
goto nextSequence;
1601
}
1602
1603
/*
1604
* Note: it's important for the keysym check to go before
1605
* the modifier check, so we can ignore unwanted modifier
1606
* keys before choking on the modifier check.
1607
*/
1608
1609
if ((patPtr->detail.clientData != 0)
1610
&& (patPtr->detail.clientData != detailPtr->clientData)) {
1611
/*
1612
* The detail appears not to match. However, if the event
1613
* is a KeyPress for a modifier key then just ignore the
1614
* event. Otherwise event sequences like "aD" never match
1615
* because the shift key goes down between the "a" and the
1616
* "D".
1617
*/
1618
1619
if (eventPtr->xany.type == KeyPress) {
1620
int i;
1621
1622
for (i = 0; i < dispPtr->numModKeyCodes; i++) {
1623
if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
1624
goto nextEvent;
1625
}
1626
}
1627
}
1628
goto nextSequence;
1629
}
1630
flags = flagArray[eventPtr->type];
1631
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
1632
state = eventPtr->xkey.state;
1633
} else if (flags & CROSSING) {
1634
state = eventPtr->xcrossing.state;
1635
} else {
1636
state = 0;
1637
}
1638
if (patPtr->needMods != 0) {
1639
modMask = patPtr->needMods;
1640
if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
1641
modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
1642
}
1643
if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
1644
modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
1645
}
1646
if ((state & modMask) != modMask) {
1647
goto nextSequence;
1648
}
1649
}
1650
if (psPtr->flags & PAT_NEARBY) {
1651
XEvent *firstPtr;
1652
int timeDiff;
1653
1654
firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
1655
timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;
1656
if ((firstPtr->xkey.x_root
1657
< (eventPtr->xkey.x_root - NEARBY_PIXELS))
1658
|| (firstPtr->xkey.x_root
1659
> (eventPtr->xkey.x_root + NEARBY_PIXELS))
1660
|| (firstPtr->xkey.y_root
1661
< (eventPtr->xkey.y_root - NEARBY_PIXELS))
1662
|| (firstPtr->xkey.y_root
1663
> (eventPtr->xkey.y_root + NEARBY_PIXELS))
1664
|| (timeDiff > NEARBY_MS)) {
1665
goto nextSequence;
1666
}
1667
}
1668
patPtr++;
1669
patCount--;
1670
nextEvent:
1671
if (eventPtr == bindPtr->eventRing) {
1672
eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
1673
detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
1674
} else {
1675
eventPtr--;
1676
detailPtr--;
1677
}
1678
ringCount--;
1679
}
1680
1681
matchPtr = psPtr;
1682
command = matchPtr->command;
1683
1684
if (object != NULL) {
1685
int iVirt;
1686
VirtualOwners *voPtr;
1687
PatternTableKey key;
1688
1689
/*
1690
* The sequence matches the physical constraints.
1691
* Is this object interested in any of the virtual events
1692
* that correspond to this sequence?
1693
*/
1694
1695
voPtr = psPtr->voPtr;
1696
1697
memset(&key, 0, sizeof(key));
1698
key.object = object;
1699
key.type = VirtualEvent;
1700
key.detail.clientData = 0;
1701
1702
for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
1703
Tcl_HashEntry *hPtr = voPtr->owners[iVirt];
1704
1705
key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr,
1706
hPtr);
1707
hPtr = Tcl_FindHashEntry(&bindPtr->patternTable,
1708
(char *) &key);
1709
if (hPtr != NULL) {
1710
1711
/*
1712
* This tag is interested in this virtual event and its
1713
* corresponding physical event is a good match with the
1714
* virtual event's definition.
1715
*/
1716
1717
PatSeq *virtMatchPtr;
1718
1719
virtMatchPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
1720
if ((virtMatchPtr->numPats != 1)
1721
|| (virtMatchPtr->nextSeqPtr != NULL)) {
1722
panic("MatchPattern: badly constructed virtual event");
1723
}
1724
command = virtMatchPtr->command;
1725
1726
goto match;
1727
}
1728
}
1729
1730
/*
1731
* The physical event matches a virtual event's definition, but
1732
* the tag isn't interested in it.
1733
*/
1734
goto nextSequence;
1735
}
1736
match:
1737
1738
/*
1739
* This sequence matches. If we've already got another match,
1740
* pick whichever is most specific. Detail is most important,
1741
* then needMods.
1742
*/
1743
1744
if (bestPtr != NULL) {
1745
Pattern *patPtr2;
1746
int i;
1747
1748
if (matchPtr->numPats != bestPtr->numPats) {
1749
if (bestPtr->numPats > matchPtr->numPats) {
1750
goto nextSequence;
1751
} else {
1752
goto newBest;
1753
}
1754
}
1755
for (i = 0, patPtr = matchPtr->pats, patPtr2 = bestPtr->pats;
1756
i < matchPtr->numPats; i++, patPtr++, patPtr2++) {
1757
if (patPtr->detail.clientData != patPtr2->detail.clientData) {
1758
if (patPtr->detail.clientData == 0) {
1759
goto nextSequence;
1760
} else {
1761
goto newBest;
1762
}
1763
}
1764
if (patPtr->needMods != patPtr2->needMods) {
1765
if ((patPtr->needMods & patPtr2->needMods)
1766
== patPtr->needMods) {
1767
goto nextSequence;
1768
} else if ((patPtr->needMods & patPtr2->needMods)
1769
== patPtr2->needMods) {
1770
goto newBest;
1771
}
1772
}
1773
}
1774
/*
1775
* Tie goes to current best pattern.
1776
*
1777
* (1) For virtual vs. virtual, the least recently defined
1778
* virtual wins, because virtuals are examined in order of
1779
* definition. This order is _not_ guaranteed in the
1780
* documentation.
1781
*
1782
* (2) For virtual vs. physical, the physical wins because all
1783
* the physicals are examined before the virtuals. This order
1784
* is guaranteed in the documentation.
1785
*
1786
* (3) For physical vs. physical pattern, the most recently
1787
* defined physical wins, because physicals are examined in
1788
* reverse order of definition. This order is guaranteed in
1789
* the documentation.
1790
*/
1791
1792
goto nextSequence;
1793
}
1794
newBest:
1795
bestPtr = matchPtr;
1796
bestCommand = command;
1797
1798
nextSequence: continue;
1799
}
1800
1801
*bestCommandPtr = bestCommand;
1802
return bestPtr;
1803
}
1804
1805
/*
1806
*--------------------------------------------------------------
1807
*
1808
* ExpandPercents --
1809
*
1810
* Given a command and an event, produce a new command
1811
* by replacing % constructs in the original command
1812
* with information from the X event.
1813
*
1814
* Results:
1815
* The new expanded command is appended to the dynamic string
1816
* given by dsPtr.
1817
*
1818
* Side effects:
1819
* None.
1820
*
1821
*--------------------------------------------------------------
1822
*/
1823
1824
static void
1825
ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
1826
TkWindow *winPtr; /* Window where event occurred: needed to
1827
* get input context. */
1828
char *before; /* Command containing percent expressions
1829
* to be replaced. */
1830
XEvent *eventPtr; /* X event containing information to be
1831
* used in % replacements. */
1832
KeySym keySym; /* KeySym: only relevant for KeyPress and
1833
* KeyRelease events). */
1834
Tcl_DString *dsPtr; /* Dynamic string in which to append new
1835
* command. */
1836
{
1837
int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
1838
* list element. */
1839
int number, flags, length;
1840
#define NUM_SIZE 40
1841
char *string;
1842
char numStorage[NUM_SIZE+1];
1843
1844
if (eventPtr->type < TK_LASTEVENT) {
1845
flags = flagArray[eventPtr->type];
1846
} else {
1847
flags = 0;
1848
}
1849
while (1) {
1850
/*
1851
* Find everything up to the next % character and append it
1852
* to the result string.
1853
*/
1854
1855
for (string = before; (*string != 0) && (*string != '%'); string++) {
1856
/* Empty loop body. */
1857
}
1858
if (string != before) {
1859
Tcl_DStringAppend(dsPtr, before, string-before);
1860
before = string;
1861
}
1862
if (*before == 0) {
1863
break;
1864
}
1865
1866
/*
1867
* There's a percent sequence here. Process it.
1868
*/
1869
1870
number = 0;
1871
string = "??";
1872
switch (before[1]) {
1873
case '#':
1874
number = eventPtr->xany.serial;
1875
goto doNumber;
1876
case 'a':
1877
sprintf(numStorage, "0x%x", (int) eventPtr->xconfigure.above);
1878
string = numStorage;
1879
goto doString;
1880
case 'b':
1881
number = eventPtr->xbutton.button;
1882
goto doNumber;
1883
case 'c':
1884
if (flags & EXPOSE) {
1885
number = eventPtr->xexpose.count;
1886
}
1887
goto doNumber;
1888
case 'd':
1889
if (flags & (CROSSING|FOCUS)) {
1890
if (flags & FOCUS) {
1891
number = eventPtr->xfocus.detail;
1892
} else {
1893
number = eventPtr->xcrossing.detail;
1894
}
1895
string = TkFindStateString(notifyDetail, number);
1896
}
1897
goto doString;
1898
case 'f':
1899
number = eventPtr->xcrossing.focus;
1900
goto doNumber;
1901
case 'h':
1902
if (flags & EXPOSE) {
1903
number = eventPtr->xexpose.height;
1904
} else if (flags & (CONFIG)) {
1905
number = eventPtr->xconfigure.height;
1906
}
1907
goto doNumber;
1908
case 'k':
1909
number = eventPtr->xkey.keycode;
1910
goto doNumber;
1911
case 'm':
1912
if (flags & CROSSING) {
1913
number = eventPtr->xcrossing.mode;
1914
} else if (flags & FOCUS) {
1915
number = eventPtr->xfocus.mode;
1916
}
1917
string = TkFindStateString(notifyMode, number);
1918
goto doString;
1919
case 'o':
1920
if (flags & CREATE) {
1921
number = eventPtr->xcreatewindow.override_redirect;
1922
} else if (flags & MAP) {
1923
number = eventPtr->xmap.override_redirect;
1924
} else if (flags & REPARENT) {
1925
number = eventPtr->xreparent.override_redirect;
1926
} else if (flags & CONFIG) {
1927
number = eventPtr->xconfigure.override_redirect;
1928
}
1929
goto doNumber;
1930
case 'p':
1931
string = TkFindStateString(circPlace, eventPtr->xcirculate.place);
1932
goto doString;
1933
case 's':
1934
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
1935
number = eventPtr->xkey.state;
1936
} else if (flags & CROSSING) {
1937
number = eventPtr->xcrossing.state;
1938
} else if (flags & VISIBILITY) {
1939
string = TkFindStateString(visNotify,
1940
eventPtr->xvisibility.state);
1941
goto doString;
1942
}
1943
goto doNumber;
1944
case 't':
1945
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
1946
number = (int) eventPtr->xkey.time;
1947
} else if (flags & CROSSING) {
1948
number = (int) eventPtr->xcrossing.time;
1949
} else if (flags & PROP) {
1950
number = (int) eventPtr->xproperty.time;
1951
}
1952
goto doNumber;
1953
case 'v':
1954
number = eventPtr->xconfigurerequest.value_mask;
1955
goto doNumber;
1956
case 'w':
1957
if (flags & EXPOSE) {
1958
number = eventPtr->xexpose.width;
1959
} else if (flags & CONFIG) {
1960
number = eventPtr->xconfigure.width;
1961
}
1962
goto doNumber;
1963
case 'x':
1964
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
1965
number = eventPtr->xkey.x;
1966
} else if (flags & CROSSING) {
1967
number = eventPtr->xcrossing.x;
1968
} else if (flags & EXPOSE) {
1969
number = eventPtr->xexpose.x;
1970
} else if (flags & (CREATE|CONFIG|GRAVITY)) {
1971
number = eventPtr->xcreatewindow.x;
1972
} else if (flags & REPARENT) {
1973
number = eventPtr->xreparent.x;
1974
}
1975
goto doNumber;
1976
case 'y':
1977
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
1978
number = eventPtr->xkey.y;
1979
} else if (flags & EXPOSE) {
1980
number = eventPtr->xexpose.y;
1981
} else if (flags & (CREATE|CONFIG|GRAVITY)) {
1982
number = eventPtr->xcreatewindow.y;
1983
} else if (flags & REPARENT) {
1984
number = eventPtr->xreparent.y;
1985
} else if (flags & CROSSING) {
1986
number = eventPtr->xcrossing.y;
1987
1988
}
1989
goto doNumber;
1990
case 'A':
1991
if (flags & KEY) {
1992
int numChars;
1993
1994
/*
1995
* If we're using input methods and this is a keypress
1996
* event, invoke XmbTkFindStateString. Otherwise just use
1997
* the older XTkFindStateString.
1998
*/
1999
2000
#ifdef TK_USE_INPUT_METHODS
2001
Status status;
2002
if ((winPtr->inputContext != NULL)
2003
&& (eventPtr->type == KeyPress)) {
2004
numChars = XmbLookupString(winPtr->inputContext,
2005
&eventPtr->xkey, numStorage, NUM_SIZE,
2006
(KeySym *) NULL, &status);
2007
if ((status != XLookupChars)
2008
&& (status != XLookupBoth)) {
2009
numChars = 0;
2010
}
2011
} else {
2012
numChars = XLookupString(&eventPtr->xkey, numStorage,
2013
NUM_SIZE, (KeySym *) NULL,
2014
(XComposeStatus *) NULL);
2015
}
2016
#else /* TK_USE_INPUT_METHODS */
2017
numChars = XLookupString(&eventPtr->xkey, numStorage,
2018
NUM_SIZE, (KeySym *) NULL,
2019
(XComposeStatus *) NULL);
2020
#endif /* TK_USE_INPUT_METHODS */
2021
numStorage[numChars] = '\0';
2022
string = numStorage;
2023
}
2024
goto doString;
2025
case 'B':
2026
number = eventPtr->xcreatewindow.border_width;
2027
goto doNumber;
2028
case 'E':
2029
number = (int) eventPtr->xany.send_event;
2030
goto doNumber;
2031
case 'K':
2032
if (flags & KEY) {
2033
char *name;
2034
2035
name = TkKeysymToString(keySym);
2036
if (name != NULL) {
2037
string = name;
2038
}
2039
}
2040
goto doString;
2041
case 'N':
2042
number = (int) keySym;
2043
goto doNumber;
2044
case 'R':
2045
number = (int) eventPtr->xkey.root;
2046
goto doNumber;
2047
case 'S':
2048
sprintf(numStorage, "0x%x", (int) eventPtr->xkey.subwindow);
2049
string = numStorage;
2050
goto doString;
2051
case 'T':
2052
number = eventPtr->type;
2053
goto doNumber;
2054
case 'W': {
2055
Tk_Window tkwin;
2056
2057
tkwin = Tk_IdToWindow(eventPtr->xany.display,
2058
eventPtr->xany.window);
2059
if (tkwin != NULL) {
2060
string = Tk_PathName(tkwin);
2061
} else {
2062
string = "??";
2063
}
2064
goto doString;
2065
}
2066
case 'X': {
2067
Tk_Window tkwin;
2068
int x, y;
2069
int width, height;
2070
2071
number = eventPtr->xkey.x_root;
2072
tkwin = Tk_IdToWindow(eventPtr->xany.display,
2073
eventPtr->xany.window);
2074
if (tkwin != NULL) {
2075
Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
2076
number -= x;
2077
}
2078
goto doNumber;
2079
}
2080
case 'Y': {
2081
Tk_Window tkwin;
2082
int x, y;
2083
int width, height;
2084
2085
number = eventPtr->xkey.y_root;
2086
tkwin = Tk_IdToWindow(eventPtr->xany.display,
2087
eventPtr->xany.window);
2088
if (tkwin != NULL) {
2089
Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
2090
number -= y;
2091
}
2092
goto doNumber;
2093
}
2094
default:
2095
numStorage[0] = before[1];
2096
numStorage[1] = '\0';
2097
string = numStorage;
2098
goto doString;
2099
}
2100
2101
doNumber:
2102
sprintf(numStorage, "%d", number);
2103
string = numStorage;
2104
2105
doString:
2106
spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2107
length = Tcl_DStringLength(dsPtr);
2108
Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2109
spaceNeeded = Tcl_ConvertElement(string,
2110
Tcl_DStringValue(dsPtr) + length,
2111
cvtFlags | TCL_DONT_USE_BRACES);
2112
Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2113
before += 2;
2114
}
2115
}
2116
2117
/*
2118
*----------------------------------------------------------------------
2119
*
2120
* FreeScreenInfo --
2121
*
2122
* This procedure is invoked when an interpreter is deleted in
2123
* order to free the ScreenInfo structure associated with the
2124
* "tkBind" AssocData.
2125
*
2126
* Results:
2127
* None.
2128
*
2129
* Side effects:
2130
* Storage is freed.
2131
*
2132
*----------------------------------------------------------------------
2133
*/
2134
2135
static void
2136
FreeScreenInfo(clientData, interp)
2137
ClientData clientData; /* Pointer to ScreenInfo structure. */
2138
Tcl_Interp *interp; /* Interpreter that is being deleted. */
2139
{
2140
ckfree((char *) clientData);
2141
}
2142
2143
/*
2144
*----------------------------------------------------------------------
2145
*
2146
* ChangeScreen --
2147
*
2148
* This procedure is invoked whenever the current screen changes
2149
* in an application. It invokes a Tcl procedure named
2150
* "tkScreenChanged", passing it the screen name as argument.
2151
* tkScreenChanged does things like making the tkPriv variable
2152
* point to an array for the current display.
2153
*
2154
* Results:
2155
* None.
2156
*
2157
* Side effects:
2158
* Depends on what tkScreenChanged does. If an error occurs
2159
* them tkError will be invoked.
2160
*
2161
*----------------------------------------------------------------------
2162
*/
2163
2164
static void
2165
ChangeScreen(interp, dispName, screenIndex)
2166
Tcl_Interp *interp; /* Interpreter in which to invoke
2167
* command. */
2168
char *dispName; /* Name of new display. */
2169
int screenIndex; /* Index of new screen. */
2170
{
2171
Tcl_DString cmd;
2172
int code;
2173
char screen[30];
2174
2175
Tcl_DStringInit(&cmd);
2176
Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);
2177
Tcl_DStringAppend(&cmd, dispName, -1);
2178
sprintf(screen, ".%d", screenIndex);
2179
Tcl_DStringAppend(&cmd, screen, -1);
2180
code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));
2181
if (code != TCL_OK) {
2182
Tcl_AddErrorInfo(interp,
2183
"\n (changing screen in event binding)");
2184
Tcl_BackgroundError(interp);
2185
}
2186
}
2187
2188
2189
/*
2190
*----------------------------------------------------------------------
2191
*
2192
* Tk_EventCmd --
2193
*
2194
* This procedure is invoked to process the "event" Tcl command.
2195
* It is used to define and generate events.
2196
*
2197
* Results:
2198
* A standard Tcl result.
2199
*
2200
* Side effects:
2201
* See the user documentation.
2202
*
2203
*----------------------------------------------------------------------
2204
*/
2205
2206
int
2207
Tk_EventCmd(clientData, interp, argc, argv)
2208
ClientData clientData; /* Main window associated with
2209
* interpreter. */
2210
Tcl_Interp *interp; /* Current interpreter. */
2211
int argc; /* Number of arguments. */
2212
char **argv; /* Argument strings. */
2213
{
2214
int i;
2215
size_t length;
2216
char *option;
2217
TkWindow *winPtr;
2218
TkVirtualEventTable *vetPtr;
2219
2220
if (argc < 2) {
2221
Tcl_AppendResult(interp, "wrong # args: should be \"",
2222
argv[0], " option ?arg1?\"", (char *) NULL);
2223
return TCL_ERROR;
2224
}
2225
2226
option = argv[1];
2227
length = strlen(option);
2228
if (length == 0) {
2229
goto badopt;
2230
}
2231
2232
winPtr = (TkWindow *) clientData;
2233
vetPtr = winPtr->mainPtr->vetPtr;
2234
2235
if (strncmp(option, "add", length) == 0) {
2236
if (argc < 4) {
2237
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2238
" add virtual sequence ?sequence ...?\"", (char *) NULL);
2239
return TCL_ERROR;
2240
}
2241
for (i = 3; i < argc; i++) {
2242
if (CreateVirtualEvent(interp, vetPtr, argv[2], argv[i])
2243
!= TCL_OK) {
2244
return TCL_ERROR;
2245
}
2246
}
2247
} else if (strncmp(option, "delete", length) == 0) {
2248
if (argc < 3) {
2249
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2250
" delete virtual ?sequence sequence ...?\"",
2251
(char *) NULL);
2252
return TCL_ERROR;
2253
}
2254
if (argc == 3) {
2255
return DeleteVirtualEvent(interp, vetPtr, argv[2], NULL);
2256
}
2257
for (i = 3; i < argc; i++) {
2258
if (DeleteVirtualEvent(interp, vetPtr, argv[2], argv[i])
2259
!= TCL_OK) {
2260
return TCL_ERROR;
2261
}
2262
}
2263
} else if (strncmp(option, "generate", length) == 0) {
2264
if (argc < 4) {
2265
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2266
" generate window event ?options?\"", (char *) NULL);
2267
return TCL_ERROR;
2268
}
2269
return HandleEventGenerate(interp, (Tk_Window) winPtr,
2270
argc - 2, argv + 2);
2271
} else if (strncmp(option, "info", length) == 0) {
2272
if (argc == 2) {
2273
GetAllVirtualEvents(interp, vetPtr);
2274
return TCL_OK;
2275
} else if (argc == 3) {
2276
return GetVirtualEvent(interp, vetPtr, argv[2]);
2277
} else {
2278
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
2279
" info ?virtual?\"", (char *) NULL);
2280
return TCL_ERROR;
2281
}
2282
} else {
2283
badopt:
2284
Tcl_AppendResult(interp, "bad option \"", argv[1],
2285
"\": should be add, delete, generate, info", (char *) NULL);
2286
return TCL_ERROR;
2287
}
2288
return TCL_OK;
2289
}
2290
2291
/*
2292
*--------------------------------------------------------------
2293
*
2294
* CreateVirtualEventTable --
2295
*
2296
* Set up a new domain in which virtual events may be defined.
2297
*
2298
* Results:
2299
* The return value is a token for the new table, which must
2300
* be passed to procedures like Tk_CreateVirtualEvent().
2301
*
2302
* Side effects:
2303
* The caller must have already called Tk_CreateBindingTable() to
2304
* properly set up memory used by the entire event-handling subsystem.
2305
* Memory is allocated for the new table.
2306
*
2307
*--------------------------------------------------------------
2308
*/
2309
static TkVirtualEventTable *
2310
CreateVirtualEventTable()
2311
{
2312
TkVirtualEventTable *vetPtr;
2313
2314
if (!initialized) {
2315
panic("CreateVirtualEvent: Tk_CreateBindingTable never called");
2316
}
2317
vetPtr = (TkVirtualEventTable *) ckalloc(sizeof(TkVirtualEventTable));
2318
Tcl_InitHashTable(&vetPtr->patternTable,
2319
sizeof(PatternTableKey)/sizeof(int));
2320
Tcl_InitHashTable(&vetPtr->virtualTable, TCL_ONE_WORD_KEYS);
2321
2322
return vetPtr;
2323
}
2324
2325
/*
2326
*--------------------------------------------------------------
2327
*
2328
* DeleteVirtualEventTable --
2329
*
2330
* Destroy a virtual event table and free up all its memory.
2331
* The caller should not use virtualEventTable again after
2332
* this procedure returns.
2333
*
2334
* Results:
2335
* None.
2336
*
2337
* Side effects:
2338
* Memory is freed.
2339
*
2340
*--------------------------------------------------------------
2341
*/
2342
2343
static void
2344
DeleteVirtualEventTable(vetPtr)
2345
TkVirtualEventTable *vetPtr;/* The virtual event table to be destroyed. */
2346
{
2347
Tcl_HashEntry *hPtr;
2348
Tcl_HashSearch search;
2349
PatSeq *psPtr, *nextPtr;
2350
2351
hPtr = Tcl_FirstHashEntry(&vetPtr->patternTable, &search);
2352
for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2353
psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
2354
for ( ; psPtr != NULL; psPtr = nextPtr) {
2355
nextPtr = psPtr->nextSeqPtr;
2356
ckfree((char *) psPtr->voPtr);
2357
ckfree((char *) psPtr);
2358
}
2359
}
2360
Tcl_DeleteHashTable(&vetPtr->patternTable);
2361
2362
hPtr = Tcl_FirstHashEntry(&vetPtr->virtualTable, &search);
2363
for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2364
ckfree((char *) Tcl_GetHashValue(hPtr));
2365
}
2366
Tcl_DeleteHashTable(&vetPtr->virtualTable);
2367
2368
ckfree((char *) vetPtr);
2369
}
2370
2371
/*
2372
*----------------------------------------------------------------------
2373
*
2374
* CreateVirtualEvent --
2375
*
2376
* Add a new definition for a virtual event. If the virtual event
2377
* is already defined, the new definition augments those that
2378
* already exist.
2379
*
2380
* Results:
2381
* The return value is TCL_ERROR if an error occured while
2382
* creating the virtual binding. In this case, an error message
2383
* will be left in interp->result. If all went well then the return
2384
* value is TCL_OK.
2385
*
2386
* Side effects:
2387
* The virtual event may cause future calls to Tk_BindEvent to
2388
* behave differently than they did previously.
2389
*
2390
*----------------------------------------------------------------------
2391
*/
2392
2393
static int
2394
CreateVirtualEvent(interp, vetPtr, virtString, eventString)
2395
Tcl_Interp *interp; /* Used for error reporting. */
2396
TkVirtualEventTable *vetPtr;/* Table in which to augment virtual event. */
2397
char *virtString; /* Name of new virtual event. */
2398
char *eventString; /* String describing physical event that
2399
* triggers virtual event. */
2400
{
2401
PatSeq *psPtr;
2402
int dummy;
2403
Tcl_HashEntry *vhPtr;
2404
unsigned long eventMask;
2405
PhysicalsOwned *poPtr;
2406
VirtualOwners *voPtr;
2407
Tk_Uid virtUid;
2408
2409
virtUid = GetVirtualEventUid(interp, virtString);
2410
if (virtUid == NULL) {
2411
return TCL_ERROR;
2412
}
2413
2414
/*
2415
* Find/create physical event
2416
*/
2417
2418
psPtr = FindSequence(interp, &vetPtr->patternTable, NULL, eventString,
2419
1, 0, &eventMask);
2420
if (psPtr == NULL) {
2421
return TCL_ERROR;
2422
}
2423
2424
/*
2425
* Find/create virtual event.
2426
*/
2427
2428
vhPtr = Tcl_CreateHashEntry(&vetPtr->virtualTable, virtUid, &dummy);
2429
2430
/*
2431
* Make virtual event own the physical event.
2432
*/
2433
2434
poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
2435
if (poPtr == NULL) {
2436
poPtr = (PhysicalsOwned *) ckalloc(sizeof(PhysicalsOwned));
2437
poPtr->numOwned = 0;
2438
} else {
2439
/*
2440
* See if this virtual event is already defined for this physical
2441
* event and just return if it is.
2442
*/
2443
2444
int i;
2445
for (i = 0; i < poPtr->numOwned; i++) {
2446
if (poPtr->patSeqs[i] == psPtr) {
2447
return TCL_OK;
2448
}
2449
}
2450
poPtr = (PhysicalsOwned *) ckrealloc((char *) poPtr,
2451
sizeof(PhysicalsOwned) + poPtr->numOwned * sizeof(PatSeq *));
2452
}
2453
Tcl_SetHashValue(vhPtr, (ClientData) poPtr);
2454
poPtr->patSeqs[poPtr->numOwned] = psPtr;
2455
poPtr->numOwned++;
2456
2457
/*
2458
* Make physical event so it can trigger the virtual event.
2459
*/
2460
2461
voPtr = psPtr->voPtr;
2462
if (voPtr == NULL) {
2463
voPtr = (VirtualOwners *) ckalloc(sizeof(VirtualOwners));
2464
voPtr->numOwners = 0;
2465
} else {
2466
voPtr = (VirtualOwners *) ckrealloc((char *) voPtr,
2467
sizeof(VirtualOwners)
2468
+ voPtr->numOwners * sizeof(Tcl_HashEntry *));
2469
}
2470
psPtr->voPtr = voPtr;
2471
voPtr->owners[voPtr->numOwners] = vhPtr;
2472
voPtr->numOwners++;
2473
2474
return TCL_OK;
2475
}
2476
2477
/*
2478
*--------------------------------------------------------------
2479
*
2480
* DeleteVirtualEvent --
2481
*
2482
* Remove the definition of a given virtual event. If the
2483
* event string is NULL, all definitions of the virtual event
2484
* will be removed. Otherwise, just the specified definition
2485
* of the virtual event will be removed.
2486
*
2487
* Results:
2488
* The result is a standard Tcl return value. If an error
2489
* occurs then interp->result will contain an error message.
2490
* It is not an error to attempt to delete a virtual event that
2491
* does not exist or a definition that does not exist.
2492
*
2493
* Side effects:
2494
* The virtual event given by virtString may be removed from the
2495
* virtual event table.
2496
*
2497
*--------------------------------------------------------------
2498
*/
2499
2500
static int
2501
DeleteVirtualEvent(interp, vetPtr, virtString, eventString)
2502
Tcl_Interp *interp; /* Used for error reporting. */
2503
TkVirtualEventTable *vetPtr;/* Table in which to delete event. */
2504
char *virtString; /* String describing event sequence that
2505
* triggers binding. */
2506
char *eventString; /* The event sequence that should be deleted,
2507
* or NULL to delete all event sequences for
2508
* the entire virtual event. */
2509
{
2510
int iPhys;
2511
Tk_Uid virtUid;
2512
Tcl_HashEntry *vhPtr;
2513
PhysicalsOwned *poPtr;
2514
PatSeq *eventPSPtr;
2515
2516
virtUid = GetVirtualEventUid(interp, virtString);
2517
if (virtUid == NULL) {
2518
return TCL_ERROR;
2519
}
2520
2521
vhPtr = Tcl_FindHashEntry(&vetPtr->virtualTable, virtUid);
2522
if (vhPtr == NULL) {
2523
return TCL_OK;
2524
}
2525
poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
2526
2527
eventPSPtr = NULL;
2528
if (eventString != NULL) {
2529
unsigned long eventMask;
2530
2531
/*
2532
* Delete only the specific physical event associated with the
2533
* virtual event. If the physical event doesn't already exist, or
2534
* the virtual event doesn't own that physical event, return w/o
2535
* doing anything.
2536
*/
2537
2538
eventPSPtr = FindSequence(interp, &vetPtr->patternTable, NULL,
2539
eventString, 0, 0, &eventMask);
2540
if (eventPSPtr == NULL) {
2541
return (interp->result[0] != '\0') ? TCL_ERROR : TCL_OK;
2542
}
2543
}
2544
2545
for (iPhys = poPtr->numOwned; --iPhys >= 0; ) {
2546
PatSeq *psPtr = poPtr->patSeqs[iPhys];
2547
if ((eventPSPtr == NULL) || (psPtr == eventPSPtr)) {
2548
int iVirt;
2549
VirtualOwners *voPtr;
2550
2551
/*
2552
* Remove association between this physical event and the given
2553
* virtual event that it triggers.
2554
*/
2555
2556
voPtr = psPtr->voPtr;
2557
for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
2558
if (voPtr->owners[iVirt] == vhPtr) {
2559
break;
2560
}
2561
}
2562
if (iVirt == voPtr->numOwners) {
2563
panic("DeleteVirtualEvent: couldn't find owner");
2564
}
2565
voPtr->numOwners--;
2566
if (voPtr->numOwners == 0) {
2567
/*
2568
* Removed last reference to this physical event, so
2569
* remove it from physical->virtual map.
2570
*/
2571
PatSeq *prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
2572
if (prevPtr == psPtr) {
2573
if (psPtr->nextSeqPtr == NULL) {
2574
Tcl_DeleteHashEntry(psPtr->hPtr);
2575
} else {
2576
Tcl_SetHashValue(psPtr->hPtr,
2577
psPtr->nextSeqPtr);
2578
}
2579
} else {
2580
for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
2581
if (prevPtr == NULL) {
2582
panic("Tk_DeleteVirtualEvent couldn't find on hash chain");
2583
}
2584
if (prevPtr->nextSeqPtr == psPtr) {
2585
prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
2586
break;
2587
}
2588
}
2589
}
2590
ckfree((char *) psPtr->voPtr);
2591
ckfree((char *) psPtr);
2592
} else {
2593
/*
2594
* This physical event still triggers some other virtual
2595
* event(s). Consolidate the list of virtual owners for
2596
* this physical event so it no longer triggers the
2597
* given virtual event.
2598
*/
2599
voPtr->owners[iVirt] = voPtr->owners[voPtr->numOwners];
2600
}
2601
2602
/*
2603
* Now delete the virtual event's reference to the physical
2604
* event.
2605
*/
2606
2607
poPtr->numOwned--;
2608
if (eventPSPtr != NULL && poPtr->numOwned != 0) {
2609
/*
2610
* Just deleting this one physical event. Consolidate list
2611
* of owned physical events and return.
2612
*/
2613
2614
poPtr->patSeqs[iPhys] = poPtr->patSeqs[poPtr->numOwned];
2615
return TCL_OK;
2616
}
2617
}
2618
}
2619
2620
if (poPtr->numOwned == 0) {
2621
/*
2622
* All the physical events for this virtual event were deleted,
2623
* either because there was only one associated physical event or
2624
* because the caller was deleting the entire virtual event. Now
2625
* the virtual event itself should be deleted.
2626
*/
2627
2628
ckfree((char *) poPtr);
2629
Tcl_DeleteHashEntry(vhPtr);
2630
}
2631
return TCL_OK;
2632
}
2633
2634
/*
2635
*---------------------------------------------------------------------------
2636
*
2637
* GetVirtualEvent --
2638
*
2639
* Return the list of physical events that can invoke the
2640
* given virtual event.
2641
*
2642
* Results:
2643
* The return value is TCL_OK and interp->result is filled with the
2644
* string representation of the physical events associated with the
2645
* virtual event; if there are no physical events for the given virtual
2646
* event, interp->result is filled with and empty string. If the
2647
* virtual event string is improperly formed, then TCL_ERROR is
2648
* returned and an error message is left in interp->result.
2649
*
2650
* Side effects:
2651
* None.
2652
*
2653
*---------------------------------------------------------------------------
2654
*/
2655
2656
static int
2657
GetVirtualEvent(interp, vetPtr, virtString)
2658
Tcl_Interp *interp; /* Interpreter for reporting. */
2659
TkVirtualEventTable *vetPtr;/* Table in which to look for event. */
2660
char *virtString; /* String describing virtual event. */
2661
{
2662
Tcl_HashEntry *vhPtr;
2663
Tcl_DString ds;
2664
int iPhys;
2665
PhysicalsOwned *poPtr;
2666
Tk_Uid virtUid;
2667
2668
virtUid = GetVirtualEventUid(interp, virtString);
2669
if (virtUid == NULL) {
2670
return TCL_ERROR;
2671
}
2672
2673
vhPtr = Tcl_FindHashEntry(&vetPtr->virtualTable, virtUid);
2674
if (vhPtr == NULL) {
2675
return TCL_OK;
2676
}
2677
2678
Tcl_DStringInit(&ds);
2679
2680
poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
2681
for (iPhys = 0; iPhys < poPtr->numOwned; iPhys++) {
2682
Tcl_DStringSetLength(&ds, 0);
2683
GetPatternString(poPtr->patSeqs[iPhys], &ds);
2684
Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
2685
}
2686
Tcl_DStringFree(&ds);
2687
2688
return TCL_OK;
2689
}
2690
2691
/*
2692
*--------------------------------------------------------------
2693
*
2694
* GetAllVirtualEvents --
2695
*
2696
* Return a list that contains the names of all the virtual
2697
* event defined.
2698
*
2699
* Results:
2700
* There is no return value. Interp->result is modified to
2701
* hold a Tcl list with one entry for each virtual event in
2702
* virtualTable.
2703
*
2704
* Side effects:
2705
* None.
2706
*
2707
*--------------------------------------------------------------
2708
*/
2709
2710
static void
2711
GetAllVirtualEvents(interp, vetPtr)
2712
Tcl_Interp *interp; /* Interpreter returning result. */
2713
TkVirtualEventTable *vetPtr;/* Table containing events. */
2714
{
2715
Tcl_HashEntry *hPtr;
2716
Tcl_HashSearch search;
2717
Tcl_DString ds;
2718
2719
Tcl_DStringInit(&ds);
2720
2721
hPtr = Tcl_FirstHashEntry(&vetPtr->virtualTable, &search);
2722
for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2723
Tcl_DStringSetLength(&ds, 0);
2724
Tcl_DStringAppend(&ds, "<<", 2);
2725
Tcl_DStringAppend(&ds, Tcl_GetHashKey(hPtr->tablePtr, hPtr), -1);
2726
Tcl_DStringAppend(&ds, ">>", 2);
2727
Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
2728
}
2729
2730
Tcl_DStringFree(&ds);
2731
}
2732
2733
/*
2734
*---------------------------------------------------------------------------
2735
*
2736
* HandleEventGenerate --
2737
*
2738
* Helper function for the "event generate" command. Generate and
2739
* process an XEvent, constructed from information parsed from the
2740
* event description string and its optional arguments.
2741
*
2742
* argv[0] contains name of the target window.
2743
* argv[1] contains pattern string for one event (e.g, <Control-v>).
2744
* argv[2..argc-1] contains -field/option pairs for specifying
2745
* additional detail in the generated event.
2746
*
2747
* Either virtual or physical events can be generated this way.
2748
* The event description string must contain the specification
2749
* for only one event.
2750
*
2751
* Results:
2752
* None.
2753
*
2754
* Side effects:
2755
* When constructing the event,
2756
* event.xany.serial is filled with the current X serial number.
2757
* event.xany.window is filled with the target window.
2758
* event.xany.display is filled with the target window's display.
2759
* Any other fields in eventPtr which are not specified by the pattern
2760
* string or the optional arguments, are set to 0.
2761
*
2762
* The event may be handled sychronously or asynchronously, depending
2763
* on the value specified by the optional "-when" option. The
2764
* default setting is synchronous.
2765
*
2766
*---------------------------------------------------------------------------
2767
*/
2768
static int
2769
HandleEventGenerate(interp, tkwin, argc, argv)
2770
Tcl_Interp *interp; /* Interp for error messages and name lookup. */
2771
Tk_Window tkwin; /* Main window of this application. */
2772
int argc; /* Number of arguments. */
2773
char **argv; /* Argument strings. */
2774
{
2775
Pattern pat;
2776
char *p;
2777
unsigned long eventMask;
2778
int count, i, state, flags, synch;
2779
Tcl_QueuePosition pos;
2780
union
2781
{
2782
XEvent E;
2783
XVirtualEvent V;
2784
} event;
2785
2786
tkwin = Tk_NameToWindow(interp, argv[0], tkwin);
2787
if (tkwin == NULL) {
2788
return TCL_ERROR;
2789
}
2790
2791
p = argv[1];
2792
count = ParseEventDescription(interp, &p, &pat, &eventMask);
2793
if (count == 0) {
2794
return TCL_ERROR;
2795
}
2796
if (count != 1) {
2797
interp->result = "Double or Triple modifier not allowed";
2798
return TCL_ERROR;
2799
}
2800
if (*p != '\0') {
2801
interp->result = "only one event specification allowed";
2802
return TCL_ERROR;
2803
}
2804
if (argc & 1) {
2805
Tcl_AppendResult(interp, "value for \"", argv[argc - 1],
2806
"\" missing", (char *) NULL);
2807
return TCL_ERROR;
2808
}
2809
2810
memset((VOID *) &event, 0, sizeof(event));
2811
event.E.xany.type = pat.eventType;
2812
event.E.xany.serial = NextRequest(Tk_Display(tkwin));
2813
event.E.xany.send_event = False;
2814
event.E.xany.window = Tk_WindowId(tkwin);
2815
event.E.xany.display = Tk_Display(tkwin);
2816
2817
flags = flagArray[event.E.xany.type];
2818
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
2819
event.E.xkey.state = pat.needMods;
2820
if (flags & KEY) {
2821
/*
2822
* When mapping from a keysym to a keycode, need information about
2823
* the modifier state that should be used so that when they call
2824
* XKeycodeToKeysym taking into account the xkey.state, they will
2825
* get back the original keysym.
2826
*/
2827
2828
if (pat.detail.keySym == NoSymbol) {
2829
event.E.xkey.keycode = 0;
2830
} else {
2831
event.E.xkey.keycode = XKeysymToKeycode(event.E.xany.display,
2832
pat.detail.keySym);
2833
}
2834
if (event.E.xkey.keycode != 0) {
2835
for (state = 0; state < 4; state++) {
2836
if (XKeycodeToKeysym(event.E.xany.display,
2837
event.E.xkey.keycode, state) == pat.detail.keySym) {
2838
if (state & 1) {
2839
event.E.xkey.state |= ShiftMask;
2840
}
2841
if (state & 2) {
2842
TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
2843
event.E.xkey.state |= dispPtr->modeModMask;
2844
}
2845
break;
2846
}
2847
}
2848
}
2849
} else if (flags & BUTTON) {
2850
event.E.xbutton.button = pat.detail.button;
2851
} else if (flags & VIRTUAL) {
2852
event.V.name = pat.detail.name;
2853
}
2854
}
2855
if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) {
2856
event.E.xcreatewindow.window = event.E.xany.window;
2857
}
2858
2859
/*
2860
* Process the remaining arguments to fill in additional fields
2861
* of the event.
2862
*/
2863
2864
synch = 1;
2865
pos = TCL_QUEUE_TAIL;
2866
for (i = 2; i < argc; i += 2) {
2867
char *field, *value;
2868
Tk_Window tkwin2;
2869
int number;
2870
KeySym keysym;
2871
2872
field = argv[i];
2873
value = argv[i+1];
2874
2875
if (strcmp(field, "-when") == 0) {
2876
if (strcmp(value, "now") == 0) {
2877
synch = 1;
2878
} else if (strcmp(value, "head") == 0) {
2879
pos = TCL_QUEUE_HEAD;
2880
synch = 0;
2881
} else if (strcmp(value, "mark") == 0) {
2882
pos = TCL_QUEUE_MARK;
2883
synch = 0;
2884
} else if (strcmp(value, "tail") == 0) {
2885
pos = TCL_QUEUE_TAIL;
2886
synch = 0;
2887
} else {
2888
Tcl_AppendResult(interp, "bad position \"", value,
2889
"\": should be now, head, mark, tail", (char *) NULL);
2890
return TCL_ERROR;
2891
}
2892
} else if (strcmp(field, "-above") == 0) {
2893
if (value[0] == '.') {
2894
tkwin2 = Tk_NameToWindow(interp, value, tkwin);
2895
if (tkwin2 == NULL) {
2896
return TCL_ERROR;
2897
}
2898
number = Tk_WindowId(tkwin2);
2899
} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
2900
return TCL_ERROR;
2901
}
2902
if (flags & CONFIG) {
2903
event.E.xconfigure.above = number;
2904
} else {
2905
goto badopt;
2906
}
2907
} else if (strcmp(field, "-borderwidth") == 0) {
2908
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
2909
return TCL_ERROR;
2910
}
2911
if (flags & (CREATE|CONFIG)) {
2912
event.E.xcreatewindow.border_width = number;
2913
} else {
2914
goto badopt;
2915
}
2916
} else if (strcmp(field, "-button") == 0) {
2917
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
2918
return TCL_ERROR;
2919
}
2920
if (flags & BUTTON) {
2921
event.E.xbutton.button = number;
2922
} else {
2923
goto badopt;
2924
}
2925
} else if (strcmp(field, "-count") == 0) {
2926
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
2927
return TCL_ERROR;
2928
}
2929
if (flags & EXPOSE) {
2930
event.E.xexpose.count = number;
2931
} else {
2932
goto badopt;
2933
}
2934
} else if (strcmp(field, "-detail") == 0) {
2935
number = TkFindStateNum(interp, field, notifyDetail, value);
2936
if (number < 0) {
2937
return TCL_ERROR;
2938
}
2939
if (flags & FOCUS) {
2940
event.E.xfocus.detail = number;
2941
} else if (flags & CROSSING) {
2942
event.E.xcrossing.detail = number;
2943
} else {
2944
goto badopt;
2945
}
2946
} else if (strcmp(field, "-focus") == 0) {
2947
if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
2948
return TCL_ERROR;
2949
}
2950
if (flags & CROSSING) {
2951
event.E.xcrossing.focus = number;
2952
} else {
2953
goto badopt;
2954
}
2955
} else if (strcmp(field, "-height") == 0) {
2956
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
2957
return TCL_ERROR;
2958
}
2959
if (flags & EXPOSE) {
2960
event.E.xexpose.height = number;
2961
} else if (flags & CONFIG) {
2962
event.E.xconfigure.height = number;
2963
} else {
2964
goto badopt;
2965
}
2966
} else if (strcmp(field, "-keycode") == 0) {
2967
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
2968
return TCL_ERROR;
2969
}
2970
if (flags & KEY) {
2971
event.E.xkey.keycode = number;
2972
} else {
2973
goto badopt;
2974
}
2975
} else if (strcmp(field, "-keysym") == 0) {
2976
keysym = TkStringToKeysym(value);
2977
if (keysym == NoSymbol) {
2978
Tcl_AppendResult(interp, "unknown keysym \"", value,
2979
"\"", (char *) NULL);
2980
return TCL_ERROR;
2981
}
2982
/*
2983
* When mapping from a keysym to a keycode, need information about
2984
* the modifier state that should be used so that when they call
2985
* XKeycodeToKeysym taking into account the xkey.state, they will
2986
* get back the original keysym.
2987
*/
2988
2989
number = XKeysymToKeycode(event.E.xany.display, keysym);
2990
if (number == 0) {
2991
Tcl_AppendResult(interp, "no keycode for keysym \"", value,
2992
"\"", (char *) NULL);
2993
return TCL_ERROR;
2994
}
2995
for (state = 0; state < 4; state++) {
2996
if (XKeycodeToKeysym(event.E.xany.display, (unsigned) number,
2997
state) == keysym) {
2998
if (state & 1) {
2999
event.E.xkey.state |= ShiftMask;
3000
}
3001
if (state & 2) {
3002
TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
3003
event.E.xkey.state |= dispPtr->modeModMask;
3004
}
3005
break;
3006
}
3007
}
3008
if (flags & KEY) {
3009
event.E.xkey.keycode = number;
3010
} else {
3011
goto badopt;
3012
}
3013
} else if (strcmp(field, "-mode") == 0) {
3014
number = TkFindStateNum(interp, field, notifyMode, value);
3015
if (number < 0) {
3016
return TCL_ERROR;
3017
}
3018
if (flags & CROSSING) {
3019
event.E.xcrossing.mode = number;
3020
} else if (flags & FOCUS) {
3021
event.E.xfocus.mode = number;
3022
} else {
3023
goto badopt;
3024
}
3025
} else if (strcmp(field, "-override") == 0) {
3026
if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
3027
return TCL_ERROR;
3028
}
3029
if (flags & CREATE) {
3030
event.E.xcreatewindow.override_redirect = number;
3031
} else if (flags & MAP) {
3032
event.E.xmap.override_redirect = number;
3033
} else if (flags & REPARENT) {
3034
event.E.xreparent.override_redirect = number;
3035
} else if (flags & CONFIG) {
3036
event.E.xconfigure.override_redirect = number;
3037
} else {
3038
goto badopt;
3039
}
3040
} else if (strcmp(field, "-place") == 0) {
3041
number = TkFindStateNum(interp, field, circPlace, value);
3042
if (number < 0) {
3043
return TCL_ERROR;
3044
}
3045
if (flags & CIRC) {
3046
event.E.xcirculate.place = number;
3047
} else {
3048
goto badopt;
3049
}
3050
} else if (strcmp(field, "-root") == 0) {
3051
if (value[0] == '.') {
3052
tkwin2 = Tk_NameToWindow(interp, value, tkwin);
3053
if (tkwin2 == NULL) {
3054
return TCL_ERROR;
3055
}
3056
number = Tk_WindowId(tkwin2);
3057
} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3058
return TCL_ERROR;
3059
}
3060
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3061
event.E.xkey.root = number;
3062
} else {
3063
goto badopt;
3064
}
3065
} else if (strcmp(field, "-rootx") == 0) {
3066
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
3067
return TCL_ERROR;
3068
}
3069
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3070
event.E.xkey.x_root = number;
3071
} else {
3072
goto badopt;
3073
}
3074
} else if (strcmp(field, "-rooty") == 0) {
3075
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
3076
return TCL_ERROR;
3077
}
3078
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3079
event.E.xkey.y_root = number;
3080
} else {
3081
goto badopt;
3082
}
3083
} else if (strcmp(field, "-sendevent") == 0) {
3084
if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
3085
return TCL_ERROR;
3086
}
3087
event.E.xany.send_event = number;
3088
} else if (strcmp(field, "-serial") == 0) {
3089
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3090
return TCL_ERROR;
3091
}
3092
event.E.xany.serial = number;
3093
} else if (strcmp(field, "-state") == 0) {
3094
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3095
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3096
return TCL_ERROR;
3097
}
3098
if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
3099
event.E.xkey.state = number;
3100
} else {
3101
event.E.xcrossing.state = number;
3102
}
3103
} else if (flags & VISIBILITY) {
3104
number = TkFindStateNum(interp, field, visNotify, value);
3105
if (number < 0) {
3106
return TCL_ERROR;
3107
}
3108
event.E.xvisibility.state = number;
3109
} else {
3110
goto badopt;
3111
}
3112
} else if (strcmp(field, "-subwindow") == 0) {
3113
if (value[0] == '.') {
3114
tkwin2 = Tk_NameToWindow(interp, value, tkwin);
3115
if (tkwin2 == NULL) {
3116
return TCL_ERROR;
3117
}
3118
number = Tk_WindowId(tkwin2);
3119
} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3120
return TCL_ERROR;
3121
}
3122
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3123
event.E.xkey.subwindow = number;
3124
} else {
3125
goto badopt;
3126
}
3127
} else if (strcmp(field, "-time") == 0) {
3128
if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3129
return TCL_ERROR;
3130
}
3131
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3132
event.E.xkey.time = (Time) number;
3133
} else if (flags & PROP) {
3134
event.E.xproperty.time = (Time) number;
3135
} else {
3136
goto badopt;
3137
}
3138
} else if (strcmp(field, "-width") == 0) {
3139
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
3140
return TCL_ERROR;
3141
}
3142
if (flags & EXPOSE) {
3143
event.E.xexpose.width = number;
3144
} else if (flags & (CREATE|CONFIG)) {
3145
event.E.xcreatewindow.width = number;
3146
} else {
3147
goto badopt;
3148
}
3149
} else if (strcmp(field, "-window") == 0) {
3150
if (value[0] == '.') {
3151
tkwin2 = Tk_NameToWindow(interp, value, tkwin);
3152
if (tkwin2 == NULL) {
3153
return TCL_ERROR;
3154
}
3155
number = Tk_WindowId(tkwin2);
3156
} else if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
3157
return TCL_ERROR;
3158
}
3159
if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG
3160
|GRAVITY|CIRC)) {
3161
event.E.xcreatewindow.window = number;
3162
} else {
3163
goto badopt;
3164
}
3165
} else if (strcmp(field, "-x") == 0) {
3166
int rootX, rootY;
3167
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
3168
return TCL_ERROR;
3169
}
3170
Tk_GetRootCoords(tkwin, &rootX, &rootY);
3171
rootX += number;
3172
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3173
event.E.xkey.x = number;
3174
event.E.xkey.x_root = rootX;
3175
} else if (flags & EXPOSE) {
3176
event.E.xexpose.x = number;
3177
} else if (flags & (CREATE|CONFIG|GRAVITY)) {
3178
event.E.xcreatewindow.x = number;
3179
} else if (flags & REPARENT) {
3180
event.E.xreparent.x = number;
3181
} else {
3182
goto badopt;
3183
}
3184
} else if (strcmp(field, "-y") == 0) {
3185
int rootX, rootY;
3186
if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
3187
return TCL_ERROR;
3188
}
3189
Tk_GetRootCoords(tkwin, &rootX, &rootY);
3190
rootY += number;
3191
if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
3192
event.E.xkey.y = number;
3193
event.E.xkey.y_root = rootY;
3194
} else if (flags & EXPOSE) {
3195
event.E.xexpose.y = number;
3196
} else if (flags & (CREATE|CONFIG|GRAVITY)) {
3197
event.E.xcreatewindow.y = number;
3198
} else if (flags & REPARENT) {
3199
event.E.xreparent.y = number;
3200
} else {
3201
goto badopt;
3202
}
3203
} else {
3204
badopt:
3205
Tcl_AppendResult(interp, "bad option to ", argv[1],
3206
" event: \"", field, "\"", (char *) NULL);
3207
return TCL_ERROR;
3208
}
3209
}
3210
3211
if (synch != 0) {
3212
Tk_HandleEvent(&event.E);
3213
} else {
3214
Tk_QueueWindowEvent(&event.E, pos);
3215
}
3216
return TCL_OK;
3217
}
3218
3219
/*
3220
*-------------------------------------------------------------------------
3221
*
3222
* GetVirtualEventUid --
3223
*
3224
* Determine if the given string is in the proper format for a
3225
* virtual event.
3226
*
3227
* Results:
3228
* The return value is NULL if the virtual event string was
3229
* not in the proper format. In this case, an error message
3230
* will be left in interp->result. Otherwise the return
3231
* value is a Tk_Uid that represents the virtual event.
3232
*
3233
* Side effects:
3234
* None.
3235
*
3236
*-------------------------------------------------------------------------
3237
*/
3238
static Tk_Uid
3239
GetVirtualEventUid(interp, virtString)
3240
Tcl_Interp *interp;
3241
char *virtString;
3242
{
3243
Tk_Uid uid;
3244
int length;
3245
3246
length = strlen(virtString);
3247
3248
if (length < 5 || virtString[0] != '<' || virtString[1] != '<' ||
3249
virtString[length - 2] != '>' || virtString[length - 1] != '>') {
3250
Tcl_AppendResult(interp, "virtual event \"", virtString,
3251
"\" is badly formed", (char *) NULL);
3252
return NULL;
3253
}
3254
virtString[length - 2] = '\0';
3255
uid = Tk_GetUid(virtString + 2);
3256
virtString[length - 2] = '>';
3257
3258
return uid;
3259
}
3260
3261
3262
/*
3263
*----------------------------------------------------------------------
3264
*
3265
* FindSequence --
3266
*
3267
* Find the entry in the pattern table that corresponds to a
3268
* particular pattern string, and return a pointer to that
3269
* entry.
3270
*
3271
* Results:
3272
* The return value is normally a pointer to the PatSeq
3273
* in patternTable that corresponds to eventString. If an error
3274
* was found while parsing eventString, or if "create" is 0 and
3275
* no pattern sequence previously existed, then NULL is returned
3276
* and interp->result contains a message describing the problem.
3277
* If no pattern sequence previously existed for eventString, then
3278
* a new one is created with a NULL command field. In a successful
3279
* return, *maskPtr is filled in with a mask of the event types
3280
* on which the pattern sequence depends.
3281
*
3282
* Side effects:
3283
* A new pattern sequence may be allocated.
3284
*
3285
*----------------------------------------------------------------------
3286
*/
3287
3288
static PatSeq *
3289
FindSequence(interp, patternTablePtr, object, eventString, create,
3290
allowVirtual, maskPtr)
3291
Tcl_Interp *interp; /* Interpreter to use for error
3292
* reporting. */
3293
Tcl_HashTable *patternTablePtr; /* Table to use for lookup. */
3294
ClientData object; /* For binding table, token for object with
3295
* which binding is associated.
3296
* For virtual event table, NULL. */
3297
char *eventString; /* String description of pattern to
3298
* match on. See user documentation
3299
* for details. */
3300
int create; /* 0 means don't create the entry if
3301
* it doesn't already exist. Non-zero
3302
* means create. */
3303
int allowVirtual; /* 0 means that virtual events are not
3304
* allowed in the sequence. Non-zero
3305
* otherwise. */
3306
unsigned long *maskPtr; /* *maskPtr is filled in with the event
3307
* types on which this pattern sequence
3308
* depends. */
3309
{
3310
3311
Pattern pats[EVENT_BUFFER_SIZE];
3312
int numPats, virtualFound;
3313
char *p;
3314
Pattern *patPtr;
3315
PatSeq *psPtr;
3316
Tcl_HashEntry *hPtr;
3317
int flags, count, new;
3318
size_t sequenceSize;
3319
unsigned long eventMask;
3320
PatternTableKey key;
3321
3322
/*
3323
*-------------------------------------------------------------
3324
* Step 1: parse the pattern string to produce an array
3325
* of Patterns. The array is generated backwards, so
3326
* that the lowest-indexed pattern corresponds to the last
3327
* event that must occur.
3328
*-------------------------------------------------------------
3329
*/
3330
3331
p = eventString;
3332
flags = 0;
3333
eventMask = 0;
3334
virtualFound = 0;
3335
3336
patPtr = &pats[EVENT_BUFFER_SIZE-1];
3337
for (numPats = 0; numPats < EVENT_BUFFER_SIZE; numPats++, patPtr--) {
3338
while (isspace(UCHAR(*p))) {
3339
p++;
3340
}
3341
if (*p == '\0') {
3342
break;
3343
}
3344
3345
count = ParseEventDescription(interp, &p, patPtr, &eventMask);
3346
if (count == 0) {
3347
return NULL;
3348
}
3349
3350
if (eventMask & VirtualEventMask) {
3351
if (allowVirtual == 0) {
3352
interp->result =
3353
"virtual event not allowed in definition of another virtual event";
3354
return NULL;
3355
}
3356
virtualFound = 1;
3357
}
3358
3359
/*
3360
* Replicate events for DOUBLE and TRIPLE.
3361
*/
3362
3363
if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
3364
flags |= PAT_NEARBY;
3365
patPtr[-1] = patPtr[0];
3366
patPtr--;
3367
numPats++;
3368
if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
3369
patPtr[-1] = patPtr[0];
3370
patPtr--;
3371
numPats++;
3372
}
3373
}
3374
}
3375
3376
/*
3377
*-------------------------------------------------------------
3378
* Step 2: find the sequence in the binding table if it exists,
3379
* and add a new sequence to the table if it doesn't.
3380
*-------------------------------------------------------------
3381
*/
3382
3383
if (numPats == 0) {
3384
interp->result = "no events specified in binding";
3385
return NULL;
3386
}
3387
if ((numPats > 1) && (virtualFound != 0)) {
3388
interp->result = "virtual events may not be composed";
3389
return NULL;
3390
}
3391
3392
patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
3393
memset(&key, 0, sizeof(key));
3394
key.object = object;
3395
key.type = patPtr->eventType;
3396
key.detail = patPtr->detail;
3397
hPtr = Tcl_CreateHashEntry(patternTablePtr, (char *) &key, &new);
3398
sequenceSize = numPats*sizeof(Pattern);
3399
if (!new) {
3400
for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
3401
psPtr = psPtr->nextSeqPtr) {
3402
if ((numPats == psPtr->numPats)
3403
&& ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
3404
&& (memcmp((char *) patPtr, (char *) psPtr->pats,
3405
sequenceSize) == 0)) {
3406
goto done;
3407
}
3408
}
3409
}
3410
if (!create) {
3411
if (new) {
3412
Tcl_DeleteHashEntry(hPtr);
3413
}
3414
/* Tcl_AppendResult(interp, "no binding exists for \"",
3415
eventString, "\"", (char *) NULL);*/
3416
return NULL;
3417
}
3418
psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
3419
+ (numPats-1)*sizeof(Pattern)));
3420
psPtr->numPats = numPats;
3421
psPtr->command = NULL;
3422
psPtr->flags = flags;
3423
psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
3424
psPtr->hPtr = hPtr;
3425
psPtr->voPtr = NULL;
3426
psPtr->nextObjPtr = NULL;
3427
Tcl_SetHashValue(hPtr, psPtr);
3428
3429
memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
3430
3431
done:
3432
*maskPtr = eventMask;
3433
return psPtr;
3434
}
3435
3436
/*
3437
*---------------------------------------------------------------------------
3438
*
3439
* ParseEventDescription --
3440
*
3441
* Fill Pattern buffer with information about event from
3442
* event string.
3443
*
3444
* Results:
3445
* Leaves error message in interp and returns 0 if there was an
3446
* error due to a badly formed event string. Returns 1 if proper
3447
* event was specified, 2 if Double modifier was used in event
3448
* string, or 3 if Triple was used.
3449
*
3450
* Side effects:
3451
* On exit, eventStringPtr points to rest of event string (after the
3452
* closing '>', so that this procedure can be called repeatedly to
3453
* parse all the events in the entire sequence.
3454
*
3455
*---------------------------------------------------------------------------
3456
*/
3457
3458
static int
3459
ParseEventDescription(interp, eventStringPtr, patPtr,
3460
eventMaskPtr)
3461
Tcl_Interp *interp; /* For error messages. */
3462
char **eventStringPtr; /* On input, holds a pointer to start of
3463
* event string. On exit, gets pointer to
3464
* rest of string after parsed event. */
3465
Pattern *patPtr; /* Filled with the pattern parsed from the
3466
* event string. */
3467
unsigned long *eventMaskPtr;/* Filled with event mask of matched event. */
3468
3469
{
3470
char *p;
3471
unsigned long eventMask;
3472
int count, eventFlags;
3473
#define FIELD_SIZE 48
3474
char field[FIELD_SIZE];
3475
Tcl_HashEntry *hPtr;
3476
3477
p = *eventStringPtr;
3478
3479
patPtr->eventType = -1;
3480
patPtr->needMods = 0;
3481
patPtr->detail.clientData = 0;
3482
3483
eventMask = 0;
3484
count = 1;
3485
3486
/*
3487
* Handle simple ASCII characters.
3488
*/
3489
3490
if (*p != '<') {
3491
char string[2];
3492
3493
patPtr->eventType = KeyPress;
3494
eventMask = KeyPressMask;
3495
string[0] = *p;
3496
string[1] = 0;
3497
patPtr->detail.keySym = TkStringToKeysym(string);
3498
if (patPtr->detail.keySym == NoSymbol) {
3499
if (isprint(UCHAR(*p))) {
3500
patPtr->detail.keySym = *p;
3501
} else {
3502
sprintf(interp->result,
3503
"bad ASCII character 0x%x", (unsigned char) *p);
3504
return 0;
3505
}
3506
}
3507
p++;
3508
goto end;
3509
}
3510
3511
/*
3512
* A fancier event description. This can be either a virtual event
3513
* or a physical event.
3514
*
3515
* A virtual event description consists of:
3516
*
3517
* 1. double open angle brackets.
3518
* 2. virtual event name.
3519
* 3. double close angle brackets.
3520
*
3521
* A physical event description consists of:
3522
*
3523
* 1. open angle bracket.
3524
* 2. any number of modifiers, each followed by spaces
3525
* or dashes.
3526
* 3. an optional event name.
3527
* 4. an option button or keysym name. Either this or
3528
* item 3 *must* be present; if both are present
3529
* then they are separated by spaces or dashes.
3530
* 5. a close angle bracket.
3531
*/
3532
3533
p++;
3534
if (*p == '<') {
3535
/*
3536
* This is a virtual event: soak up all the characters up to
3537
* the next '>'.
3538
*/
3539
3540
char *field = p + 1;
3541
p = strchr(field, '>');
3542
if (p == field) {
3543
interp->result = "virtual event \"<<>>\" is badly formed";
3544
return 0;
3545
}
3546
if ((p == NULL) || (p[1] != '>')) {
3547
interp->result = "missing \">\" in virtual binding";
3548
return 0;
3549
}
3550
*p = '\0';
3551
patPtr->eventType = VirtualEvent;
3552
eventMask = VirtualEventMask;
3553
patPtr->detail.name = Tk_GetUid(field);
3554
*p = '>';
3555
3556
p += 2;
3557
goto end;
3558
}
3559
3560
while (1) {
3561
ModInfo *modPtr;
3562
p = GetField(p, field, FIELD_SIZE);
3563
hPtr = Tcl_FindHashEntry(&modTable, field);
3564
if (hPtr == NULL) {
3565
break;
3566
}
3567
modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
3568
patPtr->needMods |= modPtr->mask;
3569
if (modPtr->flags & (DOUBLE|TRIPLE)) {
3570
if (modPtr->flags & DOUBLE) {
3571
count = 2;
3572
} else {
3573
count = 3;
3574
}
3575
}
3576
while ((*p == '-') || isspace(UCHAR(*p))) {
3577
p++;
3578
}
3579
}
3580
3581
eventFlags = 0;
3582
hPtr = Tcl_FindHashEntry(&eventTable, field);
3583
if (hPtr != NULL) {
3584
EventInfo *eiPtr;
3585
eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
3586
3587
patPtr->eventType = eiPtr->type;
3588
eventFlags = flagArray[eiPtr->type];
3589
eventMask = eiPtr->eventMask;
3590
while ((*p == '-') || isspace(UCHAR(*p))) {
3591
p++;
3592
}
3593
p = GetField(p, field, FIELD_SIZE);
3594
}
3595
if (*field != '\0') {
3596
if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
3597
if (eventFlags == 0) {
3598
patPtr->eventType = ButtonPress;
3599
eventMask = ButtonPressMask;
3600
} else if (eventFlags & KEY) {
3601
goto getKeysym;
3602
} else if ((eventFlags & BUTTON) == 0) {
3603
Tcl_AppendResult(interp, "specified button \"", field,
3604
"\" for non-button event", (char *) NULL);
3605
return 0;
3606
}
3607
patPtr->detail.button = (*field - '0');
3608
} else {
3609
getKeysym:
3610
patPtr->detail.keySym = TkStringToKeysym(field);
3611
if (patPtr->detail.keySym == NoSymbol) {
3612
Tcl_AppendResult(interp, "bad event type or keysym \"",
3613
field, "\"", (char *) NULL);
3614
return 0;
3615
}
3616
if (eventFlags == 0) {
3617
patPtr->eventType = KeyPress;
3618
eventMask = KeyPressMask;
3619
} else if ((eventFlags & KEY) == 0) {
3620
Tcl_AppendResult(interp, "specified keysym \"", field,
3621
"\" for non-key event", (char *) NULL);
3622
return 0;
3623
}
3624
}
3625
} else if (eventFlags == 0) {
3626
interp->result = "no event type or button # or keysym";
3627
return 0;
3628
}
3629
3630
while ((*p == '-') || isspace(UCHAR(*p))) {
3631
p++;
3632
}
3633
if (*p != '>') {
3634
while (*p != '\0') {
3635
p++;
3636
if (*p == '>') {
3637
interp->result = "extra characters after detail in binding";
3638
return 0;
3639
}
3640
}
3641
interp->result = "missing \">\" in binding";
3642
return 0;
3643
}
3644
p++;
3645
3646
end:
3647
*eventStringPtr = p;
3648
*eventMaskPtr |= eventMask;
3649
return count;
3650
}
3651
3652
/*
3653
*----------------------------------------------------------------------
3654
*
3655
* GetField --
3656
*
3657
* Used to parse pattern descriptions. Copies up to
3658
* size characters from p to copy, stopping at end of
3659
* string, space, "-", ">", or whenever size is
3660
* exceeded.
3661
*
3662
* Results:
3663
* The return value is a pointer to the character just
3664
* after the last one copied (usually "-" or space or
3665
* ">", but could be anything if size was exceeded).
3666
* Also places NULL-terminated string (up to size
3667
* character, including NULL), at copy.
3668
*
3669
* Side effects:
3670
* None.
3671
*
3672
*----------------------------------------------------------------------
3673
*/
3674
3675
static char *
3676
GetField(p, copy, size)
3677
char *p; /* Pointer to part of pattern. */
3678
char *copy; /* Place to copy field. */
3679
int size; /* Maximum number of characters to
3680
* copy. */
3681
{
3682
while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
3683
&& (*p != '-') && (size > 1)) {
3684
*copy = *p;
3685
p++;
3686
copy++;
3687
size--;
3688
}
3689
*copy = '\0';
3690
return p;
3691
}
3692
3693
/*
3694
*---------------------------------------------------------------------------
3695
*
3696
* GetPatternString --
3697
*
3698
* Produce a string version of the given event, for displaying to
3699
* the user.
3700
*
3701
* Results:
3702
* The string is left in dsPtr.
3703
*
3704
* Side effects:
3705
* It is the caller's responsibility to initialize the DString before
3706
* and to free it after calling this procedure.
3707
*
3708
*---------------------------------------------------------------------------
3709
*/
3710
static void
3711
GetPatternString(psPtr, dsPtr)
3712
PatSeq *psPtr;
3713
Tcl_DString *dsPtr;
3714
{
3715
Pattern *patPtr;
3716
char c, buffer[10];
3717
int patsLeft, needMods;
3718
ModInfo *modPtr;
3719
EventInfo *eiPtr;
3720
3721
/*
3722
* The order of the patterns in the sequence is backwards from the order
3723
* in which they must be output.
3724
*/
3725
3726
for (patsLeft = psPtr->numPats, patPtr = &psPtr->pats[psPtr->numPats - 1];
3727
patsLeft > 0; patsLeft--, patPtr--) {
3728
3729
/*
3730
* Check for simple case of an ASCII character.
3731
*/
3732
3733
if ((patPtr->eventType == KeyPress)
3734
&& ((psPtr->flags & PAT_NEARBY) == 0)
3735
&& (patPtr->needMods == 0)
3736
&& (patPtr->detail.keySym < 128)
3737
&& isprint(UCHAR(patPtr->detail.keySym))
3738
&& (patPtr->detail.keySym != '<')
3739
&& (patPtr->detail.keySym != ' ')) {
3740
3741
c = (char) patPtr->detail.keySym;
3742
Tcl_DStringAppend(dsPtr, &c, 1);
3743
continue;
3744
}
3745
3746
/*
3747
* Check for virtual event.
3748
*/
3749
3750
if (patPtr->eventType == VirtualEvent) {
3751
Tcl_DStringAppend(dsPtr, "<<", 2);
3752
Tcl_DStringAppend(dsPtr, patPtr->detail.name, -1);
3753
Tcl_DStringAppend(dsPtr, ">>", 2);
3754
continue;
3755
}
3756
3757
/*
3758
* It's a more general event specification. First check
3759
* for "Double" or "Triple", then modifiers, then event type,
3760
* then keysym or button detail.
3761
*/
3762
3763
Tcl_DStringAppend(dsPtr, "<", 1);
3764
if ((psPtr->flags & PAT_NEARBY) && (patsLeft > 1)
3765
&& (memcmp((char *) patPtr, (char *) (patPtr-1),
3766
sizeof(Pattern)) == 0)) {
3767
patsLeft--;
3768
patPtr--;
3769
if ((patsLeft > 1) && (memcmp((char *) patPtr,
3770
(char *) (patPtr-1), sizeof(Pattern)) == 0)) {
3771
patsLeft--;
3772
patPtr--;
3773
Tcl_DStringAppend(dsPtr, "Triple-", 7);
3774
} else {
3775
Tcl_DStringAppend(dsPtr, "Double-", 7);
3776
}
3777
}
3778
for (needMods = patPtr->needMods, modPtr = modArray;
3779
needMods != 0; modPtr++) {
3780
if (modPtr->mask & needMods) {
3781
needMods &= ~modPtr->mask;
3782
Tcl_DStringAppend(dsPtr, modPtr->name, -1);
3783
Tcl_DStringAppend(dsPtr, "-", 1);
3784
}
3785
}
3786
for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
3787
if (eiPtr->type == patPtr->eventType) {
3788
Tcl_DStringAppend(dsPtr, eiPtr->name, -1);
3789
if (patPtr->detail.clientData != 0) {
3790
Tcl_DStringAppend(dsPtr, "-", 1);
3791
}
3792
break;
3793
}
3794
}
3795
3796
if (patPtr->detail.clientData != 0) {
3797
if ((patPtr->eventType == KeyPress)
3798
|| (patPtr->eventType == KeyRelease)) {
3799
char *string;
3800
3801
string = TkKeysymToString(patPtr->detail.keySym);
3802
if (string != NULL) {
3803
Tcl_DStringAppend(dsPtr, string, -1);
3804
}
3805
} else {
3806
sprintf(buffer, "%d", patPtr->detail.button);
3807
Tcl_DStringAppend(dsPtr, buffer, -1);
3808
}
3809
}
3810
Tcl_DStringAppend(dsPtr, ">", 1);
3811
}
3812
}
3813
3814
/*
3815
*----------------------------------------------------------------------
3816
*
3817
* GetKeySym --
3818
*
3819
* Given an X KeyPress or KeyRelease event, map the
3820
* keycode in the event into a KeySym.
3821
*
3822
* Results:
3823
* The return value is the KeySym corresponding to
3824
* eventPtr, or NoSymbol if no matching Keysym could be
3825
* found.
3826
*
3827
* Side effects:
3828
* In the first call for a given display, keycode-to-
3829
* KeySym maps get loaded.
3830
*
3831
*----------------------------------------------------------------------
3832
*/
3833
3834
static KeySym
3835
GetKeySym(dispPtr, eventPtr)
3836
TkDisplay *dispPtr; /* Display in which to
3837
* map keycode. */
3838
XEvent *eventPtr; /* Description of X event. */
3839
{
3840
KeySym sym;
3841
int index;
3842
3843
/*
3844
* Refresh the mapping information if it's stale
3845
*/
3846
3847
if (dispPtr->bindInfoStale) {
3848
InitKeymapInfo(dispPtr);
3849
}
3850
3851
/*
3852
* Figure out which of the four slots in the keymap vector to
3853
* use for this key. Refer to Xlib documentation for more info
3854
* on how this computation works.
3855
*/
3856
3857
index = 0;
3858
if (eventPtr->xkey.state & dispPtr->modeModMask) {
3859
index = 2;
3860
}
3861
if ((eventPtr->xkey.state & ShiftMask)
3862
|| ((dispPtr->lockUsage != LU_IGNORE)
3863
&& (eventPtr->xkey.state & LockMask))) {
3864
index += 1;
3865
}
3866
sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index);
3867
3868
/*
3869
* Special handling: if the key was shifted because of Lock, but
3870
* lock is only caps lock, not shift lock, and the shifted keysym
3871
* isn't upper-case alphabetic, then switch back to the unshifted
3872
* keysym.
3873
*/
3874
3875
if ((index & 1) && !(eventPtr->xkey.state & ShiftMask)
3876
&& (dispPtr->lockUsage == LU_CAPS)) {
3877
if (!(((sym >= XK_A) && (sym <= XK_Z))
3878
|| ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
3879
|| ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) {
3880
index &= ~1;
3881
sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
3882
index);
3883
}
3884
}
3885
3886
/*
3887
* Another bit of special handling: if this is a shifted key and there
3888
* is no keysym defined, then use the keysym for the unshifted key.
3889
*/
3890
3891
if ((index & 1) && (sym == NoSymbol)) {
3892
sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
3893
index & ~1);
3894
}
3895
return sym;
3896
}
3897
3898
/*
3899
*--------------------------------------------------------------
3900
*
3901
* InitKeymapInfo --
3902
*
3903
* This procedure is invoked to scan keymap information
3904
* to recompute stuff that's important for binding, such
3905
* as the modifier key (if any) that corresponds to "mode
3906
* switch".
3907
*
3908
* Results:
3909
* None.
3910
*
3911
* Side effects:
3912
* Keymap-related information in dispPtr is updated.
3913
*
3914
*--------------------------------------------------------------
3915
*/
3916
3917
static void
3918
InitKeymapInfo(dispPtr)
3919
TkDisplay *dispPtr; /* Display for which to recompute keymap
3920
* information. */
3921
{
3922
XModifierKeymap *modMapPtr;
3923
KeyCode *codePtr;
3924
KeySym keysym;
3925
int count, i, j, max, arraySize;
3926
#define KEYCODE_ARRAY_SIZE 20
3927
3928
dispPtr->bindInfoStale = 0;
3929
modMapPtr = XGetModifierMapping(dispPtr->display);
3930
3931
/*
3932
* Check the keycodes associated with the Lock modifier. If
3933
* any of them is associated with the XK_Shift_Lock modifier,
3934
* then Lock has to be interpreted as Shift Lock, not Caps Lock.
3935
*/
3936
3937
dispPtr->lockUsage = LU_IGNORE;
3938
codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;
3939
for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {
3940
if (*codePtr == 0) {
3941
continue;
3942
}
3943
keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
3944
if (keysym == XK_Shift_Lock) {
3945
dispPtr->lockUsage = LU_SHIFT;
3946
break;
3947
}
3948
if (keysym == XK_Caps_Lock) {
3949
dispPtr->lockUsage = LU_CAPS;
3950
break;
3951
}
3952
}
3953
3954
/*
3955
* Look through the keycodes associated with modifiers to see if
3956
* the the "mode switch", "meta", or "alt" keysyms are associated
3957
* with any modifiers. If so, remember their modifier mask bits.
3958
*/
3959
3960
dispPtr->modeModMask = 0;
3961
dispPtr->metaModMask = 0;
3962
dispPtr->altModMask = 0;
3963
codePtr = modMapPtr->modifiermap;
3964
max = 8*modMapPtr->max_keypermod;
3965
for (i = 0; i < max; i++, codePtr++) {
3966
if (*codePtr == 0) {
3967
continue;
3968
}
3969
keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
3970
if (keysym == XK_Mode_switch) {
3971
dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
3972
}
3973
if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
3974
dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
3975
}
3976
if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
3977
dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
3978
}
3979
}
3980
3981
/*
3982
* Create an array of the keycodes for all modifier keys.
3983
*/
3984
3985
if (dispPtr->modKeyCodes != NULL) {
3986
ckfree((char *) dispPtr->modKeyCodes);
3987
}
3988
dispPtr->numModKeyCodes = 0;
3989
arraySize = KEYCODE_ARRAY_SIZE;
3990
dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)
3991
(KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));
3992
for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {
3993
if (*codePtr == 0) {
3994
continue;
3995
}
3996
3997
/*
3998
* Make sure that the keycode isn't already in the array.
3999
*/
4000
4001
for (j = 0; j < dispPtr->numModKeyCodes; j++) {
4002
if (dispPtr->modKeyCodes[j] == *codePtr) {
4003
goto nextModCode;
4004
}
4005
}
4006
if (dispPtr->numModKeyCodes >= arraySize) {
4007
KeyCode *new;
4008
4009
/*
4010
* Ran out of space in the array; grow it.
4011
*/
4012
4013
arraySize *= 2;
4014
new = (KeyCode *) ckalloc((unsigned)
4015
(arraySize * sizeof(KeyCode)));
4016
memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,
4017
(dispPtr->numModKeyCodes * sizeof(KeyCode)));
4018
ckfree((char *) dispPtr->modKeyCodes);
4019
dispPtr->modKeyCodes = new;
4020
}
4021
dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;
4022
dispPtr->numModKeyCodes++;
4023
nextModCode: continue;
4024
}
4025
XFreeModifiermap(modMapPtr);
4026
}
4027
4028
4029
/*
4030
*----------------------------------------------------------------------
4031
*
4032
* TkStringToKeysym --
4033
*
4034
* This procedure finds the keysym associated with a given keysym
4035
* name.
4036
*
4037
* Results:
4038
* The return value is the keysym that corresponds to name, or
4039
* NoSymbol if there is no such keysym.
4040
*
4041
* Side effects:
4042
* None.
4043
*
4044
*----------------------------------------------------------------------
4045
*/
4046
4047
KeySym
4048
TkStringToKeysym(name)
4049
char *name; /* Name of a keysym. */
4050
{
4051
#ifdef REDO_KEYSYM_LOOKUP
4052
Tcl_HashEntry *hPtr;
4053
KeySym keysym;
4054
4055
hPtr = Tcl_FindHashEntry(&keySymTable, name);
4056
if (hPtr != NULL) {
4057
return (KeySym) Tcl_GetHashValue(hPtr);
4058
}
4059
if (strlen(name) == 1) {
4060
keysym = (KeySym) (unsigned char) name[0];
4061
if (TkKeysymToString(keysym) != NULL) {
4062
return keysym;
4063
}
4064
}
4065
#endif /* REDO_KEYSYM_LOOKUP */
4066
return XStringToKeysym(name);
4067
}
4068
4069
/*
4070
*----------------------------------------------------------------------
4071
*
4072
* TkKeysymToString --
4073
*
4074
* This procedure finds the keysym name associated with a given
4075
* keysym.
4076
*
4077
* Results:
4078
* The return value is a pointer to a static string containing
4079
* the name of the given keysym, or NULL if there is no known name.
4080
*
4081
* Side effects:
4082
* None.
4083
*
4084
*----------------------------------------------------------------------
4085
*/
4086
4087
char *
4088
TkKeysymToString(keysym)
4089
KeySym keysym;
4090
{
4091
#ifdef REDO_KEYSYM_LOOKUP
4092
Tcl_HashEntry *hPtr;
4093
4094
hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym);
4095
if (hPtr != NULL) {
4096
return (char *) Tcl_GetHashValue(hPtr);
4097
}
4098
#endif /* REDO_KEYSYM_LOOKUP */
4099
return XKeysymToString(keysym);
4100
}
4101
4102
/*
4103
*----------------------------------------------------------------------
4104
*
4105
* TkCopyAndGlobalEval --
4106
*
4107
* This procedure makes a copy of a script then calls Tcl_GlobalEval
4108
* to evaluate it. It's used in situations where the execution of
4109
* a command may cause the original command string to be reallocated.
4110
*
4111
* Results:
4112
* Returns the result of evaluating script, including both a standard
4113
* Tcl completion code and a string in interp->result.
4114
*
4115
* Side effects:
4116
* None.
4117
*
4118
*----------------------------------------------------------------------
4119
*/
4120
4121
int
4122
TkCopyAndGlobalEval(interp, script)
4123
Tcl_Interp *interp; /* Interpreter in which to evaluate
4124
* script. */
4125
char *script; /* Script to evaluate. */
4126
{
4127
Tcl_DString buffer;
4128
int code;
4129
4130
Tcl_DStringInit(&buffer);
4131
Tcl_DStringAppend(&buffer, script, -1);
4132
code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
4133
Tcl_DStringFree(&buffer);
4134
return code;
4135
}
4136
4137
4138
4139