diff options
36 files changed, 5199 insertions, 2314 deletions
@@ -633,8 +633,9 @@ several matching sequences is more specific: an event pattern that specifies a specific button or key is more specific than one that does not; .IP (b) -a longer sequence (in terms of number -of events matched) is more specific than a shorter sequence; +a sequence with the most highest-ordered patterns (in term of highest +repetition count) is more specific than a sequence with less +highest-ordered patterns; .IP (c) if the modifiers specified in one pattern are a subset of the modifiers in another pattern, then the pattern with more modifiers diff --git a/doc/event.n b/doc/event.n index 5109794..9ab48e5 100644 --- a/doc/event.n +++ b/doc/event.n @@ -239,7 +239,8 @@ Similar to \fB%S\fR substitution for binding scripts. .TP \fB\-time\fI integer\fR \fIInteger\fR must be an integer value; it specifies the \fItime\fR field -for the event. +for the event. Additonally the special value \fBcurrent\fR is allowed, +this value will be substituted by the current event time. Valid for \fBKeyPress\fR, \fBKeyRelease\fR, \fBButtonPress\fR, \fBButtonRelease\fR, \fBEnter\fR, \fBLeave\fR, \fBMotion\fR, and \fBProperty\fR events. @@ -457,7 +457,7 @@ widths: .PP .CS button .b \-text "Foo" -entry .e \-variable foo +entry .e \-textvariable foo ; set foo "Hello World!" label .l \-text "This is a fairly long piece of text" \fBgrid\fR .b .e .l \-sticky ew diff --git a/doc/ttk_progressbar.n b/doc/ttk_progressbar.n index b0d5615..df469b6 100644 --- a/doc/ttk_progressbar.n +++ b/doc/ttk_progressbar.n @@ -57,9 +57,11 @@ In \fIindeterminate\fR mode, it is interpreted modulo \fB\-maximum\fR; that is, the progress bar completes one .QW cycle when the \fB\-value\fR increases by \fB\-maximum\fR. +If \fB\-variable\fR is set to an existing variable, specifying \fB\-value\fR +has no effect (the variable value takes precedence). .OP \-variable variable Variable The name of a global Tcl variable which is linked to the \fB\-value\fR. -If specified, the \fB\-value\fR of the progress bar is +If specified to an existing variable, the \fB\-value\fR of the progress bar is automatically set to the value of the variable whenever the latter is modified. .SH "WIDGET COMMAND" diff --git a/doc/ttk_scale.n b/doc/ttk_scale.n index f8f5072..aa851b9 100644 --- a/doc/ttk_scale.n +++ b/doc/ttk_scale.n @@ -42,6 +42,8 @@ Specifies a real value corresponding to the right or bottom end of the scale. This value may be either less than or greater than the \fB\-from\fR option. .OP \-value value Value Specifies the current floating-point value of the variable. +If \fB\-variable\fR is set to an existing variable, specifying \fB\-value\fR +has no effect (the variable value takes precedence). .OP \-variable variable Variable Specifies the name of a global variable to link to the scale. Whenever the value of the variable changes, the scale will update to reflect this value. diff --git a/generic/tkArray.h b/generic/tkArray.h new file mode 100644 index 0000000..65693fe --- /dev/null +++ b/generic/tkArray.h @@ -0,0 +1,610 @@ +/* + * tkArray.h -- + * + * An array is a sequence of items, stored in a contiguous memory region. + * Random access to any item is very fast. New items can be either appended + * or prepended. An array may be traversed in the forward or backward direction. + * + * Copyright (c) 2018-2019 by Gregor Cramer. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +/* + * Note that this file will not be included in header files, it is the purpose + * of this file to be included in source files only. Thus we are not using the + * prefix "Tk_" here for functions, because all the functions have private scope. + */ + +/* + * ------------------------------------------------------------------------------- + * Use the array in the following way: + * ------------------------------------------------------------------------------- + * typedef struct { int key, value; } Pair; + * TK_PTR_ARRAY_DEFINE(MyArray, Pair); + * MyArray *arr = NULL; + * if (MyArray_IsEmpty(arr)) { + * MyArray_Append(&arr, MakePair(1, 2)); + * MyArray_Append(&arr, MakePair(2, 3)); + * for (i = 0; i < MyArray_Size(arr); ++i) { + * Pair *p = MyArray_Get(arr, i); + * printf("%d -> %d\n", p->key, p->value); + * ckfree(p); + * } + * MyArray_Free(&arr); + * assert(arr == NULL); + * } + * ------------------------------------------------------------------------------- + * Or with aggregated elements: + * ------------------------------------------------------------------------------- + * typedef struct { int key, value; } Pair; + * TK_ARRAY_DEFINE(MyArray, Pair); + * Pair p1 = { 1, 2 }; + * Pair p2 = { 2, 3 }; + * MyArray *arr = NULL; + * if (MyArray_IsEmpty(arr)) { + * MyArray_Append(&arr, p1); + * MyArray_Append(&arr, p2); + * for (i = 0; i < MyArray_Size(arr); ++i) { + * const Pair *p = MyArray_Get(arr, i); + * printf("%d -> %d\n", p->key, p->value); + * } + * MyArray_Free(&arr); + * assert(arr == NULL); + * } + * ------------------------------------------------------------------------------- + */ + +/*************************************************************************/ +/* + * Two array types will be provided: + * Use TK_ARRAY_DEFINE if your array is aggregating the elements. Use + * TK_PTR_ARRAY_DEFINE if your array contains pointers to elements. But + * in latter case the array is not responsible for the lifetime of the + * elements. + */ +/*************************************************************************/ +/* + * Array_ElemSize: Returns the memory size for one array element. + */ +/*************************************************************************/ +/* + * Array_BufferSize: Returns the memory size for given number of elements. + */ +/*************************************************************************/ +/* + * Array_IsEmpty: Array is empty? + */ +/*************************************************************************/ +/* + * Array_Size: Number of elements in array. + */ +/*************************************************************************/ +/* + * Array_Capacity: Capacity of given array. This is the maximal number of + * elements fitting into current array memory without resizing the buffer. + */ +/*************************************************************************/ +/* + * Array_SetSize: Set array size, new size must not exceed the capacity of + * the array. This function has to be used with care when increasing the + * array size. + */ +/*************************************************************************/ +/* + * Array_First: Returns position of first element in array. Given array + * may be NULL. + */ +/*************************************************************************/ +/* + * Array_Last: Returns position after last element in array. Given array + * may be empty. + */ +/*************************************************************************/ +/* + * Array_Front: Returns first element in array. Given array must not be + * empty. + */ +/*************************************************************************/ +/* + * Array_Back: Returns last element in array. Given array must not be + * empty. + */ +/*************************************************************************/ +/* + * Array_Resize: Resize buffer of array for given number of elements. The + * array may grow or shrink. Note that this function is not initializing + * the increased buffer. + */ +/*************************************************************************/ +/* + * Array_ResizeAndClear: Resize buffer of array for given number of + * elements. The array may grow or shrink. The increased memory will be + * filled with zeroes. + */ +/*************************************************************************/ +/* + * Array_Clear: Fill specified range with zeroes. + */ +/*************************************************************************/ +/* + * Array_Free: Resize array to size zero. This function will release the + * array buffer. + */ +/*************************************************************************/ +/* + * Array_Append: Insert given element after end of array. + */ +/*************************************************************************/ +/* + * Array_PopBack: Shrink array by one element. Given array must not be + * empty. + */ +/*************************************************************************/ +/* + * Array_Get: Random access to array element at given position. The given + * index must not exceed current array size. + */ +/*************************************************************************/ +/* + * Array_Set: Replace array element at given position with new value. The + * given index must not exceed current array size. + */ +/*************************************************************************/ +/* + * Array_Find: Return index position of element which matches given + * argument. If not found then -1 will be returned. + */ +/*************************************************************************/ + +#ifndef TK_ARRAY_DEFINED +#define TK_ARRAY_DEFINED + +#include "tkInt.h" + +#if defined(__GNUC__) || defined(__clang__) +# define __TK_ARRAY_UNUSED __attribute__((unused)) +#else +# define __TK_ARRAY_UNUSED +#endif + +#define TK_ARRAY_DEFINE(AT, ElemType) /* AT = type of array */ \ +/* ------------------------------------------------------------------------- */ \ +typedef struct AT { \ + size_t size; \ + size_t capacity; \ + ElemType buf[1]; \ +} AT; \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Init(AT *arr) \ +{ \ + assert(arr); \ + arr->size = 0; \ + arr->capacity = 0; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_ElemSize() \ +{ \ + return sizeof(ElemType); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_BufferSize(size_t numElems) \ +{ \ + return numElems*sizeof(ElemType); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_IsEmpty(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return !arr || arr->size == 0u; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_Size(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->size : 0u; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_Capacity(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->capacity : 0u; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_First(AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->buf : NULL; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Last(AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->buf + arr->size : NULL; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Front(AT *arr) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(!AT##_IsEmpty(arr)); \ + return &arr->buf[0]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Back(AT *arr) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(!AT##_IsEmpty(arr)); \ + return &arr->buf[arr->size - 1]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Resize(AT **arrp, size_t newSize) \ +{ \ + assert(arrp); \ + assert(!*arrp || (*arrp)->size != 0xdeadbeef); \ + if (newSize == 0) { \ + assert(!*arrp || ((*arrp)->size = 0xdeadbeef)); \ + ckfree(*arrp); \ + *arrp = NULL; \ + } else { \ + int init = *arrp == NULL; \ + size_t memSize = AT##_BufferSize(newSize - 1) + sizeof(AT); \ + *arrp = ckrealloc(*arrp, memSize); \ + if (init) { \ + (*arrp)->size = 0; \ + } else if (newSize < (*arrp)->size) { \ + (*arrp)->size = newSize; \ + } \ + (*arrp)->capacity = newSize; \ + } \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Clear(AT *arr, size_t from, size_t to) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(to <= AT##_Capacity(arr)); \ + assert(from <= to); \ + memset(arr->buf + from, 0, AT##_BufferSize(to - from)); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_ResizeAndClear(AT **arrp, size_t newSize) \ +{ \ + size_t oldCapacity; \ + assert(arrp); \ + oldCapacity = *arrp ? (*arrp)->capacity : 0; \ + AT##_Resize(arrp, newSize); \ + if (newSize > oldCapacity) { \ + AT##_Clear(*arrp, oldCapacity, newSize); \ + } \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_SetSize(AT *arr, size_t newSize) \ +{ \ + assert(newSize <= AT##_Capacity(arr)); \ + assert(!arr || arr->size != 0xdeadbeef); \ + if (arr) { \ + arr->size = newSize; \ + } \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Append(AT **arrp, ElemType *elem) \ +{ \ + assert(arrp); \ + if (!*arrp) { \ + AT##_Resize(arrp, 1); \ + } else if ((*arrp)->size == (*arrp)->capacity) { \ + AT##_Resize(arrp, (*arrp)->capacity + ((*arrp)->capacity + 1)/2); \ + } \ + (*arrp)->buf[(*arrp)->size++] = *elem; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_PopBack(AT *arr) \ +{ \ + assert(!AT##_IsEmpty(arr)); \ + return arr->size -= 1; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Get(const AT *arr, size_t at) \ +{ \ + assert(arr); \ + assert(at < AT##_Size(arr)); \ + return (ElemType *) &arr->buf[at]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Set(AT *arr, size_t at, ElemType *elem) \ +{ \ + assert(arr); \ + assert(at < AT##_Size(arr)); \ + arr->buf[at] = *elem; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Free(AT **arrp) \ +{ \ + AT##_Resize(arrp, 0); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_Find(const AT *arr, const ElemType *elem) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + if (arr) { \ + const ElemType *buf = arr->buf; \ + size_t i; \ + for (i = 0; i < arr->size; ++i) { \ + if (memcmp(&buf[i], elem, sizeof(ElemType)) == 0) { \ + return (int) i; \ + } \ + } \ + } \ + return -1; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_Contains(const AT *arr, const ElemType *elem) \ +{ \ + return AT##_Find(arr, elem) != -1; \ +} \ +/* ------------------------------------------------------------------------- */ + +#define TK_PTR_ARRAY_DEFINE(AT, ElemType) /* AT = type of array */ \ +/* ------------------------------------------------------------------------- */ \ +typedef struct AT { \ + size_t size; \ + size_t capacity; \ + ElemType *buf[1]; \ +} AT; \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_ElemSize() \ +{ \ + return sizeof(ElemType); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_BufferSize(size_t numElems) \ +{ \ + return numElems*sizeof(ElemType *); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_IsEmpty(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return !arr || arr->size == 0; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType ** \ +AT##_First(AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->buf : NULL; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType ** \ +AT##_Last(AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->buf + arr->size : NULL; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Front(AT *arr) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(!AT##_IsEmpty(arr)); \ + return arr->buf[0]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Back(AT *arr) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(!AT##_IsEmpty(arr)); \ + return arr->buf[arr->size - 1]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_Size(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->size : 0; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_Capacity(const AT *arr) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + return arr ? arr->capacity : 0; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Resize(AT **arrp, size_t newCapacity) \ +{ \ + assert(arrp); \ + assert(!*arrp || (*arrp)->size != 0xdeadbeef); \ + if (newCapacity == 0) { \ + assert(!*arrp || ((*arrp)->size = 0xdeadbeef)); \ + ckfree(*arrp); \ + *arrp = NULL; \ + } else { \ + int init = *arrp == NULL; \ + size_t memSize = AT##_BufferSize(newCapacity - 1) + sizeof(AT); \ + *arrp = ckrealloc(*arrp, memSize); \ + if (init) { \ + (*arrp)->size = 0; \ + } else if (newCapacity < (*arrp)->size) { \ + (*arrp)->size = newCapacity; \ + } \ + (*arrp)->capacity = newCapacity; \ + } \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Clear(AT *arr, size_t from, size_t to) \ +{ \ + assert(arr); \ + assert(arr->size != 0xdeadbeef); \ + assert(to <= AT##_Capacity(arr)); \ + assert(from <= to); \ + memset(arr->buf + from, 0, AT##_BufferSize(to - from)); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_ResizeAndClear(AT **arrp, size_t newCapacity) \ +{ \ + size_t oldCapacity; \ + assert(arrp); \ + oldCapacity = *arrp ? (*arrp)->capacity : 0; \ + AT##_Resize(arrp, newCapacity); \ + if (newCapacity > oldCapacity) { \ + AT##_Clear(*arrp, oldCapacity, newCapacity); \ + } \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_SetSize(AT *arr, size_t newSize) \ +{ \ + assert(arr); \ + assert(newSize <= AT##_Capacity(arr)); \ + arr->size = newSize; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Append(AT **arrp, ElemType *elem) \ +{ \ + assert(arrp); \ + if (!*arrp) { \ + AT##_Resize(arrp, 1); \ + } else if ((*arrp)->size == (*arrp)->capacity) { \ + AT##_Resize(arrp, (*arrp)->capacity + ((*arrp)->capacity + 1)/2); \ + } \ + (*arrp)->buf[(*arrp)->size++] = elem; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static size_t \ +AT##_PopBack(AT *arr) \ +{ \ + assert(!AT##_IsEmpty(arr)); \ + return arr->size -= 1; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static ElemType * \ +AT##_Get(const AT *arr, size_t at) \ +{ \ + assert(arr); \ + assert(at < AT##_Size(arr)); \ + return arr->buf[at]; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Set(AT *arr, size_t at, ElemType *elem) \ +{ \ + assert(arr); \ + assert(at < AT##_Size(arr)); \ + arr->buf[at] = elem; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static void \ +AT##_Free(AT **arrp) \ +{ \ + AT##_Resize(arrp, 0); \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_Find(const AT *arr, const ElemType *elem) \ +{ \ + assert(!arr || arr->size != 0xdeadbeef); \ + if (arr) { \ + ElemType *const *buf = arr->buf; \ + size_t i; \ + for (i = 0; i < arr->size; ++i) { \ + if (buf[i] == elem) { \ + return (int) i; \ + } \ + } \ + } \ + return -1; \ +} \ + \ +__TK_ARRAY_UNUSED \ +static int \ +AT##_Contains(const AT *arr, const ElemType *elem) \ +{ \ + return AT##_Find(arr, elem) != -1; \ +} \ +/* ------------------------------------------------------------------------- */ + +#endif /* TK_ARRAY_DEFINED */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 105 + * End: + * vi:set ts=8 sw=4: + */ diff --git a/generic/tkBind.c b/generic/tkBind.c index 45e07be..a533dde 100644 --- a/generic/tkBind.c +++ b/generic/tkBind.c @@ -7,21 +7,40 @@ * Copyright (c) 1989-1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * Copyright (c) 1998 by Scriptics Corporation. + * Copyright (c) 2018-2019 by Gregor Cramer. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkInt.h" +#include "tkDList.h" +#include "tkArray.h" -#ifdef _WIN32 +#if defined(_WIN32) #include "tkWinInt.h" #elif defined(MAC_OSX_TK) #include "tkMacOSXInt.h" -#else +#else /* if defined(__unix__) */ #include "tkUnixInt.h" #endif +#if NDEBUG +# define DEBUG(expr) +#else +# define DEBUG(expr) expr +#endif + +#ifdef _MSC_VER +/* + * Earlier versions of MSVC don't know snprintf, but _snprintf is compatible. + * Note that sprintf is deprecated. + */ +# define snprintf _snprintf +#endif + +#define SIZE_OF_ARRAY(arr) (sizeof(arr)/sizeof(arr[0])) + /* * File structure: * @@ -29,8 +48,7 @@ * * Init/Free this package. * - * Tcl "bind" command (actually located in tkCmds.c) core implementation, plus - * helpers. + * Tcl "bind" command (actually located in tkCmds.c) core implementation, plus helpers. * * Tcl "event" command implementation, plus helpers. * @@ -40,21 +58,129 @@ */ /* + * In old implementation (the one that used an event ring), <Double-1> and <1><1> were + * equivalent sequences. However it is logical to give <Double-1> higher precedence. + * This is achieved by setting PREFER_MOST_SPECIALIZED_EVENT to 1. + */ + +#ifndef PREFER_MOST_SPECIALIZED_EVENT +# define PREFER_MOST_SPECIALIZED_EVENT 1 +#endif + +/* + * Traditionally motion events can be combined with buttons in this way: <B1-B2-Motion>. + * However it should be allowed to express this as <Motion-1-2> in addition. This is achieved + * by setting SUPPORT_ADDITIONAL_MOTION_SYNTAX to 1. + */ + +#ifndef SUPPORT_ADDITIONAL_MOTION_SYNTAX +# define SUPPORT_ADDITIONAL_MOTION_SYNTAX 1 +#endif + +/* + * The output for motion events is of the type <B1-Motion>. This can be changed to become + * <Motion-1> instead by setting PRINT_SHORT_MOTION_SYNTAX to 1, however this would be a + * backwards incompatibility. + */ + +#ifndef PRINT_SHORT_MOTION_SYNTAX +# define PRINT_SHORT_MOTION_SYNTAX 0 /* set to 1 if wanted */ +#endif + +#if !SUPPORT_ADDITIONAL_MOTION_SYNTAX +# undef PRINT_SHORT_MOTION_SYNTAX +# define PRINT_SHORT_MOTION_SYNTAX 0 +#endif + +/* + * For debugging only, normally set to zero. + */ + +#ifdef SUPPORT_DEBUGGING +# undef SUPPORT_DEBUGGING +#endif +#define SUPPORT_DEBUGGING 0 + +/* + * Test validity of PSEntry items. + */ + +# define TEST_PSENTRY(psPtr) psPtr->number != 0xdeadbeef +# define MARK_PSENTRY(psPtr) psPtr->number = 0xdeadbeef + +/* * The following union is used to hold the detail information from an XEvent * (including Tk's XVirtualEvent extension). */ +typedef KeySym Info; + typedef union { - KeySym keySym; /* KeySym that corresponds to xkey.keycode. */ - int button; /* Button that was pressed (xbutton.button). */ - Tk_Uid name; /* Tk_Uid of virtual event. */ - ClientData clientData; /* Used when type of Detail is unknown, and to - * ensure that all bytes of Detail are - * initialized when this structure is used in - * a hash key. */ + Info info; /* This either corresponds to xkey.keycode, or to xbutton.button, + * or is meaningless, depending on event type. */ + Tk_Uid name; /* Tk_Uid of virtual event. */ } Detail; /* + * We need an extended event definition. + */ + +typedef struct { + XEvent xev; /* The original event from server. */ + Detail detail; /* Additional information (for hashing). */ + unsigned countAny; /* Count of multi-events, like multi-clicks, or repeated key pressing, + * this count does not depend on detail (keySym or button). */ + unsigned countDetailed; + /* Count of multi-events, like multi-clicks, or repeated key pressing, + * this count considers the detail (keySym or button). */ +} Event; + +/* + * We need a structure providing a list of pattern sequences. + */ + +typedef unsigned EventMask; +typedef unsigned long ModMask; + +struct PatSeq; /* forward declaration */ + +/* We need this array for bookkeeping the last matching modifier mask per pattern. */ +TK_ARRAY_DEFINE(PSModMaskArr, ModMask); + +typedef struct PSEntry { + TK_DLIST_LINKS(PSEntry); /* Makes this struct a double linked list; must be first entry. */ + Window window; /* Window of last match. */ + struct PatSeq* psPtr; /* Pointer to pattern sequence. */ + PSModMaskArr *lastModMaskArr; + /* Last matching modifier mask per pattern (except last pattern). + * Only needed if pattern sequence is not single (more than one + * pattern), and if one of these patterns contains a non-zero + * modifier mask. */ + unsigned count; /* Only promote to next level if this count has reached count of + * pattern. */ + unsigned expired:1; /* Whether this entry is expired, this means it has to be removed + * from promotion list. */ + unsigned keepIt:1; /* Whether to keep this entry, even if expired. */ +} PSEntry; + +/* Defining the whole PSList_* stuff (list of PSEntry items). */ +TK_DLIST_DEFINE(PSList, PSEntry); + +/* Don't keep larger arrays of modifier masks inside PSEntry. */ +#define MAX_MOD_MASK_ARR_SIZE 8 + +/* + * Maps and lookup tables from an event to a list of patterns that match that event. + */ + +typedef struct { + Tcl_HashTable patternTable; /* Keys are PatternTableKey structs, values are (PatSeq *). */ + Tcl_HashTable listTable; /* Keys are PatternTableKey structs, values are (PSList *). */ + PSList entryPool; /* Contains free (unused) list items. */ + unsigned number; /* Needed for enumeration of pattern sequences. */ +} LookupTables; + +/* * The structure below represents a binding table. A binding table represents * a domain in which event bindings may occur. It includes a space of objects * relative to which events occur (usually windows, but not always), a history @@ -65,49 +191,21 @@ typedef union { * application with separate event bindings for each (for example, each canvas * widget has a separate binding table for associating events with the items * in the canvas). - * - * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much below 30. - * To see this, consider a triple mouse button click while the Shift key is - * down (and auto-repeating). There may be as many as 3 auto-repeat events - * after each mouse button press or release (see the first large comment block - * within Tk_BindEvent for more on this), for a total of 20 events to cover - * the three button presses and two intervening releases. If you reduce - * EVENT_BUFFER_SIZE too much, shift multi-clicks will be lost. */ -/* - * NOTE: The changes which were needed to make Tk work on OSX 10.14 (Mojave) - * also demand that the event ring be a bit bigger. It might be wise to - * augment the current double-click pattern matching by adding a new - * DoubleClick modifier bit which could be set based on the clickCount of the - * Apple NSEvent object. - */ - -#ifdef MAC_OSX_TK - #define EVENT_BUFFER_SIZE 90 -#else - #define EVENT_BUFFER_SIZE 30 -#endif +/* defining the whole Promotion_* stuff (array of PSList entries) */ +TK_ARRAY_DEFINE(PromArr, PSList); typedef struct Tk_BindingTable_ { - XEvent eventRing[EVENT_BUFFER_SIZE]; - /* Circular queue of recent events (higher - * indices are for more recent events). */ - Detail detailRing[EVENT_BUFFER_SIZE]; - /* "Detail" information (keySym, button, - * Tk_Uid, or 0) for each entry in - * eventRing. */ - int curEvent; /* Index in eventRing of most recent event. - * Newer events have higher indices. */ - Tcl_HashTable patternTable; /* Used to map from an event to a list of - * patterns that may match that event. Keys - * are PatternTableKey structs, values are - * (PatSeq *). */ - Tcl_HashTable objectTable; /* Used to map from an object to a list of - * patterns associated with that object. Keys - * are ClientData, values are (PatSeq *). */ - Tcl_Interp *interp; /* Interpreter in which commands are - * executed. */ + Event eventInfo[TK_LASTEVENT]; + /* Containing the most recent event for every event type. */ + PromArr *promArr; /* Contains the promoted pattern sequences. */ + Event *curEvent; /* Pointing to most recent event. */ + ModMask curModMask; /* Containing the current modifier mask. */ + LookupTables lookupTables; /* Containing hash tables for fast lookup. */ + Tcl_HashTable objectTable; /* Used to map from an object to a list of patterns associated with + * that object. Keys are ClientData, values are (PatSeq *). */ + Tcl_Interp *interp; /* Interpreter in which commands are executed. */ } BindingTable; /* @@ -124,15 +222,10 @@ typedef struct Tk_BindingTable_ { */ typedef struct { - Tcl_HashTable patternTable; /* Used to map from a physical event to a list - * of patterns that may match that event. Keys - * are PatternTableKey structs, values are - * (PatSeq *). */ - Tcl_HashTable nameTable; /* Used to map a virtual event name to the - * array of physical events that can trigger - * it. Keys are the Tk_Uid names of the - * virtual events, values are PhysicalsOwned - * structs. */ + LookupTables lookupTables; /* Providing fast lookup tables to lists of pattern sequences. */ + Tcl_HashTable nameTable; /* Used to map a virtual event name to the array of physical events + * that can trigger it. Keys are the Tk_Uid names of the virtual + * events, values are PhysOwned structs. */ } VirtualEventTable; /* @@ -151,38 +244,45 @@ typedef struct { */ typedef struct { - ClientData object; /* For binding table, identifies the binding - * tag of the object (or class of objects) - * relative to which the event occurred. For - * virtual event table, always NULL. */ - int type; /* Type of event (from X). */ - Detail detail; /* Additional information, such as keysym, - * button, Tk_Uid, or 0 if nothing - * additional. */ + ClientData object; /* For binding table, identifies the binding tag of the object + * (or class of objects) relative to which the event occurred. + * For virtual event table, always NULL. */ + unsigned type; /* Type of event (from X). */ + Detail detail; /* Additional information, such as keysym, button, Tk_Uid, or zero + * if nothing additional. */ } PatternTableKey; /* * The following structure defines a pattern, which is matched against X * events as part of the process of converting X events into Tcl commands. + * + * For technical reasons we do not use 'union Detail', although this would + * be possible, instead 'info' and 'name' are both included. */ typedef struct { - int eventType; /* Type of X event, e.g. ButtonPress. */ - int needMods; /* Mask of modifiers that must be present (0 - * means no modifiers are required). */ - Detail detail; /* Additional information that must match - * event. Normally this is 0, meaning no - * additional information must match. For - * KeyPress and KeyRelease events, a keySym - * may be specified to select a particular - * keystroke (0 means any keystrokes). For - * button events, specifies a particular - * button (0 means any buttons are OK). For - * virtual events, specifies the Tk_Uid of the - * virtual event name (never 0). */ + unsigned eventType; /* Type of X event, e.g. ButtonPress. */ + unsigned count; /* Multi-event count, e.g. double-clicks, triple-clicks, etc. */ + ModMask modMask; /* Mask of modifiers that must be present (zero means no modifiers + * are required). */ + Info info; /* Additional information that must match event. Normally this is zero, + * meaning no additional information must match. For KeyPress and + * KeyRelease events, it may be specified to select a particular + * keystroke (zero means any keystrokes). For button events, specifies + * a particular button (zero means any buttons are OK). */ + Tk_Uid name; /* Specifies the Tk_Uid of the virtual event name. NULL if not a + * virtual event. */ } TkPattern; /* + * The following structure keeps track of all the virtual events that are + * associated with a particular physical event. It is pointed to by the 'owners' + * field in a PatSeq in the patternTable of a virtual event table. + */ + +TK_PTR_ARRAY_DEFINE(VirtOwners, Tcl_HashEntry); /* define array of hash entries */ + +/* * The following structure defines a pattern sequence, which consists of one * or more patterns. In order to trigger, a pattern sequence must match the * most recent X events (first pattern to most recent event, next pattern to @@ -201,78 +301,63 @@ typedef struct { */ typedef struct PatSeq { - int numPats; /* Number of patterns in sequence (usually - * 1). */ - char *script; /* Binding script to evaluate when sequence - * matches (ckalloc()ed) */ - int flags; /* Miscellaneous flag values; see below for - * definitions. */ - struct PatSeq *nextSeqPtr; /* Next in list of all pattern sequences that - * have the same initial pattern. NULL means - * end of list. */ - Tcl_HashEntry *hPtr; /* Pointer to hash table entry for the initial - * pattern. This is the head of the list of - * which nextSeqPtr forms a part. */ - struct VirtualOwners *voPtr;/* In a binding table, always NULL. In a - * virtual event table, identifies the array - * of virtual events that can be triggered by - * this event. */ - struct PatSeq *nextObjPtr; /* In a binding table, next in list of all - * pattern sequences for the same object (NULL - * for end of list). Needed to implement - * Tk_DeleteAllBindings. In a virtual event - * table, always NULL. */ - TkPattern pats[1]; /* Array of "numPats" patterns. Only one - * element is declared here but in actuality - * enough space will be allocated for - * "numPats" patterns. To match, pats[0] must - * match event n, pats[1] must match event - * n-1, etc. */ + unsigned numPats; /* Number of patterns in sequence (usually 1). */ + unsigned count; /* Total number of repetition counts, summed over count in TkPattern. */ + unsigned number; /* Needed for the decision whether a binding is less recently defined + * than another, it is guaranteed that the most recently bound event + * has the highest number. */ + unsigned added:1; /* Is this pattern sequence already added to lookup table? */ + unsigned modMaskUsed:1; /* Does at least one pattern contain a non-zero modifier mask? */ + DEBUG(unsigned owned:1); /* For debugging purposes. */ + char *script; /* Binding script to evaluate when sequence matches (ckalloc()ed) */ + Tcl_Obj* object; /* Token for object with which binding is associated. For virtual + * event table this is NULL. */ + struct PatSeq *nextSeqPtr; /* Next in list of all pattern sequences that have the same initial + * pattern. NULL means end of list. */ + Tcl_HashEntry *hPtr; /* Pointer to hash table entry for the initial pattern. This is the + * head of the list of which nextSeqPtr forms a part. */ + union { + VirtOwners *owners; /* In a binding table it has no meaning. In a virtual event table, + * identifies the array of virtual events that can be triggered + * by this event. */ + struct PatSeq *nextObj; /* In a binding table, next in list of all pattern sequences for + * the same object (NULL for end of list). Needed to implement + * Tk_DeleteAllBindings. In a virtual event table it has no meaning. */ + } ptr; + TkPattern pats[1]; /* Array of "numPats" patterns. Only one element is declared here + * but in actuality enough space will be allocated for "numPats" + * patterns (but usually 1). */ } PatSeq; /* - * Flag values for PatSeq structures: - * - * PAT_NEARBY 1 means that all of the events matching this sequence - * must occur with nearby X and Y mouse coordinates and - * close in time. This is typically used to restrict - * multiple button presses. + * Compute memory size of struct PatSeq with given pattern size. + * The caller must be sure that pattern size is greater than zero. */ - -#define PAT_NEARBY 0x1 +#define PATSEQ_MEMSIZE(numPats) (sizeof(PatSeq) + (numPats - 1)*sizeof(TkPattern)) /* * Constants that define how close together two events must be in milliseconds * or pixels to meet the PAT_NEARBY constraint: */ -#define NEARBY_PIXELS 5 -#define NEARBY_MS 500 +#define NEARBY_PIXELS 5 +#define NEARBY_MS 500 /* - * The following structure keeps track of all the virtual events that are - * associated with a particular physical event. It is pointed to by the voPtr - * field in a PatSeq in the patternTable of a virtual event table. + * Needed as "no-number" constant for integers. The value of this constant is + * outside of integer range (type "int"). (Unfortunatly current version of + * Tcl/Tk does not provide C99 integer support.) */ -typedef struct VirtualOwners { - int numOwners; /* Number of virtual events to trigger. */ - Tcl_HashEntry *owners[1]; /* Array of pointers to entries in nameTable. - * Enough space will actually be allocated for - * numOwners hash entries. */ -} VirtualOwners; +#define NO_NUMBER (((Tcl_WideInt) (~ (unsigned) 0)) + 1) /* * The following structure is used in the nameTable of a virtual event table * to associate a virtual event with all the physical events that can trigger * it. */ -typedef struct { - int numOwned; /* Number of physical events owned. */ - PatSeq *patSeqs[1]; /* Array of pointers to physical event - * patterns. Enough space will actually be - * allocated to hold numOwned. */ -} PhysicalsOwned; + +TK_PTR_ARRAY_DEFINE(PhysOwned, PatSeq); /* define array of pattern seqs */ /* * One of the following structures exists for each interpreter. This structure @@ -282,11 +367,9 @@ typedef struct { */ typedef struct { - TkDisplay *curDispPtr; /* Display for last binding command invoked in - * this application. */ + TkDisplay *curDispPtr; /* Display for last binding command invoked in this application. */ int curScreenIndex; /* Index of screen for last binding command */ - int bindingDepth; /* Number of active instances of Tk_BindEvent - * in this application. */ + unsigned bindingDepth; /* Number of active instances of Tk_BindEvent in this application. */ } ScreenInfo; /* @@ -296,13 +379,13 @@ typedef struct { typedef struct TkBindInfo_ { VirtualEventTable virtualEventTable; - /* The virtual events that exist in this - * interpreter. */ - ScreenInfo screenInfo; /* Keeps track of the current display and - * screen, so it can be restored after a - * binding has executed. */ - int deleted; /* 1 the application has been deleted but the - * structure has been preserved. */ + /* The virtual events that exist in this interpreter. */ + ScreenInfo screenInfo; /* Keeps track of the current display and screen, so it can be + * restored after a binding has executed. */ + int deleted; /* 1 if the application has been deleted but the structure has been + * preserved. */ + Time lastEventTime; /* Needed for time measurement. */ + Time lastCurrentTime; /* Needed for time measurement. */ } BindInfo; /* @@ -317,7 +400,7 @@ typedef struct TkBindInfo_ { #ifdef REDO_KEYSYM_LOOKUP typedef struct { - const char *name; /* Name of keysym. */ + const char *name; /* Name of keysym. */ KeySym value; /* Numeric identifier for keysym. */ } KeySymInfo; static const KeySymInfo keyArray[] = { @@ -326,19 +409,11 @@ static const KeySymInfo keyArray[] = { #endif {NULL, 0} }; -static Tcl_HashTable keySymTable; /* keyArray hashed by keysym value. */ -static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */ +static Tcl_HashTable keySymTable; /* keyArray hashed by keysym value. */ +static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */ #endif /* REDO_KEYSYM_LOOKUP */ /* - * Set to non-zero when the package-wide static variables have been - * initialized. - */ - -static int initialized = 0; -TCL_DECLARE_MUTEX(bindMutex) - -/* * A hash table is kept to map from the string names of event modifiers to * information about those modifiers. The structure for storing this * information, and the hash table built at initialization time, are defined @@ -346,29 +421,24 @@ TCL_DECLARE_MUTEX(bindMutex) */ typedef struct { - const char *name; /* Name of modifier. */ - int mask; /* Button/modifier mask value, such as - * Button1Mask. */ - int flags; /* Various flags; see below for - * definitions. */ + const char *name; /* Name of modifier. */ + ModMask mask; /* Button/modifier mask value, such as Button1Mask. */ + unsigned flags; /* Various flags; see below for definitions. */ } ModInfo; /* * Flags for ModInfo structures: * - * DOUBLE - Non-zero means duplicate this event, - * e.g. for double-clicks. - * TRIPLE - Non-zero means triplicate this event, - * e.g. for triple-clicks. - * QUADRUPLE - Non-zero means quadruple this event, - * e.g. for 4-fold-clicks. + * DOUBLE - Non-zero means duplicate this event, e.g. for double-clicks. + * TRIPLE - Non-zero means triplicate this event, e.g. for triple-clicks. + * QUADRUPLE - Non-zero means quadruple this event, e.g. for 4-fold-clicks. * MULT_CLICKS - Combination of all of above. */ -#define DOUBLE 1 -#define TRIPLE 2 -#define QUADRUPLE 4 -#define MULT_CLICKS 7 +#define DOUBLE (1<<0) +#define TRIPLE (1<<1) +#define QUADRUPLE (1<<2) +#define MULT_CLICKS (DOUBLE|TRIPLE|QUADRUPLE) static const ModInfo modArray[] = { {"Control", ControlMask, 0}, @@ -424,9 +494,8 @@ static Tcl_HashTable modTable; typedef struct { const char *name; /* Name of event. */ - int type; /* Event type for X, such as ButtonPress. */ - int eventMask; /* Mask bits (for XSelectInput) for this event - * type. */ + unsigned type; /* Event type for X, such as ButtonPress. */ + unsigned eventMask; /* Mask bits (for XSelectInput) for this event type. */ } EventInfo; /* @@ -442,10 +511,8 @@ static const EventInfo eventArray[] = { {"KeyRelease", KeyRelease, KeyPressMask|KeyReleaseMask}, {"Button", ButtonPress, ButtonPressMask}, {"ButtonPress", ButtonPress, ButtonPressMask}, - {"ButtonRelease", ButtonRelease, - ButtonPressMask|ButtonReleaseMask}, - {"Motion", MotionNotify, - ButtonPressMask|PointerMotionMask}, + {"ButtonRelease", ButtonRelease, ButtonPressMask|ButtonReleaseMask}, + {"Motion", MotionNotify, ButtonPressMask|PointerMotionMask}, {"Enter", EnterNotify, EnterWindowMask}, {"Leave", LeaveNotify, LeaveWindowMask}, {"FocusIn", FocusIn, FocusChangeMask}, @@ -473,6 +540,8 @@ static const EventInfo eventArray[] = { }; static Tcl_HashTable eventTable; +static int eventArrayIndex[TK_LASTEVENT]; + /* * The defines and table below are used to classify events into various * groups. The reason for this is that logically identical fields (e.g. @@ -481,29 +550,29 @@ static Tcl_HashTable eventTable; * information from events. */ -#define KEY 0x1 -#define BUTTON 0x2 -#define MOTION 0x4 -#define CROSSING 0x8 -#define FOCUS 0x10 -#define EXPOSE 0x20 -#define VISIBILITY 0x40 -#define CREATE 0x80 -#define DESTROY 0x100 -#define UNMAP 0x200 -#define MAP 0x400 -#define REPARENT 0x800 -#define CONFIG 0x1000 -#define GRAVITY 0x2000 -#define CIRC 0x4000 -#define PROP 0x8000 -#define COLORMAP 0x10000 -#define VIRTUAL 0x20000 -#define ACTIVATE 0x40000 -#define MAPREQ 0x80000 -#define CONFIGREQ 0x100000 -#define RESIZEREQ 0x200000 -#define CIRCREQ 0x400000 +#define KEY (1<<0) +#define BUTTON (1<<1) +#define MOTION (1<<2) +#define CROSSING (1<<3) +#define FOCUS (1<<4) +#define EXPOSE (1<<5) +#define VISIBILITY (1<<6) +#define CREATE (1<<7) +#define DESTROY (1<<8) +#define UNMAP (1<<9) +#define MAP (1<<10) +#define REPARENT (1<<11) +#define CONFIG (1<<12) +#define GRAVITY (1<<13) +#define CIRC (1<<14) +#define PROP (1<<15) +#define COLORMAP (1<<16) +#define VIRTUAL (1<<17) +#define ACTIVATE (1<<18) +#define MAPREQ (1<<19) +#define CONFIGREQ (1<<20) +#define RESIZEREQ (1<<21) +#define CIRCREQ (1<<22) #define KEY_BUTTON_MOTION_VIRTUAL (KEY|BUTTON|MOTION|VIRTUAL) #define KEY_BUTTON_MOTION_CROSSING (KEY|BUTTON|MOTION|VIRTUAL|CROSSING) @@ -556,11 +625,11 @@ static const int flagArray[TK_LASTEVENT] = { */ static const TkStateMap queuePosition[] = { - {-1, "now"}, - {TCL_QUEUE_HEAD, "head"}, - {TCL_QUEUE_MARK, "mark"}, - {TCL_QUEUE_TAIL, "tail"}, - {-2, NULL} + {-1, "now"}, + {TCL_QUEUE_HEAD, "head"}, + {TCL_QUEUE_MARK, "mark"}, + {TCL_QUEUE_TAIL, "tail"}, + {-2, NULL} }; /* @@ -591,8 +660,8 @@ static const TkStateMap notifyDetail[] = { }; static const TkStateMap circPlace[] = { - {PlaceOnTop, "PlaceOnTop"}, - {PlaceOnBottom, "PlaceOnBottom"}, + {PlaceOnTop, "PlaceOnTop"}, + {PlaceOnBottom, "PlaceOnBottom"}, {-1, NULL} }; @@ -604,12 +673,12 @@ static const TkStateMap visNotify[] = { }; static const TkStateMap configureRequestDetail[] = { - {None, "None"}, - {Above, "Above"}, - {Below, "Below"}, - {BottomIf, "BottomIf"}, - {TopIf, "TopIf"}, - {Opposite, "Opposite"}, + {None, "None"}, + {Above, "Above"}, + {Below, "Below"}, + {BottomIf, "BottomIf"}, + {TopIf, "TopIf"}, + {Opposite, "Opposite"}, {-1, NULL} }; @@ -619,47 +688,522 @@ static const TkStateMap propNotify[] = { {-1, NULL} }; +DEBUG(static int countTableItems = 0); +DEBUG(static int countEntryItems = 0); +DEBUG(static int countListItems = 0); +DEBUG(static int countBindItems = 0); +DEBUG(static int countSeqItems = 0); + /* * Prototypes for local functions defined in this file: */ -static void ChangeScreen(Tcl_Interp *interp, char *dispName, - int screenIndex); -static int CreateVirtualEvent(Tcl_Interp *interp, - VirtualEventTable *vetPtr, char *virtString, - const char *eventString); -static int DeleteVirtualEvent(Tcl_Interp *interp, - VirtualEventTable *vetPtr, char *virtString, - const char *eventString); +static void ChangeScreen(Tcl_Interp *interp, char *dispName, int screenIndex); +static int CreateVirtualEvent(Tcl_Interp *interp, VirtualEventTable *vetPtr, + char *virtString, const char *eventString); +static int DeleteVirtualEvent(Tcl_Interp *interp, VirtualEventTable *vetPtr, + char *virtString, const char *eventString); static void DeleteVirtualEventTable(VirtualEventTable *vetPtr); -static void ExpandPercents(TkWindow *winPtr, const char *before, - XEvent *eventPtr,KeySym keySym, - unsigned int scriptCount, Tcl_DString *dsPtr); -static PatSeq * FindSequence(Tcl_Interp *interp, - Tcl_HashTable *patternTablePtr, ClientData object, - const char *eventString, int create, - int allowVirtual, unsigned long *maskPtr); -static void GetAllVirtualEvents(Tcl_Interp *interp, - VirtualEventTable *vetPtr); -static char * GetField(char *p, char *copy, int size); -static Tcl_Obj * GetPatternObj(PatSeq *psPtr); -static int GetVirtualEvent(Tcl_Interp *interp, - VirtualEventTable *vetPtr, Tcl_Obj *virtName); -static Tk_Uid GetVirtualEventUid(Tcl_Interp *interp, - char *virtString); +static void ExpandPercents(TkWindow *winPtr, const char *before, Event *eventPtr, + unsigned scriptCount, Tcl_DString *dsPtr); +static PatSeq * FindSequence(Tcl_Interp *interp, LookupTables *lookupTables, + ClientData object, const char *eventString, int create, + int allowVirtual, EventMask *maskPtr); +static void GetAllVirtualEvents(Tcl_Interp *interp, VirtualEventTable *vetPtr); +static const char * GetField(const char *p, char *copy, unsigned size); +static Tcl_Obj * GetPatternObj(const PatSeq *psPtr); +static int GetVirtualEvent(Tcl_Interp *interp, VirtualEventTable *vetPtr, + Tcl_Obj *virtName); +static Tk_Uid GetVirtualEventUid(Tcl_Interp *interp, char *virtString); static int HandleEventGenerate(Tcl_Interp *interp, Tk_Window main, int objc, Tcl_Obj *const objv[]); static void InitVirtualEventTable(VirtualEventTable *vetPtr); -static PatSeq * MatchPatterns(TkDisplay *dispPtr, - BindingTable *bindPtr, PatSeq *psPtr, - PatSeq *bestPtr, ClientData *objectPtr, - PatSeq **sourcePtrPtr); +static PatSeq * MatchPatterns(TkDisplay *dispPtr, Tk_BindingTable bindPtr, PSList *psList, + PSList *psSuccList, unsigned patIndex, const Event *eventPtr, + ClientData object, PatSeq **physPtrPtr); static int NameToWindow(Tcl_Interp *interp, Tk_Window main, Tcl_Obj *objPtr, Tk_Window *tkwinPtr); -static int ParseEventDescription(Tcl_Interp *interp, - const char **eventStringPtr, TkPattern *patPtr, - unsigned long *eventMaskPtr); +static unsigned ParseEventDescription(Tcl_Interp *interp, const char **eventStringPtr, + TkPattern *patPtr, EventMask *eventMaskPtr); static void DoWarp(ClientData clientData); +static PSList * GetLookupForEvent(LookupTables* lookupPtr, const Event *eventPtr, + Tcl_Obj *object, int onlyConsiderDetailedEvents); +static void ClearLookupTable(LookupTables *lookupTables, ClientData object); +static void ClearPromotionLists(Tk_BindingTable bindPtr, ClientData object); +static PSEntry * MakeListEntry(PSList *pool, PatSeq *psPtr, int needModMasks); +static void RemovePatSeqFromLookup(LookupTables *lookupTables, PatSeq *psPtr); +static void RemovePatSeqFromPromotionLists(Tk_BindingTable bindPtr, PatSeq *psPtr); +static PatSeq * DeletePatSeq(PatSeq *psPtr); +static void InsertPatSeq(LookupTables *lookupTables, PatSeq *psPtr); +#if SUPPORT_DEBUGGING +void TkpDumpPS(const PatSeq *psPtr); +void TkpDumpPSList(const PSList *psList); +#endif + +/* + * Some useful helper functions. + */ +#ifdef SUPPORT_DEBUGGING +static int BindCount = 0; +#endif + +static unsigned Max(unsigned a, unsigned b) { return a < b ? b : a; } +static int Abs(int n) { return n < 0 ? -n : n; } +static int IsOdd(int n) { return n & 1; } + +static int TestNearbyTime(int lhs, int rhs) { return Abs(lhs - rhs) <= NEARBY_MS; } +static int TestNearbyCoords(int lhs, int rhs) { return Abs(lhs - rhs) <= NEARBY_PIXELS; } + +static int +IsSubsetOf( + ModMask lhsMask, /* this is a subset */ + ModMask rhsMask) /* of this bit field? */ +{ + return (lhsMask & rhsMask) == lhsMask; +} + +static const char* +SkipSpaces( + const char* s) +{ + assert(s); + while (isspace(UCHAR(*s))) + ++s; + return s; +} + +static const char* +SkipFieldDelims( + const char* s) +{ + assert(s); + while (*s == '-' || isspace(UCHAR(*s))) { + ++s; + } + return s; +} + +static unsigned +GetButtonNumber( + const char *field) +{ + int button; + assert(field); + button = (field[0] >= '1' && field[0] <= '9' && field[1] == '\0') ? field[0] - '0' : 0; + return (button > 5) ? (button + 4) : button; +} + +static Time +CurrentTimeInMilliSecs() +{ + Tcl_Time now; + Tcl_GetTime(&now); + return ((Time) now.sec)*1000 + ((Time) now.usec)/1000; +} + +static Info +GetInfo( + const PatSeq *psPtr, + unsigned index) +{ + assert(psPtr); + assert(index < psPtr->numPats); + + return psPtr->pats[index].info; +} + +static unsigned +GetCount( + const PatSeq *psPtr, + unsigned index) +{ + assert(psPtr); + assert(index < psPtr->numPats); + + return psPtr->pats[index].count; +} + +static int +CountSpecialized( + const PatSeq *fstMatchPtr, + const PatSeq *sndMatchPtr) +{ + int fstCount = 0; + int sndCount = 0; + unsigned i; + + assert(fstMatchPtr); + assert(sndMatchPtr); + + for (i = 0; i < fstMatchPtr->numPats; ++i) { + if (GetInfo(fstMatchPtr, i)) { fstCount += GetCount(fstMatchPtr, i); } + } + for (i = 0; i < sndMatchPtr->numPats; ++i) { + if (GetInfo(sndMatchPtr, i)) { sndCount += GetCount(sndMatchPtr, i); } + } + + return sndCount - fstCount; +} + +static int +MatchEventNearby( + const XEvent *lhs, /* previous button event */ + const XEvent *rhs) /* current button event */ +{ + assert(lhs); + assert(rhs); + assert(lhs->type == ButtonPress || lhs->type == ButtonRelease); + assert(lhs->type == rhs->type); + + /* assert: lhs->xbutton.time <= rhs->xbutton.time */ + + return TestNearbyTime(rhs->xbutton.time, lhs->xbutton.time) + && TestNearbyCoords(rhs->xbutton.x_root, lhs->xbutton.x_root) + && TestNearbyCoords(rhs->xbutton.y_root, lhs->xbutton.y_root); +} + +static int +MatchEventRepeat( + const XEvent *lhs, /* previous key event */ + const XEvent *rhs) /* current key event */ +{ + assert(lhs); + assert(rhs); + assert(lhs->type == KeyPress || lhs->type == KeyRelease); + assert(lhs->type == rhs->type); + + /* assert: lhs->xkey.time <= rhs->xkey.time */ + return TestNearbyTime(rhs->xkey.time, lhs->xkey.time); +} + +static void +FreePatSeq( + PatSeq *psPtr) +{ + assert(psPtr); + assert(!psPtr->owned); + DEBUG(MARK_PSENTRY(psPtr)); + ckfree(psPtr->script); + if (!psPtr->object) { + VirtOwners_Free(&psPtr->ptr.owners); + } + ckfree(psPtr); + DEBUG(countSeqItems -= 1); +} + +static void +RemoveListEntry( + PSList *pool, + PSEntry *psEntry) +{ + assert(pool); + assert(psEntry); + + if (PSModMaskArr_Capacity(psEntry->lastModMaskArr) > MAX_MOD_MASK_ARR_SIZE) { + PSModMaskArr_Free(&psEntry->lastModMaskArr); + } + PSList_Remove(psEntry); + PSList_Append(pool, psEntry); +} + +static void +ClearList( + PSList *psList, + PSList *pool, + ClientData object) +{ + assert(psList); + assert(pool); + + if (object) { + PSEntry *psEntry; + PSEntry *psNext; + + for (psEntry = PSList_First(psList); psEntry; psEntry = psNext) { + psNext = PSList_Next(psEntry); + if (psEntry->psPtr->object == object) { + RemoveListEntry(pool, psEntry); + } + } + } else { + PSList_Move(pool, psList); + } +} + +static PSEntry * +FreePatSeqEntry( + PSList *pool, + PSEntry *entry) +{ + PSEntry *next = PSList_Next(entry); + PSModMaskArr_Free(&entry->lastModMaskArr); + ckfree(entry); + return next; +} + +static unsigned +ResolveModifiers( + TkDisplay *dispPtr, + unsigned modMask) +{ + assert(dispPtr); + + if (dispPtr->metaModMask) { + if (modMask & META_MASK) { + modMask &= ~(ModMask)META_MASK; + modMask |= dispPtr->metaModMask; + } + } + if (dispPtr->altModMask) { + if (modMask & ALT_MASK) { + modMask &= ~(ModMask)ALT_MASK; + modMask |= dispPtr->altModMask; + } + } + + return modMask; +} + +static int +ButtonNumberFromState( + ModMask state) +{ + if (!(state & ALL_BUTTONS)) { return 0; } + if (state & Button1Mask) { return 1; } + if (state & Button2Mask) { return 2; } + if (state & Button3Mask) { return 3; } + if (state & Button4Mask) { return 4; } + if (state & Button5Mask) { return 5; } + if (state & Button6Mask) { return 6; } + if (state & Button7Mask) { return 7; } + if (state & Button8Mask) { return 8; } + return 9; +} + +static void +SetupPatternKey( + PatternTableKey *key, + const PatSeq *psPtr) +{ + const TkPattern *patPtr; + + assert(key); + assert(psPtr); + + /* otherwise on some systems the key contains uninitialized bytes */ + memset(key, 0, sizeof(PatternTableKey)); + + patPtr = psPtr->pats; + assert(!patPtr->info || !patPtr->name); + + key->object = psPtr->object; + key->type = patPtr->eventType; + if (patPtr->info) { + key->detail.info = patPtr->info; + } else { + key->detail.name = patPtr->name; + } +} + +/* + *-------------------------------------------------------------- + * + * MakeListEntry -- + * + * Makes new entry item for lookup table. We are using a + * pool of items, this avoids superfluous memory allocation/ + * deallocation. + * + * Results: + * New entry item. + * + * Side effects: + * Memory allocated. + * + *-------------------------------------------------------------- + */ + +static PSEntry * +MakeListEntry( + PSList *pool, + PatSeq *psPtr, + int needModMasks) +{ + PSEntry *newEntry = NULL; + + assert(pool); + assert(psPtr); + assert(psPtr->numPats > 0); + assert(TEST_PSENTRY(psPtr)); + + if (PSList_IsEmpty(pool)) { + newEntry = ckalloc(sizeof(PSEntry)); + newEntry->lastModMaskArr = NULL; + DEBUG(countEntryItems += 1); + } else { + newEntry = PSList_First(pool); + PSList_RemoveHead(pool); + } + + if (!needModMasks) { + PSModMaskArr_SetSize(newEntry->lastModMaskArr, 0); + } else { + if (PSModMaskArr_Capacity(newEntry->lastModMaskArr) < psPtr->numPats - 1) { + PSModMaskArr_Resize(&newEntry->lastModMaskArr, psPtr->numPats - 1); + } + PSModMaskArr_SetSize(newEntry->lastModMaskArr, psPtr->numPats - 1); + } + + newEntry->psPtr = psPtr; + newEntry->window = None; + newEntry->expired = 0; + newEntry->keepIt = 1; + newEntry->count = 1; + DEBUG(psPtr->owned = 0); + + return newEntry; +} + +/* + *-------------------------------------------------------------- + * + * GetLookupForEvent -- + * + * Get specific pattern sequence table for given event. + * + * Results: + * Specific pattern sequence table for given event. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static PSList * +GetLookupForEvent( + LookupTables* lookupTables, + const Event *eventPtr, + Tcl_Obj *object, + int onlyConsiderDetailedEvents) +{ + PatternTableKey key; + Tcl_HashEntry *hPtr; + + assert(lookupTables); + assert(eventPtr); + + /* otherwise on some systems the key contains uninitialized bytes */ + memset(&key, 0, sizeof(PatternTableKey)); + + if (onlyConsiderDetailedEvents) { + switch (eventPtr->xev.type) { + case ButtonPress: /* fallthru */ + case ButtonRelease: key.detail.info = eventPtr->xev.xbutton.button; break; + case MotionNotify: key.detail.info = ButtonNumberFromState(eventPtr->xev.xmotion.state); break; + case KeyPress: /* fallthru */ + case KeyRelease: key.detail.info = eventPtr->detail.info; break; + case VirtualEvent: key.detail.name = eventPtr->detail.name; break; + } + if (!key.detail.name) { + assert(!key.detail.info); + return NULL; + } + } + + key.object = object; + key.type = eventPtr->xev.type; + hPtr = Tcl_FindHashEntry(&lookupTables->listTable, (char *) &key); + return hPtr ? Tcl_GetHashValue(hPtr) : NULL; +} + +/* + *-------------------------------------------------------------- + * + * ClearLookupTable -- + * + * Clear lookup table in given binding table, but only those + * bindings associated to given object. If object is NULL + * then remove all entries. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +static void +ClearLookupTable( + LookupTables *lookupTables, + ClientData object) +{ + Tcl_HashSearch search; + Tcl_HashEntry *hPtr; + Tcl_HashEntry *nextPtr; + PSList *pool = &lookupTables->entryPool; + + assert(lookupTables); + + for (hPtr = Tcl_FirstHashEntry(&lookupTables->listTable, &search); hPtr; hPtr = nextPtr) { + PSList *psList; + + nextPtr = Tcl_NextHashEntry(&search); + + if (object) { + const PatternTableKey *key = Tcl_GetHashKey(&lookupTables->listTable, hPtr); + if (key->object != object) { + continue; + } + Tcl_DeleteHashEntry(hPtr); + } + + psList = Tcl_GetHashValue(hPtr); + PSList_Move(pool, psList); + ckfree(psList); + DEBUG(countListItems -= 1); + } +} + +/* + *-------------------------------------------------------------- + * + * ClearPromotionLists -- + * + * Clear all the lists holding the promoted pattern + * sequences, which belongs to given object. If object + * is NULL then remove all patterns. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static void +ClearPromotionLists( + Tk_BindingTable bindPtr, + ClientData object) +{ + unsigned newArraySize = 0; + unsigned i; + + assert(bindPtr); + + for (i = 0; i < PromArr_Size(bindPtr->promArr); ++i) { + PSList *psList = PromArr_Get(bindPtr->promArr, i); + ClearList(psList, &bindPtr->lookupTables.entryPool, object); + if (!PSList_IsEmpty(psList)) { + newArraySize = i + 1; + } + } + + PromArr_SetSize(bindPtr->promArr, newArraySize); +} /* *--------------------------------------------------------------------------- @@ -679,15 +1223,96 @@ static void DoWarp(ClientData clientData); *--------------------------------------------------------------------------- */ +/* + * Windoze compiler does not allow the definition of these static variables inside a function, + * otherwise this should belong to function TkBindInit(). + */ +TCL_DECLARE_MUTEX(bindMutex); +static int initialized = 0; + void TkBindInit( TkMainInfo *mainPtr) /* The newly created application. */ { BindInfo *bindInfoPtr; - if (sizeof(XEvent) < sizeof(XVirtualEvent)) { - Tcl_Panic("TkBindInit: virtual events can't be supported"); - } + assert(mainPtr); + + /* otherwise virtual events can't be supported */ + assert(sizeof(XEvent) >= sizeof(XVirtualEvent)); + + /* type of TkPattern.info is well defined? */ + assert(sizeof(Info) >= sizeof(KeySym)); + assert(sizeof(Info) >= sizeof(unsigned)); + + /* ensure that our matching algorithm is working (when testing detail) */ + assert(sizeof(Detail) == sizeof(Tk_Uid)); + + /* test that constant NO_NUMBER is indeed out of integer range */ + assert(sizeof(NO_NUMBER) > sizeof(int)); + assert(((int) NO_NUMBER) == 0 && NO_NUMBER != 0); + + /* test expected indices of Button1..Button5, otherwise our button handling is not working */ + assert(Button1 == 1 && Button2 == 2 && Button3 == 3 && Button4 == 4 && Button5 == 5); + assert(Button2Mask == (Button1Mask << 1)); + assert(Button3Mask == (Button1Mask << 2)); + assert(Button4Mask == (Button1Mask << 3)); + assert(Button5Mask == (Button1Mask << 4)); + + /* test expected values of button motion masks, otherwise our button handling is not working */ + assert(Button1MotionMask == Button1Mask); + assert(Button2MotionMask == Button2Mask); + assert(Button3MotionMask == Button3Mask); + assert(Button4MotionMask == Button4Mask); + assert(Button5MotionMask == Button5Mask); + + /* because we expect zero if keySym is empty */ + assert(NoSymbol == 0L); + + /* this must be a union, not a struct, otherwise comparison with NULL will not work */ + assert(offsetof(Detail, name) == offsetof(Detail, info)); + + /* we use some constraints about X*Event */ + assert(offsetof(XButtonEvent, time) == offsetof(XMotionEvent, time)); + assert(offsetof(XButtonEvent, x_root) == offsetof(XMotionEvent, x_root)); + assert(offsetof(XButtonEvent, y_root) == offsetof(XMotionEvent, y_root)); + assert(offsetof(XCreateWindowEvent, border_width) == offsetof(XConfigureEvent, border_width)); + assert(offsetof(XCreateWindowEvent, width) == offsetof(XConfigureEvent, width)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XCirculateRequestEvent, window)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XConfigureEvent, window)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XGravityEvent, window)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XMapEvent, window)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XReparentEvent, window)); + assert(offsetof(XCreateWindowEvent, window) == offsetof(XUnmapEvent, window)); + assert(offsetof(XCreateWindowEvent, x) == offsetof(XConfigureEvent, x)); + assert(offsetof(XCreateWindowEvent, x) == offsetof(XGravityEvent, x)); + assert(offsetof(XCreateWindowEvent, y) == offsetof(XConfigureEvent, y)); + assert(offsetof(XCreateWindowEvent, y) == offsetof(XGravityEvent, y)); + assert(offsetof(XCrossingEvent, time) == offsetof(XEnterWindowEvent, time)); + assert(offsetof(XCrossingEvent, time) == offsetof(XLeaveWindowEvent, time)); + assert(offsetof(XCrossingEvent, time) == offsetof(XKeyEvent, time)); + assert(offsetof(XKeyEvent, root) == offsetof(XButtonEvent, root)); + assert(offsetof(XKeyEvent, root) == offsetof(XCrossingEvent, root)); + assert(offsetof(XKeyEvent, root) == offsetof(XMotionEvent, root)); + assert(offsetof(XKeyEvent, state) == offsetof(XButtonEvent, state)); + assert(offsetof(XKeyEvent, state) == offsetof(XMotionEvent, state)); + assert(offsetof(XKeyEvent, subwindow) == offsetof(XButtonEvent, subwindow)); + assert(offsetof(XKeyEvent, subwindow) == offsetof(XCrossingEvent, subwindow)); + assert(offsetof(XKeyEvent, subwindow) == offsetof(XMotionEvent, subwindow)); + assert(offsetof(XKeyEvent, time) == offsetof(XButtonEvent, time)); + assert(offsetof(XKeyEvent, time) == offsetof(XMotionEvent, time)); + assert(offsetof(XKeyEvent, x) == offsetof(XButtonEvent, x)); + assert(offsetof(XKeyEvent, x) == offsetof(XCrossingEvent, x)); + assert(offsetof(XKeyEvent, x) == offsetof(XMotionEvent, x)); + assert(offsetof(XKeyEvent, x_root) == offsetof(XButtonEvent, x_root)); + assert(offsetof(XKeyEvent, x_root) == offsetof(XCrossingEvent, x_root)); + assert(offsetof(XKeyEvent, x_root) == offsetof(XMotionEvent, x_root)); + assert(offsetof(XKeyEvent, y) == offsetof(XButtonEvent, y)); + assert(offsetof(XKeyEvent, y) == offsetof(XCrossingEvent, y)); + assert(offsetof(XKeyEvent, y) == offsetof(XMotionEvent, y)); + assert(offsetof(XKeyEvent, y_root) == offsetof(XButtonEvent, y_root)); + assert(offsetof(XKeyEvent, y_root) == offsetof(XCrossingEvent, y_root)); + assert(offsetof(XKeyEvent, y_root) == offsetof(XMotionEvent, y_root)); /* * Initialize the static data structures used by the binding package. They @@ -701,33 +1326,46 @@ TkBindInit( const ModInfo *modPtr; const EventInfo *eiPtr; int newEntry; + unsigned i; #ifdef REDO_KEYSYM_LOOKUP const KeySymInfo *kPtr; Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS); Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS); - for (kPtr = keyArray; kPtr->name != NULL; kPtr++) { + for (kPtr = keyArray; kPtr->name; ++kPtr) { hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &newEntry); - Tcl_SetHashValue(hPtr, INT2PTR(kPtr->value)); - hPtr = Tcl_CreateHashEntry(&nameTable, INT2PTR(kPtr->value), - &newEntry); + Tcl_SetHashValue(hPtr, kPtr->value); + hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value, &newEntry); if (newEntry) { - Tcl_SetHashValue(hPtr, (char *) kPtr->name); + Tcl_SetHashValue(hPtr, kPtr->name); } } #endif /* REDO_KEYSYM_LOOKUP */ + for (i = 0; i < SIZE_OF_ARRAY(eventArrayIndex); ++i) { + eventArrayIndex[i] = -1; + } + for (i = 0; i < SIZE_OF_ARRAY(eventArray); ++i) { + unsigned type = eventArray[i].type; + assert(type < TK_LASTEVENT); + assert(type > 0 || i == SIZE_OF_ARRAY(eventArray) - 1); + if (type > 0 && eventArrayIndex[type] == -1) { + eventArrayIndex[type] = i; + } + } + Tcl_InitHashTable(&modTable, TCL_STRING_KEYS); - for (modPtr = modArray; modPtr->name != NULL; modPtr++) { + for (modPtr = modArray; modPtr->name; ++modPtr) { hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &newEntry); - Tcl_SetHashValue(hPtr, (ModInfo *) modPtr); + Tcl_SetHashValue(hPtr, modPtr); } Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS); - for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) { + for (eiPtr = eventArray; eiPtr->name; ++eiPtr) { hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &newEntry); - Tcl_SetHashValue(hPtr, (EventInfo *) eiPtr); + Tcl_SetHashValue(hPtr, eiPtr); } + initialized = 1; } Tcl_MutexUnlock(&bindMutex); @@ -741,7 +1379,10 @@ TkBindInit( bindInfoPtr->screenInfo.curScreenIndex = -1; bindInfoPtr->screenInfo.bindingDepth = 0; bindInfoPtr->deleted = 0; + bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs(); + bindInfoPtr->lastEventTime = 0; mainPtr->bindInfo = bindInfoPtr; + DEBUG(countBindItems += 1); TkpInitializeMenuBindings(mainPtr->interp, mainPtr->bindingTable); } @@ -769,14 +1410,21 @@ TkBindFree( { BindInfo *bindInfoPtr; + assert(mainPtr); + Tk_DeleteBindingTable(mainPtr->bindingTable); mainPtr->bindingTable = NULL; - bindInfoPtr = mainPtr->bindInfo; DeleteVirtualEventTable(&bindInfoPtr->virtualEventTable); bindInfoPtr->deleted = 1; Tcl_EventuallyFree(bindInfoPtr, TCL_DYNAMIC); mainPtr->bindInfo = NULL; + + DEBUG(countBindItems -= 1); + assert(countBindItems > 0 || countTableItems == 0); + assert(countBindItems > 0 || countEntryItems == 0); + assert(countBindItems > 0 || countListItems == 0); + assert(countBindItems > 0 || countSeqItems == 0); } /* @@ -798,23 +1446,28 @@ TkBindFree( Tk_BindingTable Tk_CreateBindingTable( - Tcl_Interp *interp) /* Interpreter to associate with the binding - * table: commands are executed in this - * interpreter. */ + Tcl_Interp *interp) /* Interpreter to associate with the binding table: commands are + * executed in this interpreter. */ { BindingTable *bindPtr = ckalloc(sizeof(BindingTable)); - int i; + unsigned i; + + assert(interp); + DEBUG(countTableItems += 1); /* * Create and initialize a new binding table. */ - for (i = 0; i < EVENT_BUFFER_SIZE; i++) { - bindPtr->eventRing[i].type = -1; + memset(bindPtr, 0, sizeof(BindingTable)); + for (i = 0; i < SIZE_OF_ARRAY(bindPtr->eventInfo); ++i) { + bindPtr->eventInfo[i].xev.type = -1; } - bindPtr->curEvent = 0; - Tcl_InitHashTable(&bindPtr->patternTable, - sizeof(PatternTableKey)/sizeof(int)); + bindPtr->curEvent = bindPtr->eventInfo; /* do not assign NULL */ + bindPtr->lookupTables.number = 0; + PromArr_ResizeAndClear(&bindPtr->promArr, 2); + Tcl_InitHashTable(&bindPtr->lookupTables.listTable, sizeof(PatternTableKey)/sizeof(int)); + Tcl_InitHashTable(&bindPtr->lookupTables.patternTable, sizeof(PatternTableKey)/sizeof(int)); Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS); bindPtr->interp = interp; return bindPtr; @@ -841,35 +1494,103 @@ void Tk_DeleteBindingTable( Tk_BindingTable bindPtr) /* Token for the binding table to destroy. */ { - PatSeq *psPtr, *nextPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch search; + assert(bindPtr); + /* * Find and delete all of the patterns associated with the binding table. */ - for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - for (psPtr = Tcl_GetHashValue(hPtr); psPtr != NULL; psPtr = nextPtr) { + hPtr = Tcl_FirstHashEntry(&bindPtr->lookupTables.patternTable, &search); + for ( ; hPtr; hPtr = Tcl_NextHashEntry(&search)) { + PatSeq *nextPtr; + PatSeq *psPtr; + + for (psPtr = Tcl_GetHashValue(hPtr); psPtr; psPtr = nextPtr) { + assert(TEST_PSENTRY(psPtr)); nextPtr = psPtr->nextSeqPtr; - ckfree(psPtr->script); - ckfree(psPtr); + FreePatSeq(psPtr); } } /* + * Don't forget to release lookup elements. + */ + + ClearLookupTable(&bindPtr->lookupTables, NULL); + ClearPromotionLists(bindPtr, NULL); + PromArr_Free(&bindPtr->promArr); + DEBUG(countEntryItems -= PSList_Size(&bindPtr->lookupTables.entryPool)); + PSList_Traverse(&bindPtr->lookupTables.entryPool, FreePatSeqEntry); + + /* * Clean up the rest of the information associated with the binding table. */ - Tcl_DeleteHashTable(&bindPtr->patternTable); + Tcl_DeleteHashTable(&bindPtr->lookupTables.patternTable); + Tcl_DeleteHashTable(&bindPtr->lookupTables.listTable); Tcl_DeleteHashTable(&bindPtr->objectTable); + ckfree(bindPtr); + DEBUG(countTableItems -= 1); } /* *-------------------------------------------------------------- * + * InsertPatSeq -- + * + * Insert given pattern sequence into lookup table for fast + * access. + * + * Results: + * None. + * + * Side effects: + * Memory allocated. + * + *-------------------------------------------------------------- + */ + +static void +InsertPatSeq( + LookupTables *lookupTables, + PatSeq *psPtr) +{ + assert(lookupTables); + assert(psPtr); + assert(TEST_PSENTRY(psPtr)); + assert(psPtr->numPats >= 1u); + + if (!(psPtr->added)) { + PatternTableKey key; + Tcl_HashEntry *hPtr; + int isNew; + PSList *psList; + PSEntry *psEntry; + + SetupPatternKey(&key, psPtr); + hPtr = Tcl_CreateHashEntry(&lookupTables->listTable, (char *) &key, &isNew); + + if (isNew) { + psList = ckalloc(sizeof(PSList)); + PSList_Init(psList); + Tcl_SetHashValue(hPtr, psList); + DEBUG(countListItems += 1); + } else { + psList = Tcl_GetHashValue(hPtr); + } + + psEntry = MakeListEntry(&lookupTables->entryPool, psPtr, 0); + PSList_Append(psList, psEntry); + psPtr->added = 1; + } +} +/* + *-------------------------------------------------------------- + * * Tk_CreateBinding -- * * Add a binding to a binding table, so that future calls to Tk_BindEvent @@ -895,35 +1616,50 @@ unsigned long Tk_CreateBinding( Tcl_Interp *interp, /* Used for error reporting. */ Tk_BindingTable bindPtr, /* Table in which to create binding. */ - ClientData object, /* Token for object with which binding is - * associated. */ - const char *eventString, /* String describing event sequence that - * triggers binding. */ - const char *script, /* Contains Tcl script to execute when - * binding triggers. */ - int append) /* 0 means replace any existing binding for - * eventString; 1 means append to that - * binding. If the existing binding is for a - * callback function and not a Tcl command - * string, the existing binding will always be - * replaced. */ + ClientData object, /* Token for object with which binding is associated. */ + const char *eventString, /* String describing event sequence that triggers binding. */ + const char *script, /* Contains Tcl script to execute when binding triggers. */ + int append) /* 0 means replace any existing binding for eventString; + * 1 means append to that binding. If the existing binding is + * for a callback function and not a Tcl command string, the + * existing binding will always be replaced. */ { PatSeq *psPtr; - unsigned long eventMask; - char *newStr, *oldStr; + EventMask eventMask; + char *oldStr; + char *newStr; + + assert(bindPtr); + assert(object); + assert(eventString); + assert(script); + + psPtr = FindSequence(interp, &bindPtr->lookupTables, object, eventString, + !!*script, 1, &eventMask); if (!*script) { + assert(!psPtr || psPtr->added); /* Silently ignore empty scripts -- see SF#3006842 */ - return 1; + return eventMask; } - psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString, - 1, 1, &eventMask); - if (psPtr == NULL) { + if (!psPtr) { return 0; } - if (psPtr->script == NULL) { - int isNew; + assert(TEST_PSENTRY(psPtr)); + + if (psPtr->numPats > PromArr_Capacity(bindPtr->promArr)) { + /* + * We have to increase the size of array containing the lists of promoted sequences. + * Normally the maximal size is 1, only in very seldom cases a bigger size is needed. + * Note that for technical reasons the capacity should be one higher than the expected + * maximal size. + */ + PromArr_ResizeAndClear(&bindPtr->promArr, psPtr->numPats); + } + + if (!psPtr->script) { Tcl_HashEntry *hPtr; + int isNew; /* * This pattern sequence was just created. Link the pattern into the @@ -931,33 +1667,28 @@ Tk_CreateBinding( * these bindings will all automatically be deleted. */ - hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, - &isNew); - if (isNew) { - psPtr->nextObjPtr = NULL; - } else { - psPtr->nextObjPtr = Tcl_GetHashValue(hPtr); - } + hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &isNew); + psPtr->ptr.nextObj = isNew ? NULL : Tcl_GetHashValue(hPtr); Tcl_SetHashValue(hPtr, psPtr); + InsertPatSeq(&bindPtr->lookupTables, psPtr); } oldStr = psPtr->script; - if ((append != 0) && (oldStr != NULL)) { - size_t length1 = strlen(oldStr), length2 = strlen(script); + if (append && oldStr) { + size_t length1 = strlen(oldStr); + size_t length2 = strlen(script); newStr = ckalloc(length1 + length2 + 2); memcpy(newStr, oldStr, length1); newStr[length1] = '\n'; - memcpy(newStr+length1+1, script, length2+1); + memcpy(newStr + length1 + 1, script, length2 + 1); } else { size_t length = strlen(script); newStr = ckalloc(length + 1); - memcpy(newStr, script, length+1); - } - if (oldStr != NULL) { - ckfree(oldStr); + memcpy(newStr, script, length + 1); } + ckfree(oldStr); psPtr->script = newStr; return eventMask; } @@ -984,66 +1715,51 @@ int Tk_DeleteBinding( Tcl_Interp *interp, /* Used for error reporting. */ Tk_BindingTable bindPtr, /* Table in which to delete binding. */ - ClientData object, /* Token for object with which binding is - * associated. */ - const char *eventString) /* String describing event sequence that - * triggers binding. */ + ClientData object, /* Token for object with which binding is associated. */ + const char *eventString) /* String describing event sequence that triggers binding. */ { - PatSeq *psPtr, *prevPtr; - unsigned long eventMask; - Tcl_HashEntry *hPtr; + PatSeq *psPtr; + + assert(bindPtr); + assert(object); + assert(eventString); - psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString, - 0, 1, &eventMask); - if (psPtr == NULL) { + psPtr = FindSequence(interp, &bindPtr->lookupTables, object, eventString, 0, 1, NULL); + if (!psPtr) { Tcl_ResetResult(interp); - return TCL_OK; - } + } else { + Tcl_HashEntry *hPtr; + PatSeq *prevPtr; - /* - * Unlink the binding from the list for its object, then from the list for - * its pattern. - */ + assert(TEST_PSENTRY(psPtr)); - hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, object); - if (hPtr == NULL) { - Tcl_Panic("Tk_DeleteBinding couldn't find object table entry"); - } - prevPtr = Tcl_GetHashValue(hPtr); - if (prevPtr == psPtr) { - Tcl_SetHashValue(hPtr, psPtr->nextObjPtr); - } else { - for ( ; ; prevPtr = prevPtr->nextObjPtr) { - if (prevPtr == NULL) { - Tcl_Panic("Tk_DeleteBinding couldn't find on object list"); - } - if (prevPtr->nextObjPtr == psPtr) { - prevPtr->nextObjPtr = psPtr->nextObjPtr; - break; - } + /* + * Unlink the binding from the list for its object. + */ + + if (!(hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object))) { + Tcl_Panic("Tk_DeleteBinding couldn't find object table entry"); } - } - prevPtr = Tcl_GetHashValue(psPtr->hPtr); - if (prevPtr == psPtr) { - if (psPtr->nextSeqPtr == NULL) { - Tcl_DeleteHashEntry(psPtr->hPtr); + prevPtr = Tcl_GetHashValue(hPtr); + if (prevPtr == psPtr) { + Tcl_SetHashValue(hPtr, psPtr->ptr.nextObj); } else { - Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); - } - } else { - for ( ; ; prevPtr = prevPtr->nextSeqPtr) { - if (prevPtr == NULL) { - Tcl_Panic("Tk_DeleteBinding couldn't find on hash chain"); - } - if (prevPtr->nextSeqPtr == psPtr) { - prevPtr->nextSeqPtr = psPtr->nextSeqPtr; - break; + for ( ; ; prevPtr = prevPtr->ptr.nextObj) { + if (!prevPtr) { + Tcl_Panic("Tk_DeleteBinding couldn't find on object list"); + } + if (prevPtr->ptr.nextObj == psPtr) { + prevPtr->ptr.nextObj = psPtr->ptr.nextObj; + break; + } } } + + RemovePatSeqFromLookup(&bindPtr->lookupTables, psPtr); + RemovePatSeqFromPromotionLists(bindPtr, psPtr); + DeletePatSeq(psPtr); } - ckfree(psPtr->script); - ckfree(psPtr); return TCL_OK; } @@ -1072,20 +1788,18 @@ const char * Tk_GetBinding( Tcl_Interp *interp, /* Interpreter for error reporting. */ Tk_BindingTable bindPtr, /* Table in which to look for binding. */ - ClientData object, /* Token for object with which binding is - * associated. */ - const char *eventString) /* String describing event sequence that - * triggers binding. */ + ClientData object, /* Token for object with which binding is associated. */ + const char *eventString) /* String describing event sequence that triggers binding. */ { - PatSeq *psPtr; - unsigned long eventMask; + const PatSeq *psPtr; - psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString, - 0, 1, &eventMask); - if (psPtr == NULL) { - return NULL; - } - return psPtr->script; + assert(bindPtr); + assert(object); + assert(eventString); + + psPtr = FindSequence(interp, &bindPtr->lookupTables, object, eventString, 0, 1, NULL); + assert(!psPtr || TEST_PSENTRY(psPtr)); + return psPtr ? psPtr->script : NULL; } /* @@ -1114,26 +1828,171 @@ Tk_GetAllBindings( Tk_BindingTable bindPtr, /* Table in which to look for bindings. */ ClientData object) /* Token for object. */ { - PatSeq *psPtr; Tcl_HashEntry *hPtr; - Tcl_Obj *resultObj; - hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, object); - if (hPtr == NULL) { - return; - } + assert(bindPtr); + assert(object); + + if ((hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object))) { + const PatSeq *psPtr; + Tcl_Obj *resultObj = Tcl_NewObj(); - resultObj = Tcl_NewObj(); - for (psPtr = Tcl_GetHashValue(hPtr); psPtr != NULL; - psPtr = psPtr->nextObjPtr) { /* - * For each binding, output information about each of the patterns in - * its sequence. + * For each binding, output information about each of the patterns in its sequence. */ - Tcl_ListObjAppendElement(NULL, resultObj, GetPatternObj(psPtr)); + for (psPtr = Tcl_GetHashValue(hPtr); psPtr; psPtr = psPtr->ptr.nextObj) { + assert(TEST_PSENTRY(psPtr)); + Tcl_ListObjAppendElement(NULL, resultObj, GetPatternObj(psPtr)); + } + Tcl_SetObjResult(interp, resultObj); } - Tcl_SetObjResult(interp, resultObj); +} + +/* + *-------------------------------------------------------------- + * + * RemovePatSeqFromLookup -- + * + * Remove given pattern sequence from lookup tables. This + * can be required before deleting the pattern sequence. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static void +RemovePatSeqFromLookup( + LookupTables *lookupTables, /* Remove from this lookup tables. */ + PatSeq *psPtr) /* Remove this pattern sequence. */ +{ + PatternTableKey key; + Tcl_HashEntry *hPtr; + + assert(lookupTables); + assert(psPtr); + + SetupPatternKey(&key, psPtr); + + if ((hPtr = Tcl_FindHashEntry(&lookupTables->listTable, (char *) &key))) { + PSList *psList = Tcl_GetHashValue(hPtr); + PSEntry *psEntry; + + TK_DLIST_FOREACH(psEntry, psList) { + if (psEntry->psPtr == psPtr) { + psPtr->added = 0; + RemoveListEntry(&lookupTables->entryPool, psEntry); + return; + } + } + } + + assert(!"couldn't find pattern sequence in lookup"); +} + +/* + *-------------------------------------------------------------- + * + * RemovePatSeqFromPromotionLists -- + * + * Remove given pattern sequence from promotion lists. This + * can be required before deleting the pattern sequence. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static void +RemovePatSeqFromPromotionLists( + Tk_BindingTable bindPtr, /* Table in which to look for bindings. */ + PatSeq *psPtr) /* Remove this pattern sequence. */ +{ + unsigned i; + + assert(bindPtr); + assert(psPtr); + + for (i = 0; i < PromArr_Size(bindPtr->promArr); ++i) { + PSList *psList = PromArr_Get(bindPtr->promArr, i); + PSEntry *psEntry; + + TK_DLIST_FOREACH(psEntry, psList) { + if (psEntry->psPtr == psPtr) { + RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); + break; + } + } + } +} + +/* + *-------------------------------------------------------------- + * + * DeletePatSeq -- + * + * Delete given pattern sequence. Possibly it is required + * to invoke RemovePatSeqFromLookup(), and RemovePatSeqFromPromotionLists() + * before. + * + * Results: + * Pointer to succeeding pattern sequence. + * + * Side effects: + * Deallocation of memory. + * + *-------------------------------------------------------------- + */ + +static PatSeq * +DeletePatSeq( + PatSeq *psPtr) /* Delete this pattern sequence. */ +{ + PatSeq *prevPtr; + PatSeq *nextPtr; + + assert(psPtr); + assert(!psPtr->added); + assert(!psPtr->owned); + + prevPtr = Tcl_GetHashValue(psPtr->hPtr); + nextPtr = psPtr->ptr.nextObj; + + /* + * Be sure to remove each binding from its hash chain in the pattern + * table. If this is the last pattern in the chain, then delete the + * hash entry too. + */ + + if (prevPtr == psPtr) { + if (!psPtr->nextSeqPtr) { + Tcl_DeleteHashEntry(psPtr->hPtr); + } else { + Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); + } + } else { + for ( ; ; prevPtr = prevPtr->nextSeqPtr) { + if (!prevPtr) { + Tcl_Panic("DeletePatSeq couldn't find on hash chain"); + } + if (prevPtr->nextSeqPtr == psPtr) { + prevPtr->nextSeqPtr = psPtr->nextSeqPtr; + break; + } + } + } + + FreePatSeq(psPtr); + return nextPtr; } /* @@ -1158,45 +2017,30 @@ Tk_DeleteAllBindings( Tk_BindingTable bindPtr, /* Table in which to delete bindings. */ ClientData object) /* Token for object. */ { - PatSeq *psPtr, *prevPtr; + PatSeq *psPtr; PatSeq *nextPtr; Tcl_HashEntry *hPtr; - hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, object); - if (hPtr == NULL) { + assert(bindPtr); + assert(object); + + if (!(hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object))) { return; } - for (psPtr = Tcl_GetHashValue(hPtr); psPtr != NULL; - psPtr = nextPtr) { - nextPtr = psPtr->nextObjPtr; - /* - * Be sure to remove each binding from its hash chain in the pattern - * table. If this is the last pattern in the chain, then delete the - * hash entry too. - */ + /* + * Don't forget to clear lookup tables. + */ - prevPtr = Tcl_GetHashValue(psPtr->hPtr); - if (prevPtr == psPtr) { - if (psPtr->nextSeqPtr == NULL) { - Tcl_DeleteHashEntry(psPtr->hPtr); - } else { - Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); - } - } else { - for ( ; ; prevPtr = prevPtr->nextSeqPtr) { - if (prevPtr == NULL) { - Tcl_Panic("Tk_DeleteAllBindings couldn't find on hash chain"); - } - if (prevPtr->nextSeqPtr == psPtr) { - prevPtr->nextSeqPtr = psPtr->nextSeqPtr; - break; - } - } - } - ckfree(psPtr->script); - ckfree(psPtr); + ClearLookupTable(&bindPtr->lookupTables, object); + ClearPromotionLists(bindPtr, object); + + for (psPtr = Tcl_GetHashValue(hPtr); psPtr; psPtr = nextPtr) { + assert(TEST_PSENTRY(psPtr)); + DEBUG(psPtr->added = 0); + nextPtr = DeletePatSeq(psPtr); } + Tcl_DeleteHashEntry(hPtr); } @@ -1225,42 +2069,139 @@ Tk_DeleteAllBindings( *--------------------------------------------------------------------------- */ +/* helper function */ +static void +ResetCounters( + Event *eventInfo, + unsigned eventType, + Window window) +{ + Event *curEvent; + + assert(eventInfo); + curEvent = eventInfo + eventType; + + if (curEvent->xev.xany.window == window) { + curEvent->xev.xany.window = None; + eventInfo[eventType].countAny = 0; + eventInfo[eventType].countDetailed = 0; + } +} + +/* helper function */ +static int +IsBetterMatch( + const PatSeq *fstMatchPtr, + const PatSeq *sndMatchPtr) /* this is a better match? */ +{ + int diff; + + if (!sndMatchPtr) { return 0; } + if (!fstMatchPtr) { return 1; } + + diff = CountSpecialized(fstMatchPtr, sndMatchPtr); + if (diff > 0) { return 1; } + if (diff < 0) { return 0; } + +#if PREFER_MOST_SPECIALIZED_EVENT + { /* local scope */ +#define M (Tcl_WideUInt)1000000 + static const Tcl_WideUInt weight[5] = { 0, 1, M, M*M, M*M*M }; +#undef M + Tcl_WideUInt fstCount = 0; + Tcl_WideUInt sndCount = 0; + unsigned i; + + /* + * Count the most high-ordered patterns. + * + * (This computation assumes that a sequence does not contain more than + * 1,000,000 single patterns. It can be precluded that in practice this + * assumption will not be violated.) + */ + + for (i = 0; i < fstMatchPtr->numPats; ++i) { + assert(GetCount(fstMatchPtr, i) < SIZE_OF_ARRAY(weight)); + fstCount += weight[GetCount(fstMatchPtr, i)]; + } + for (i = 0; i < sndMatchPtr->numPats; ++i) { + assert(GetCount(sndMatchPtr, i) < SIZE_OF_ARRAY(weight)); + sndCount += weight[GetCount(sndMatchPtr, i)]; + } + if (sndCount > fstCount) { return 1; } + if (sndCount < fstCount) { return 0; } + } +#endif + + return sndMatchPtr->number > fstMatchPtr->number; +} + void Tk_BindEvent( Tk_BindingTable bindPtr, /* Table in which to look for bindings. */ XEvent *eventPtr, /* What actually happened. */ - Tk_Window tkwin, /* Window on display where event occurred - * (needed in order to locate display - * information). */ - int numObjects, /* Number of objects at *objectPtr. */ - ClientData *objectPtr) /* Array of one or more objects to check for a - * matching binding. */ + Tk_Window tkwin, /* Window on display where event occurred (needed in order to + * locate display information). */ + int numObjects, /* Number of objects at *objArr. */ + ClientData *objArr) /* Array of one or more objects to check for a matching binding. */ { - TkDisplay *dispPtr; + Tcl_Interp *interp; ScreenInfo *screenPtr; - BindInfo *bindInfoPtr; + TkDisplay *dispPtr; TkDisplay *oldDispPtr; - XEvent *ringPtr; - PatSeq *vMatchDetailList, *vMatchNoDetailList; - int flags, oldScreen; - unsigned int scriptCount; - Tcl_Interp *interp; - Tcl_DString scripts; - Tcl_InterpState interpState; - Detail detail; - char *p, *end; + Event *curEvent; TkWindow *winPtr = (TkWindow *) tkwin; - PatternTableKey key; + BindInfo *bindInfoPtr; + Tcl_InterpState interpState; + LookupTables *physTables; + PatSeq *psPtr[2]; + PatSeq *matchPtrBuf[32]; + PatSeq **matchPtrArr = matchPtrBuf; + PSList *psl[2]; + Tcl_DString scripts; + const char *p; + const char *end; + unsigned scriptCount; + int oldScreen; + unsigned flags; + unsigned arraySize; + unsigned newArraySize; + unsigned i, k; + + assert(bindPtr); + assert(eventPtr); + assert(tkwin); + assert(numObjects >= 0); /* * Ignore events on windows that don't have names: these are windows like * wrapper windows that shouldn't be visible to the application. */ - if (winPtr->pathName == NULL) { + if (!winPtr->pathName) { return; } + flags = flagArray[eventPtr->type]; + + /* + * Ignore event types which are not in flagArray and all zeroes there. + */ + + if (eventPtr->type >= TK_LASTEVENT || !flags) { + return; + } + + if (flags & KEY_BUTTON_MOTION_VIRTUAL) { + bindPtr->curModMask = eventPtr->xkey.state; + } else if (flags & CROSSING) { + bindPtr->curModMask = eventPtr->xcrossing.state; + } + + dispPtr = ((TkWindow *) tkwin)->dispPtr; + bindInfoPtr = winPtr->mainPtr->bindInfo; + curEvent = bindPtr->eventInfo + eventPtr->type; + /* * Ignore the event completely if it is an Enter, Leave, FocusIn, or * FocusOut event with detail NotifyInferior. The reason for ignoring @@ -1268,205 +2209,339 @@ Tk_BindEvent( * children to visible to bindings on the parent: this would cause * problems for mega-widgets, since the internal structure of a * mega-widget isn't supposed to be visible to people watching the parent. + * + * Furthermore we have to compute current time, needed for "event generate". */ - if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { + switch (eventPtr->type) { + case EnterNotify: + case LeaveNotify: + if (eventPtr->xcrossing.time) { + bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs(); + bindInfoPtr->lastEventTime = eventPtr->xcrossing.time; + } if (eventPtr->xcrossing.detail == NotifyInferior) { return; } - } - if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) { + break; + case FocusIn: + case FocusOut: if (eventPtr->xfocus.detail == NotifyInferior) { return; } - } - - /* - * Ignore event types which are not in flagArray and all zeroes there. - * Most notably, NoExpose events can fill the ring buffer and disturb - * (thus masking out) event sequences of interest. - */ + break; + case KeyPress: + case KeyRelease: { + int reset = 1; - if ((eventPtr->type >= TK_LASTEVENT) || !flagArray[eventPtr->type]) { - return; + if (eventPtr->xkey.time) { + bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs(); + bindInfoPtr->lastEventTime = eventPtr->xkey.time; + } + /* modifier keys should not influence button events */ + for (i = 0; i < (unsigned) dispPtr->numModKeyCodes; ++i) { + if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) { + reset = 0; + } + } + if (reset) { + /* reset repetition count for button events */ + bindPtr->eventInfo[ButtonPress].countAny = 0; + bindPtr->eventInfo[ButtonPress].countDetailed = 0; + bindPtr->eventInfo[ButtonRelease].countAny = 0; + bindPtr->eventInfo[ButtonRelease].countDetailed = 0; + } + break; + } + case ButtonPress: + case ButtonRelease: + /* reset repetition count for key events */ + bindPtr->eventInfo[KeyPress].countAny = 0; + bindPtr->eventInfo[KeyPress].countDetailed = 0; + bindPtr->eventInfo[KeyRelease].countAny = 0; + bindPtr->eventInfo[KeyRelease].countDetailed = 0; + /* fallthru */ + case MotionNotify: + if (eventPtr->xmotion.time) { + bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs(); + bindInfoPtr->lastEventTime = eventPtr->xmotion.time; + } + break; + case PropertyNotify: + if (eventPtr->xproperty.time) { + bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs(); + bindInfoPtr->lastEventTime = eventPtr->xproperty.time; + } + break; + case DestroyNotify: + ResetCounters(bindPtr->eventInfo, KeyPress, eventPtr->xany.window); + ResetCounters(bindPtr->eventInfo, KeyRelease, eventPtr->xany.window); + ResetCounters(bindPtr->eventInfo, ButtonPress, eventPtr->xany.window); + ResetCounters(bindPtr->eventInfo, ButtonRelease, eventPtr->xany.window); + break; } - - dispPtr = ((TkWindow *) tkwin)->dispPtr; - bindInfoPtr = winPtr->mainPtr->bindInfo; /* - * Add the new event to the ring of saved events for the binding table. - * Two tricky points: - * - * 1. Combine consecutive MotionNotify events. Do this by putting the new - * event *on top* of the previous event. - * 2. If a modifier key is held down, it auto-repeats to generate - * continuous KeyPress and KeyRelease events. These can flush the event - * ring so that valuable information is lost (such as repeated button - * clicks). To handle this, check for the special case of a modifier - * KeyPress arriving when the previous two events are a KeyRelease and - * KeyPress of the same key. If this happens, mark the most recent - * event (the KeyRelease) invalid and put the new event on top of the - * event before that (the KeyPress). + * Now check whether this is a repeating event (multi-click, repeated key press, and so on). */ - if ((eventPtr->type == MotionNotify) - && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) { - /* - * Don't advance the ring pointer. - */ - } else if (eventPtr->type == KeyPress) { - int i; - - for (i = 0; ; i++) { - if (i >= dispPtr->numModKeyCodes) { - goto advanceRingPointer; + /* NOTE: if curEvent is not yet set, then the following cannot match: */ + if (curEvent->xev.xany.window == eventPtr->xany.window) { + switch (eventPtr->type) { + case KeyPress: + case KeyRelease: + if (MatchEventRepeat(&curEvent->xev, eventPtr)) { + if (curEvent->xev.xkey.keycode == eventPtr->xkey.keycode) { + ++curEvent->countDetailed; + } else { + curEvent->countDetailed = 1; + } + ++curEvent->countAny; + } else { + curEvent->countAny = curEvent->countDetailed = 1; } - if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) { - break; + break; + case ButtonPress: + case ButtonRelease: + if (MatchEventNearby(&curEvent->xev, eventPtr)) { + if (curEvent->xev.xbutton.button == eventPtr->xbutton.button) { + ++curEvent->countDetailed; + } else { + curEvent->countDetailed = 1; + } + ++curEvent->countAny; + } else { + curEvent->countAny = curEvent->countDetailed = 1; } + break; + case EnterNotify: + case LeaveNotify: + if (TestNearbyTime(eventPtr->xcrossing.time, curEvent->xev.xcrossing.time)) { + ++curEvent->countAny; + } else { + curEvent->countAny = 1; + } + break; + case PropertyNotify: + if (TestNearbyTime(eventPtr->xproperty.time, curEvent->xev.xproperty.time)) { + ++curEvent->countAny; + } else { + curEvent->countAny = 1; + } + break; + default: + ++curEvent->countAny; + break; } - ringPtr = &bindPtr->eventRing[bindPtr->curEvent]; - if ((ringPtr->type != KeyRelease) - || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) { - goto advanceRingPointer; - } - if (bindPtr->curEvent <= 0) { - i = EVENT_BUFFER_SIZE - 1; - } else { - i = bindPtr->curEvent - 1; - } - ringPtr = &bindPtr->eventRing[i]; - if ((ringPtr->type != KeyPress) - || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) { - goto advanceRingPointer; - } - bindPtr->eventRing[bindPtr->curEvent].type = -1; - bindPtr->curEvent = i; } else { - - advanceRingPointer: - bindPtr->curEvent++; - if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) { - bindPtr->curEvent = 0; - } + curEvent->countAny = curEvent->countDetailed = 1; } - ringPtr = &bindPtr->eventRing[bindPtr->curEvent]; - memcpy(ringPtr, eventPtr, sizeof(XEvent)); - detail.clientData = 0; - flags = flagArray[ringPtr->type]; + + /* + * Now update the details. + */ + + curEvent->xev = *eventPtr; if (flags & KEY) { - detail.keySym = TkpGetKeySym(dispPtr, ringPtr); - if (detail.keySym == NoSymbol) { - detail.keySym = 0; - } + curEvent->detail.info = TkpGetKeySym(dispPtr, eventPtr); } else if (flags & BUTTON) { - detail.button = ringPtr->xbutton.button; + curEvent->detail.info = eventPtr->xbutton.button; + } else if (flags & MOTION) { + curEvent->detail.info = ButtonNumberFromState(eventPtr->xmotion.state); } else if (flags & VIRTUAL) { - detail.name = ((XVirtualEvent *) ringPtr)->name; + curEvent->detail.name = ((XVirtualEvent *) eventPtr)->name; } - bindPtr->detailRing[bindPtr->curEvent] = detail; - /* - * Find out if there are any virtual events that correspond to this - * physical event (or sequence of physical events). - */ + bindPtr->curEvent = curEvent; + physTables = &bindPtr->lookupTables; + scriptCount = 0; + arraySize = 0; + Tcl_DStringInit(&scripts); - vMatchDetailList = NULL; - vMatchNoDetailList = NULL; - memset(&key, 0, sizeof(key)); + if ((size_t) numObjects > SIZE_OF_ARRAY(matchPtrBuf)) { + /* it's unrealistic that the buffer size is too small, but who knows? */ + matchPtrArr = ckalloc(numObjects*sizeof(matchPtrArr[0])); + } + memset(matchPtrArr, 0, numObjects*sizeof(matchPtrArr[0])); - if (ringPtr->type != VirtualEvent) { - Tcl_HashTable *veptPtr = &bindInfoPtr->virtualEventTable.patternTable; - Tcl_HashEntry *hPtr; + if (!PromArr_IsEmpty(bindPtr->promArr)) { + for (k = 0; k < (unsigned) numObjects; ++k) { + psl[1] = PromArr_Last(bindPtr->promArr); + psl[0] = psl[1] - 1; - key.object = NULL; - key.type = ringPtr->type; - key.detail = detail; + /* + * Loop over all promoted bindings, finding the longest matching one. + * + * Note that we must process all lists, because all matching patterns + * have to be promoted. Normally at most one list will be processed, and + * usually this list only contains one or two patterns. + */ - hPtr = Tcl_FindHashEntry(veptPtr, &key); - if (hPtr != NULL) { - vMatchDetailList = Tcl_GetHashValue(hPtr); - } + for (i = PromArr_Size(bindPtr->promArr); i > 0; --i, --psl[0], --psl[1]) { + psPtr[0] = MatchPatterns(dispPtr, bindPtr, psl[0], psl[1], i, curEvent, objArr[k], NULL); - if (key.detail.clientData != 0) { - key.detail.clientData = 0; - hPtr = Tcl_FindHashEntry(veptPtr, &key); - if (hPtr != NULL) { - vMatchNoDetailList = Tcl_GetHashValue(hPtr); + if (IsBetterMatch(matchPtrArr[k], psPtr[0])) { + /* we will process it later, because we still may find a pattern with better match */ + matchPtrArr[k] = psPtr[0]; + } + if (!PSList_IsEmpty(psl[1])) { + /* we have promoted sequences, adjust array size */ + arraySize = Max(i + 1, arraySize); + } } } } /* - * Loop over all the binding tags, finding the binding script or callback - * for each one. Append all of the binding scripts, with %-sequences - * expanded, to "scripts", with null characters separating the scripts for - * each object. + * 1. Look for bindings for the specific detail (button and key events). + * 2. Look for bindings without detail. */ - scriptCount = 0; - Tcl_DStringInit(&scripts); + for (k = 0; k < (unsigned) numObjects; ++k) { + PSList *psSuccList = PromArr_First(bindPtr->promArr); + PatSeq *bestPtr; - for ( ; numObjects > 0; numObjects--, objectPtr++) { - PatSeq *matchPtr = NULL, *sourcePtr = NULL; - Tcl_HashEntry *hPtr; + psl[0] = GetLookupForEvent(physTables, curEvent, objArr[k], 1); + psl[1] = GetLookupForEvent(physTables, curEvent, objArr[k], 0); - /* - * Match the new event against those recorded in the pattern table, - * saving the longest matching pattern. For events with details - * (button and key events), look for a binding for the specific key or - * button. First see if the event matches a physical event that the - * object is interested in, then look for a virtual event. - */ + assert(psl[0] == NULL || psl[0] != psl[1]); - key.object = *objectPtr; - key.type = ringPtr->type; - key.detail = detail; - hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, &key); - if (hPtr != NULL) { - matchPtr = MatchPatterns(dispPtr, bindPtr, Tcl_GetHashValue(hPtr), - matchPtr, NULL, &sourcePtr); - } + psPtr[0] = MatchPatterns(dispPtr, bindPtr, psl[0], psSuccList, 0, curEvent, objArr[k], NULL); + psPtr[1] = MatchPatterns(dispPtr, bindPtr, psl[1], psSuccList, 0, curEvent, objArr[k], NULL); - if (vMatchDetailList != NULL) { - matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList, - matchPtr, objectPtr, &sourcePtr); + if (!PSList_IsEmpty(psSuccList)) { + /* we have promoted sequences, adjust array size */ + arraySize = Max(1u, arraySize); } - /* - * If no match was found, look for a binding for all keys or buttons - * (detail of 0). Again, first match on a virtual event. - */ + bestPtr = psPtr[0] ? psPtr[0] : psPtr[1]; - if ((detail.clientData != 0) && (matchPtr == NULL)) { - key.detail.clientData = 0; - hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, &key); - if (hPtr != NULL) { - matchPtr = MatchPatterns(dispPtr, bindPtr, - Tcl_GetHashValue(hPtr), matchPtr, NULL, &sourcePtr); + if (matchPtrArr[k]) { + if (IsBetterMatch(matchPtrArr[k], bestPtr)) { + matchPtrArr[k] = bestPtr; + } else { + /* + * We've already found a higher level match, nevertheless it was required to + * process the level zero patterns because of possible promotions. + */ } + /* + * Now we have to catch up the processing of the script. + */ + } else { + /* + * We have to look whether we can find a better match in virtual table, provided that we + * don't have a higher level match. + */ + + matchPtrArr[k] = bestPtr; + + if (eventPtr->type != VirtualEvent) { + LookupTables *virtTables = &bindInfoPtr->virtualEventTable.lookupTables; + PatSeq *matchPtr = matchPtrArr[k]; + PatSeq *mPtr; + PSList *psl[2]; - if (vMatchNoDetailList != NULL) { - matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList, - matchPtr, objectPtr, &sourcePtr); + /* + * Note that virtual events cannot promote. + */ + + psl[0] = GetLookupForEvent(virtTables, curEvent, NULL, 1); + psl[1] = GetLookupForEvent(virtTables, curEvent, NULL, 0); + + assert(psl[0] == NULL || psl[0] != psl[1]); + + mPtr = MatchPatterns(dispPtr, bindPtr, psl[0], NULL, 0, curEvent, objArr[k], &matchPtr); + if (mPtr) { + matchPtrArr[k] = matchPtr; + matchPtr = mPtr; + } + if (MatchPatterns(dispPtr, bindPtr, psl[1], NULL, 0, curEvent, objArr[k], &matchPtr)) { + matchPtrArr[k] = matchPtr; + } } } - if (matchPtr != NULL) { - ExpandPercents(winPtr, sourcePtr->script, eventPtr, - detail.keySym, scriptCount++, &scripts); + if (matchPtrArr[k]) { + ExpandPercents(winPtr, matchPtrArr[k]->script, curEvent, scriptCount++, &scripts); + /* nul is added to the scripts string to separate the various scripts */ + Tcl_DStringAppend(&scripts, "", 1); + } + } + + PromArr_SetSize(bindPtr->promArr, arraySize); + + /* + * Remove expired pattern sequences. + */ + + for (i = 0, newArraySize = 0; i < arraySize; ++i) { + PSList *psList = PromArr_Get(bindPtr->promArr, i); + PSEntry *psEntry; + PSEntry *psNext; + + for (psEntry = PSList_First(psList); psEntry; psEntry = psNext) { + const TkPattern *patPtr; + + assert(i + 1 < psEntry->psPtr->numPats); + + psNext = PSList_Next(psEntry); + patPtr = &psEntry->psPtr->pats[i + 1]; /* - * A "" is added to the scripts string to separate the various - * scripts that should be invoked. + * We have to remove the following entries from promotion list (but + * only if we don't want to keep it): + * ------------------------------------------------------------------ + * 1) It is marked as expired (see MatchPatterns()). + * 2) If we have a Key event, and current entry is matching a Button. + * 3) If we have a Button event, and current entry is matching a Key. + * 4) If we have a detailed event, current entry it is also detailed, + * we have matching event types, but the details are different. + * 5) Current entry has been matched with a different window. */ - Tcl_DStringAppend(&scripts, "", 1); + if (psEntry->keepIt) { + assert(!psEntry->expired); + psEntry->keepIt = 0; + } else if (psEntry->expired + || psEntry->window != curEvent->xev.xany.window + || (patPtr->info + && curEvent->detail.info + && patPtr->eventType == (unsigned) curEvent->xev.type + && patPtr->info != curEvent->detail.info)) { + RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); + } else { + switch (patPtr->eventType) { + case ButtonPress: + case ButtonRelease: + if (curEvent->xev.type == KeyPress || curEvent->xev.type == KeyRelease) { + RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); + } + break; + case KeyPress: + case KeyRelease: + if (curEvent->xev.type == ButtonPress || curEvent->xev.type == ButtonRelease) { + RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); + } + break; + } + } + } + + if (!PSList_IsEmpty(psList)) { + /* we still have promoted sequences, adjust array size */ + newArraySize = Max(i + 1, newArraySize); } } + + PromArr_SetSize(bindPtr->promArr, newArraySize); + + if (matchPtrArr != matchPtrBuf) { + ckfree(matchPtrArr); + } + if (Tcl_DStringLength(&scripts) == 0) { - return; + return; /* nothing to do */ } /* @@ -1497,16 +2572,13 @@ Tk_BindEvent( screenPtr = &bindInfoPtr->screenInfo; oldDispPtr = screenPtr->curDispPtr; oldScreen = screenPtr->curScreenIndex; - if ((dispPtr != screenPtr->curDispPtr) - || (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) { + + if (dispPtr != screenPtr->curDispPtr || Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex) { screenPtr->curDispPtr = dispPtr; screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin); ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex); } - p = Tcl_DStringValue(&scripts); - end = p + Tcl_DStringLength(&scripts); - /* * Be careful when dereferencing screenPtr or bindInfoPtr. If we evaluate * something that destroys ".", bindInfoPtr would have been freed, but we @@ -1514,12 +2586,13 @@ Tk_BindEvent( */ Tcl_Preserve(bindInfoPtr); - while (p < end) { - int len = (int) strlen(p); + + for (p = Tcl_DStringValue(&scripts), end = p + Tcl_DStringLength(&scripts); p < end; ) { + unsigned len = strlen(p); int code; if (!bindInfoPtr->deleted) { - screenPtr->bindingDepth++; + ++screenPtr->bindingDepth; } Tcl_AllowExceptions(interp); @@ -1527,38 +2600,30 @@ Tk_BindEvent( p += len + 1; if (!bindInfoPtr->deleted) { - screenPtr->bindingDepth--; + --screenPtr->bindingDepth; } - if (code != TCL_OK) { - if (code == TCL_CONTINUE) { - /* - * Do nothing: just go on to the next command. - */ - } else if (code == TCL_BREAK) { - break; - } else { + if (code != TCL_OK && code != TCL_CONTINUE) { + if (code != TCL_BREAK) { Tcl_AddErrorInfo(interp, "\n (command bound to event)"); Tcl_BackgroundException(interp, code); - break; } + break; } } - if (!bindInfoPtr->deleted && (screenPtr->bindingDepth != 0) - && ((oldDispPtr != screenPtr->curDispPtr) - || (oldScreen != screenPtr->curScreenIndex))) { + if (!bindInfoPtr->deleted + && screenPtr->bindingDepth > 0 + && (oldDispPtr != screenPtr->curDispPtr || oldScreen != screenPtr->curScreenIndex)) { /* * Some other binding script is currently executing, but its screen is * no longer current. Change the current display back again. */ - screenPtr->curDispPtr = oldDispPtr; screenPtr->curScreenIndex = oldScreen; ChangeScreen(interp, oldDispPtr->name, oldScreen); } - (void) Tcl_RestoreInterpState(interp, interpState); + Tcl_RestoreInterpState(interp, interpState); Tcl_DStringFree(&scripts); - Tcl_Release(bindInfoPtr); } @@ -1567,31 +2632,15 @@ Tk_BindEvent( * * MatchPatterns -- * - * Given a list of pattern sequences and a list of recent events, return - * the pattern sequence that best matches the event list, if there is + * Given a list of pattern sequences and the recent event, return + * the pattern sequence that best matches this event, if there is * one. * - * This function is used in two different ways. In the simplest use, - * "object" is NULL and psPtr is a list of pattern sequences, each of - * which corresponds to a binding. In this case, the function finds the - * pattern sequences that match the event list and returns the most - * specific of those, if there is more than one. - * - * In the second case, psPtr is a list of pattern sequences, each of - * which corresponds to a definition for a virtual binding. In order for - * one of these sequences to "match", it must match the events (as above) - * but in addition there must be a binding for its associated virtual - * event on the current object. The "object" argument indicates which - * object the binding must be for. - * * Results: - - * The return value is NULL if bestPtr is NULL and no pattern matches the - * recent events from bindPtr. Otherwise the return value is the most - * specific pattern sequence among bestPtr and all those at psPtr that - * match the event list and object. If a pattern sequence other than - * bestPtr is returned, then *bestCommandPtr is filled in with a pointer - * to the command from the best sequence. + * + * The return value is NULL if no match is found. Otherwise the + * return value is the most specific pattern sequence among all + * those that match the event table. * * Side effects: * None. @@ -1599,313 +2648,253 @@ Tk_BindEvent( *---------------------------------------------------------------------- */ -static PatSeq * -MatchPatterns( - TkDisplay *dispPtr, /* Display from which the event came. */ - BindingTable *bindPtr, /* Information about binding table, such as - * ring of recent events. */ - PatSeq *psPtr, /* List of pattern sequences. */ - PatSeq *bestPtr, /* The best match seen so far, from a previous - * call to this function. NULL means no prior - * best match. */ - ClientData *objectPtr, /* If NULL, the sequences at psPtr correspond - * to "normal" bindings. If non-NULL, the - * sequences at psPtr correspond to virtual - * bindings; in order to match each sequence - * must correspond to a virtual binding for - * which a binding exists for object in - * bindPtr. */ - PatSeq **sourcePtrPtr) /* Filled with the pattern sequence that - * contains the eventProc and clientData - * associated with the best match. If this - * differs from the return value, it is the - * virtual event that most closely matched the - * return value (a physical event). Not - * modified unless a result other than bestPtr - * is returned. */ +/* helper function */ +static int +VirtPatIsBound( + Tk_BindingTable bindPtr, /* Table in which to look for bindings. */ + PatSeq *psPtr, /* Test this pattern. */ + ClientData object, /* Check for this binding tag. */ + PatSeq **physPtrPtr) /* Input: the best physical event. + * Output: the physical event associated with matching virtual event. */ { - PatSeq *matchPtr, *bestSourcePtr, *sourcePtr; + PatternTableKey key; + const struct VirtOwners *owners; + unsigned i; - bestSourcePtr = *sourcePtrPtr; + assert(bindPtr); + assert(psPtr); + assert(!psPtr->object); + assert(physPtrPtr); - /* - * Iterate over all the pattern sequences. - */ + if (*physPtrPtr) { + const TkPattern *physPatPtr = (*physPtrPtr)->pats; + const TkPattern *virtPatPtr = psPtr->pats; - for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) { - XEvent *eventPtr = &bindPtr->eventRing[bindPtr->curEvent]; - Detail *detailPtr = &bindPtr->detailRing[bindPtr->curEvent]; - TkPattern *patPtr = psPtr->pats; - Window window = eventPtr->xany.window; - int patCount, ringCount, flags, state, modMask, i; + if (physPatPtr->info || !virtPatPtr->info) { + if (IsSubsetOf(virtPatPtr->modMask, physPatPtr->modMask)) { + return 0; /* we cannot surpass this match */ + } + } + } - /* - * Iterate over all the patterns in a sequence to be sure that they - * all match. - */ + /* otherwise on some systems the key contains uninitialized bytes */ + memset(&key, 0, sizeof(key)); - patCount = psPtr->numPats; - ringCount = EVENT_BUFFER_SIZE; - while (patCount > 0) { - if (ringCount <= 0) { - goto nextSequence; - } - if (eventPtr->xany.type != patPtr->eventType) { - /* - * Most of the event types are considered superfluous in that - * they are ignored if they occur in the middle of a pattern - * sequence and have mismatching types. The only ones that - * cannot be ignored are ButtonPress and ButtonRelease events - * (if the next event in the pattern is a KeyPress or - * KeyRelease) and KeyPress and KeyRelease events (if the next - * pattern event is a ButtonPress or ButtonRelease). Here are - * some tricky cases to consider: - * 1. Double-Button or Double-Key events. - * 2. Double-ButtonRelease or Double-KeyRelease events. - * 3. The arrival of various events like Enter and Leave and - * FocusIn and GraphicsExpose between two button presses or - * key presses. - * 4. Modifier keys like Shift and Control shouldn't generate - * conflicts with button events. - */ + key.object = object; + key.type = VirtualEvent; + owners = psPtr->ptr.owners; - if ((patPtr->eventType == KeyPress) - || (patPtr->eventType == KeyRelease)) { - if ((eventPtr->xany.type == ButtonPress) - || (eventPtr->xany.type == ButtonRelease)) { - goto nextSequence; - } - } else if ((patPtr->eventType == ButtonPress) - || (patPtr->eventType == ButtonRelease)) { - if ((eventPtr->xany.type == KeyPress) - || (eventPtr->xany.type == KeyRelease)) { - /* - * Ignore key events if they are modifier keys. - */ + for (i = 0; i < VirtOwners_Size(owners); ++i) { + Tcl_HashEntry *hPtr = VirtOwners_Get(owners, i); - for (i = 0; i < dispPtr->numModKeyCodes; i++) { - if (dispPtr->modKeyCodes[i] - == eventPtr->xkey.keycode) { - /* - * This key is a modifier key, so ignore it. - */ + key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr, hPtr); - goto nextEvent; - } - } - goto nextSequence; - } - } - goto nextEvent; - } - if (eventPtr->xany.type == CreateNotify - && eventPtr->xcreatewindow.parent != window) { - goto nextSequence; - } else if (eventPtr->xany.window != window) { - goto nextSequence; - } + if ((hPtr = Tcl_FindHashEntry(&bindPtr->lookupTables.patternTable, (char *) &key))) { + /* The physical event matches this virtual event's definition. */ + *physPtrPtr = (PatSeq *) Tcl_GetHashValue(hPtr); + return 1; + } + } - /* - * Note: it's important for the keysym check to go before the - * modifier check, so we can ignore unwanted modifier keys before - * choking on the modifier check. - */ + return 0; +} - if ((patPtr->detail.clientData != 0) - && (patPtr->detail.clientData != detailPtr->clientData)) { - /* - * The detail appears not to match. However, if the event is a - * KeyPress for a modifier key then just ignore the event. - * Otherwise event sequences like "aD" never match because the - * shift key goes down between the "a" and the "D". - */ +/* helper function */ +static int +Compare( + const PatSeq *fstMatchPtr, + const PatSeq *sndMatchPtr) /* most recent match */ +{ + int diff; - if (eventPtr->xany.type == KeyPress) { - for (i = 0; i < dispPtr->numModKeyCodes; i++) { - if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) { - goto nextEvent; - } - } - } - goto nextSequence; - } - flags = flagArray[eventPtr->type]; - if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - state = eventPtr->xkey.state; - } else if (flags & CROSSING) { - state = eventPtr->xcrossing.state; - } else { - state = 0; - } - if (patPtr->needMods != 0) { - modMask = patPtr->needMods; - if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) { - modMask = (modMask & ~META_MASK) | dispPtr->metaModMask; - } - if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) { - modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask; - } + if (!fstMatchPtr) { return +1; } + assert(sndMatchPtr); + diff = CountSpecialized(fstMatchPtr, sndMatchPtr); + return diff ? diff : (int) sndMatchPtr->count - (int) fstMatchPtr->count; +} - if ((state & META_MASK) && (dispPtr->metaModMask != 0)) { - state = (state & ~META_MASK) | dispPtr->metaModMask; - } - if ((state & ALT_MASK) && (dispPtr->altModMask != 0)) { - state = (state & ~ALT_MASK) | dispPtr->altModMask; - } +/* helper function */ +static int +CompareModMasks( + const PSModMaskArr *fstModMaskArr, + const PSModMaskArr *sndModMaskArr, + ModMask fstModMask, + ModMask sndModMask) +{ + int fstCount = 0; + int sndCount = 0; + int i; - if ((state & modMask) != modMask) { - goto nextSequence; + if (PSModMaskArr_IsEmpty(fstModMaskArr)) { + if (!PSModMaskArr_IsEmpty(sndModMaskArr)) { + for (i = PSModMaskArr_Size(sndModMaskArr) - 1; i >= 0; --i) { + if (*PSModMaskArr_Get(sndModMaskArr, i)) { + ++sndCount; } } - if (psPtr->flags & PAT_NEARBY) { - XEvent *firstPtr = &bindPtr->eventRing[bindPtr->curEvent]; - long timeDiff; - - timeDiff = ((long)firstPtr->xkey.time - - (long)eventPtr->xkey.time); - if ((firstPtr->xkey.x_root - < (eventPtr->xkey.x_root - NEARBY_PIXELS)) - || (firstPtr->xkey.x_root - > (eventPtr->xkey.x_root + NEARBY_PIXELS)) - || (firstPtr->xkey.y_root - < (eventPtr->xkey.y_root - NEARBY_PIXELS)) - || (firstPtr->xkey.y_root - > (eventPtr->xkey.y_root + NEARBY_PIXELS)) - || (timeDiff > NEARBY_MS)) { - goto nextSequence; - } - } - patPtr++; - patCount--; - nextEvent: - if (eventPtr == bindPtr->eventRing) { - eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1]; - detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1]; - } else { - eventPtr--; - detailPtr--; + } + } else if (PSModMaskArr_IsEmpty(sndModMaskArr)) { + for (i = PSModMaskArr_Size(fstModMaskArr) - 1; i >= 0; --i) { + if (*PSModMaskArr_Get(fstModMaskArr, i)) { + ++fstCount; } - ringCount--; } + } else { + assert(PSModMaskArr_Size(fstModMaskArr) == PSModMaskArr_Size(sndModMaskArr)); - matchPtr = psPtr; - sourcePtr = psPtr; + for (i = PSModMaskArr_Size(fstModMaskArr) - 1; i >= 0; --i) { + ModMask fstModMask = *PSModMaskArr_Get(fstModMaskArr, i); + ModMask sndModMask = *PSModMaskArr_Get(sndModMaskArr, i); - if (objectPtr != NULL) { - int iVirt; - VirtualOwners *voPtr; - PatternTableKey key; + if (IsSubsetOf(fstModMask, sndModMask)) { ++sndCount; } + if (IsSubsetOf(sndModMask, fstModMask)) { ++fstCount; } + } + } - /* - * The sequence matches the physical constraints. Is this object - * interested in any of the virtual events that correspond to this - * sequence? - */ + /* Finally compare modifier masks of last pattern. */ - voPtr = psPtr->voPtr; + if (IsSubsetOf(fstModMask, sndModMask)) { ++sndCount; } + if (IsSubsetOf(sndModMask, fstModMask)) { ++fstCount; } - memset(&key, 0, sizeof(key)); - key.object = *objectPtr; - key.type = VirtualEvent; - key.detail.clientData = 0; + return fstCount - sndCount; +} - for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) { - Tcl_HashEntry *hPtr = voPtr->owners[iVirt]; +static PatSeq * +MatchPatterns( + TkDisplay *dispPtr, /* Display from which the event came. */ + Tk_BindingTable bindPtr, /* Table in which to look for bindings. */ + PSList *psList, /* List of potentially matching patterns, can be NULL. */ + PSList *psSuccList, /* Add all matching higher-level pattern sequences to this list. + * Can be NULL. */ + unsigned patIndex, /* Match only this tag in sequence. */ + const Event *curEvent, /* Match this event. */ + ClientData object, /* Check for this binding tag. */ + PatSeq **physPtrPtr) /* Input: the best physical event; NULL if we test physical events. + * Output: the associated physical event for the best matching virtual + * event; NULL when we match physical events. */ +{ + Window window; + PSEntry *psEntry; + PatSeq *bestPtr; + PatSeq *bestPhysPtr; + ModMask bestModMask; + const PSModMaskArr *bestModMaskArr = NULL; + + assert(dispPtr); + assert(bindPtr); + assert(curEvent); + + if (!psList) { + return NULL; + } - key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr, - hPtr); - hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, &key); - if (hPtr != NULL) { + bestModMask = 0; + bestPtr = NULL; + bestPhysPtr = NULL; + window = curEvent->xev.xany.window; + + for (psEntry = PSList_First(psList); psEntry; psEntry = PSList_Next(psEntry)) { + if (patIndex == 0 || psEntry->window == window) { + PatSeq* psPtr = psEntry->psPtr; + + assert(TEST_PSENTRY(psPtr)); + assert((psPtr->object == NULL) == (physPtrPtr != NULL)); + assert(psPtr->object || patIndex == 0); + assert(psPtr->numPats > patIndex); + + if (psPtr->object + ? psPtr->object == object + : VirtPatIsBound(bindPtr, psPtr, object, physPtrPtr)) { + TkPattern *patPtr = psPtr->pats + patIndex; + + if (patPtr->eventType == (unsigned) curEvent->xev.type + && (curEvent->xev.type != CreateNotify + || curEvent->xev.xcreatewindow.parent == window) + && (!patPtr->name || patPtr->name == curEvent->detail.name) + && (!patPtr->info || patPtr->info == curEvent->detail.info)) { /* - * This tag is interested in this virtual event and its - * corresponding physical event is a good match with the - * virtual event's definition. + * Resolve the modifier mask for Alt and Mod keys. Unfortunately this + * cannot be done in ParseEventDescription, otherwise this function would + * be the better place. */ + ModMask modMask = ResolveModifiers(dispPtr, patPtr->modMask); + ModMask curModMask = ResolveModifiers(dispPtr, bindPtr->curModMask); - PatSeq *virtMatchPtr = Tcl_GetHashValue(hPtr); - - if ((virtMatchPtr->numPats != 1) - || (virtMatchPtr->nextSeqPtr != NULL)) { - Tcl_Panic("MatchPattern: badly constructed virtual event"); - } - sourcePtr = virtMatchPtr; - goto match; - } - } + psEntry->expired = 1; /* remove it from promotion list */ - /* - * The physical event matches a virtual event's definition, but - * the tag isn't interested in it. - */ + if ((modMask & ~curModMask) == 0) { + unsigned count = patPtr->info ? curEvent->countDetailed : curEvent->countAny; - goto nextSequence; - } - match: + if (patIndex < PSModMaskArr_Size(psEntry->lastModMaskArr)) { + PSModMaskArr_Set(psEntry->lastModMaskArr, patIndex, &modMask); + } - /* - * This sequence matches. If we've already got another match, pick - * whichever is most specific. Detail is most important, then - * needMods. - */ + /* + * This pattern is finally matching. + */ - if (bestPtr != NULL) { - TkPattern *patPtr2; + if (psPtr->numPats == patIndex + 1) { + if (patPtr->count <= count) { + /* + * This is also a final pattern. + * We always prefer the pattern with better match. + * If completely equal than prefer most recently defined pattern. + */ - if (matchPtr->numPats != bestPtr->numPats) { - if (bestPtr->numPats > matchPtr->numPats) { - goto nextSequence; - } else { - goto newBest; - } - } - for (i = 0, patPtr = matchPtr->pats, patPtr2 = bestPtr->pats; - i < matchPtr->numPats; i++, patPtr++, patPtr2++) { - if (patPtr->detail.clientData != patPtr2->detail.clientData) { - if (patPtr->detail.clientData == 0) { - goto nextSequence; - } else { - goto newBest; - } - } - if (patPtr->needMods != patPtr2->needMods) { - if ((patPtr->needMods & patPtr2->needMods) - == patPtr->needMods) { - goto nextSequence; - } else if ((patPtr->needMods & patPtr2->needMods) - == patPtr2->needMods) { - goto newBest; + int cmp = Compare(bestPtr, psPtr); + + if (cmp == 0) { + cmp = CompareModMasks(psEntry->lastModMaskArr, bestModMaskArr, + modMask, bestModMask); + } + + if (cmp > 0 || (cmp == 0 && bestPtr->number < psPtr->number)) { + bestPtr = psPtr; + bestModMask = modMask; + bestModMaskArr = psEntry->lastModMaskArr; + if (physPtrPtr) { + bestPhysPtr = *physPtrPtr; + } + } + } else { + DEBUG(psEntry->expired = 0); + psEntry->keepIt = 1; /* don't remove it from promotion list */ + } + } else if (psSuccList) { + /* + * Not a final pattern, but matching, so promote it to next level. + * But do not promote if count of current pattern is not yet reached. + */ + if (patPtr->count == psEntry->count) { + PSEntry *psNewEntry; + + assert(!patPtr->name); + psNewEntry = MakeListEntry( + &bindPtr->lookupTables.entryPool, psPtr, psPtr->modMaskUsed); + if (!PSModMaskArr_IsEmpty(psNewEntry->lastModMaskArr)) { + PSModMaskArr_Set(psNewEntry->lastModMaskArr, patIndex, &modMask); + } + assert(psNewEntry->keepIt); + assert(psNewEntry->count == 1u); + PSList_Append(psSuccList, psNewEntry); + psNewEntry->window = window; /* bind to current window */ + } else { + assert(psEntry->count < patPtr->count); + DEBUG(psEntry->expired = 0); + psEntry->count += 1; + psEntry->keepIt = 1; /* don't remove it from promotion list */ + } + } } } } - - /* - * Tie goes to current best pattern. - * - * (1) For virtual vs. virtual, the least recently defined virtual - * wins, because virtuals are examined in order of definition. - * This order is _not_ guaranteed in the documentation. - * - * (2) For virtual vs. physical, the physical wins because all the - * physicals are examined before the virtuals. This order is - * guaranteed in the documentation. - * - * (3) For physical vs. physical pattern, the most recently - * defined physical wins, because physicals are examined in - * reverse order of definition. This order is guaranteed in the - * documentation. - */ - - goto nextSequence; } - newBest: - bestPtr = matchPtr; - bestSourcePtr = sourcePtr; - - nextSequence: - continue; } - *sourcePtrPtr = bestSourcePtr; + if (bestPhysPtr) { + assert(physPtrPtr); + *physPtrPtr = bestPhysPtr; + } return bestPtr; } @@ -1929,50 +2918,43 @@ MatchPatterns( static void ExpandPercents( - TkWindow *winPtr, /* Window where event occurred: needed to get - * input context. */ - const char *before, /* Command containing percent expressions to - * be replaced. */ - XEvent *eventPtr, /* X event containing information to be used - * in % replacements. */ - KeySym keySym, /* KeySym: only relevant for KeyPress and - * KeyRelease events). */ - unsigned int scriptCount, /* The number of script-based binding patterns - * matched so far for this event. */ - Tcl_DString *dsPtr) /* Dynamic string in which to append new - * command. */ + TkWindow *winPtr, /* Window where event occurred: needed to get input context. */ + const char *before, /* Command containing percent expressions to be replaced. */ + Event *eventPtr, /* Event containing information to be used in % replacements. */ + unsigned scriptCount, /* The number of script-based binding patterns matched so far for + * this event. */ + Tcl_DString *dsPtr) /* Dynamic string in which to append new command. */ { - size_t spaceNeeded; - int cvtFlags; /* Used to substitute string as proper Tcl - * list element. */ - int number, flags, length; -#define NUM_SIZE 40 - const char *string; + unsigned flags; Tcl_DString buf; - char numStorage[NUM_SIZE+1]; + XEvent *evPtr; - Tcl_DStringInit(&buf); + assert(winPtr); + assert(before); + assert(eventPtr); + assert(dsPtr); - if (eventPtr->type < TK_LASTEVENT) { - flags = flagArray[eventPtr->type]; - } else { - flags = 0; - } + Tcl_DStringInit(&buf); + evPtr = &eventPtr->xev; + flags = (evPtr->type < TK_LASTEVENT) ? flagArray[evPtr->type] : 0; while (1) { + char numStorage[TCL_INTEGER_SPACE]; + const char *string; + Tcl_WideInt number; + /* * Find everything up to the next % character and append it to the * result string. */ - for (string = before; (*string != 0) && (*string != '%'); string++) { - /* Empty loop body. */ - } + for (string = before; *string && *string != '%'; ++string) + ; if (string != before) { - Tcl_DStringAppend(dsPtr, before, (int) (string-before)); + Tcl_DStringAppend(dsPtr, before, string - before); before = string; } - if (*before == 0) { + if (!*before) { break; } @@ -1980,331 +2962,280 @@ ExpandPercents( * There's a percent sequence here. Process it. */ - number = 0; + number = NO_NUMBER; string = "??"; + switch (before[1]) { case '#': - number = eventPtr->xany.serial; - goto doNumber; + number = evPtr->xany.serial; + break; case 'a': if (flags & CONFIG) { - TkpPrintWindowId(numStorage, eventPtr->xconfigure.above); + TkpPrintWindowId(numStorage, evPtr->xconfigure.above); string = numStorage; } - goto doString; + break; case 'b': if (flags & BUTTON) { - number = eventPtr->xbutton.button; + number = evPtr->xbutton.button; if (number >= Button8) { number -= (Button8 - Button4); } - goto doNumber; } - goto doString; + break; case 'c': if (flags & EXPOSE) { - number = eventPtr->xexpose.count; - goto doNumber; + number = evPtr->xexpose.count; } - goto doString; + break; case 'd': if (flags & (CROSSING|FOCUS)) { - if (flags & FOCUS) { - number = eventPtr->xfocus.detail; - } else { - number = eventPtr->xcrossing.detail; - } - string = TkFindStateString(notifyDetail, number); + int detail = (flags & FOCUS) ? evPtr->xfocus.detail : evPtr->xcrossing.detail; + string = TkFindStateString(notifyDetail, detail); } else if (flags & CONFIGREQ) { - if (eventPtr->xconfigurerequest.value_mask & CWStackMode) { - string = TkFindStateString(configureRequestDetail, - eventPtr->xconfigurerequest.detail); + if (evPtr->xconfigurerequest.value_mask & CWStackMode) { + string = TkFindStateString(configureRequestDetail, evPtr->xconfigurerequest.detail); } else { string = ""; } } else if (flags & VIRTUAL) { - XVirtualEvent *vePtr = (XVirtualEvent *) eventPtr; - - if (vePtr->user_data != NULL) { - string = Tcl_GetString(vePtr->user_data); - } else { - string = ""; - } + XVirtualEvent *vePtr = (XVirtualEvent *) evPtr; + string = vePtr->user_data ? Tcl_GetString(vePtr->user_data) : ""; } - goto doString; + break; case 'f': if (flags & CROSSING) { - number = eventPtr->xcrossing.focus; - goto doNumber; + number = evPtr->xcrossing.focus; } - goto doString; + break; case 'h': if (flags & EXPOSE) { - number = eventPtr->xexpose.height; + number = evPtr->xexpose.height; } else if (flags & CONFIG) { - number = eventPtr->xconfigure.height; + number = evPtr->xconfigure.height; } else if (flags & CREATE) { - number = eventPtr->xcreatewindow.height; + number = evPtr->xcreatewindow.height; } else if (flags & CONFIGREQ) { - number = eventPtr->xconfigurerequest.height; + number = evPtr->xconfigurerequest.height; } else if (flags & RESIZEREQ) { - number = eventPtr->xresizerequest.height; - } else { - goto doString; + number = evPtr->xresizerequest.height; } - goto doNumber; + break; case 'i': if (flags & CREATE) { - TkpPrintWindowId(numStorage, eventPtr->xcreatewindow.window); + TkpPrintWindowId(numStorage, evPtr->xcreatewindow.window); } else if (flags & CONFIGREQ) { - TkpPrintWindowId(numStorage, - eventPtr->xconfigurerequest.window); + TkpPrintWindowId(numStorage, evPtr->xconfigurerequest.window); } else if (flags & MAPREQ) { - TkpPrintWindowId(numStorage, eventPtr->xmaprequest.window); + TkpPrintWindowId(numStorage, evPtr->xmaprequest.window); } else { - TkpPrintWindowId(numStorage, eventPtr->xany.window); + TkpPrintWindowId(numStorage, evPtr->xany.window); } string = numStorage; - goto doString; + break; case 'k': - if ((flags & KEY) && (eventPtr->type != MouseWheelEvent)) { - number = eventPtr->xkey.keycode; - goto doNumber; + if ((flags & KEY) && evPtr->type != MouseWheelEvent) { + number = evPtr->xkey.keycode; } - goto doString; + break; case 'm': if (flags & CROSSING) { - number = eventPtr->xcrossing.mode; - string = TkFindStateString(notifyMode, number); + string = TkFindStateString(notifyMode, evPtr->xcrossing.mode); } else if (flags & FOCUS) { - number = eventPtr->xfocus.mode; - string = TkFindStateString(notifyMode, number); + string = TkFindStateString(notifyMode, evPtr->xfocus.mode); } - goto doString; + break; case 'o': if (flags & CREATE) { - number = eventPtr->xcreatewindow.override_redirect; + number = evPtr->xcreatewindow.override_redirect; } else if (flags & MAP) { - number = eventPtr->xmap.override_redirect; + number = evPtr->xmap.override_redirect; } else if (flags & REPARENT) { - number = eventPtr->xreparent.override_redirect; + number = evPtr->xreparent.override_redirect; } else if (flags & CONFIG) { - number = eventPtr->xconfigure.override_redirect; - } else { - goto doString; + number = evPtr->xconfigure.override_redirect; } - goto doNumber; + break; case 'p': if (flags & CIRC) { - string = TkFindStateString(circPlace, - eventPtr->xcirculate.place); + string = TkFindStateString(circPlace, evPtr->xcirculate.place); } else if (flags & CIRCREQ) { - string = TkFindStateString(circPlace, - eventPtr->xcirculaterequest.place); + string = TkFindStateString(circPlace, evPtr->xcirculaterequest.place); } - goto doString; + break; case 's': if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - number = eventPtr->xkey.state; - goto doNumber; + number = evPtr->xkey.state; } else if (flags & CROSSING) { - number = eventPtr->xcrossing.state; - goto doNumber; + number = evPtr->xcrossing.state; } else if (flags & PROP) { - string = TkFindStateString(propNotify, - eventPtr->xproperty.state); + string = TkFindStateString(propNotify, evPtr->xproperty.state); } else if (flags & VISIBILITY) { - string = TkFindStateString(visNotify, - eventPtr->xvisibility.state); + string = TkFindStateString(visNotify, evPtr->xvisibility.state); } - goto doString; + break; case 't': if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - number = (int) eventPtr->xkey.time; + number = (int) evPtr->xkey.time; } else if (flags & CROSSING) { - number = (int) eventPtr->xcrossing.time; + number = (int) evPtr->xcrossing.time; } else if (flags & PROP) { - number = (int) eventPtr->xproperty.time; - } else { - goto doString; + number = (int) evPtr->xproperty.time; } - goto doNumber; + break; case 'v': - number = eventPtr->xconfigurerequest.value_mask; - goto doNumber; + number = evPtr->xconfigurerequest.value_mask; + break; case 'w': if (flags & EXPOSE) { - number = eventPtr->xexpose.width; + number = evPtr->xexpose.width; } else if (flags & CONFIG) { - number = eventPtr->xconfigure.width; + number = evPtr->xconfigure.width; } else if (flags & CREATE) { - number = eventPtr->xcreatewindow.width; + number = evPtr->xcreatewindow.width; } else if (flags & CONFIGREQ) { - number = eventPtr->xconfigurerequest.width; + number = evPtr->xconfigurerequest.width; } else if (flags & RESIZEREQ) { - number = eventPtr->xresizerequest.width; - } else { - goto doString; + number = evPtr->xresizerequest.width; } - goto doNumber; + break; case 'x': if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - number = eventPtr->xkey.x; + number = evPtr->xkey.x; } else if (flags & CROSSING) { - number = eventPtr->xcrossing.x; + number = evPtr->xcrossing.x; } else if (flags & EXPOSE) { - number = eventPtr->xexpose.x; + number = evPtr->xexpose.x; } else if (flags & (CREATE|CONFIG|GRAVITY)) { - number = eventPtr->xcreatewindow.x; + number = evPtr->xcreatewindow.x; } else if (flags & REPARENT) { - number = eventPtr->xreparent.x; + number = evPtr->xreparent.x; } else if (flags & CREATE) { - number = eventPtr->xcreatewindow.x; + number = evPtr->xcreatewindow.x; } else if (flags & CONFIGREQ) { - number = eventPtr->xconfigurerequest.x; - } else { - goto doString; + number = evPtr->xconfigurerequest.x; } - goto doNumber; + break; case 'y': if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - number = eventPtr->xkey.y; + number = evPtr->xkey.y; } else if (flags & EXPOSE) { - number = eventPtr->xexpose.y; + number = evPtr->xexpose.y; } else if (flags & (CREATE|CONFIG|GRAVITY)) { - number = eventPtr->xcreatewindow.y; + number = evPtr->xcreatewindow.y; } else if (flags & REPARENT) { - number = eventPtr->xreparent.y; + number = evPtr->xreparent.y; } else if (flags & CROSSING) { - number = eventPtr->xcrossing.y; + number = evPtr->xcrossing.y; } else if (flags & CREATE) { - number = eventPtr->xcreatewindow.y; + number = evPtr->xcreatewindow.y; } else if (flags & CONFIGREQ) { - number = eventPtr->xconfigurerequest.y; - } else { - goto doString; + number = evPtr->xconfigurerequest.y; } - goto doNumber; + break; case 'A': - if ((flags & KEY) && (eventPtr->type != MouseWheelEvent)) { + if ((flags & KEY) && evPtr->type != MouseWheelEvent) { Tcl_DStringFree(&buf); - string = TkpGetString(winPtr, eventPtr, &buf); + string = TkpGetString(winPtr, evPtr, &buf); } - goto doString; + break; case 'B': if (flags & CREATE) { - number = eventPtr->xcreatewindow.border_width; + number = evPtr->xcreatewindow.border_width; } else if (flags & CONFIGREQ) { - number = eventPtr->xconfigurerequest.border_width; + number = evPtr->xconfigurerequest.border_width; } else if (flags & CONFIG) { - number = eventPtr->xconfigure.border_width; - } else { - goto doString; + number = evPtr->xconfigure.border_width; } - goto doNumber; + break; case 'D': /* * This is used only by the MouseWheel event. */ - - if ((flags & KEY) && (eventPtr->type == MouseWheelEvent)) { - number = eventPtr->xkey.keycode; - goto doNumber; + if ((flags & KEY) && evPtr->type == MouseWheelEvent) { + number = evPtr->xkey.keycode; } - goto doString; + break; case 'E': - number = (int) eventPtr->xany.send_event; - goto doNumber; + number = (int) evPtr->xany.send_event; + break; case 'K': - if ((flags & KEY) && (eventPtr->type != MouseWheelEvent)) { - const char *name = TkKeysymToString(keySym); - - if (name != NULL) { + if ((flags & KEY) && evPtr->type != MouseWheelEvent) { + const char *name = TkKeysymToString(eventPtr->detail.info); + if (name) { string = name; } } - goto doString; + break; case 'M': number = scriptCount; - goto doNumber; + break; case 'N': - if ((flags & KEY) && (eventPtr->type != MouseWheelEvent)) { - number = (int) keySym; - goto doNumber; + if ((flags & KEY) && evPtr->type != MouseWheelEvent) { + number = (int) eventPtr->detail.info; } - goto doString; + break; case 'P': if (flags & PROP) { - string = Tk_GetAtomName((Tk_Window) winPtr, - eventPtr->xproperty.atom); + string = Tk_GetAtomName((Tk_Window) winPtr, evPtr->xproperty.atom); } - goto doString; + break; case 'R': if (flags & KEY_BUTTON_MOTION_CROSSING) { - TkpPrintWindowId(numStorage, eventPtr->xkey.root); + TkpPrintWindowId(numStorage, evPtr->xkey.root); string = numStorage; } - goto doString; + break; case 'S': if (flags & KEY_BUTTON_MOTION_CROSSING) { - TkpPrintWindowId(numStorage, eventPtr->xkey.subwindow); + TkpPrintWindowId(numStorage, evPtr->xkey.subwindow); string = numStorage; } - goto doString; + break; case 'T': - number = eventPtr->type; - goto doNumber; + number = evPtr->type; + break; case 'W': { - Tk_Window tkwin; - - tkwin = Tk_IdToWindow(eventPtr->xany.display, - eventPtr->xany.window); - if (tkwin != NULL) { + Tk_Window tkwin = Tk_IdToWindow(evPtr->xany.display, evPtr->xany.window); + if (tkwin) { string = Tk_PathName(tkwin); - } else { - string = "??"; } - goto doString; + break; } case 'X': if (flags & KEY_BUTTON_MOTION_CROSSING) { - - number = eventPtr->xkey.x_root; - Tk_IdToWindow(eventPtr->xany.display, - eventPtr->xany.window); - goto doNumber; + number = evPtr->xkey.x_root; } - goto doString; + break; case 'Y': if (flags & KEY_BUTTON_MOTION_CROSSING) { - - number = eventPtr->xkey.y_root; - Tk_IdToWindow(eventPtr->xany.display, - eventPtr->xany.window); - goto doNumber; + number = evPtr->xkey.y_root; } - goto doString; + break; default: numStorage[0] = before[1]; numStorage[1] = '\0'; string = numStorage; - goto doString; + break; } - doNumber: - sprintf(numStorage, "%d", number); - string = numStorage; - - doString: - spaceNeeded = Tcl_ScanElement(string, &cvtFlags); - length = Tcl_DStringLength(dsPtr); - Tcl_DStringSetLength(dsPtr, length + spaceNeeded); - spaceNeeded = Tcl_ConvertElement(string, - Tcl_DStringValue(dsPtr) + length, - cvtFlags | TCL_DONT_USE_BRACES); - Tcl_DStringSetLength(dsPtr, length + spaceNeeded); - before += 2; + if (number != NO_NUMBER) { + snprintf(numStorage, sizeof(numStorage), "%d", (int) number); + string = numStorage; + } + { /* local scope */ + int cvtFlags; + unsigned spaceNeeded = Tcl_ScanElement(string, &cvtFlags); + unsigned length = Tcl_DStringLength(dsPtr); + + Tcl_DStringSetLength(dsPtr, length + spaceNeeded); + spaceNeeded = Tcl_ConvertElement( + string, Tcl_DStringValue(dsPtr) + length, cvtFlags | TCL_DONT_USE_BRACES); + Tcl_DStringSetLength(dsPtr, length + spaceNeeded); + before += 2; + } } + Tcl_DStringFree(&buf); } @@ -2335,15 +3266,13 @@ ChangeScreen( char *dispName, /* Name of new display. */ int screenIndex) /* Index of new screen. */ { - Tcl_Obj *cmdObj = Tcl_ObjPrintf("::tk::ScreenChanged %s.%d", - dispName, screenIndex); + Tcl_Obj *cmdObj = Tcl_ObjPrintf("::tk::ScreenChanged %s.%d", dispName, screenIndex); int code; Tcl_IncrRefCount(cmdObj); code = Tcl_EvalObjEx(interp, cmdObj, TCL_EVAL_GLOBAL); if (code != TCL_OK) { - Tcl_AddErrorInfo(interp, - "\n (changing screen in event binding)"); + Tcl_AddErrorInfo(interp, "\n (changing screen in event binding)"); Tcl_BackgroundException(interp, code); } Tcl_DecrRefCount(cmdObj); @@ -2376,38 +3305,48 @@ Tk_EventObjCmd( int index, i; char *name; const char *event; - Tk_Window tkwin = clientData; - TkBindInfo bindInfo = ((TkWindow *) tkwin)->mainPtr->bindInfo; - VirtualEventTable *vetPtr = &bindInfo->virtualEventTable; - static const char *const optionStrings[] = { - "add", "delete", "generate", "info", - NULL - }; - enum options { - EVENT_ADD, EVENT_DELETE, EVENT_GENERATE, EVENT_INFO - }; + Tk_Window tkwin; + TkBindInfo bindInfo; + VirtualEventTable *vetPtr; + static const char *const optionStrings[] = { "add", "delete", "generate", "info", NULL }; + enum options { EVENT_ADD, EVENT_DELETE, EVENT_GENERATE, EVENT_INFO }; + + assert(clientData); if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); return TCL_ERROR; } - if (Tcl_GetIndexFromObjStruct(interp, objv[1], optionStrings, - sizeof(char *), "option", 0, &index) != TCL_OK) { + if (Tcl_GetIndexFromObjStruct( + interp, objv[1], optionStrings, sizeof(char *), "option", 0, &index) != TCL_OK) { +#ifdef SUPPORT_DEBUGGING + if (strcmp(Tcl_GetString(objv[1]), "debug") == 0) { + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "debug number"); + return TCL_ERROR; + } + Tcl_GetIntFromObj(interp, objv[2], &BindCount); + return TCL_OK; + } +#endif return TCL_ERROR; } + tkwin = (Tk_Window) clientData; + bindInfo = ((TkWindow *) tkwin)->mainPtr->bindInfo; + vetPtr = &bindInfo->virtualEventTable; + switch ((enum options) index) { case EVENT_ADD: if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, - "virtual sequence ?sequence ...?"); + Tcl_WrongNumArgs(interp, 2, objv, "virtual sequence ?sequence ...?"); return TCL_ERROR; } name = Tcl_GetString(objv[2]); - for (i = 3; i < objc; i++) { + for (i = 3; i < objc; ++i) { event = Tcl_GetString(objv[i]); - if (CreateVirtualEvent(interp, vetPtr, name, event) != TCL_OK) { + if (!CreateVirtualEvent(interp, vetPtr, name, event)) { return TCL_ERROR; } } @@ -2421,7 +3360,7 @@ Tk_EventObjCmd( if (objc == 3) { return DeleteVirtualEvent(interp, vetPtr, name, NULL); } - for (i = 3; i < objc; i++) { + for (i = 3; i < objc; ++i) { event = Tcl_GetString(objv[i]); if (DeleteVirtualEvent(interp, vetPtr, name, event) != TCL_OK) { return TCL_ERROR; @@ -2430,8 +3369,7 @@ Tk_EventObjCmd( break; case EVENT_GENERATE: if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, - "window event ?-option value ...?"); + Tcl_WrongNumArgs(interp, 2, objv, "window event ?-option value ...?"); return TCL_ERROR; } return HandleEventGenerate(interp, tkwin, objc - 2, objv + 2); @@ -2439,12 +3377,12 @@ Tk_EventObjCmd( if (objc == 2) { GetAllVirtualEvents(interp, vetPtr); return TCL_OK; - } else if (objc == 3) { + } + if (objc == 3) { return GetVirtualEvent(interp, vetPtr, objv[2]); - } else { - Tcl_WrongNumArgs(interp, 2, objv, "?virtual?"); - return TCL_ERROR; } + Tcl_WrongNumArgs(interp, 2, objv, "?virtual?"); + return TCL_ERROR; } return TCL_OK; } @@ -2468,12 +3406,14 @@ Tk_EventObjCmd( static void InitVirtualEventTable( - VirtualEventTable *vetPtr) /* Pointer to virtual event table. Memory is - * supplied by the caller. */ + VirtualEventTable *vetPtr) /* Pointer to virtual event table. Memory is supplied by the caller. */ { - Tcl_InitHashTable(&vetPtr->patternTable, - sizeof(PatternTableKey) / sizeof(int)); + assert(vetPtr); + memset(vetPtr, 0, sizeof(*vetPtr)); + Tcl_InitHashTable(&vetPtr->lookupTables.patternTable, sizeof(PatternTableKey)/sizeof(int)); + Tcl_InitHashTable(&vetPtr->lookupTables.listTable, sizeof(PatternTableKey)/sizeof(int)); Tcl_InitHashTable(&vetPtr->nameTable, TCL_ONE_WORD_KEYS); + PSList_Init(&vetPtr->lookupTables.entryPool); } /* @@ -2499,24 +3439,33 @@ DeleteVirtualEventTable( { Tcl_HashEntry *hPtr; Tcl_HashSearch search; - PatSeq *psPtr, *nextPtr; - hPtr = Tcl_FirstHashEntry(&vetPtr->patternTable, &search); - for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - psPtr = Tcl_GetHashValue(hPtr); - for ( ; psPtr != NULL; psPtr = nextPtr) { + assert(vetPtr); + + hPtr = Tcl_FirstHashEntry(&vetPtr->lookupTables.patternTable, &search); + for ( ; hPtr; hPtr = Tcl_NextHashEntry(&search)) { + PatSeq *nextPtr; + PatSeq *psPtr; + + for (psPtr = Tcl_GetHashValue(hPtr); psPtr; psPtr = nextPtr) { + assert(TEST_PSENTRY(psPtr)); nextPtr = psPtr->nextSeqPtr; - ckfree(psPtr->voPtr); - ckfree(psPtr); + DEBUG(psPtr->owned = 0); + FreePatSeq(psPtr); } } - Tcl_DeleteHashTable(&vetPtr->patternTable); + Tcl_DeleteHashTable(&vetPtr->lookupTables.patternTable); hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search); - for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + for ( ; hPtr; hPtr = Tcl_NextHashEntry(&search)) { ckfree(Tcl_GetHashValue(hPtr)); } Tcl_DeleteHashTable(&vetPtr->nameTable); + Tcl_DeleteHashTable(&vetPtr->lookupTables.listTable); + + ClearLookupTable(&vetPtr->lookupTables, NULL); + DEBUG(countEntryItems -= PSList_Size(&vetPtr->lookupTables.entryPool)); + PSList_Traverse(&vetPtr->lookupTables.entryPool, FreePatSeqEntry); } /* @@ -2528,7 +3477,7 @@ DeleteVirtualEventTable( * already defined, the new definition augments those that already exist. * * Results: - * The return value is TCL_ERROR if an error occurred while creating the + * The return value is TCL_ERROR if an error occured while creating the * virtual binding. In this case, an error message will be left in the * interp's result. If all went well then the return value is TCL_OK. * @@ -2544,31 +3493,30 @@ CreateVirtualEvent( Tcl_Interp *interp, /* Used for error reporting. */ VirtualEventTable *vetPtr, /* Table in which to augment virtual event. */ char *virtString, /* Name of new virtual event. */ - const char *eventString) /* String describing physical event that - * triggers virtual event. */ + const char *eventString) /* String describing physical event that triggers virtual event. */ { PatSeq *psPtr; int dummy; Tcl_HashEntry *vhPtr; - unsigned long eventMask; - PhysicalsOwned *poPtr; - VirtualOwners *voPtr; + PhysOwned *owned; Tk_Uid virtUid; - virtUid = GetVirtualEventUid(interp, virtString); - if (virtUid == NULL) { - return TCL_ERROR; + assert(vetPtr); + assert(virtString); + assert(eventString); + + if (!(virtUid = GetVirtualEventUid(interp, virtString))) { + return 0; } /* * Find/create physical event */ - psPtr = FindSequence(interp, &vetPtr->patternTable, NULL, eventString, - 1, 0, &eventMask); - if (psPtr == NULL) { - return TCL_ERROR; + if (!(psPtr = FindSequence(interp, &vetPtr->lookupTables, NULL, eventString, 1, 0, NULL))) { + return 0; } + assert(TEST_PSENTRY(psPtr)); /* * Find/create virtual event. @@ -2580,47 +3528,18 @@ CreateVirtualEvent( * Make virtual event own the physical event. */ - poPtr = Tcl_GetHashValue(vhPtr); - if (poPtr == NULL) { - poPtr = ckalloc(sizeof(PhysicalsOwned)); - poPtr->numOwned = 0; - } else { - /* - * See if this virtual event is already defined for this physical - * event and just return if it is. - */ + owned = Tcl_GetHashValue(vhPtr); - int i; - - for (i = 0; i < poPtr->numOwned; i++) { - if (poPtr->patSeqs[i] == psPtr) { - return TCL_OK; - } - } - poPtr = ckrealloc(poPtr, sizeof(PhysicalsOwned) - + poPtr->numOwned * sizeof(PatSeq *)); - } - Tcl_SetHashValue(vhPtr, poPtr); - poPtr->patSeqs[poPtr->numOwned] = psPtr; - poPtr->numOwned++; - - /* - * Make physical event so it can trigger the virtual event. - */ - - voPtr = psPtr->voPtr; - if (voPtr == NULL) { - voPtr = ckalloc(sizeof(VirtualOwners)); - voPtr->numOwners = 0; - } else { - voPtr = ckrealloc(voPtr, sizeof(VirtualOwners) - + voPtr->numOwners * sizeof(Tcl_HashEntry *)); + if (!PhysOwned_Contains(owned, psPtr)) { + PhysOwned_Append(&owned, psPtr); + Tcl_SetHashValue(vhPtr, owned); + DEBUG(psPtr->owned = 1); + InsertPatSeq(&vetPtr->lookupTables, psPtr); + /* Make physical event so it can trigger the virtual event. */ + VirtOwners_Append(&psPtr->ptr.owners, vhPtr); } - psPtr->voPtr = voPtr; - voPtr->owners[voPtr->numOwners] = vhPtr; - voPtr->numOwners++; - return TCL_OK; + return 1; } /* @@ -2650,32 +3569,32 @@ static int DeleteVirtualEvent( Tcl_Interp *interp, /* Used for error reporting. */ VirtualEventTable *vetPtr, /* Table in which to delete event. */ - char *virtString, /* String describing event sequence that - * triggers binding. */ - const char *eventString) /* The event sequence that should be deleted, - * or NULL to delete all event sequences for - * the entire virtual event. */ + char *virtString, /* String describing event sequence that triggers binding. */ + const char *eventString) /* The event sequence that should be deleted, or NULL to delete + * all event sequences for the entire virtual event. */ { int iPhys; Tk_Uid virtUid; Tcl_HashEntry *vhPtr; - PhysicalsOwned *poPtr; - PatSeq *eventPSPtr; + PhysOwned *owned; + const PatSeq *eventPSPtr; + PatSeq *lastElemPtr; + + assert(vetPtr); + assert(virtString); - virtUid = GetVirtualEventUid(interp, virtString); - if (virtUid == NULL) { + if (!(virtUid = GetVirtualEventUid(interp, virtString))) { return TCL_ERROR; } - vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid); - if (vhPtr == NULL) { + if (!(vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid))) { return TCL_OK; } - poPtr = Tcl_GetHashValue(vhPtr); + owned = Tcl_GetHashValue(vhPtr); eventPSPtr = NULL; - if (eventString != NULL) { - unsigned long eventMask; + if (eventString) { + LookupTables *lookupTables = &vetPtr->lookupTables; /* * Delete only the specific physical event associated with the virtual @@ -2683,102 +3602,75 @@ DeleteVirtualEvent( * event doesn't own that physical event, return w/o doing anything. */ - eventPSPtr = FindSequence(interp, &vetPtr->patternTable, NULL, - eventString, 0, 0, &eventMask); - if (eventPSPtr == NULL) { + eventPSPtr = FindSequence(interp, lookupTables, NULL, eventString, 0, 0, NULL); + if (!eventPSPtr) { const char *string = Tcl_GetString(Tcl_GetObjResult(interp)); - - return (string[0] != '\0') ? TCL_ERROR : TCL_OK; + return string[0] ? TCL_ERROR : TCL_OK; } } - for (iPhys = poPtr->numOwned; --iPhys >= 0; ) { - PatSeq *psPtr = poPtr->patSeqs[iPhys]; + for (iPhys = PhysOwned_Size(owned); --iPhys >= 0; ) { + PatSeq *psPtr = PhysOwned_Get(owned, iPhys); + + assert(TEST_PSENTRY(psPtr)); + + if (!eventPSPtr || psPtr == eventPSPtr) { + VirtOwners *owners = psPtr->ptr.owners; + int iVirt = VirtOwners_Find(owners, vhPtr); - if ((eventPSPtr == NULL) || (psPtr == eventPSPtr)) { - int iVirt; - VirtualOwners *voPtr; + assert(iVirt != -1); /* otherwise we couldn't find owner, and this should not happen */ /* * Remove association between this physical event and the given * virtual event that it triggers. */ - voPtr = psPtr->voPtr; - for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) { - if (voPtr->owners[iVirt] == vhPtr) { - break; - } - } - if (iVirt == voPtr->numOwners) { - Tcl_Panic("DeleteVirtualEvent: couldn't find owner"); - } - voPtr->numOwners--; - if (voPtr->numOwners == 0) { - /* - * Removed last reference to this physical event, so remove it - * from physical->virtual map. - */ - - PatSeq *prevPtr = Tcl_GetHashValue(psPtr->hPtr); - - if (prevPtr == psPtr) { - if (psPtr->nextSeqPtr == NULL) { - Tcl_DeleteHashEntry(psPtr->hPtr); - } else { - Tcl_SetHashValue(psPtr->hPtr, - psPtr->nextSeqPtr); - } - } else { - for ( ; ; prevPtr = prevPtr->nextSeqPtr) { - if (prevPtr == NULL) { - Tcl_Panic("DeleteVirtualEvent couldn't find on hash chain"); - } - if (prevPtr->nextSeqPtr == psPtr) { - prevPtr->nextSeqPtr = psPtr->nextSeqPtr; - break; - } - } - } - ckfree(psPtr->voPtr); - ckfree(psPtr); - } else { + if (VirtOwners_Size(owners) > 1) { /* * This physical event still triggers some other virtual * event(s). Consolidate the list of virtual owners for this * physical event so it no longer triggers the given virtual * event. */ - - voPtr->owners[iVirt] = voPtr->owners[voPtr->numOwners]; + VirtOwners_Set(owners, iVirt, VirtOwners_Back(owners)); + VirtOwners_PopBack(owners); + } else { + /* + * Removed last reference to this physical event, so remove it + * from lookup table. + */ + DEBUG(psPtr->owned = 0); + RemovePatSeqFromLookup(&vetPtr->lookupTables, psPtr); + DeletePatSeq(psPtr); } /* * Now delete the virtual event's reference to the physical event. */ - poPtr->numOwned--; - if (eventPSPtr != NULL && poPtr->numOwned != 0) { + lastElemPtr = PhysOwned_Back(owned); + + if (PhysOwned_PopBack(owned) > 0 && eventPSPtr) { /* * Just deleting this one physical event. Consolidate list of * owned physical events and return. */ - - poPtr->patSeqs[iPhys] = poPtr->patSeqs[poPtr->numOwned]; + if ((size_t) iPhys < PhysOwned_Size(owned)) { + PhysOwned_Set(owned, iPhys, lastElemPtr); + } return TCL_OK; } } } - if (poPtr->numOwned == 0) { + if (PhysOwned_IsEmpty(owned)) { /* * All the physical events for this virtual event were deleted, either * because there was only one associated physical event or because the * caller was deleting the entire virtual event. Now the virtual event * itself should be deleted. */ - - ckfree(poPtr); + PhysOwned_Free(&owned); Tcl_DeleteHashEntry(vhPtr); } return TCL_OK; @@ -2813,26 +3705,26 @@ GetVirtualEvent( Tcl_Obj *virtName) /* String describing virtual event. */ { Tcl_HashEntry *vhPtr; - int iPhys; - PhysicalsOwned *poPtr; + unsigned iPhys; + const PhysOwned *owned; Tk_Uid virtUid; Tcl_Obj *resultObj; - virtUid = GetVirtualEventUid(interp, Tcl_GetString(virtName)); - if (virtUid == NULL) { + assert(vetPtr); + assert(virtName); + + if (!(virtUid = GetVirtualEventUid(interp, Tcl_GetString(virtName)))) { return TCL_ERROR; } - vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid); - if (vhPtr == NULL) { + if (!(vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid))) { return TCL_OK; } resultObj = Tcl_NewObj(); - poPtr = Tcl_GetHashValue(vhPtr); - for (iPhys = 0; iPhys < poPtr->numOwned; iPhys++) { - Tcl_ListObjAppendElement(NULL, resultObj, - GetPatternObj(poPtr->patSeqs[iPhys])); + owned = Tcl_GetHashValue(vhPtr); + for (iPhys = 0; iPhys < PhysOwned_Size(owned); ++iPhys) { + Tcl_ListObjAppendElement(NULL, resultObj, GetPatternObj(PhysOwned_Get(owned, iPhys))); } Tcl_SetObjResult(interp, resultObj); @@ -2866,11 +3758,13 @@ GetAllVirtualEvents( Tcl_HashSearch search; Tcl_Obj *resultObj; + assert(vetPtr); + resultObj = Tcl_NewObj(); hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search); - for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - Tcl_ListObjAppendElement(NULL, resultObj, Tcl_ObjPrintf( - "<<%s>>", (char *) Tcl_GetHashKey(hPtr->tablePtr, hPtr))); + for ( ; hPtr; hPtr = Tcl_NextHashEntry(&search)) { + Tcl_Obj* msg = Tcl_ObjPrintf("<<%s>>", (char *) Tcl_GetHashKey(hPtr->tablePtr, hPtr)); + Tcl_ListObjAppendElement(NULL, resultObj, msg); } Tcl_SetObjResult(interp, resultObj); } @@ -2917,16 +3811,24 @@ HandleEventGenerate( int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - union {XEvent general; XVirtualEvent virtual;} event; + union { XEvent general; XVirtualEvent virtual; } event; + const char *p; - const char *name, *windowName; - int count, flags, synch, i, number, warp; + const char *name; + const char *windowName; Tcl_QueuePosition pos; TkPattern pat; - Tk_Window tkwin, tkwin2; + Tk_Window tkwin; + Tk_Window tkwin2; TkWindow *mainPtr; - unsigned long eventMask; + EventMask eventMask; Tcl_Obj *userDataObj; + int synch; + int warp; + unsigned count; + unsigned flags; + int number; + unsigned i; static const char *const fieldStrings[] = { "-when", "-above", "-borderwidth", "-button", @@ -2936,7 +3838,7 @@ HandleEventGenerate( "-place", "-root", "-rootx", "-rooty", "-sendevent", "-serial", "-state", "-subwindow", "-time", "-warp", "-width", "-window", - "-x", "-y", NULL + "-x", "-y", NULL }; enum field { EVENT_WHEN, EVENT_ABOVE, EVENT_BORDER, EVENT_BUTTON, @@ -2949,42 +3851,39 @@ HandleEventGenerate( EVENT_X, EVENT_Y }; + assert(mainWin); + windowName = Tcl_GetString(objv[0]); if (!windowName[0]) { tkwin = mainWin; - } else if (NameToWindow(interp, mainWin, objv[0], &tkwin) != TCL_OK) { + } else if (!NameToWindow(interp, mainWin, objv[0], &tkwin)) { return TCL_ERROR; } mainPtr = (TkWindow *) mainWin; - if ((tkwin == NULL) - || (mainPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr)) { + if (!tkwin || mainPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "window id \"%s\" doesn't exist in this application", Tcl_GetString(objv[0]))); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "WINDOW", - Tcl_GetString(objv[0]), NULL); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "WINDOW", Tcl_GetString(objv[0]), NULL); return TCL_ERROR; } name = Tcl_GetString(objv[1]); - p = name; eventMask = 0; userDataObj = NULL; - count = ParseEventDescription(interp, &p, &pat, &eventMask); - if (count == 0) { + if ((count = ParseEventDescription(interp, &p, &pat, &eventMask)) == 0) { return TCL_ERROR; } - if (count != 1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "Double or Triple modifier not allowed", -1)); + if (count != 1u) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("Double, Triple, or Quadruple modifier not allowed", -1)); Tcl_SetErrorCode(interp, "TK", "EVENT", "BAD_MODIFIER", NULL); return TCL_ERROR; } - if (*p != '\0') { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "only one event specification allowed", -1)); + if (*p) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("only one event specification allowed", -1)); Tcl_SetErrorCode(interp, "TK", "EVENT", "MULTIPLE", NULL); return TCL_ERROR; } @@ -2992,12 +3891,11 @@ HandleEventGenerate( memset(&event, 0, sizeof(event)); event.general.xany.type = pat.eventType; event.general.xany.serial = NextRequest(Tk_Display(tkwin)); - event.general.xany.send_event = False; + event.general.xany.send_event = 0; if (windowName[0]) { event.general.xany.window = Tk_WindowId(tkwin); } else { - event.general.xany.window = - RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); + event.general.xany.window = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); } event.general.xany.display = Tk_Display(tkwin); @@ -3006,18 +3904,17 @@ HandleEventGenerate( /* * Event DestroyNotify should be generated by destroying the window. */ - Tk_DestroyWindow(tkwin); return TCL_OK; } if (flags & KEY_BUTTON_MOTION_VIRTUAL) { - event.general.xkey.state = pat.needMods; - if ((flags & KEY) && (event.general.xany.type != MouseWheelEvent)) { - TkpSetKeycodeAndState(tkwin, pat.detail.keySym, &event.general); + event.general.xkey.state = pat.modMask; + if ((flags & KEY) && event.general.xany.type != MouseWheelEvent) { + TkpSetKeycodeAndState(tkwin, pat.info, &event.general); } else if (flags & BUTTON) { - event.general.xbutton.button = pat.detail.button; + event.general.xbutton.button = pat.info; } else if (flags & VIRTUAL) { - event.virtual.name = pat.detail.name; + event.virtual.name = pat.name; } } if (flags & (CREATE|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) { @@ -3029,21 +3926,21 @@ HandleEventGenerate( event.general.xkey.y_root = -1; } - if (event.general.xany.type == FocusIn - || event.general.xany.type == FocusOut) { + if (event.general.xany.type == FocusIn || event.general.xany.type == FocusOut) { event.general.xany.send_event = GENERATED_FOCUS_EVENT_MAGIC; } /* - * Process the remaining arguments to fill in additional fields of the - * event. + * Process the remaining arguments to fill in additional fields of the event. */ synch = 1; warp = 0; pos = TCL_QUEUE_TAIL; - for (i = 2; i < objc; i += 2) { + + for (i = 2; i < (unsigned) objc; i += 2) { Tcl_Obj *optionPtr, *valuePtr; + int badOpt = 0; int index; optionPtr = objv[i]; @@ -3053,7 +3950,7 @@ HandleEventGenerate( sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { return TCL_ERROR; } - if (objc & 1) { + if (IsOdd(objc)) { /* * This test occurs after Tcl_GetIndexFromObj() so that "event * generate <Button> -xyz" will return the error message that @@ -3073,38 +3970,34 @@ HandleEventGenerate( return TCL_ERROR; } if (!(flags & KEY_BUTTON_MOTION_VIRTUAL)) { - goto badopt; + badOpt = 1; } break; case EVENT_WHEN: - pos = (Tcl_QueuePosition) TkFindStateNumObj(interp, optionPtr, - queuePosition, valuePtr); + pos = (Tcl_QueuePosition) TkFindStateNumObj(interp, optionPtr, queuePosition, valuePtr); if ((int) pos < -1) { return TCL_ERROR; } - synch = 0; - if ((int) pos == -1) { - synch = 1; - } + synch = ((int) pos == -1); break; case EVENT_ABOVE: - if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) { + if (!NameToWindow(interp, tkwin, valuePtr, &tkwin2)) { return TCL_ERROR; } if (flags & CONFIG) { event.general.xconfigure.above = Tk_WindowId(tkwin2); } else { - goto badopt; + badOpt = 1; } break; case EVENT_BORDER: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & (CREATE|CONFIG)) { event.general.xcreatewindow.border_width = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_BUTTON: @@ -3117,7 +4010,7 @@ HandleEventGenerate( } event.general.xbutton.button = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_COUNT: @@ -3127,7 +4020,7 @@ HandleEventGenerate( if (flags & EXPOSE) { event.general.xexpose.count = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_DATA: @@ -3137,25 +4030,23 @@ HandleEventGenerate( * completes and we know that the event generation is really * going to happen. */ - userDataObj = valuePtr; } else { - goto badopt; + badOpt = 1; } break; case EVENT_DELTA: if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } - if ((flags & KEY) && (event.general.xkey.type == MouseWheelEvent)) { + if ((flags & KEY) && event.general.xkey.type == MouseWheelEvent) { event.general.xkey.keycode = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_DETAIL: - number = TkFindStateNumObj(interp, optionPtr, notifyDetail, - valuePtr); + number = TkFindStateNumObj(interp, optionPtr, notifyDetail, valuePtr); if (number < 0) { return TCL_ERROR; } @@ -3164,7 +4055,7 @@ HandleEventGenerate( } else if (flags & CROSSING) { event.general.xcrossing.detail = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_FOCUS: @@ -3174,12 +4065,11 @@ HandleEventGenerate( if (flags & CROSSING) { event.general.xcrossing.focus = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_HEIGHT: - if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, - &number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & EXPOSE) { @@ -3187,17 +4077,17 @@ HandleEventGenerate( } else if (flags & CONFIG) { event.general.xconfigure.height = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_KEYCODE: if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } - if ((flags & KEY) && (event.general.xkey.type != MouseWheelEvent)) { + if ((flags & KEY) && event.general.xkey.type != MouseWheelEvent) { event.general.xkey.keycode = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_KEYSYM: { @@ -3207,30 +4097,24 @@ HandleEventGenerate( value = Tcl_GetString(valuePtr); keysym = TkStringToKeysym(value); if (keysym == NoSymbol) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown keysym \"%s\"", value)); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "KEYSYM", value, - NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown keysym \"%s\"", value)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "KEYSYM", value, NULL); return TCL_ERROR; } TkpSetKeycodeAndState(tkwin, keysym, &event.general); if (event.general.xkey.keycode == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "no keycode for keysym \"%s\"", value)); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "KEYCODE", value, - NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("no keycode for keysym \"%s\"", value)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "KEYCODE", value, NULL); return TCL_ERROR; } - if (!(flags & KEY) - || (event.general.xkey.type == MouseWheelEvent)) { - goto badopt; + if (!(flags & KEY) || (event.general.xkey.type == MouseWheelEvent)) { + badOpt = 1; } break; } case EVENT_MODE: - number = TkFindStateNumObj(interp,optionPtr,notifyMode,valuePtr); - if (number < 0) { + if ((number = TkFindStateNumObj(interp, optionPtr, notifyMode, valuePtr)) < 0) { return TCL_ERROR; } if (flags & CROSSING) { @@ -3238,7 +4122,7 @@ HandleEventGenerate( } else if (flags & FOCUS) { event.general.xfocus.mode = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_OVERRIDE: @@ -3254,48 +4138,47 @@ HandleEventGenerate( } else if (flags & CONFIG) { event.general.xconfigure.override_redirect = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_PLACE: - number = TkFindStateNumObj(interp, optionPtr, circPlace, valuePtr); - if (number < 0) { + if ((number = TkFindStateNumObj(interp, optionPtr, circPlace, valuePtr)) < 0) { return TCL_ERROR; } if (flags & CIRC) { event.general.xcirculate.place = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_ROOT: - if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) { + if (!NameToWindow(interp, tkwin, valuePtr, &tkwin2)) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { event.general.xkey.root = Tk_WindowId(tkwin2); } else { - goto badopt; + badOpt = 1; } break; case EVENT_ROOTX: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { event.general.xkey.x_root = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_ROOTY: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { event.general.xkey.y_root = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_SEND: { @@ -3307,16 +4190,21 @@ HandleEventGenerate( * Allow arbitrary integer values for the field; they are * needed by a few of the tests in the Tk test suite. */ - if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } - } else { - if (Tcl_GetBooleanFromObj(interp,valuePtr,&number) != TCL_OK) { - return TCL_ERROR; + if (number) { + /* + * send_event only expects 1 or 0. We cannot allow arbitrary non-zero + * values, otherwise the thing with GENERATED_FOCUS_EVENT_MAGIC will not + * work. + */ + number = 1; } + } else if (Tcl_GetBooleanFromObj(interp, valuePtr, &number) != TCL_OK) { + return TCL_ERROR; } - event.general.xany.send_event = number; + event.general.xany.send_event |= number; break; } case EVENT_SERIAL: @@ -3336,28 +4224,30 @@ HandleEventGenerate( event.general.xcrossing.state = number; } } else if (flags & VISIBILITY) { - number = TkFindStateNumObj(interp, optionPtr, visNotify, - valuePtr); - if (number < 0) { + if ((number = TkFindStateNumObj(interp, optionPtr, visNotify, valuePtr)) < 0) { return TCL_ERROR; } event.general.xvisibility.state = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_SUBWINDOW: - if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) { + if (!NameToWindow(interp, tkwin, valuePtr, &tkwin2)) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { event.general.xkey.subwindow = Tk_WindowId(tkwin2); } else { - goto badopt; + badOpt = 1; } break; - case EVENT_TIME: - if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) { + case EVENT_TIME: { + if (strcmp(Tcl_GetString(valuePtr), "current") == 0) { + TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; + BindInfo *biPtr = mainPtr->mainPtr->bindInfo; + number = dispPtr->lastEventTime + (CurrentTimeInMilliSecs() - biPtr->lastCurrentTime); + } else if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { @@ -3365,11 +4255,12 @@ HandleEventGenerate( } else if (flags & PROP) { event.general.xproperty.time = number; } else { - goto badopt; + badOpt = 1; } break; + } case EVENT_WIDTH: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & EXPOSE) { @@ -3377,21 +4268,21 @@ HandleEventGenerate( } else if (flags & (CREATE|CONFIG)) { event.general.xcreatewindow.width = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_WINDOW: - if (NameToWindow(interp, tkwin, valuePtr, &tkwin2) != TCL_OK) { + if (!NameToWindow(interp, tkwin, valuePtr, &tkwin2)) { return TCL_ERROR; } if (flags & (CREATE|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) { event.general.xcreatewindow.window = Tk_WindowId(tkwin2); } else { - goto badopt; + badOpt = 1; } break; case EVENT_X: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { @@ -3414,11 +4305,11 @@ HandleEventGenerate( } else if (flags & REPARENT) { event.general.xreparent.x = number; } else { - goto badopt; + badOpt = 1; } break; case EVENT_Y: - if (Tk_GetPixelsFromObj(interp,tkwin,valuePtr,&number) != TCL_OK) { + if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) { return TCL_ERROR; } if (flags & KEY_BUTTON_MOTION_CROSSING) { @@ -3441,85 +4332,94 @@ HandleEventGenerate( } else if (flags & REPARENT) { event.general.xreparent.y = number; } else { - goto badopt; + badOpt = 1; } break; } - continue; - badopt: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "%s event doesn't accept \"%s\" option", - name, Tcl_GetString(optionPtr))); - Tcl_SetErrorCode(interp, "TK", "EVENT", "BAD_OPTION", NULL); - return TCL_ERROR; + if (badOpt) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s event doesn't accept \"%s\" option", name, Tcl_GetString(optionPtr))); + Tcl_SetErrorCode(interp, "TK", "EVENT", "BAD_OPTION", NULL); + return TCL_ERROR; + } } /* * Don't generate events for windows that don't exist yet. */ - if (!event.general.xany.window) { - goto done; - } - - if (userDataObj != NULL) { + if (event.general.xany.window) { + if (userDataObj) { + /* + * Must be virtual event to set that variable to non-NULL. Now we want + * to install the object into the event. Note that we must incr the + * refcount before firing it into the low-level event subsystem; the + * refcount will be decremented once the event has been processed. + */ + event.virtual.user_data = userDataObj; + Tcl_IncrRefCount(userDataObj); + } /* - * Must be virtual event to set that variable to non-NULL. Now we want - * to install the object into the event. Note that we must incr the - * refcount before firing it into the low-level event subsystem; the - * refcount will be decremented once the event has been processed. + * Now we have constructed the event, inject it into the event handling + * code. */ - event.virtual.user_data = userDataObj; - Tcl_IncrRefCount(userDataObj); - } - - /* - * Now we have constructed the event, inject it into the event handling - * code. - */ - - if (synch != 0) { - Tk_HandleEvent(&event.general); - } else { - Tk_QueueWindowEvent(&event.general, pos); - } - - /* - * We only allow warping if the window is mapped. - */ + if (synch) { + Tk_HandleEvent(&event.general); + } else { + Tk_QueueWindowEvent(&event.general, pos); + } - if ((warp != 0) && Tk_IsMapped(tkwin)) { - TkDisplay *dispPtr = TkGetDisplay(event.general.xmotion.display); + /* + * We only allow warping if the window is mapped. + */ - Tk_Window warpWindow = Tk_IdToWindow(dispPtr->display, - event.general.xmotion.window); + if (warp && Tk_IsMapped(tkwin)) { + TkDisplay *dispPtr = TkGetDisplay(event.general.xmotion.display); - if (!(dispPtr->flags & TK_DISPLAY_IN_WARP)) { - Tcl_DoWhenIdle(DoWarp, dispPtr); - dispPtr->flags |= TK_DISPLAY_IN_WARP; - } + Tk_Window warpWindow = Tk_IdToWindow(dispPtr->display, event.general.xmotion.window); - if (warpWindow != dispPtr->warpWindow) { - if (warpWindow) { - Tcl_Preserve(warpWindow); + if (!(dispPtr->flags & TK_DISPLAY_IN_WARP)) { + Tcl_DoWhenIdle(DoWarp, dispPtr); + dispPtr->flags |= TK_DISPLAY_IN_WARP; } - if (dispPtr->warpWindow) { - Tcl_Release(dispPtr->warpWindow); + + if (warpWindow != dispPtr->warpWindow) { + if (warpWindow) { + Tcl_Preserve(warpWindow); + } + if (dispPtr->warpWindow) { + Tcl_Release(dispPtr->warpWindow); + } + dispPtr->warpWindow = warpWindow; } - dispPtr->warpWindow = warpWindow; + dispPtr->warpMainwin = mainWin; + dispPtr->warpX = event.general.xmotion.x; + dispPtr->warpY = event.general.xmotion.y; } - dispPtr->warpMainwin = mainWin; - dispPtr->warpX = event.general.xmotion.x; - dispPtr->warpY = event.general.xmotion.y; } - done: Tcl_ResetResult(interp); return TCL_OK; } +/* + *--------------------------------------------------------------------------- + * + * NameToWindow -- + * + * Helper function for lookup of a window given its path name. Our + * version is able to handle window id's. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ static int NameToWindow( @@ -3528,38 +4428,43 @@ NameToWindow( Tcl_Obj *objPtr, /* Contains name or id string of window. */ Tk_Window *tkwinPtr) /* Filled with token for window. */ { - const char *name = Tcl_GetString(objPtr); + const char *name; Tk_Window tkwin; + assert(mainWin); + assert(objPtr); + assert(tkwinPtr); + + name = Tcl_GetString(objPtr); + if (name[0] == '.') { - tkwin = Tk_NameToWindow(interp, name, mainWin); - if (tkwin == NULL) { - return TCL_ERROR; + if (!(tkwin = Tk_NameToWindow(interp, name, mainWin))) { + return 0; } } else { Window id; + tkwin = NULL; + /* - * Check for the winPtr being valid, even if it looks ok to + * Check for the winPtr being valid, even if it looks okay to * TkpScanWindowId. [Bug #411307] */ - if (TkpScanWindowId(NULL, name, &id) != TCL_OK) { - goto badWindow; + if (TkpScanWindowId(NULL, name, &id) == TCL_OK) { + tkwin = Tk_IdToWindow(Tk_Display(mainWin), id); } - tkwin = Tk_IdToWindow(Tk_Display(mainWin), id); - if (tkwin == NULL) { - goto badWindow; + + if (!tkwin) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad window name/identifier \"%s\"", name)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "WINDOW_ID", name, NULL); + return 0; } } - *tkwinPtr = tkwin; - return TCL_OK; - badWindow: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad window name/identifier \"%s\"", name)); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "WINDOW_ID", name, NULL); - return TCL_ERROR; + assert(tkwin); + *tkwinPtr = tkwin; + return 1; } /* @@ -3584,6 +4489,8 @@ DoWarp( { TkDisplay *dispPtr = clientData; + assert(clientData); + /* * DoWarp was scheduled only if the window was mapped. It needs to be * still mapped at the time the present idle callback is executed. Also @@ -3592,9 +4499,8 @@ DoWarp( * the whole screen. */ - if ((dispPtr->warpWindow == NULL) || - (Tk_IsMapped(dispPtr->warpWindow) - && (Tk_WindowId(dispPtr->warpWindow) != None))) { + if (!dispPtr->warpWindow || + (Tk_IsMapped(dispPtr->warpWindow) && Tk_WindowId(dispPtr->warpWindow) != None)) { TkpWarpPointer(dispPtr); XForceScreenSaver(dispPtr->display, ScreenSaverReset); } @@ -3634,12 +4540,16 @@ GetVirtualEventUid( Tk_Uid uid; size_t length; + assert(virtString); + length = strlen(virtString); - if (length < 5 || virtString[0] != '<' || virtString[1] != '<' || - virtString[length - 2] != '>' || virtString[length - 1] != '>') { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "virtual event \"%s\" is badly formed", virtString)); + if (length < 5 + || virtString[0] != '<' + || virtString[1] != '<' + || virtString[length - 2] != '>' + || virtString[length - 1] != '>') { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("virtual event \"%s\" is badly formed", virtString)); Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "MALFORMED", NULL); return NULL; } @@ -3677,118 +4587,116 @@ GetVirtualEventUid( static PatSeq * FindSequence( Tcl_Interp *interp, /* Interpreter to use for error reporting. */ - Tcl_HashTable *patternTablePtr, - /* Table to use for lookup. */ - ClientData object, /* For binding table, token for object with - * which binding is associated. For virtual - * event table, NULL. */ - const char *eventString, /* String description of pattern to match on. - * See user documentation for details. */ - int create, /* 0 means don't create the entry if it - * doesn't already exist. Non-zero means - * create. */ - int allowVirtual, /* 0 means that virtual events are not allowed - * in the sequence. Non-zero otherwise. */ - unsigned long *maskPtr) /* *maskPtr is filled in with the event types - * on which this pattern sequence depends. */ + LookupTables *lookupTables, /* Tables used for lookup. */ + ClientData object, /* For binding table, token for object with which binding is + * associated. For virtual event table, NULL. */ + const char *eventString, /* String description of pattern to match on. See user + * documentation for details. */ + int create, /* 0 means don't create the entry if it doesn't already exist. + * 1 means create. */ + int allowVirtual, /* 0 means that virtual events are not allowed in the sequence. + * 1 otherwise. */ + EventMask *maskPtr) /* *maskPtr is filled in with the event types on which this + * pattern sequence depends. */ { - TkPattern pats[EVENT_BUFFER_SIZE]; - int numPats, virtualFound; - const char *p; + unsigned patsBufSize = 1; + unsigned numPats; + unsigned totalCount = 0; + int virtualFound = 0; + const char *p = eventString; TkPattern *patPtr; PatSeq *psPtr; Tcl_HashEntry *hPtr; - int flags, count, isNew; - size_t sequenceSize; - unsigned long eventMask; + int isNew; + unsigned count; + unsigned maxCount = 0; + EventMask eventMask = 0; + ModMask modMask = 0; PatternTableKey key; + assert(lookupTables); + assert(eventString); + + psPtr = ckalloc(PATSEQ_MEMSIZE(patsBufSize)); + /* - *------------------------------------------------------------- - * Step 1: parse the pattern string to produce an array of Patterns. The - * array is generated backwards, so that the lowest-indexed pattern - * corresponds to the last event that must occur. - *------------------------------------------------------------- + *------------------------------------------------------------------ + * Step 1: parse the pattern string to produce an array of Patterns. + *------------------------------------------------------------------ */ - p = eventString; - flags = 0; - eventMask = 0; - virtualFound = 0; - - patPtr = &pats[EVENT_BUFFER_SIZE-1]; - for (numPats = 0; numPats < EVENT_BUFFER_SIZE; numPats++, patPtr--) { - while (isspace(UCHAR(*p))) { - p++; - } - if (*p == '\0') { - break; + for (patPtr = psPtr->pats, numPats = 0; *(p = SkipSpaces(p)); ++patPtr, ++numPats) { + if (numPats >= patsBufSize) { + unsigned pos = patPtr - psPtr->pats; + patsBufSize += patsBufSize; + psPtr = ckrealloc(psPtr, PATSEQ_MEMSIZE(patsBufSize)); + patPtr = psPtr->pats + pos; } - count = ParseEventDescription(interp, &p, patPtr, &eventMask); - if (count == 0) { + if ((count = ParseEventDescription(interp, &p, patPtr, &eventMask)) == 0) { + /* error encountered */ + ckfree(psPtr); return NULL; } if (eventMask & VirtualEventMask) { - if (allowVirtual == 0) { + if (!allowVirtual) { Tcl_SetObjResult(interp, Tcl_NewStringObj( - "virtual event not allowed in definition of another virtual event", - -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "INNER", - NULL); + "virtual event not allowed in definition of another virtual event", -1)); + Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "INNER", NULL); + ckfree(psPtr); return NULL; } virtualFound = 1; } - /* - * Replicate events for DOUBLE, TRIPLE, QUADRUPLE. - */ - - while ((count-- > 1) && (numPats < EVENT_BUFFER_SIZE-1)) { - flags |= PAT_NEARBY; - patPtr[-1] = patPtr[0]; - patPtr--; - numPats++; + if (count > 1u) { + maxCount = Max(count, maxCount); } + + totalCount += count; + modMask |= patPtr->modMask; } /* - *------------------------------------------------------------- - * Step 2: find the sequence in the binding table if it exists, and add a - * new sequence to the table if it doesn't. - *------------------------------------------------------------- + *------------------------------------------------------------------ + * Step 2: find the sequence in the binding table if it exists, and + * add a new sequence to the table if it doesn't. + *------------------------------------------------------------------ */ if (numPats == 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "no events specified in binding", -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj("no events specified in binding", -1)); Tcl_SetErrorCode(interp, "TK", "EVENT", "NO_EVENTS", NULL); + ckfree(psPtr); return NULL; } - if ((numPats > 1) && (virtualFound != 0)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "virtual events may not be composed", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "COMPOSITION", - NULL); + if (numPats > 1u && virtualFound) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("virtual events may not be composed", -1)); + Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "COMPOSITION", NULL); + ckfree(psPtr); return NULL; } + if (patsBufSize > numPats) { + psPtr = ckrealloc(psPtr, PATSEQ_MEMSIZE(numPats)); + } - patPtr = &pats[EVENT_BUFFER_SIZE-numPats]; - memset(&key, 0, sizeof(key)); - key.object = object; - key.type = patPtr->eventType; - key.detail = patPtr->detail; - hPtr = Tcl_CreateHashEntry(patternTablePtr, (char *) &key, &isNew); - sequenceSize = numPats*sizeof(TkPattern); + patPtr = psPtr->pats; + psPtr->object = object; + SetupPatternKey(&key, psPtr); + hPtr = Tcl_CreateHashEntry(&lookupTables->patternTable, (char *) &key, &isNew); if (!isNew) { - for (psPtr = Tcl_GetHashValue(hPtr); psPtr != NULL; - psPtr = psPtr->nextSeqPtr) { - if ((numPats == psPtr->numPats) - && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY)) - && (memcmp(patPtr, psPtr->pats, sequenceSize) == 0)) { - goto done; + unsigned sequenceSize = numPats*sizeof(TkPattern); + PatSeq *psPtr2; + + for (psPtr2 = Tcl_GetHashValue(hPtr); psPtr2; psPtr2 = psPtr2->nextSeqPtr) { + assert(TEST_PSENTRY(psPtr2)); + if (numPats == psPtr2->numPats && memcmp(patPtr, psPtr2->pats, sequenceSize) == 0) { + ckfree(psPtr); + if (maskPtr) { + *maskPtr = eventMask; + } + return psPtr2; } } } @@ -3805,22 +4713,28 @@ FindSequence( * silently ignore missing bindings. */ + ckfree(psPtr); return NULL; } - psPtr = ckalloc(sizeof(PatSeq) + (numPats-1)*sizeof(TkPattern)); + + DEBUG(countSeqItems += 1); + psPtr->numPats = numPats; + psPtr->count = totalCount; + psPtr->number = lookupTables->number++; + psPtr->added = 0; + psPtr->modMaskUsed = (modMask != 0); psPtr->script = NULL; - psPtr->flags = flags; psPtr->nextSeqPtr = Tcl_GetHashValue(hPtr); psPtr->hPtr = hPtr; - psPtr->voPtr = NULL; - psPtr->nextObjPtr = NULL; + psPtr->ptr.nextObj = NULL; + assert(psPtr->ptr.owners == NULL); + DEBUG(psPtr->owned = 0); Tcl_SetHashValue(hPtr, psPtr); - memcpy(psPtr->pats, patPtr, sequenceSize); - - done: - *maskPtr = eventMask; + if (maskPtr) { + *maskPtr = eventMask; + } return psPtr; } @@ -3845,33 +4759,44 @@ FindSequence( *--------------------------------------------------------------------------- */ -static int +/* helper function */ +static unsigned +FinalizeParseEventDescription( + Tcl_Interp *interp, + TkPattern *patPtr, + unsigned count, + Tcl_Obj* errorObj, + const char* errCode) +{ + assert(patPtr); + assert(!errorObj == (count > 0)); + + if (errorObj) { + Tcl_SetObjResult(interp, errorObj); + Tcl_SetErrorCode(interp, "TK", "EVENT", errCode, NULL); + } + patPtr->count = count; + return count; +} + +static unsigned ParseEventDescription( Tcl_Interp *interp, /* For error messages. */ - const char **eventStringPtr,/* On input, holds a pointer to start of event - * string. On exit, gets pointer to rest of - * string after parsed event. */ - TkPattern *patPtr, /* Filled with the pattern parsed from the - * event string. */ - unsigned long *eventMaskPtr)/* Filled with event mask of matched event. */ + const char **eventStringPtr,/* On input, holds a pointer to start of event string. On exit, + * gets pointer to rest of string after parsed event. */ + TkPattern *patPtr, /* Filled with the pattern parsed from the event string. */ + EventMask *eventMaskPtr) /* Filled with event mask of matched event. */ { - char *p; - unsigned long eventMask; - int count, eventFlags; -#define FIELD_SIZE 48 - char field[FIELD_SIZE]; - Tcl_HashEntry *hPtr; - Tcl_DString copy; + const char *p; + EventMask eventMask = 0; + unsigned count = 1; - Tcl_DStringInit(©); - p = Tcl_DStringAppend(©, *eventStringPtr, -1); + assert(eventStringPtr); + assert(patPtr); + assert(eventMaskPtr); - patPtr->eventType = -1; - patPtr->needMods = 0; - patPtr->detail.clientData = 0; - - eventMask = 0; - count = 1; + p = *eventStringPtr; + memset(patPtr, 0, sizeof(TkPattern)); /* otherwise memcmp doesn't work */ /* * Handle simple ASCII characters. @@ -3883,203 +4808,214 @@ ParseEventDescription( patPtr->eventType = KeyPress; eventMask = KeyPressMask; string[0] = *p; - string[1] = 0; - patPtr->detail.keySym = TkStringToKeysym(string); - if (patPtr->detail.keySym == NoSymbol) { - if (isprint(UCHAR(*p))) { - patPtr->detail.keySym = *p; - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad ASCII character 0x%x", UCHAR(*p))); - Tcl_SetErrorCode(interp, "TK", "EVENT", "BAD_CHAR", NULL); - count = 0; - goto done; - } + string[1] = '\0'; + patPtr->info = TkStringToKeysym(string); + if (patPtr->info == NoSymbol) { + if (!isprint(UCHAR(*p))) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("bad ASCII character 0x%x", UCHAR(*p)), "BAD_CHAR"); + } + patPtr->info = *p; } - p++; - goto end; - } - - /* - * A fancier event description. This can be either a virtual event or a - * physical event. - * - * A virtual event description consists of: - * - * 1. double open angle brackets. - * 2. virtual event name. - * 3. double close angle brackets. - * - * A physical event description consists of: - * - * 1. open angle bracket. - * 2. any number of modifiers, each followed by spaces or dashes. - * 3. an optional event name. - * 4. an option button or keysym name. Either this or item 3 *must* be - * present; if both are present then they are separated by spaces or - * dashes. - * 5. a close angle bracket. - */ - - p++; - if (*p == '<') { + ++p; + } else { /* - * This is a virtual event: soak up all the characters up to the next - * '>'. + * A fancier event description. This can be either a virtual event or a physical event. + * + * A virtual event description consists of: + * + * 1. double open angle brackets. + * 2. virtual event name. + * 3. double close angle brackets. + * + * A physical event description consists of: + * + * 1. open angle bracket. + * 2. any number of modifiers, each followed by spaces or dashes. + * 3. an optional event name. + * 4. an option button or keysym name. Either this or item 3 *must* be present; if both + * are present then they are separated by spaces or dashes. + * 5. a close angle bracket. */ - char *field = p + 1; - - p = strchr(field, '>'); - if (p == field) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "virtual event \"<<>>\" is badly formed", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "MALFORMED", - NULL); - count = 0; - goto done; - } - if ((p == NULL) || (p[1] != '>')) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "missing \">\" in virtual binding", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "VIRTUAL", "MALFORMED", - NULL); - count = 0; - goto done; - } - *p = '\0'; - patPtr->eventType = VirtualEvent; - eventMask = VirtualEventMask; - patPtr->detail.name = Tk_GetUid(field); - *p = '>'; - - p += 2; - goto end; - } - - while (1) { - ModInfo *modPtr; - - p = GetField(p, field, FIELD_SIZE); - if (*p == '>') { + ++p; + if (*p == '<') { /* - * This solves the problem of, e.g., <Control-M> being - * misinterpreted as Control + Meta + missing keysym instead of - * Control + KeyPress + M. + * This is a virtual event: soak up all the characters up to the next '>'. */ - break; - } - hPtr = Tcl_FindHashEntry(&modTable, field); - if (hPtr == NULL) { - break; - } - modPtr = Tcl_GetHashValue(hPtr); - patPtr->needMods |= modPtr->mask; - if (modPtr->flags & MULT_CLICKS) { - int i = modPtr->flags & MULT_CLICKS; - - count = 2; - while (i >>= 1) { - count++; - } - } - while ((*p == '-') || isspace(UCHAR(*p))) { - p++; - } - } - - eventFlags = 0; - hPtr = Tcl_FindHashEntry(&eventTable, field); - if (hPtr != NULL) { - const EventInfo *eiPtr = Tcl_GetHashValue(hPtr); - - patPtr->eventType = eiPtr->type; - eventFlags = flagArray[eiPtr->type]; - eventMask = eiPtr->eventMask; - while ((*p == '-') || isspace(UCHAR(*p))) { - p++; - } - p = GetField(p, field, FIELD_SIZE); - } - if (*field != '\0') { - if ((*field >= '1') && (*field <= '9') && (field[1] == '\0')) { - if (eventFlags == 0) { - patPtr->eventType = ButtonPress; - eventMask = ButtonPressMask; - } else if (eventFlags & KEY) { - goto getKeysym; - } else if (!(eventFlags & BUTTON)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "specified button \"%s\" for non-button event", - field)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "NON_BUTTON", NULL); - count = 0; - goto done; - } - patPtr->detail.button = (*field - '0'); - if (patPtr->detail.button >= Button4) { - patPtr->detail.button += (Button8 - Button4); - } + const char *field = p + 1; + char buf[256]; + char* bufPtr = buf; + unsigned size; + + p = strchr(field, '>'); + if (p == field) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_NewStringObj("virtual event \"<<>>\" is badly formed", -1), "MALFORMED"); + } + if (!p || p[1] != '>') { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_NewStringObj("missing \">\" in virtual binding", -1), "MALFORMED"); + } + + size = p - field; + if (size >= sizeof(buf)) { + bufPtr = ckalloc(size + 1); + } + strncpy(bufPtr, field, size); + bufPtr[size] = '\0'; + eventMask = VirtualEventMask; + patPtr->eventType = VirtualEvent; + patPtr->name = Tk_GetUid(bufPtr); + if (bufPtr != buf) { + ckfree(bufPtr); + } + p += 2; } else { + unsigned eventFlags; + char field[512]; + Tcl_HashEntry *hPtr; - getKeysym: - patPtr->detail.keySym = TkStringToKeysym(field); - if (patPtr->detail.keySym == NoSymbol) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad event type or keysym \"%s\"", field)); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "KEYSYM", field, - NULL); - count = 0; - goto done; - } - if (eventFlags == 0) { - patPtr->eventType = KeyPress; - eventMask = KeyPressMask; - } else if (!(eventFlags & KEY)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "specified keysym \"%s\" for non-key event", field)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "NON_KEY", NULL); - count = 0; - goto done; - } - } - } else if (eventFlags == 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "no event type or button # or keysym", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "UNMODIFIABLE", NULL); - count = 0; - goto done; - } + while (1) { + ModInfo *modPtr; - while ((*p == '-') || isspace(UCHAR(*p))) { - p++; - } - if (*p != '>') { - while (*p != '\0') { - p++; - if (*p == '>') { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "extra characters after detail in binding", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "PAST_DETAIL", NULL); - count = 0; - goto done; + p = GetField(p, field, sizeof(field)); + if (*p == '>') { + /* + * This solves the problem of, e.g., <Control-M> being + * misinterpreted as Control + Meta + missing keysym instead of + * Control + KeyPress + M. + */ + + break; + } + if (!(hPtr = Tcl_FindHashEntry(&modTable, field))) { + break; + } + modPtr = Tcl_GetHashValue(hPtr); + patPtr->modMask |= modPtr->mask; + if (modPtr->flags & MULT_CLICKS) { + unsigned i = modPtr->flags & MULT_CLICKS; + + count = 2; + while (i >>= 1) { + ++count; + } + } + p = SkipFieldDelims(p); + } + + eventFlags = 0; + if ((hPtr = Tcl_FindHashEntry(&eventTable, field))) { + const EventInfo *eiPtr = Tcl_GetHashValue(hPtr); + + patPtr->eventType = eiPtr->type; + eventFlags = flagArray[eiPtr->type]; + eventMask = eiPtr->eventMask; + p = GetField(SkipFieldDelims(p), field, sizeof(field)); + } + if (*field) { + unsigned button = GetButtonNumber(field); + + if ((eventFlags & BUTTON) + || (button && eventFlags == 0) + || (SUPPORT_ADDITIONAL_MOTION_SYNTAX && (eventFlags & MOTION) && button == 0)) { + /* This must be a button (or bad motion) event */ + if (button == 0) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("bad button number \"%s\"", field), "BUTTON"); + } + patPtr->info = button; + if (!(eventFlags & BUTTON)) { + patPtr->eventType = ButtonPress; + eventMask = ButtonPressMask; + } + } else if ((eventFlags & KEY) || eventFlags == 0) { + /* This must be a key event */ + patPtr->info = TkStringToKeysym(field); + if (patPtr->info == NoSymbol) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("bad event type or keysym \"%s\"", field), "KEYSYM"); + } + if (!(eventFlags & KEY)) { + patPtr->eventType = KeyPress; + eventMask = KeyPressMask; + } + } else if (button) { + if (!SUPPORT_ADDITIONAL_MOTION_SYNTAX || patPtr->eventType != MotionNotify) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("specified button \"%s\" for non-button event", field), + "NON_BUTTON"); + } +#if SUPPORT_ADDITIONAL_MOTION_SYNTAX + patPtr->modMask |= TkGetButtonMask(button); + p = SkipFieldDelims(p); + while (*p && *p != '>') { + p = SkipFieldDelims(GetField(p, field, sizeof(field))); + if ((button = GetButtonNumber(field)) == 0) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("bad button number \"%s\"", field), "BUTTON"); + } + patPtr->modMask |= TkGetButtonMask(button); + } + patPtr->info = ButtonNumberFromState(patPtr->modMask); +#endif + } else { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_ObjPrintf("specified keysym \"%s\" for non-key event", field), + "NON_KEY"); + } + } else if (eventFlags == 0) { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_NewStringObj("no event type or button # or keysym", -1), "UNMODIFIABLE"); + } else if (patPtr->eventType == MotionNotify) { + patPtr->info = ButtonNumberFromState(patPtr->modMask); + } + + p = SkipFieldDelims(p); + + if (*p != '>') { + while (*p) { + ++p; + if (*p == '>') { + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_NewStringObj("extra characters after detail in binding", -1), + "PAST_DETAIL"); + } + } + return FinalizeParseEventDescription( + interp, + patPtr, 0, + Tcl_NewStringObj("missing \">\" in binding", -1), "MALFORMED"); } + ++p; } - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "missing \">\" in binding", -1)); - Tcl_SetErrorCode(interp, "TK", "EVENT", "MALFORMED", NULL); - count = 0; - goto done; } - p++; - end: - *eventStringPtr += (p - Tcl_DStringValue(©)); + *eventStringPtr = p; *eventMaskPtr |= eventMask; - done: - Tcl_DStringFree(©); - return count; + return FinalizeParseEventDescription(interp, patPtr, count, NULL, NULL); } /* @@ -4103,18 +5039,17 @@ ParseEventDescription( *---------------------------------------------------------------------- */ -static char * +static const char * GetField( - char *p, /* Pointer to part of pattern. */ - char *copy, /* Place to copy field. */ - int size) /* Maximum number of characters to copy. */ + const char *p, /* Pointer to part of pattern. */ + char *copy, /* Place to copy field. */ + unsigned size) /* Maximum number of characters to copy. */ { - while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>') - && (*p != '-') && (size > 1)) { - *copy = *p; - p++; - copy++; - size--; + assert(p); + assert(copy); + + for ( ; *p && !isspace(UCHAR(*p)) && *p != '>' && *p != '-' && size > 1u; --size) { + *copy++ = *p++; } *copy = '\0'; return p; @@ -4140,112 +5075,99 @@ GetField( static Tcl_Obj * GetPatternObj( - PatSeq *psPtr) + const PatSeq *psPtr) { - TkPattern *patPtr; - int patsLeft, needMods; - const ModInfo *modPtr; - const EventInfo *eiPtr; Tcl_Obj *patternObj = Tcl_NewObj(); + unsigned i; - /* - * The order of the patterns in the sequence is backwards from the order - * in which they must be output. - */ + assert(psPtr); + + for (i = 0; i < psPtr->numPats; ++i) { + const TkPattern *patPtr = psPtr->pats + i; - for (patsLeft = psPtr->numPats, patPtr = &psPtr->pats[psPtr->numPats - 1]; - patsLeft > 0; patsLeft--, patPtr--) { /* * Check for simple case of an ASCII character. */ - - if ((patPtr->eventType == KeyPress) - && !(psPtr->flags & PAT_NEARBY) - && (patPtr->needMods == 0) - && (patPtr->detail.keySym < 128) - && isprint(UCHAR(patPtr->detail.keySym)) - && (patPtr->detail.keySym != '<') - && (patPtr->detail.keySym != ' ')) { - char c = (char) patPtr->detail.keySym; - + if (patPtr->eventType == KeyPress + && patPtr->count == 1 + && patPtr->modMask == 0 + && patPtr->info < 128 + && isprint(UCHAR(patPtr->info)) + && patPtr->info != '<' + && patPtr->info != ' ') { + char c = (char) patPtr->info; Tcl_AppendToObj(patternObj, &c, 1); - continue; - } - - /* - * Check for virtual event. - */ + } else if (patPtr->eventType == VirtualEvent) { + assert(patPtr->name); + Tcl_AppendPrintfToObj(patternObj, "<<%s>>", patPtr->name); + } else { + ModMask modMask; + const ModInfo *modPtr; - if (patPtr->eventType == VirtualEvent) { - Tcl_AppendPrintfToObj(patternObj, "<<%s>>", patPtr->detail.name); - continue; - } + /* + * It's a more general event specification. First check for "Double", + * "Triple", "Quadruple", then modifiers, then event type, then keysym + * or button detail. + */ - /* - * It's a more general event specification. First check for "Double", - * "Triple", "Quadruple", then modifiers, then event type, then keysym - * or button detail. - */ + Tcl_AppendToObj(patternObj, "<", 1); - Tcl_AppendToObj(patternObj, "<", 1); - - if ((psPtr->flags & PAT_NEARBY) && (patsLeft > 1) - && (memcmp(patPtr, patPtr-1, sizeof(TkPattern)) == 0)) { - patsLeft--; - patPtr--; - if ((patsLeft > 1) && - (memcmp(patPtr, patPtr-1, sizeof(TkPattern)) == 0)) { - patsLeft--; - patPtr--; - if ((patsLeft > 1) && - (memcmp(patPtr, patPtr-1, sizeof(TkPattern)) == 0)) { - patsLeft--; - patPtr--; - Tcl_AppendToObj(patternObj, "Quadruple-", 10); - } else { - Tcl_AppendToObj(patternObj, "Triple-", 7); - } - } else { - Tcl_AppendToObj(patternObj, "Double-", 7); + switch (patPtr->count) { + case 2: Tcl_AppendToObj(patternObj, "Double-", 7); break; + case 3: Tcl_AppendToObj(patternObj, "Triple-", 7); break; + case 4: Tcl_AppendToObj(patternObj, "Quadruple-", 10); break; } - } - for (needMods = patPtr->needMods, modPtr = modArray; - needMods != 0; modPtr++) { - if (modPtr->mask & needMods) { - needMods &= ~modPtr->mask; - Tcl_AppendPrintfToObj(patternObj, "%s-", modPtr->name); + modMask = patPtr->modMask; +#if PRINT_SHORT_MOTION_SYNTAX + if (patPtr->eventType == MotionNotify) { + modMask &= ~(ModMask)ALL_BUTTONS; } - } +#endif - for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) { - if (eiPtr->type == patPtr->eventType) { - Tcl_AppendToObj(patternObj, eiPtr->name, -1); - if (patPtr->detail.clientData != 0) { - Tcl_AppendToObj(patternObj, "-", 1); + for (modPtr = modArray; modMask; ++modPtr) { + if (modPtr->mask & modMask) { + modMask &= ~modPtr->mask; + Tcl_AppendPrintfToObj(patternObj, "%s-", modPtr->name); } - break; } - } - if (patPtr->detail.clientData != 0) { - if ((patPtr->eventType == KeyPress) - || (patPtr->eventType == KeyRelease)) { - const char *string = TkKeysymToString(patPtr->detail.keySym); + assert(patPtr->eventType < TK_LASTEVENT); + assert(((size_t) eventArrayIndex[patPtr->eventType]) < SIZE_OF_ARRAY(eventArray)); + Tcl_AppendToObj(patternObj, eventArray[eventArrayIndex[patPtr->eventType]].name, -1); - if (string != NULL) { - Tcl_AppendToObj(patternObj, string, -1); + if (patPtr->info) { + switch (patPtr->eventType) { + case KeyPress: + case KeyRelease: { + const char *string = TkKeysymToString(patPtr->info); + if (string) { + Tcl_AppendToObj(patternObj, "-", 1); + Tcl_AppendToObj(patternObj, string, -1); + } + break; } - } else { - int button = patPtr->detail.button; - if (button >= Button8) { - button -= (Button8 - Button4); + case ButtonPress: + case ButtonRelease: + assert(patPtr->info <= 13); + Tcl_AppendPrintfToObj(patternObj, "-%d", (int) ((patPtr->info > 9) ? (patPtr->info - 4) : patPtr->info)); + break; +#if PRINT_SHORT_MOTION_SYNTAX + case MotionNotify: { + ModMask mask = patPtr->modMask; + while (mask & ALL_BUTTONS) { + int button = ButtonNumberFromState(mask); + Tcl_AppendPrintfToObj(patternObj, "-%d", (button > 9) ? (button - 4) : button); + mask &= ~TkGetButtonMask(button); + } + break; + } +#endif } - Tcl_AppendPrintfToObj(patternObj, "%d", button); } - } - Tcl_AppendToObj(patternObj, ">", 1); + Tcl_AppendToObj(patternObj, ">", 1); + } } return patternObj; @@ -4275,17 +5197,19 @@ TkStringToKeysym( #ifdef REDO_KEYSYM_LOOKUP Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&keySymTable, name); - if (hPtr != NULL) { + if (hPtr) { return (KeySym) Tcl_GetHashValue(hPtr); } - if (strlen(name) == 1) { + assert(name); + if (strlen(name) == 1u) { KeySym keysym = (KeySym) (unsigned char) name[0]; - if (TkKeysymToString(keysym) != NULL) { + if (TkKeysymToString(keysym)) { return keysym; } } #endif /* REDO_KEYSYM_LOOKUP */ + assert(name); return XStringToKeysym(name); } @@ -4311,9 +5235,9 @@ TkKeysymToString( KeySym keysym) { #ifdef REDO_KEYSYM_LOOKUP - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&nameTable, keysym); + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym); - if (hPtr != NULL) { + if (hPtr) { return Tcl_GetHashValue(hPtr); } #endif /* REDO_KEYSYM_LOOKUP */ @@ -4344,10 +5268,10 @@ XEvent * TkpGetBindingXEvent( Tcl_Interp *interp) /* Interpreter. */ { - TkWindow *winPtr = (TkWindow *) Tk_MainWindow(interp); - BindingTable *bindPtr = winPtr->mainPtr->bindingTable; + TkWindow *winPtr = (TkWindow *) Tk_MainWindow(interp); + BindingTable *bindPtr = winPtr->mainPtr->bindingTable; - return &(bindPtr->eventRing[bindPtr->curEvent]); + return &bindPtr->curEvent->xev; } /* @@ -4371,6 +5295,8 @@ void TkpCancelWarp( TkDisplay *dispPtr) { + assert(dispPtr); + if (dispPtr->flags & TK_DISPLAY_IN_WARP) { Tcl_CancelIdleCall(DoWarp, dispPtr); dispPtr->flags &= ~TK_DISPLAY_IN_WARP; @@ -4378,9 +5304,81 @@ TkpCancelWarp( } /* + *---------------------------------------------------------------------- + * + * TkpDumpPS -- + * + * Dump given pattern sequence to stdout. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +#if SUPPORT_DEBUGGING +void +TkpDumpPS( + const PatSeq *psPtr) +{ + if (!psPtr) { + fprintf(stdout, "<null>\n"); + } else { + Tcl_Obj* result = GetPatternObj(psPtr); + Tcl_IncrRefCount(result); + fprintf(stdout, "%s", Tcl_GetString(result)); + if (psPtr->object) { + fprintf(stdout, " (%s)", (char *) psPtr->object); + } + fprintf(stdout, "\n"); + Tcl_DecrRefCount(result); + } +} +#endif + +/* + *---------------------------------------------------------------------- + * + * TkpDumpPSList -- + * + * Dump given pattern sequence list to stdout. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +#if SUPPORT_DEBUGGING +void +TkpDumpPSList( + const PSList *psList) +{ + if (!psList) { + fprintf(stdout, "<null>\n"); + } else { + const PSEntry *psEntry; + + fprintf(stdout, "Dump PSList ========================================\n"); + TK_DLIST_FOREACH(psEntry, psList) { + TkpDumpPS(psEntry->psPtr); + } + fprintf(stdout, "====================================================\n"); + } +} +#endif + +/* * Local Variables: * mode: c * c-basic-offset: 4 - * fill-column: 78 + * fill-column: 105 * End: + * vi:set ts=8 sw=4: */ diff --git a/generic/tkCanvUtil.c b/generic/tkCanvUtil.c index 52d7bf6..2c6d480 100644 --- a/generic/tkCanvUtil.c +++ b/generic/tkCanvUtil.c @@ -12,7 +12,6 @@ #include "tkInt.h" #include "tkCanvas.h" -#include <assert.h> /* * Structures defined only in this file. diff --git a/generic/tkDList.h b/generic/tkDList.h new file mode 100644 index 0000000..2448439 --- /dev/null +++ b/generic/tkDList.h @@ -0,0 +1,546 @@ +/* + * tkDList.h -- + * + * A list is headed by pointers to first and last element. The elements + * are doubly linked so that an arbitrary element can be removed without + * a need to traverse the list. New elements can be added to the list + * before or after an existing element or at the head/tail of the list. + * A list may be traversed in the forward or backward direction. + * + * Copyright (c) 2018 by Gregor Cramer. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +/* + * Note that this file will not be included in header files, it is the purpose + * of this file to be included in source files only. Thus we are not using the + * prefix "Tk_" here for functions, because all the functions have private scope. + */ + +/* + * ------------------------------------------------------------------------------- + * Use the double linked list in the following way: + * ------------------------------------------------------------------------------- + * typedef struct MyListEntry { TK_DLIST_LINKS(MyListEntry); int value; } MyListEntry; + * TK_DLIST_DEFINE(MyList, MyListEntry); + * MyList listHdr = TK_DLIST_LIST_INITIALIZER; // or MyList_Init(&listHdr) + * MyListEntry *p; + * int i = 0; + * MyList_Append(&listHdr, ckalloc(sizeof(MyListEntry))); + * MyList_Append(&listHdr, ckalloc(sizeof(MyListEntry))); + * TK_DLIST_FOREACH(p, &listHdr) { p->value = i++; } + * // ... + * MyList_RemoveHead(&listHdr); + * MyList_RemoveHead(&listHdr); + * MyList_Clear(MyListEntry, &listHdr); // invokes ckfree() for each element + * ------------------------------------------------------------------------------- + * IMPORTANT NOTE: TK_DLIST_LINKS must be used at start of struct! + */ + +#ifndef TK_DLIST_DEFINED +#define TK_DLIST_DEFINED + +#include "tkInt.h" + +/* + * List definitions. + */ + +#define TK_DLIST_LINKS(ElemType) \ +struct { \ + struct ElemType *prev; /* previous element */ \ + struct ElemType *next; /* next element */ \ +} _dl_ + +#define TK_DLIST_LIST_INITIALIZER { NULL, NULL } +#define TK_DLIST_ELEM_INITIALIZER { NULL, NULL } + +/*************************************************************************/ +/* + * DList_Init: Initialize list header. This can also be used to clear the + * list. + * + * See also: DList_Clear() + */ +/*************************************************************************/ +/* + * DList_ElemInit: Initialize a list element. + */ +/*************************************************************************/ +/* + * DList_InsertAfter: Insert 'elem' after 'listElem'. 'elem' must not + * be linked, but 'listElem' must be linked. + */ +/*************************************************************************/ +/* + * DList_InsertBefore: Insert 'elem' before 'listElem'. 'elem' must not + * be linked, but 'listElem' must be linked. + */ +/*************************************************************************/ +/* + * DList_Prepend: Insert 'elem' at start of list. This element must not + * be linked. + * + * See also: DList_Append() + */ +/*************************************************************************/ +/* + * DList_Append: Append 'elem' to end of list. This element must not + * be linked. + * + * See also: DList_Prepend() + */ +/*************************************************************************/ +/* + * DList_Move: Append all list items of 'src' to end of 'dst'. + */ +/*************************************************************************/ +/* + * DList_Remove: Remove 'elem' from list. This element must be linked. + * + * See also: DList_Free() + */ +/*************************************************************************/ +/* + * DList_Free: Remove 'elem' from list and free it. This element must + * be linked. + * + * See also: DList_Remove() + */ +/*************************************************************************/ +/* + * DList_RemoveHead: Remove first element from list. The list must + * not be empty. + * + * See also: DList_FreeHead() + */ +/*************************************************************************/ +/* + * DList_RemoveTail: Remove last element from list. The list must + * not be empty. + * + * See also: DList_FreeTail() + */ +/*************************************************************************/ +/* + * DList_FreeHead: Remove first element from list and free it. + * The list must not be empty. + * + * See also: DList_RemoveHead() + */ +/*************************************************************************/ +/* + * DList_FreeTail: Remove last element from list and free it. + * The list must not be empty. + * + * See also: DList_RemoveTail() + */ +/*************************************************************************/ +/* + * DList_SwapElems: Swap positions of given elements 'lhs' and 'rhs'. + * Both elements must be linked, and must belong to same list. + */ +/*************************************************************************/ +/* + * DList_Clear: Clear whole list and free all elements. + * + * See also: DList_Init + */ +/*************************************************************************/ +/* + * DList_Traverse: Iterate over all elements in list from first to last. + * Call given function func(head, elem) for each element. The function has + * to return the next element in list to traverse, normally this is + * DList_Next(elem). + * + * See also: TK_DLIST_FOREACH, TK_DLIST_FOREACH_REVERSE + */ +/*************************************************************************/ +/* + * DList_First: Return pointer of first element in list, maybe it's NULL. + */ +/*************************************************************************/ +/* + * DList_Last: Return pointer of last element in list, maybe it's NULL. + */ +/*************************************************************************/ +/* + * DList_Next: Return pointer of next element after 'elem', maybe it's NULL. + * + * See also: DList_Prev() + */ +/*************************************************************************/ +/* + * DList_Prev: Return pointer of previous element before 'elem', maybe it's + * NULL. + * + * See also: DList_Next() + */ +/*************************************************************************/ +/* + * DList_IsEmpty: Test whether given list is empty. + */ +/*************************************************************************/ +/* + * DList_IsLinked: Test whether given element is linked. + */ +/*************************************************************************/ +/* + * DList_IsFirst: Test whether given element is first element in list. + * Note that 'elem' must be linked. + * + * See also: DList_IsLast(), DList_IsLinked() + */ +/*************************************************************************/ +/* + * DList_IsLast: Test whether given element is last element in list. + * Note that 'elem' must be linked. + * + * See also: DList_IsFirst(), DList_IsLinked() + */ +/*************************************************************************/ +/* + * DList_Size: Count number of elements in given list. + */ +/*************************************************************************/ +/* + * TK_DLIST_FOREACH: Iterate over all elements in list from first to last. + * 'var' is the name of the variable which points to current element. + * + * See also: TK_DLIST_FOREACH_REVERSE, DList_Traverse() + */ +/*************************************************************************/ +/* + * TK_DLIST_FOREACH_REVERSE: Iterate over all elements in list from last + * to first (backwards). 'var' is the name of the variable which points to + * current element. + */ +/*************************************************************************/ + +#if defined(__GNUC__) || defined(__clang__) +# define __TK_DLIST_UNUSED __attribute__((unused)) +#else +# define __TK_DLIST_UNUSED +#endif + +#define TK_DLIST_DEFINE(LT, ElemType) /* LT = type of head/list */ \ +/* ------------------------------------------------------------------------- */ \ +typedef struct LT { \ + struct ElemType *first; /* first element */ \ + struct ElemType *last; /* last element */ \ +} LT; \ + \ +typedef struct ElemType *(LT##_Func)(LT *head, struct ElemType *elem); \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Init(LT *head) \ +{ \ + assert(head); \ + head->first = NULL; \ + head->last = NULL; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_ElemInit(struct ElemType *elem) \ +{ \ + assert(elem); \ + elem->_dl_.next = NULL; \ + elem->_dl_.prev = NULL; \ +} \ + \ +__TK_DLIST_UNUSED \ +static int \ +LT##_IsEmpty(LT *head) \ +{ \ + assert(head); \ + return head->first == NULL; \ +} \ + \ +__TK_DLIST_UNUSED \ +static int \ +LT##_IsLinked(struct ElemType *elem) \ +{ \ + assert(elem); \ + return elem->_dl_.next && elem->_dl_.prev; \ +} \ + \ +__TK_DLIST_UNUSED \ +static int \ +LT##_IsFirst(struct ElemType *elem) \ +{ \ + assert(LT##_IsLinked(elem)); \ + return elem->_dl_.prev->_dl_.prev == elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static int \ +LT##_IsLast(struct ElemType *elem) \ +{ \ + assert(LT##_IsLinked(elem)); \ + return elem->_dl_.next->_dl_.next == elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static struct ElemType * \ +LT##_First(LT *head) \ +{ \ + assert(head); \ + return head->first; \ +} \ + \ +__TK_DLIST_UNUSED \ +static struct ElemType * \ +LT##_Last(LT *head) \ +{ \ + assert(head); \ + return head->last; \ +} \ + \ +__TK_DLIST_UNUSED \ +static struct ElemType * \ +LT##_Next(struct ElemType *elem) \ +{ \ + assert(elem); \ + return LT##_IsLast(elem) ? NULL : elem->_dl_.next; \ +} \ + \ +__TK_DLIST_UNUSED \ +static struct ElemType * \ +LT##_Prev(struct ElemType *elem) \ +{ \ + assert(elem); \ + return LT##_IsFirst(elem) ? NULL : elem->_dl_.prev; \ +} \ + \ +__TK_DLIST_UNUSED \ +static unsigned \ +LT##_Size(const LT *head) \ +{ \ + const struct ElemType *elem; \ + unsigned size = 0; \ + assert(head); \ + if ((elem = head->first)) { \ + for ( ; elem != (void *) head; elem = elem->_dl_.next) { \ + ++size; \ + } \ + } \ + return size; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_InsertAfter(struct ElemType *listElem, struct ElemType *elem) \ +{ \ + assert(listElem); \ + assert(elem); \ + elem->_dl_.next = listElem->_dl_.next; \ + elem->_dl_.prev = listElem; \ + listElem->_dl_.next->_dl_.prev = elem; \ + listElem->_dl_.next = elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_InsertBefore(struct ElemType *listElem, struct ElemType *elem) \ +{ \ + assert(listElem); \ + assert(elem); \ + elem->_dl_.next = listElem; \ + elem->_dl_.prev = listElem->_dl_.prev;; \ + listElem->_dl_.prev->_dl_.next = elem; \ + listElem->_dl_.prev = elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Prepend(LT *head, struct ElemType *elem) \ +{ \ + assert(head); \ + assert(elem); \ + elem->_dl_.prev = (void *) head; \ + if (!head->first) { \ + elem->_dl_.next = (void *) head; \ + head->last = elem; \ + } else { \ + elem->_dl_.next = head->first; \ + head->first->_dl_.prev = elem; \ + } \ + head->first = elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Append(LT *head, struct ElemType *elem) \ +{ \ + assert(head); \ + assert(elem); \ + elem->_dl_.next = (void *) head; \ + if (!head->first) { \ + elem->_dl_.prev = (void *) head; \ + head->first = elem; \ + } else { \ + elem->_dl_.prev = head->last; \ + head->last->_dl_.next = elem; \ + } \ + head->last = elem; \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Move(LT *dst, LT *src) \ +{ \ + assert(dst); \ + assert(src); \ + if (src->first) { \ + if (dst->first) { \ + dst->last->_dl_.next = src->first; \ + src->first->_dl_.prev = dst->last; \ + dst->last = src->last; \ + } else { \ + *dst = *src; \ + dst->first->_dl_.prev = (void *) dst; \ + } \ + dst->last->_dl_.next = (void *) dst; \ + LT##_Init(src); \ + } \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Remove(struct ElemType *elem) \ +{ \ + int isFirst, isLast; \ + assert(LT##_IsLinked(elem)); \ + isFirst = LT##_IsFirst(elem); \ + isLast = LT##_IsLast(elem); \ + if (isFirst && isLast) { \ + ((LT *) elem->_dl_.prev)->first = NULL; \ + ((LT *) elem->_dl_.next)->last = NULL; \ + } else { \ + if (isFirst) { \ + ((LT *) elem->_dl_.prev)->first = elem->_dl_.next; \ + } else { \ + elem->_dl_.prev->_dl_.next = elem->_dl_.next; \ + } \ + if (isLast) { \ + ((LT *) elem->_dl_.next)->last = elem->_dl_.prev; \ + } else { \ + elem->_dl_.next->_dl_.prev = elem->_dl_.prev; \ + } \ + } \ + LT##_ElemInit(elem); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Free(struct ElemType *elem) \ +{ \ + LT##_Remove(elem); \ + ckfree((void *) elem); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_RemoveHead(LT *head) \ +{ \ + assert(!LT##_IsEmpty(head)); \ + LT##_Remove(head->first); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_RemoveTail(LT *head) \ +{ \ + assert(!LT##_IsEmpty(head)); \ + LT##_Remove(head->last); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_FreeHead(LT *head) \ +{ \ + assert(!LT##_IsEmpty(head)); \ + LT##_Free(head->first); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_FreeTail(LT *head) \ +{ \ + assert(!LT##_IsEmpty(head)); \ + LT##_Free(head->last); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_SwapElems(struct ElemType *lhs, struct ElemType *rhs) \ +{ \ + assert(lhs); \ + assert(rhs); \ + if (lhs != rhs) { \ + struct ElemType tmp; \ + if (LT##_IsFirst(lhs)) { \ + ((LT *) lhs->_dl_.prev)->first = rhs; \ + } else if (LT##_IsFirst(rhs)) { \ + ((LT *) rhs->_dl_.prev)->first = lhs; \ + } \ + if (LT##_IsLast(lhs)) { \ + ((LT *) lhs->_dl_.next)->last = rhs; \ + } else if (LT##_IsLast(rhs)) { \ + ((LT *) rhs->_dl_.next)->last = lhs; \ + } \ + tmp._dl_ = lhs->_dl_; \ + lhs->_dl_ = rhs->_dl_; \ + rhs->_dl_ = tmp._dl_; \ + } \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Clear(LT *head) \ +{ \ + struct ElemType *p; \ + struct ElemType *next; \ + assert(head); \ + for (p = head->first; p; p = next) { \ + next = LT##_Next(p); \ + ckfree((void *) p); \ + } \ + LT##_Init(head); \ +} \ + \ +__TK_DLIST_UNUSED \ +static void \ +LT##_Traverse(LT *head, LT##_Func func) \ +{ \ + struct ElemType *next; \ + struct ElemType *p; \ + assert(head); \ + for (p = head->first; p; p = next) { \ + next = func(head, p); \ + } \ +} \ +/* ------------------------------------------------------------------------- */ + +#define TK_DLIST_FOREACH(var, head) \ + assert(head); \ + for (var = head->first ? head->first : (void *) head; var != (void *) head; var = var->_dl_.next) + +#define TK_DLIST_FOREACH_REVERSE(var, head) \ + assert(head); \ + for (var = head->last ? head->last : (void *) head; var != (void *) head; var = var->_dl_.prev) + +#endif /* TK_DLIST_DEFINED */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 105 + * End: + * vi:set ts=8 sw=4: + */ diff --git a/generic/tkEvent.c b/generic/tkEvent.c index edba156..f368957 100644 --- a/generic/tkEvent.c +++ b/generic/tkEvent.c @@ -207,7 +207,6 @@ static Window ParentXId(Display *display, Window w); static int RefreshKeyboardMappingIfNeeded(XEvent *eventPtr); static int TkXErrorHandler(ClientData clientData, XErrorEvent *errEventPtr); -static void UpdateButtonEventState(XEvent *eventPtr); static int WindowEventProc(Tcl_Event *evPtr, int flags); #ifdef TK_USE_INPUT_METHODS static void CreateXIC(TkWindow *winPtr); @@ -554,69 +553,6 @@ TkGetButtonMask( /* *---------------------------------------------------------------------- * - * UpdateButtonEventState -- - * - * Update the button event state in our TkDisplay using the XEvent - * passed. We also may modify the the XEvent passed to fit some aspects - * of our TkDisplay. - * - * Results: - * None. - * - * Side effects: - * The TkDisplay's private button state may be modified. The eventPtr's - * state may be updated to reflect masks stored in our TkDisplay that the - * event doesn't contain. The eventPtr may also be modified to not - * contain a button state for the window in which it was not pressed in. - * - *---------------------------------------------------------------------- - */ - -static void -UpdateButtonEventState( - XEvent *eventPtr) -{ - TkDisplay *dispPtr; - - switch (eventPtr->type) { - case ButtonPress: - dispPtr = TkGetDisplay(eventPtr->xbutton.display); - dispPtr->mouseButtonWindow = eventPtr->xbutton.window; - eventPtr->xbutton.state |= dispPtr->mouseButtonState; - - dispPtr->mouseButtonState |= TkGetButtonMask(eventPtr->xbutton.button); - break; - - case ButtonRelease: - dispPtr = TkGetDisplay(eventPtr->xbutton.display); - dispPtr->mouseButtonWindow = None; - dispPtr->mouseButtonState &= ~TkGetButtonMask(eventPtr->xbutton.button); - eventPtr->xbutton.state |= dispPtr->mouseButtonState; - break; - - case MotionNotify: - dispPtr = TkGetDisplay(eventPtr->xmotion.display); - if (dispPtr->mouseButtonState & ALL_BUTTONS) { - if (eventPtr->xbutton.window != dispPtr->mouseButtonWindow) { - /* - * This motion event should not be interpreted as a button - * press + motion event since this is not the same window the - * button was pressed down in. - */ - - dispPtr->mouseButtonState &= ~ALL_BUTTONS; - dispPtr->mouseButtonWindow = None; - } else { - eventPtr->xmotion.state |= dispPtr->mouseButtonState; - } - } - break; - } -} - -/* - *---------------------------------------------------------------------- - * * InvokeClientMessageHandlers -- * * Iterate the list of handlers and invoke the function pointer for each. @@ -1229,8 +1165,6 @@ Tk_HandleEvent( } #endif - UpdateButtonEventState(eventPtr); - /* * If the generic handler processed this event we are done and can return. */ diff --git a/generic/tkFocus.c b/generic/tkFocus.c index eae981e..7a4bdbc 100644 --- a/generic/tkFocus.c +++ b/generic/tkFocus.c @@ -279,8 +279,8 @@ TkFocusFilterEvent( * pass the event through to Tk bindings. */ - if (eventPtr->xfocus.send_event == GENERATED_FOCUS_EVENT_MAGIC) { - eventPtr->xfocus.send_event = 0; + if ((eventPtr->xfocus.send_event & GENERATED_FOCUS_EVENT_MAGIC) == GENERATED_FOCUS_EVENT_MAGIC) { + eventPtr->xfocus.send_event &= ~GENERATED_FOCUS_EVENT_MAGIC; return 1; } diff --git a/generic/tkImgPNG.c b/generic/tkImgPNG.c index 871684d..3331d3d 100644 --- a/generic/tkImgPNG.c +++ b/generic/tkImgPNG.c @@ -10,7 +10,6 @@ * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#include "assert.h" #include "tkInt.h" #define PNG_INT32(a,b,c,d) \ diff --git a/generic/tkInt.decls b/generic/tkInt.decls index 73fda96..a6677ab 100644 --- a/generic/tkInt.decls +++ b/generic/tkInt.decls @@ -851,6 +851,9 @@ declare 45 win { int TkpTestsendCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) } +declare 47 win { + Tk_Window TkpGetCapture(void) +} ################################ # Aqua specific functions @@ -1013,7 +1016,7 @@ declare 46 aqua { int TkpIsWindowFloating(void *window) } declare 47 aqua { - Tk_Window TkMacOSXGetCapture(void) + Tk_Window TkpGetCapture(void) } declare 49 aqua { Tk_Window TkGetTransientMaster(TkWindow *winPtr) @@ -1829,6 +1832,42 @@ declare 90 aqua { declare 91 aqua { int XSync(Display *display, Bool flag) } +declare 120 aqua { + int XOffsetRegion(Region rgn, int dx, int dy) +} +declare 121 aqua { + int XUnionRegion(Region srca, Region srcb, Region dr_return) +} +declare 122 aqua { + Window XCreateWindow(Display *display, Window parent, int x, int y, + unsigned int width, unsigned int height, + unsigned int border_width, int depth, unsigned int clazz, + Visual *visual, unsigned long value_mask, + XSetWindowAttributes *attributes) +} +declare 130 aqua { + int XFillArcs(Display *d, Drawable dr, GC gc, XArc *a, int n) +} +declare 131 aqua { + int XDrawArcs(Display *d, Drawable dr, GC gc, XArc *a, int n) +} +declare 132 aqua { + int XDrawRectangles(Display *d, Drawable dr, GC gc, XRectangle *r, int n) +} +declare 136 aqua { + int XReparentWindow(Display *d, Window w, Window p, int x, int y) +} +declare 137 aqua { + int XPutImage(Display *d, Drawable dr, GC gc, XImage *im, + int sx, int sy, int dx, int dy, + unsigned int w, unsigned int h) +} +declare 138 aqua { + Region XPolygonRegion(XPoint *pts, int n, int rule) +} +declare 139 aqua { + int XPointInRegion(Region rgn, int x, int y) +} # Local Variables: # mode: tcl diff --git a/generic/tkInt.h b/generic/tkInt.h index 9b0616e..dfd1bb3 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -512,10 +512,17 @@ typedef struct TkDisplay { * The following field were all added for Tk8.3 */ +#if TCL_MAJOR_VERSION < 9 +#if !defined(TK_NO_DEPRECATED) int mouseButtonState; /* Current mouse button state for this - * display. */ + * display. NOT USED as of 8.6.10 */ Window mouseButtonWindow; /* Window the button state was set in, added * in Tk 8.4. */ +#else + int notused1; + XID notused2; +#endif /* !TK_NO_DEPRECATED */ +#endif Tk_Window warpWindow; Tk_Window warpMainwin; /* For finding the root window for warping * purposes. */ @@ -1057,7 +1064,6 @@ MODULE_SCOPE const char *const tkWebColors[20]; void Tcl_Panic(const char *, ...) __attribute__((analyzer_noreturn)); #endif #if !defined(CLANG_ASSERT) -#include <assert.h> #define CLANG_ASSERT(x) assert(x) #endif #elif !defined(CLANG_ASSERT) diff --git a/generic/tkIntPlatDecls.h b/generic/tkIntPlatDecls.h index 4162396..a620683 100644 --- a/generic/tkIntPlatDecls.h +++ b/generic/tkIntPlatDecls.h @@ -142,6 +142,9 @@ EXTERN void TkSendCleanup(TkDisplay *dispPtr); EXTERN int TkpTestsendCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +/* Slot 46 is reserved */ +/* 47 */ +EXTERN Tk_Window TkpGetCapture(void); #endif /* WIN */ #ifdef MAC_OSX_TK /* AQUA */ /* 0 */ @@ -240,7 +243,7 @@ EXTERN void TkMacOSXPreprocessMenu(void); /* 46 */ EXTERN int TkpIsWindowFloating(void *window); /* 47 */ -EXTERN Tk_Window TkMacOSXGetCapture(void); +EXTERN Tk_Window TkpGetCapture(void); /* Slot 48 is reserved */ /* 49 */ EXTERN Tk_Window TkGetTransientMaster(TkWindow *winPtr); @@ -341,6 +344,8 @@ typedef struct TkIntPlatStubs { void (*tkWmCleanup) (TkDisplay *dispPtr); /* 43 */ void (*tkSendCleanup) (TkDisplay *dispPtr); /* 44 */ int (*tkpTestsendCmd) (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); /* 45 */ + void (*reserved46)(void); + Tk_Window (*tkpGetCapture) (void); /* 47 */ #endif /* WIN */ #ifdef MAC_OSX_TK /* AQUA */ void (*tkGenerateActivateEvents) (TkWindow *winPtr, int active); /* 0 */ @@ -390,7 +395,7 @@ typedef struct TkIntPlatStubs { MacDrawable * (*tkMacOSXGetHostToplevel) (TkWindow *winPtr); /* 44 */ void (*tkMacOSXPreprocessMenu) (void); /* 45 */ int (*tkpIsWindowFloating) (void *window); /* 46 */ - Tk_Window (*tkMacOSXGetCapture) (void); /* 47 */ + Tk_Window (*tkpGetCapture) (void); /* 47 */ void (*reserved48)(void); Tk_Window (*tkGetTransientMaster) (TkWindow *winPtr); /* 49 */ int (*tkGenerateButtonEvent) (int x, int y, Window window, unsigned int state); /* 50 */ @@ -522,6 +527,9 @@ extern const TkIntPlatStubs *tkIntPlatStubsPtr; (tkIntPlatStubsPtr->tkSendCleanup) /* 44 */ #define TkpTestsendCmd \ (tkIntPlatStubsPtr->tkpTestsendCmd) /* 45 */ +/* Slot 46 is reserved */ +#define TkpGetCapture \ + (tkIntPlatStubsPtr->tkpGetCapture) /* 47 */ #endif /* WIN */ #ifdef MAC_OSX_TK /* AQUA */ #define TkGenerateActivateEvents \ @@ -611,8 +619,8 @@ extern const TkIntPlatStubs *tkIntPlatStubsPtr; (tkIntPlatStubsPtr->tkMacOSXPreprocessMenu) /* 45 */ #define TkpIsWindowFloating \ (tkIntPlatStubsPtr->tkpIsWindowFloating) /* 46 */ -#define TkMacOSXGetCapture \ - (tkIntPlatStubsPtr->tkMacOSXGetCapture) /* 47 */ +#define TkpGetCapture \ + (tkIntPlatStubsPtr->tkpGetCapture) /* 47 */ /* Slot 48 is reserved */ #define TkGetTransientMaster \ (tkIntPlatStubsPtr->tkGetTransientMaster) /* 49 */ diff --git a/generic/tkIntXlibDecls.h b/generic/tkIntXlibDecls.h index 3c9f150..e71b611 100644 --- a/generic/tkIntXlibDecls.h +++ b/generic/tkIntXlibDecls.h @@ -35,6 +35,11 @@ #undef XOffsetRegion #undef XUnionRegion +#if defined(MAC_OSX_TK) +# define Cursor XCursor +# define Region XRegion +#endif + #ifdef BUILD_tk # undef TCL_STORAGE_CLASS # define TCL_STORAGE_CLASS DLLEXPORT @@ -689,6 +694,77 @@ EXTERN Status XQueryTree(Display *d, Window w1, Window *w2, Window *w3, Window **w4, unsigned int *ui); /* 91 */ EXTERN int XSync(Display *display, Bool flag); +/* Slot 92 is reserved */ +/* Slot 93 is reserved */ +/* Slot 94 is reserved */ +/* Slot 95 is reserved */ +/* Slot 96 is reserved */ +/* Slot 97 is reserved */ +/* Slot 98 is reserved */ +/* Slot 99 is reserved */ +/* Slot 100 is reserved */ +/* Slot 101 is reserved */ +/* Slot 102 is reserved */ +/* Slot 103 is reserved */ +/* Slot 104 is reserved */ +/* Slot 105 is reserved */ +/* Slot 106 is reserved */ +/* Slot 107 is reserved */ +/* Slot 108 is reserved */ +/* Slot 109 is reserved */ +/* Slot 110 is reserved */ +/* Slot 111 is reserved */ +/* Slot 112 is reserved */ +/* Slot 113 is reserved */ +/* Slot 114 is reserved */ +/* Slot 115 is reserved */ +/* Slot 116 is reserved */ +/* Slot 117 is reserved */ +/* Slot 118 is reserved */ +/* Slot 119 is reserved */ +/* 120 */ +EXTERN int XOffsetRegion(Region rgn, int dx, int dy); +/* 121 */ +EXTERN int XUnionRegion(Region srca, Region srcb, + Region dr_return); +/* 122 */ +EXTERN Window XCreateWindow(Display *display, Window parent, int x, + int y, unsigned int width, + unsigned int height, + unsigned int border_width, int depth, + unsigned int clazz, Visual *visual, + unsigned long value_mask, + XSetWindowAttributes *attributes); +/* Slot 123 is reserved */ +/* Slot 124 is reserved */ +/* Slot 125 is reserved */ +/* Slot 126 is reserved */ +/* Slot 127 is reserved */ +/* Slot 128 is reserved */ +/* Slot 129 is reserved */ +/* 130 */ +EXTERN int XFillArcs(Display *d, Drawable dr, GC gc, XArc *a, + int n); +/* 131 */ +EXTERN int XDrawArcs(Display *d, Drawable dr, GC gc, XArc *a, + int n); +/* 132 */ +EXTERN int XDrawRectangles(Display *d, Drawable dr, GC gc, + XRectangle *r, int n); +/* Slot 133 is reserved */ +/* Slot 134 is reserved */ +/* Slot 135 is reserved */ +/* 136 */ +EXTERN int XReparentWindow(Display *d, Window w, Window p, + int x, int y); +/* 137 */ +EXTERN int XPutImage(Display *d, Drawable dr, GC gc, XImage *im, + int sx, int sy, int dx, int dy, + unsigned int w, unsigned int h); +/* 138 */ +EXTERN Region XPolygonRegion(XPoint *pts, int n, int rule); +/* 139 */ +EXTERN int XPointInRegion(Region rgn, int x, int y); #endif /* AQUA */ typedef struct TkIntXlibStubs { @@ -930,6 +1006,54 @@ typedef struct TkIntXlibStubs { int (*xQueryColors) (Display *display, Colormap colormap, XColor *defs_in_out, int ncolors); /* 89 */ Status (*xQueryTree) (Display *d, Window w1, Window *w2, Window *w3, Window **w4, unsigned int *ui); /* 90 */ int (*xSync) (Display *display, Bool flag); /* 91 */ + void (*reserved92)(void); + void (*reserved93)(void); + void (*reserved94)(void); + void (*reserved95)(void); + void (*reserved96)(void); + void (*reserved97)(void); + void (*reserved98)(void); + void (*reserved99)(void); + void (*reserved100)(void); + void (*reserved101)(void); + void (*reserved102)(void); + void (*reserved103)(void); + void (*reserved104)(void); + void (*reserved105)(void); + void (*reserved106)(void); + void (*reserved107)(void); + void (*reserved108)(void); + void (*reserved109)(void); + void (*reserved110)(void); + void (*reserved111)(void); + void (*reserved112)(void); + void (*reserved113)(void); + void (*reserved114)(void); + void (*reserved115)(void); + void (*reserved116)(void); + void (*reserved117)(void); + void (*reserved118)(void); + void (*reserved119)(void); + int (*xOffsetRegion) (Region rgn, int dx, int dy); /* 120 */ + int (*xUnionRegion) (Region srca, Region srcb, Region dr_return); /* 121 */ + Window (*xCreateWindow) (Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int clazz, Visual *visual, unsigned long value_mask, XSetWindowAttributes *attributes); /* 122 */ + void (*reserved123)(void); + void (*reserved124)(void); + void (*reserved125)(void); + void (*reserved126)(void); + void (*reserved127)(void); + void (*reserved128)(void); + void (*reserved129)(void); + int (*xFillArcs) (Display *d, Drawable dr, GC gc, XArc *a, int n); /* 130 */ + int (*xDrawArcs) (Display *d, Drawable dr, GC gc, XArc *a, int n); /* 131 */ + int (*xDrawRectangles) (Display *d, Drawable dr, GC gc, XRectangle *r, int n); /* 132 */ + void (*reserved133)(void); + void (*reserved134)(void); + void (*reserved135)(void); + int (*xReparentWindow) (Display *d, Window w, Window p, int x, int y); /* 136 */ + int (*xPutImage) (Display *d, Drawable dr, GC gc, XImage *im, int sx, int sy, int dx, int dy, unsigned int w, unsigned int h); /* 137 */ + Region (*xPolygonRegion) (XPoint *pts, int n, int rule); /* 138 */ + int (*xPointInRegion) (Region rgn, int x, int y); /* 139 */ #endif /* AQUA */ } TkIntXlibStubs; @@ -1400,12 +1524,75 @@ extern const TkIntXlibStubs *tkIntXlibStubsPtr; (tkIntXlibStubsPtr->xQueryTree) /* 90 */ #define XSync \ (tkIntXlibStubsPtr->xSync) /* 91 */ +/* Slot 92 is reserved */ +/* Slot 93 is reserved */ +/* Slot 94 is reserved */ +/* Slot 95 is reserved */ +/* Slot 96 is reserved */ +/* Slot 97 is reserved */ +/* Slot 98 is reserved */ +/* Slot 99 is reserved */ +/* Slot 100 is reserved */ +/* Slot 101 is reserved */ +/* Slot 102 is reserved */ +/* Slot 103 is reserved */ +/* Slot 104 is reserved */ +/* Slot 105 is reserved */ +/* Slot 106 is reserved */ +/* Slot 107 is reserved */ +/* Slot 108 is reserved */ +/* Slot 109 is reserved */ +/* Slot 110 is reserved */ +/* Slot 111 is reserved */ +/* Slot 112 is reserved */ +/* Slot 113 is reserved */ +/* Slot 114 is reserved */ +/* Slot 115 is reserved */ +/* Slot 116 is reserved */ +/* Slot 117 is reserved */ +/* Slot 118 is reserved */ +/* Slot 119 is reserved */ +#define XOffsetRegion \ + (tkIntXlibStubsPtr->xOffsetRegion) /* 120 */ +#define XUnionRegion \ + (tkIntXlibStubsPtr->xUnionRegion) /* 121 */ +#define XCreateWindow \ + (tkIntXlibStubsPtr->xCreateWindow) /* 122 */ +/* Slot 123 is reserved */ +/* Slot 124 is reserved */ +/* Slot 125 is reserved */ +/* Slot 126 is reserved */ +/* Slot 127 is reserved */ +/* Slot 128 is reserved */ +/* Slot 129 is reserved */ +#define XFillArcs \ + (tkIntXlibStubsPtr->xFillArcs) /* 130 */ +#define XDrawArcs \ + (tkIntXlibStubsPtr->xDrawArcs) /* 131 */ +#define XDrawRectangles \ + (tkIntXlibStubsPtr->xDrawRectangles) /* 132 */ +/* Slot 133 is reserved */ +/* Slot 134 is reserved */ +/* Slot 135 is reserved */ +#define XReparentWindow \ + (tkIntXlibStubsPtr->xReparentWindow) /* 136 */ +#define XPutImage \ + (tkIntXlibStubsPtr->xPutImage) /* 137 */ +#define XPolygonRegion \ + (tkIntXlibStubsPtr->xPolygonRegion) /* 138 */ +#define XPointInRegion \ + (tkIntXlibStubsPtr->xPointInRegion) /* 139 */ #endif /* AQUA */ #endif /* defined(USE_TK_STUBS) */ /* !END!: Do not edit above this line. */ +#if defined(MAC_OSX_TK) +# undef Cursor +# undef Region +#endif + #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLIMPORT diff --git a/generic/tkPointer.c b/generic/tkPointer.c index 57680c3..75b1791 100644 --- a/generic/tkPointer.c +++ b/generic/tkPointer.c @@ -503,7 +503,15 @@ TkPointerDeadWindow( tsdPtr->restrictWinPtr = NULL; } if (!(tsdPtr->restrictWinPtr || tsdPtr->grabWinPtr)) { - TkpSetCapture(NULL); + + /* + * Release mouse capture only if the dead window is the capturing + * window. + */ + + if (winPtr == (TkWindow *)TkpGetCapture()) { + TkpSetCapture(NULL); + } } } diff --git a/generic/tkStubInit.c b/generic/tkStubInit.c index 731fa50..cf60cfb 100644 --- a/generic/tkStubInit.c +++ b/generic/tkStubInit.c @@ -99,11 +99,6 @@ TkCreateXEventSource(void) # define TkUnixContainerId 0 # define TkUnixDoOneXEvent 0 # define TkUnixSetMenubar 0 -# define XCreateWindow 0 -# define XOffsetRegion 0 -# define XUnionRegion 0 -# define XPolygonRegion 0 -# define XPointInRegion 0 # define TkWmCleanup (void (*)(TkDisplay *)) TkpSync # define TkSendCleanup (void (*)(TkDisplay *)) TkpSync # define TkpTestsendCmd 0 @@ -217,6 +212,7 @@ void TkSubtractRegion (TkRegion a, TkRegion b, TkRegion c) # define TkAlignImageData 0 # define TkGenerateActivateEvents 0 # define TkpGetMS 0 +# define TkpGetCapture 0 # define TkPointerDeadWindow 0 # define TkpSetCapture 0 # define TkpSetCursor 0 @@ -552,6 +548,8 @@ static const TkIntPlatStubs tkIntPlatStubs = { TkWmCleanup, /* 43 */ TkSendCleanup, /* 44 */ TkpTestsendCmd, /* 45 */ + 0, /* 46 */ + TkpGetCapture, /* 47 */ #endif /* WIN */ #ifdef MAC_OSX_TK /* AQUA */ TkGenerateActivateEvents, /* 0 */ @@ -601,7 +599,7 @@ static const TkIntPlatStubs tkIntPlatStubs = { TkMacOSXGetHostToplevel, /* 44 */ TkMacOSXPreprocessMenu, /* 45 */ TkpIsWindowFloating, /* 46 */ - TkMacOSXGetCapture, /* 47 */ + TkpGetCapture, /* 47 */ 0, /* 48 */ TkGetTransientMaster, /* 49 */ TkGenerateButtonEvent, /* 50 */ @@ -867,6 +865,54 @@ static const TkIntXlibStubs tkIntXlibStubs = { XQueryColors, /* 89 */ XQueryTree, /* 90 */ XSync, /* 91 */ + 0, /* 92 */ + 0, /* 93 */ + 0, /* 94 */ + 0, /* 95 */ + 0, /* 96 */ + 0, /* 97 */ + 0, /* 98 */ + 0, /* 99 */ + 0, /* 100 */ + 0, /* 101 */ + 0, /* 102 */ + 0, /* 103 */ + 0, /* 104 */ + 0, /* 105 */ + 0, /* 106 */ + 0, /* 107 */ + 0, /* 108 */ + 0, /* 109 */ + 0, /* 110 */ + 0, /* 111 */ + 0, /* 112 */ + 0, /* 113 */ + 0, /* 114 */ + 0, /* 115 */ + 0, /* 116 */ + 0, /* 117 */ + 0, /* 118 */ + 0, /* 119 */ + XOffsetRegion, /* 120 */ + XUnionRegion, /* 121 */ + XCreateWindow, /* 122 */ + 0, /* 123 */ + 0, /* 124 */ + 0, /* 125 */ + 0, /* 126 */ + 0, /* 127 */ + 0, /* 128 */ + 0, /* 129 */ + XFillArcs, /* 130 */ + XDrawArcs, /* 131 */ + XDrawRectangles, /* 132 */ + 0, /* 133 */ + 0, /* 134 */ + 0, /* 135 */ + XReparentWindow, /* 136 */ + XPutImage, /* 137 */ + XPolygonRegion, /* 138 */ + XPointInRegion, /* 139 */ #endif /* AQUA */ }; diff --git a/generic/tkTextTag.c b/generic/tkTextTag.c index 16e6f54..7b89fd9 100644 --- a/generic/tkTextTag.c +++ b/generic/tkTextTag.c @@ -1457,7 +1457,7 @@ TkTextBindProc( if (eventPtr->type == ButtonPress) { textPtr->flags |= BUTTON_DOWN; } else if (eventPtr->type == ButtonRelease) { - unsigned int mask; + unsigned long mask; mask = TkGetButtonMask(eventPtr->xbutton.button); if ((eventPtr->xbutton.state & ALL_BUTTONS) == mask) { diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 103a72e..7ca6e12 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -943,7 +943,6 @@ XDrawRectangle( return Success; } -#ifdef TK_MACOSXDRAW_UNUSED /* *---------------------------------------------------------------------- * @@ -968,7 +967,7 @@ XDrawRectangle( *---------------------------------------------------------------------- */ -void +int XDrawRectangles( Display *display, Drawable drawable, @@ -982,8 +981,8 @@ XDrawRectangles( int i, lw = gc->line_width; display->request++; - if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { - return; + if (!TkMacOSXSetupDrawingContext(drawable, gc, 1, &dc)) { + return BadDrawable; } if (dc.context) { CGRect rect; @@ -1001,8 +1000,8 @@ XDrawRectangles( } } TkMacOSXRestoreDrawingContext(&dc); + return Success; } -#endif /* *---------------------------------------------------------------------- @@ -1173,7 +1172,6 @@ XDrawArc( return Success; } -#ifdef TK_MACOSXDRAW_UNUSED /* *---------------------------------------------------------------------- * @@ -1254,7 +1252,6 @@ XDrawArcs( TkMacOSXRestoreDrawingContext(&dc); return Success; } -#endif /* *---------------------------------------------------------------------- @@ -1335,7 +1332,6 @@ XFillArc( return Success; } -#ifdef TK_MACOSXDRAW_UNUSED /* *---------------------------------------------------------------------- * @@ -1352,7 +1348,7 @@ XFillArc( *---------------------------------------------------------------------- */ -void +int XFillArcs( Display *display, Drawable d, @@ -1367,7 +1363,7 @@ XFillArcs( display->request++; if (!TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { - return; + return BadDrawable; } if (dc.context) { CGRect rect; @@ -1415,25 +1411,8 @@ XFillArcs( } } TkMacOSXRestoreDrawingContext(&dc); + return Success; } -#endif - -#ifdef TK_MACOSXDRAW_UNUSED -/* - *---------------------------------------------------------------------- - * - * XMaxRequestSize -- - * - *---------------------------------------------------------------------- - */ - -long -XMaxRequestSize( - Display *display) -{ - return (SHRT_MAX / 4); -} -#endif /* *---------------------------------------------------------------------- diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index 36f1fc6..cee9d74 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -591,6 +591,22 @@ TkPutImage( TkMacOSXRestoreDrawingContext(&dc); return Success; } + +int +XPutImage( + Display *display, + Drawable d, /* Destination drawable. */ + GC gc, + XImage *image, /* Source image. */ + int src_x, int src_y, /* Offset of subimage. */ + int dest_x, int dest_y, /* Position of subimage origin in drawable. */ + unsigned int width, unsigned int height) + /* Dimensions of subimage. */ +{ + return TkPutImage(NULL, 0, display, d, gc, image, + src_x, src_y, dest_x, dest_y, width, height); +} + /* * Local Variables: diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index f62e07c..3230b1c 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -538,7 +538,7 @@ XGrabKeyboard( Time time) { keyboardGrabWinPtr = Tk_IdToWindow(display, grab_window); - TkWindow *captureWinPtr = (TkWindow *) TkMacOSXGetCapture(); + TkWindow *captureWinPtr = (TkWindow *) TkpGetCapture(); if (keyboardGrabWinPtr && captureWinPtr) { NSWindow *w = TkMacOSXDrawableWindow(grab_window); diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 92d2daf..17aaa0f 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -93,7 +93,7 @@ enum { if (eventWindow) { local = [theEvent locationInWindow]; global = [eventWindow tkConvertPointToScreen: local]; - tkwin = TkMacOSXGetCapture(); + tkwin = TkpGetCapture(); if (tkwin) { winPtr = (TkWindow *) tkwin; eventWindow = TkMacOSXDrawableWindow(winPtr->window); @@ -112,7 +112,7 @@ enum { */ global = [theEvent locationInWindow]; - tkwin = TkMacOSXGetCapture(); + tkwin = TkpGetCapture(); if (tkwin) { winPtr = (TkWindow *) tkwin; eventWindow = TkMacOSXDrawableWindow(winPtr->window); @@ -609,7 +609,7 @@ GenerateButtonEvent( if ((medPtr->activeNonFloating == NULL) || ((!(TkpIsWindowFloating(medPtr->whichWin)) && (medPtr->activeNonFloating != medPtr->whichWin)) - && TkMacOSXGetCapture() == NULL)) { + && TkpGetCapture() == NULL)) { return false; } #endif @@ -688,7 +688,7 @@ TkpSetCapture( /* *---------------------------------------------------------------------- * - * TkMacOSXGetCapture -- + * TkpGetCapture -- * * Results: * Returns the current grab window @@ -700,7 +700,7 @@ TkpSetCapture( */ Tk_Window -TkMacOSXGetCapture(void) +TkpGetCapture(void) { return captureWinPtr; } diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 7267b00..535cd9e 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -173,7 +173,7 @@ GetRunLoopMode(NSModalSession modalSession) if (modalSession) { runLoopMode = NSModalPanelRunLoopMode; - } else if (TkMacOSXGetCapture()) { + } else if (TkpGetCapture()) { runLoopMode = NSEventTrackingRunLoopMode; } if (!runLoopMode) { diff --git a/macosx/tkMacOSXPort.h b/macosx/tkMacOSXPort.h index 617abda..e26328a 100644 --- a/macosx/tkMacOSXPort.h +++ b/macosx/tkMacOSXPort.h @@ -23,6 +23,7 @@ #include <math.h> #include <pwd.h> #include <stdlib.h> +#include <assert.h> #include <string.h> #include <sys/types.h> #include <sys/file.h> diff --git a/tests/bind.test b/tests/bind.test index 607c25a..fc1cb36 100644 --- a/tests/bind.test +++ b/tests/bind.test @@ -34,19 +34,6 @@ proc unsetBindings {} { bind .t <Enter> {} } -# This function fills the pattern matcher's ring buffer with events of -# the specified type. This can be used when testing with generated -# events to make sure that there are no stray events in the ring -# buffer which might cause the pattern matcher to find unintended -# matches. The size of the ring buffer is EVENT_BUFFER_SIZE, which is -# currently set to 30 (or 45 on macOS). If this changes, the code -# below will need to change. -proc clearRingBuffer {{event}} { - for {set i 0} {$i < 45} {incr i} { - event generate . $event - } -} - # move the mouse pointer away of the testing area # otherwise some spurious events may pollute the tests toplevel .top @@ -90,10 +77,10 @@ test bind-1.7 {bind command} -body { } -result {test script more text} test bind-1.8 {bind command} -body { - bind .t <gorp-> {test script} + bind .t <gorp-> {test script} } -returnCodes error -result {bad event type or keysym "gorp"} test bind-1.9 {bind command} -body { - catch {bind .t <gorp-> {test script}} + catch {bind .t <gorp-> {test script}} bind .t } -result {} test bind-1.10 {bind command} -body { @@ -154,10 +141,10 @@ test bind-2.8 {bindtags command} -body { test bind-2.9 {bindtags command} -body { frame .t.f bindtags .t.f {a b c} - bindtags .t.f "\{" + bindtags .t.f "\{" } -cleanup { destroy .t.f -} -returnCodes error -result {unmatched open brace in list} +} -returnCodes error -result {unmatched open brace in list} test bind-2.10 {bindtags command} -body { frame .t.f bindtags .t.f {a b c} @@ -169,10 +156,10 @@ test bind-2.10 {bindtags command} -body { test bind-2.11 {bindtags command} -body { frame .t.f bindtags .t.f {a b c} - bindtags .t.f "a .gorp b" + bindtags .t.f "a .gorp b" } -cleanup { destroy .t.f -} -returnCodes ok +} -returnCodes ok test bind-2.12 {bindtags command} -body { frame .t.f bindtags .t.f {a b c} @@ -212,7 +199,7 @@ test bind-4.1 {TkBindEventProc procedure} -setup { bind {a b} <Enter> {lappend x "%W enter {a b}"} bind .t <Enter> {lappend x "%W enter .t"} bind .t.f <Enter> {lappend x "%W enter .t.f"} - + event generate .t.f <Enter> return $x } -cleanup { @@ -232,9 +219,9 @@ test bind-4.2 {TkBindEventProc procedure} -setup { bind {a b} <Enter> {lappend x "%W enter {a b}"} bind .t <Enter> {lappend x "%W enter .t"} bind .t.f <Enter> {lappend x "%W enter .t.f"} - + bindtags .t.f {.t.f {a b} xyz} - event generate .t.f <Enter> + event generate .t.f <Enter> return $x } -cleanup { destroy .t.f @@ -248,7 +235,7 @@ test bind-4.3 {TkBindEventProc procedure} -body { bind xyz <Enter> {lappend x "%W enter xyz"} bind {a b} <Enter> {lappend x "%W enter {a b}"} bind .t <Enter> {lappend x "%W enter .t"} - + event generate .t <Enter> return $x } -cleanup { @@ -268,7 +255,7 @@ test bind-4.4 {TkBindEventProc procedure} -setup { bind xyz <Enter> {lappend x "%W enter xyz"} bind {a b} <Enter> {lappend x "%W enter {a b}"} bind .t <Enter> {lappend x "%W enter .t"} - + bindtags .t.f {.t.f .t.f2 .t.f3} bind .t.f <Enter> {lappend x "%W enter .t.f"} bind .t.f3 <Enter> {lappend x "%W enter .t.f3"} @@ -292,7 +279,7 @@ test bind-4.5 {TkBindEventProc procedure} -setup { bind {a b} <Enter> {lappend x "%W enter {a b}"} bind .t <Enter> {lappend x "%W enter .t"} bindtags .t.f {a b c d e f g h i j k l m n o p q r s t u v w x y z} - + event generate .t.f <Enter> } -cleanup { destroy .t.f @@ -396,7 +383,7 @@ test bind-10.2 {Tk_GetBinding procedure} -body { } -result {Test} test bind-11.1 {Tk_GetAllBindings procedure} -body { - frame .t.f + frame .t.f foreach i "! a \\\{ ~ <Delete> <space> <<Paste>> <Tab> <Linefeed> <less> <Meta-a> <Acircumflex>" { bind .t.f $i Test } @@ -405,7 +392,7 @@ test bind-11.1 {Tk_GetAllBindings procedure} -body { destroy .t.f } -result {! <<Paste>> <Key-Acircumflex> <Key-Delete> <Key-Linefeed> <Key-Tab> <Key-less> <Key-space> <Meta-Key-a> a \{ ~} test bind-11.2 {Tk_GetAllBindings procedure} -body { - frame .t.f + frame .t.f foreach i "<Double-1> <Triple-1> <Meta-Control-a> <Double-Alt-Enter> <1>" { bind .t.f $i Test } @@ -414,7 +401,7 @@ test bind-11.2 {Tk_GetAllBindings procedure} -body { destroy .t.f } -result {<Button-1> <Control-Meta-Key-a> <Double-Alt-Enter> <Double-Button-1> <Triple-Button-1>} test bind-11.3 {Tk_GetAllBindings procedure} -body { - frame .t.f + frame .t.f foreach i "<Double-Triple-1> abcd a<Leave>b" { bind .t.f $i Test } @@ -448,7 +435,7 @@ test bind-13.1 {Tk_BindEvent procedure} -setup { bind Test : {lappend x "%W %K Test :"} bind all _ {lappend x "%W %K all _"} bind .t.f : {lappend x "%W %K .t.f :"} - + event generate .t.f <Key-colon> event generate .t.f <Key-plus> event generate .t.f <Key-underscore> @@ -471,7 +458,7 @@ test bind-13.2 {Tk_BindEvent procedure} -setup { bind Test <KeyPress> {lappend x "%W %K Test press any"; break} bind all <KeyPress> {continue; lappend x "%W %K all press any"} bind .t.f : {lappend x "%W %K .t.f pressed colon"} - + event generate .t.f <Key-colon> return $x } -cleanup { @@ -527,11 +514,11 @@ test bind-13.5 {Tk_BindEvent procedure} -body { frame .t.g -gorp foo } -cleanup { bind all <Destroy> {} -} -returnCodes error -result {unknown option "-gorp"} +} -returnCodes error -result {unknown option "-gorp"} test bind-13.6 {Tk_BindEvent procedure} -body { bind all <Destroy> {lappend x "%W destroyed"} set x {} - catch {frame .t.g -gorp foo} + catch {frame .t.g -gorp foo} return $x } -cleanup { bind all <Destroy> {} @@ -612,10 +599,10 @@ test bind-13.11 {Tk_BindEvent procedure: collapse Motions} -setup { set x {} } -body { bind .t.f <Motion> "lappend x Motion%#(%x,%y)" - event generate .t.f <Motion> -serial 100 -x 100 -y 200 -when tail + event generate .t.f <Motion> -serial 100 -x 100 -y 200 -when tail update event generate .t.f <Motion> -serial 101 -x 200 -y 300 -when tail - event generate .t.f <Motion> -serial 102 -x 300 -y 400 -when tail + event generate .t.f <Motion> -serial 102 -x 300 -y 400 -when tail update return $x } -cleanup { @@ -629,10 +616,10 @@ test bind-13.12 {Tk_BindEvent procedure: collapse repeating modifiers} -setup { } -body { bind .t.f <Key> "lappend x %K%#" bind .t.f <KeyRelease> "lappend x %K%#" - event generate .t.f <Key-Shift_L> -serial 100 -when tail - event generate .t.f <KeyRelease-Shift_L> -serial 101 -when tail - event generate .t.f <Key-Shift_L> -serial 102 -when tail - event generate .t.f <KeyRelease-Shift_L> -serial 103 -when tail + event generate .t.f <Key-Shift_L> -serial 100 -when tail + event generate .t.f <KeyRelease-Shift_L> -serial 101 -when tail + event generate .t.f <Key-Shift_L> -serial 102 -when tail + event generate .t.f <KeyRelease-Shift_L> -serial 103 -when tail update } -cleanup { destroy .t.f @@ -868,7 +855,7 @@ test bind-13.27 {Tk_BindEvent procedure: no detail virtual pattern list} -setup set x {} } -body { bind .t.f <Button-2> {set x Button-2} - event generate .t.f <Button-2> + event generate .t.f <Button-2> return $x } -cleanup { destroy .t.f @@ -1032,7 +1019,7 @@ test bind-13.43 {Tk_BindEvent procedure: break in script} -setup { } -result {b1} test bind-13.45 {Tk_BindEvent procedure: error in script} -setup { proc bgerror msg { - global x + global x lappend x $msg } frame .t.f -class Test -width 150 -height 100 @@ -1221,7 +1208,7 @@ test bind-15.11 {MatchPatterns procedure, modifier checks} -setup { } -cleanup { destroy .t.f } -result {0} -test bind-15.12 {MatchPatterns procedure, ignore modifier presses and releases} -constraints { +test bind-15.12 {MatchPatterns procedure, ignore modifier presses and releases} -constraints { nonPortable } -setup { frame .t.f -class Test -width 150 -height 100 @@ -1262,7 +1249,7 @@ test bind-15.14 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 31 -y 39 @@ -1279,7 +1266,7 @@ test bind-15.15 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 29 -y 41 @@ -1296,7 +1283,7 @@ test bind-15.16 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 40 -y 40 @@ -1313,7 +1300,7 @@ test bind-15.17 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 20 -y 40 @@ -1330,7 +1317,7 @@ test bind-15.18 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 30 -y 30 @@ -1347,7 +1334,7 @@ test bind-15.19 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -x 30 -y 40 event generate .t.f <Button-1> -x 30 -y 50 @@ -1364,7 +1351,7 @@ test bind-15.20 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -time 300 event generate .t.f <Button-1> -time 700 @@ -1381,7 +1368,7 @@ test bind-15.21 {MatchPatterns procedure, checking "nearby"} -setup { } -body { bind .t.f <Double-1> {set x 1} set x 0 - event generate .t.f <Button-2> + event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Button-1> -time 300 event generate .t.f <Button-1> -time 900 @@ -1395,7 +1382,6 @@ test bind-15.22 {MatchPatterns procedure, time wrap-around} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Key> } -body { bind .t.f <Double-1> {set x 1} set x 0 @@ -1411,7 +1397,6 @@ test bind-15.23 {MatchPatterns procedure, time wrap-around} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Key> } -body { bind .t.f <Double-1> {set x 1} set x 0 @@ -1428,7 +1413,6 @@ test bind-15.24 {MatchPatterns procedure, virtual event} -setup { focus -force .t.f update set x {} - clearRingBuffer <Key> } -body { event add <<Paste>> <Button-1> bind .t.f <<Paste>> {lappend x paste} @@ -1445,7 +1429,6 @@ test bind-15.25 {MatchPatterns procedure, reject a virtual event} -setup { focus -force .t.f update set x {} - clearRingBuffer <Key> } -body { event add <<Paste>> <Shift-Button-1> bind .t.f <<Paste>> {lappend x paste} @@ -1462,7 +1445,6 @@ test bind-15.26 {MatchPatterns procedure, reject a virtual event} -setup { focus -force .t.f update set x {} - clearRingBuffer <Key> } -body { event add <<V1>> <Button> event add <<V2>> <Button-1> @@ -1489,7 +1471,6 @@ test bind-15.27 {MatchPatterns procedure, conflict resolution} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Button> } -body { bind .t.f <KeyPress> {set x 0} bind .t.f 1 {set x 1} @@ -1504,7 +1485,6 @@ test bind-15.28 {MatchPatterns procedure, conflict resolution} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Button> } -body { bind .t.f <KeyPress> {set x 0} bind .t.f 1 {set x 1} @@ -1519,7 +1499,6 @@ test bind-15.29 {MatchPatterns procedure, conflict resolution} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Button> } -body { bind .t.f <KeyPress> {lappend x 0} bind .t.f 1 {lappend x 1} @@ -1537,7 +1516,6 @@ test bind-15.30 {MatchPatterns procedure, conflict resolution} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Key> } -body { bind .t.f <ButtonPress> {set x 0} bind .t.f <1> {set x 1} @@ -1554,7 +1532,6 @@ test bind-15.31 {MatchPatterns procedure, conflict resolution} -setup { focus -force .t.f update set x {} - clearRingBuffer <Button> } -body { bind .t.f <M1-Key> {set x 0} bind .t.f <M2-Key> {set x 1} @@ -1568,7 +1545,6 @@ test bind-15.32 {MatchPatterns procedure, conflict resolution} -setup { pack .t.f focus -force .t.f update - clearRingBuffer <Button> } -body { bind .t.f <M2-Key> {set x 0} bind .t.f <M1-Key> {set x 1} @@ -1584,7 +1560,6 @@ test bind-15.33 {MatchPatterns procedure, conflict resolution} -setup { focus -force .t.f update set x {} - clearRingBuffer <Key> } -body { bind .t.f <1> {lappend x single} bind Test <1> {lappend x single(Test)} @@ -2049,7 +2024,7 @@ test bind-16.34 {ExpandPercents procedure} -setup { destroy .t.f } -result {781 632} test bind-16.35 {ExpandPercents procedure} -constraints { - nonPortable + nonPortable } -setup { frame .t.f -class Test -width 150 -height 100 pack .t.f @@ -2232,7 +2207,6 @@ test bind-16.46 {ExpandPercents procedure} -setup { focus -force .t.e foreach p [event info] {event delete $p} update - clearRingBuffer <Button> } -body { bind all <Key> {set z "%M"} bind Entry <Key> {set y "%M"} @@ -2275,7 +2249,7 @@ test bind-17.6 {event command: add with error} -body { event add <<Paste>> <Control-v> <Button-2> abc <xyz> <1> } -cleanup { event delete <<Paste>> -} -returnCodes error -result {bad event type or keysym "xyz"} +} -returnCodes error -result {bad event type or keysym "xyz"} test bind-17.7 {event command: add with error} -body { event delete <<Paste>> catch {event add <<Paste>> <Control-v> <Button-2> abc <xyz> <1>} @@ -2293,6 +2267,7 @@ test bind-17.9 {event command: delete many} -body { event delete <<Paste>> <1> <2> lsort [event info <<Paste>>] } -cleanup { + event delete <<Paste>> event delete <<Paste>> <3> t } -result {<Button-3> t} test bind-17.10 {event command: delete all} -body { @@ -2368,7 +2343,7 @@ test bind-18.1 {CreateVirtualEvent procedure: GetVirtualEventUid} -body { test bind-18.2 {CreateVirtualEvent procedure: FindSequence} -body { event add <<asd>> <Ctrl-v> } -returnCodes error -result {bad event type or keysym "Ctrl"} -test bind-18.3 {CreateVirtualEvent procedure: new physical} -body { +test bind-18.3 {CreateVirtualEvent procedure: new physical} -body { event delete <<xyz>> event add <<xyz>> <Control-v> event info <<xyz>> @@ -2377,7 +2352,7 @@ test bind-18.3 {CreateVirtualEvent procedure: new physical} -body { } -result {<Control-Key-v>} test bind-18.4 {CreateVirtualEvent procedure: duplicate physical} -body { event delete <<xyz>> - event add <<xyz>> <Control-v> + event add <<xyz>> <Control-v> event add <<xyz>> <Control-v> event info <<xyz>> } -cleanup { @@ -2448,13 +2423,13 @@ test bind-19.7 {DeleteVirtualEvent procedure: owns 1, delete all} -body { foreach p [event info] {event delete $p} event add <<xyz>> <Control-v> event delete <<xyz>> - event info + event info } -result {} test bind-19.8 {DeleteVirtualEvent procedure: owns 1, delete 1} -body { foreach p [event info] {event delete $p} event add <<xyz>> <Control-v> event delete <<xyz>> <Control-v> - event info + event info } -result {} test bind-19.9 {DeleteVirtualEvent procedure: owns many, delete all} -body { foreach p [event info] {event delete $p} @@ -2506,7 +2481,7 @@ test bind-19.12 {DeleteVirtualEvent procedure: owned by 1, first in chain} -setu event generate .t.f <ButtonRelease-2> event generate .t.f <Control-Button-2> event generate .t.f <Control-ButtonRelease-2> - event delete <<xyz>> + event delete <<xyz>> event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Control-Button-2> @@ -2573,7 +2548,7 @@ test bind-19.14 {DeleteVirtualEvent procedure: owned by 1, last in chain} -setup event generate .t.f <Control-ButtonRelease-2> event generate .t.f <Shift-Button-2> event generate .t.f <Shift-ButtonRelease-2> - event delete <<xyz>> + event delete <<xyz>> event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.f <Control-Button-2> @@ -2590,6 +2565,7 @@ test bind-19.15 {DeleteVirtualEvent procedure: owned by many, first} -setup { pack [frame .t.f -class Test -width 150 -height 100] pack [frame .t.g -class Test -width 150 -height 100] pack [frame .t.h -class Test -width 150 -height 100] + after 250 ;# we need a bit time to ensure that .t.h is mapped (<TODO>: fix this race condition) focus -force .t.f update set x {} @@ -2609,7 +2585,7 @@ test bind-19.15 {DeleteVirtualEvent procedure: owned by many, first} -setup { event generate .t.g <ButtonRelease-2> event generate .t.h <Button-2> event generate .t.h <ButtonRelease-2> - event delete <<xyz>> + event delete <<xyz>> event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.g <Button-2> @@ -2626,6 +2602,7 @@ test bind-19.16 {DeleteVirtualEvent procedure: owned by many, middle} -setup { pack [frame .t.f -class Test -width 150 -height 100] pack [frame .t.g -class Test -width 150 -height 100] pack [frame .t.h -class Test -width 150 -height 100] + after 250 ;# we need a bit time to ensure that .t.h is mapped (<TODO>: fix this race condition) focus -force .t.f update set x {} @@ -2662,6 +2639,7 @@ test bind-19.17 {DeleteVirtualEvent procedure: owned by many, last} -setup { pack [frame .t.f -class Test -width 150 -height 100] pack [frame .t.g -class Test -width 150 -height 100] pack [frame .t.h -class Test -width 150 -height 100] + after 250 ;# we need a bit time to ensure that .t.h is mapped (<TODO>: fix this race condition) focus -force .t.f update set x {} @@ -2681,7 +2659,7 @@ test bind-19.17 {DeleteVirtualEvent procedure: owned by many, last} -setup { event generate .t.g <ButtonRelease-2> event generate .t.h <Button-2> event generate .t.h <ButtonRelease-2> - event delete <<def>> + event delete <<def>> event generate .t.f <Button-2> event generate .t.f <ButtonRelease-2> event generate .t.g <Button-2> @@ -2774,7 +2752,7 @@ test bind-22.5 {HandleEventGenerate} -body { } -returnCodes error -result {bad event type or keysym "xyz"} test bind-22.6 {HandleEventGenerate} -body { event generate . <Double-Button-1> -} -returnCodes error -result {Double or Triple modifier not allowed} +} -returnCodes error -result {Double, Triple, or Quadruple modifier not allowed} test bind-22.7 {HandleEventGenerate} -body { event generate . xyz } -returnCodes error -result {only one event specification allowed} @@ -3418,7 +3396,7 @@ test bind-22.55 {HandleEventGenerate: options <Map> -override xyz} -setup { set x {} } -body { bind .t.f <Map> "lappend x %o" - event generate .t.f <Map> -override xyz + event generate .t.f <Map> -override xyz } -cleanup { destroy .t.f } -returnCodes error -result {expected boolean value but got "xyz"} @@ -3431,7 +3409,7 @@ test bind-22.56 {HandleEventGenerate: options <Map> -override 1} -setup { set x {} } -body { bind .t.f <Map> "lappend x %o" - event generate .t.f <Map> -override 1 + event generate .t.f <Map> -override 1 return $x } -cleanup { destroy .t.f @@ -3445,7 +3423,7 @@ test bind-22.57 {HandleEventGenerate: options <Reparent> -override 1} -setup { set x {} } -body { bind .t.f <Reparent> "lappend x %o" - event generate .t.f <Reparent> -override 1 + event generate .t.f <Reparent> -override 1 return $x } -cleanup { destroy .t.f @@ -3459,7 +3437,7 @@ test bind-22.58 {HandleEventGenerate: options <Configure> -override 1} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %o" - event generate .t.f <Configure> -override 1 + event generate .t.f <Configure> -override 1 return $x } -cleanup { destroy .t.f @@ -3473,7 +3451,7 @@ test bind-22.59 {HandleEventGenerate: options <Key> -override 1} -setup { set x {} } -body { bind .t.f <Key> "lappend x %k" - event generate .t.f <Key> -override 1 + event generate .t.f <Key> -override 1 } -cleanup { destroy .t.f } -returnCodes error -result {<Key> event doesn't accept "-override" option} @@ -3486,7 +3464,7 @@ test bind-22.60 {HandleEventGenerate: options <Circulate> -place xyz} -setup { set x {} } -body { bind .t.f <Circulate> "lappend x %p" - event generate .t.f <Circulate> -place xyz + event generate .t.f <Circulate> -place xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad -place value "xyz": must be PlaceOnTop, or PlaceOnBottom} @@ -3499,7 +3477,7 @@ test bind-22.61 {HandleEventGenerate: options <Circulate> -place PlaceOnTop} -se set x {} } -body { bind .t.f <Circulate> "lappend x %p" - event generate .t.f <Circulate> -place PlaceOnTop + event generate .t.f <Circulate> -place PlaceOnTop return $x } -cleanup { destroy .t.f @@ -3513,7 +3491,7 @@ test bind-22.62 {HandleEventGenerate: options <Key> -place PlaceOnTop} -setup { set x {} } -body { bind .t.f <Key> "lappend x %k" - event generate .t.f <Key> -place PlaceOnTop + event generate .t.f <Key> -place PlaceOnTop } -cleanup { destroy .t.f } -returnCodes error -result {<Key> event doesn't accept "-place" option} @@ -3526,7 +3504,7 @@ test bind-22.63 {HandleEventGenerate: options <Key> -root .xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %R" - event generate .t.f <Key> -root .xyz + event generate .t.f <Key> -root .xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window path name ".xyz"} @@ -3539,7 +3517,7 @@ test bind-22.64 {HandleEventGenerate: options <Key> -root .t} -setup { set x {} } -body { bind .t.f <Key> "lappend x %R" - event generate .t.f <Key> -root .t + event generate .t.f <Key> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3553,7 +3531,7 @@ test bind-22.65 {HandleEventGenerate: options <Key> -root xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %R" - event generate .t.f <Key> -root xyz + event generate .t.f <Key> -root xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window name/identifier "xyz"} @@ -3566,7 +3544,7 @@ test bind-22.66 {HandleEventGenerate: options <Key> -root [winfo id .t]} -setup set x {} } -body { bind .t.f <Key> "lappend x %R" - event generate .t.f <Key> -root [winfo id .t] + event generate .t.f <Key> -root [winfo id .t] expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3580,7 +3558,7 @@ test bind-22.67 {HandleEventGenerate: options <Button> -root .t} -setup { set x {} } -body { bind .t.f <Button> "lappend x %R" - event generate .t.f <Button> -root .t + event generate .t.f <Button> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3594,7 +3572,7 @@ test bind-22.68 {HandleEventGenerate: options <ButtonRelease> -root .t} -setup { set x {} } -body { bind .t.f <ButtonRelease> "lappend x %R" - event generate .t.f <ButtonRelease> -root .t + event generate .t.f <ButtonRelease> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3608,7 +3586,7 @@ test bind-22.69 {HandleEventGenerate: options <Motion> -root .t} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %R" - event generate .t.f <Motion> -root .t + event generate .t.f <Motion> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3622,7 +3600,7 @@ test bind-22.70 {HandleEventGenerate: options <<Paste>> -root .t} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %R" - event generate .t.f <<Paste>> -root .t + event generate .t.f <<Paste>> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3636,7 +3614,7 @@ test bind-22.71 {HandleEventGenerate: options <Enter> -root .t} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %R" - event generate .t.f <Enter> -root .t + event generate .t.f <Enter> -root .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -3650,7 +3628,7 @@ test bind-22.72 {HandleEventGenerate: options <Configure> -root .t} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %R" - event generate .t.f <Configure> -root .t + event generate .t.f <Configure> -root .t } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-root" option} @@ -3663,7 +3641,7 @@ test bind-22.73 {HandleEventGenerate: options <Key> -rootx xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %X" - event generate .t.f <Key> -rootx xyz + event generate .t.f <Key> -rootx xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad screen distance "xyz"} @@ -3676,7 +3654,7 @@ test bind-22.74 {HandleEventGenerate: options <Key> -rootx 2i} -setup { set x {} } -body { bind .t.f <Key> "lappend x %X" - event generate .t.f <Key> -rootx 2i + event generate .t.f <Key> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3690,7 +3668,7 @@ test bind-22.75 {HandleEventGenerate: options <Button> -rootx 2i} -setup { set x {} } -body { bind .t.f <Button> "lappend x %X" - event generate .t.f <Button> -rootx 2i + event generate .t.f <Button> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3704,7 +3682,7 @@ test bind-22.76 {HandleEventGenerate: options <ButtonRelease> -rootx 2i} -setup set x {} } -body { bind .t.f <ButtonRelease> "lappend x %X" - event generate .t.f <ButtonRelease> -rootx 2i + event generate .t.f <ButtonRelease> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3718,7 +3696,7 @@ test bind-22.77 {HandleEventGenerate: options <Motion> -rootx 2i} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %X" - event generate .t.f <Motion> -rootx 2i + event generate .t.f <Motion> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3732,7 +3710,7 @@ test bind-22.78 {HandleEventGenerate: options <<Paste>> -rootx 2i} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %X" - event generate .t.f <<Paste>> -rootx 2i + event generate .t.f <<Paste>> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3746,7 +3724,7 @@ test bind-22.79 {HandleEventGenerate: options <Enter> -rootx 2i} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %X" - event generate .t.f <Enter> -rootx 2i + event generate .t.f <Enter> -rootx 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3760,7 +3738,7 @@ test bind-22.80 {HandleEventGenerate: options <Configure> -rootx 2i} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %X" - event generate .t.f <Configure> -rootx 2i + event generate .t.f <Configure> -rootx 2i } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-rootx" option} @@ -3773,7 +3751,7 @@ test bind-22.81 {HandleEventGenerate: options <Key> -rooty xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %Y" - event generate .t.f <Key> -rooty xyz + event generate .t.f <Key> -rooty xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad screen distance "xyz"} @@ -3786,7 +3764,7 @@ test bind-22.82 {HandleEventGenerate: options <Key> -rooty 2i} -setup { set x {} } -body { bind .t.f <Key> "lappend x %Y" - event generate .t.f <Key> -rooty 2i + event generate .t.f <Key> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3800,7 +3778,7 @@ test bind-22.83 {HandleEventGenerate: options <Button> -rooty 2i} -setup { set x {} } -body { bind .t.f <Button> "lappend x %Y" - event generate .t.f <Button> -rooty 2i + event generate .t.f <Button> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3814,7 +3792,7 @@ test bind-22.84 {HandleEventGenerate: options <ButtonRelease> -rooty 2i} -setup set x {} } -body { bind .t.f <ButtonRelease> "lappend x %Y" - event generate .t.f <ButtonRelease> -rooty 2i + event generate .t.f <ButtonRelease> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3828,7 +3806,7 @@ test bind-22.85 {HandleEventGenerate: options <Motion> -rooty 2i} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %Y" - event generate .t.f <Motion> -rooty 2i + event generate .t.f <Motion> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3842,7 +3820,7 @@ test bind-22.86 {HandleEventGenerate: options <<Paste>> -rooty 2i} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %Y" - event generate .t.f <<Paste>> -rooty 2i + event generate .t.f <<Paste>> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3856,7 +3834,7 @@ test bind-22.87 {HandleEventGenerate: options <Enter> -rooty 2i} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %Y" - event generate .t.f <Enter> -rooty 2i + event generate .t.f <Enter> -rooty 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -3870,7 +3848,7 @@ test bind-22.88 {HandleEventGenerate: options <Configure> -rooty 2i} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %Y" - event generate .t.f <Configure> -rooty 2i + event generate .t.f <Configure> -rooty 2i } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-rooty" option} @@ -3883,7 +3861,7 @@ test bind-22.89 {HandleEventGenerate: options <Key> -sendevent xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %E" - event generate .t.f <Key> -sendevent xyz + event generate .t.f <Key> -sendevent xyz } -cleanup { destroy .t.f } -returnCodes error -result {expected boolean value but got "xyz"} @@ -3896,7 +3874,7 @@ test bind-22.90 {HandleEventGenerate: options <Key> -sendevent 1} -setup { set x {} } -body { bind .t.f <Key> "lappend x %E" - event generate .t.f <Key> -sendevent 1 + event generate .t.f <Key> -sendevent 1 return $x } -cleanup { destroy .t.f @@ -3910,7 +3888,7 @@ test bind-22.91 {HandleEventGenerate: options <Key> -sendevent yes} -setup { set x {} } -body { bind .t.f <Key> "lappend x %E" - event generate .t.f <Key> -sendevent yes + event generate .t.f <Key> -sendevent yes return $x } -cleanup { destroy .t.f @@ -3924,11 +3902,11 @@ test bind-22.92 {HandleEventGenerate: options <Key> -sendevent 43} -setup { set x {} } -body { bind .t.f <Key> "lappend x %E" - event generate .t.f <Key> -sendevent 43 + event generate .t.f <Key> -sendevent 43 return $x } -cleanup { destroy .t.f -} -result {43} +} -result {1} test bind-22.93 {HandleEventGenerate: options <Key> -serial xyz} -setup { frame .t.f -class Test -width 150 -height 100 @@ -3938,7 +3916,7 @@ test bind-22.93 {HandleEventGenerate: options <Key> -serial xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %#" - event generate .t.f <Key> -serial xyz + event generate .t.f <Key> -serial xyz } -cleanup { destroy .t.f } -returnCodes error -result {expected integer but got "xyz"} @@ -3951,7 +3929,7 @@ test bind-22.94 {HandleEventGenerate: options <Key> -serial 100} -setup { set x {} } -body { bind .t.f <Key> "lappend x %#" - event generate .t.f <Key> -serial 100 + event generate .t.f <Key> -serial 100 return $x } -cleanup { destroy .t.f @@ -3965,7 +3943,7 @@ test bind-22.95 {HandleEventGenerate: options <Key> -state xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %s" - event generate .t.f <Key> -state xyz + event generate .t.f <Key> -state xyz } -cleanup { destroy .t.f } -returnCodes error -result {expected integer but got "xyz"} @@ -3978,7 +3956,7 @@ test bind-22.96 {HandleEventGenerate: options <Key> -state 1} -setup { set x {} } -body { bind .t.f <Key> "lappend x %s" - event generate .t.f <Key> -state 1 + event generate .t.f <Key> -state 1 return $x } -cleanup { destroy .t.f @@ -3992,7 +3970,7 @@ test bind-22.97 {HandleEventGenerate: options <Button> -state 1025} -setup { set x {} } -body { bind .t.f <Button> "lappend x %s" - event generate .t.f <Button> -state 1025 + event generate .t.f <Button> -state 1025 return $x } -cleanup { destroy .t.f @@ -4006,7 +3984,7 @@ test bind-22.98 {HandleEventGenerate: options <ButtonRelease> -state 1025} -setu set x {} } -body { bind .t.f <ButtonRelease> "lappend x %s" - event generate .t.f <ButtonRelease> -state 1025 + event generate .t.f <ButtonRelease> -state 1025 return $x } -cleanup { destroy .t.f @@ -4020,7 +3998,7 @@ test bind-22.99 {HandleEventGenerate: options <Motion> -state 1} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %s" - event generate .t.f <Motion> -state 1 + event generate .t.f <Motion> -state 1 return $x } -cleanup { destroy .t.f @@ -4034,7 +4012,7 @@ test bind-22.100 {HandleEventGenerate: options <<Paste>> -state 1} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %s" - event generate .t.f <<Paste>> -state 1 + event generate .t.f <<Paste>> -state 1 return $x } -cleanup { destroy .t.f @@ -4048,7 +4026,7 @@ test bind-22.101 {HandleEventGenerate: options <Enter> -state 1} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %s" - event generate .t.f <Enter> -state 1 + event generate .t.f <Enter> -state 1 return $x } -cleanup { destroy .t.f @@ -4062,7 +4040,7 @@ test bind-22.102 {HandleEventGenerate: options <Visibility> -state xyz} -setup { set x {} } -body { bind .t.f <Visibility> "lappend x %s" - event generate .t.f <Visibility> -state xyz + event generate .t.f <Visibility> -state xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad -state value "xyz": must be VisibilityUnobscured, VisibilityPartiallyObscured, or VisibilityFullyObscured} @@ -4075,7 +4053,7 @@ test bind-22.103 {HandleEventGenerate: options <Visibility> -state VisibilityUno set x {} } -body { bind .t.f <Visibility> "lappend x %s" - event generate .t.f <Visibility> -state VisibilityUnobscured + event generate .t.f <Visibility> -state VisibilityUnobscured return $x } -cleanup { destroy .t.f @@ -4089,7 +4067,7 @@ test bind-22.104 {HandleEventGenerate: options <Configure> -state xyz} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %s" - event generate .t.f <Configure> -state xyz + event generate .t.f <Configure> -state xyz } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-state" option} @@ -4102,7 +4080,7 @@ test bind-22.105 {HandleEventGenerate: options <Key> -subwindow .xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %S" - event generate .t.f <Key> -subwindow .xyz + event generate .t.f <Key> -subwindow .xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window path name ".xyz"} @@ -4115,7 +4093,7 @@ test bind-22.106 {HandleEventGenerate: options <Key> -subwindow .t} -setup { set x {} } -body { bind .t.f <Key> "lappend x %S" - event generate .t.f <Key> -subwindow .t + event generate .t.f <Key> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4129,7 +4107,7 @@ test bind-22.107 {HandleEventGenerate: options <Key> -subwindow xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %S" - event generate .t.f <Key> -subwindow xyz + event generate .t.f <Key> -subwindow xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window name/identifier "xyz"} @@ -4142,7 +4120,7 @@ test bind-22.108 {HandleEventGenerate: options <Key> -subwindow [winfo id .t]} - set x {} } -body { bind .t.f <Key> "lappend x %S" - event generate .t.f <Key> -subwindow [winfo id .t] + event generate .t.f <Key> -subwindow [winfo id .t] expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4156,7 +4134,7 @@ test bind-22.109 {HandleEventGenerate: options <Button> -subwindow .t} -setup { set x {} } -body { bind .t.f <Button> "lappend x %S" - event generate .t.f <Button> -subwindow .t + event generate .t.f <Button> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4170,7 +4148,7 @@ test bind-22.110 {HandleEventGenerate: options <ButtonRelease> -subwindow .t} -s set x {} } -body { bind .t.f <ButtonRelease> "lappend x %S" - event generate .t.f <ButtonRelease> -subwindow .t + event generate .t.f <ButtonRelease> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4184,7 +4162,7 @@ test bind-22.111 {HandleEventGenerate: options <Motion> -subwindow .t} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %S" - event generate .t.f <Motion> -subwindow .t + event generate .t.f <Motion> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4198,7 +4176,7 @@ test bind-22.112 {HandleEventGenerate: options <<Paste>> -subwindow .t} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %S" - event generate .t.f <<Paste>> -subwindow .t + event generate .t.f <<Paste>> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4212,7 +4190,7 @@ test bind-22.113 {HandleEventGenerate: options <Enter> -subwindow .t} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %S" - event generate .t.f <Enter> -subwindow .t + event generate .t.f <Enter> -subwindow .t expr {[winfo id .t] eq $x} } -cleanup { destroy .t.f @@ -4226,7 +4204,7 @@ test bind-22.114 {HandleEventGenerate: options <Configure> -subwindow .t} -setup set x {} } -body { bind .t.f <Configure> "lappend x %S" - event generate .t.f <Configure> -subwindow .t + event generate .t.f <Configure> -subwindow .t } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-subwindow" option} @@ -4239,7 +4217,7 @@ test bind-22.115 {HandleEventGenerate: options <Key> -time xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %t" - event generate .t.f <Key> -time xyz + event generate .t.f <Key> -time xyz } -cleanup { destroy .t.f } -returnCodes error -result {expected integer but got "xyz"} @@ -4252,7 +4230,7 @@ test bind-22.116 {HandleEventGenerate: options <Key> -time 100} -setup { set x {} } -body { bind .t.f <Key> "lappend x %t" - event generate .t.f <Key> -time 100 + event generate .t.f <Key> -time 100 return $x } -cleanup { destroy .t.f @@ -4266,7 +4244,7 @@ test bind-22.117 {HandleEventGenerate: options <Button> -time 100} -setup { set x {} } -body { bind .t.f <Button> "lappend x %t" - event generate .t.f <Button> -time 100 + event generate .t.f <Button> -time 100 return $x } -cleanup { destroy .t.f @@ -4280,7 +4258,7 @@ test bind-22.118 {HandleEventGenerate: options <ButtonRelease> -time 100} -setup set x {} } -body { bind .t.f <ButtonRelease> "lappend x %t" - event generate .t.f <ButtonRelease> -time 100 + event generate .t.f <ButtonRelease> -time 100 return $x } -cleanup { destroy .t.f @@ -4294,7 +4272,7 @@ test bind-22.119 {HandleEventGenerate: options <Motion> -time 100} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %t" - event generate .t.f <Motion> -time 100 + event generate .t.f <Motion> -time 100 return $x } -cleanup { destroy .t.f @@ -4308,7 +4286,7 @@ test bind-22.120 {HandleEventGenerate: options <<Paste>> -time 100} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %t" - event generate .t.f <<Paste>> -time 100 + event generate .t.f <<Paste>> -time 100 return $x } -cleanup { destroy .t.f @@ -4322,7 +4300,7 @@ test bind-22.121 {HandleEventGenerate: options <Enter> -time 100} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %t" - event generate .t.f <Enter> -time 100 + event generate .t.f <Enter> -time 100 return $x } -cleanup { destroy .t.f @@ -4336,7 +4314,7 @@ test bind-22.122 {HandleEventGenerate: options <Property> -time 100} -setup { set x {} } -body { bind .t.f <Property> "lappend x %t" - event generate .t.f <Property> -time 100 + event generate .t.f <Property> -time 100 return $x } -cleanup { destroy .t.f @@ -4350,7 +4328,7 @@ test bind-22.123 {HandleEventGenerate: options <Configure> -time 100} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %t" - event generate .t.f <Configure> -time 100 + event generate .t.f <Configure> -time 100 } -cleanup { destroy .t.f } -returnCodes error -result {<Configure> event doesn't accept "-time" option} @@ -4363,7 +4341,7 @@ test bind-22.124 {HandleEventGenerate: options <Expose> -width xyz} -setup { set x {} } -body { bind .t.f <Expose> "lappend x %w" - event generate .t.f <Expose> -width xyz + event generate .t.f <Expose> -width xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad screen distance "xyz"} @@ -4376,7 +4354,7 @@ test bind-22.125 {HandleEventGenerate: options <Expose> -width 2i} -setup { set x {} } -body { bind .t.f <Expose> "lappend x %w" - event generate .t.f <Expose> -width 2i + event generate .t.f <Expose> -width 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4390,7 +4368,7 @@ test bind-22.126 {HandleEventGenerate: options <Configure> -width 2i} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %w" - event generate .t.f <Configure> -width 2i + event generate .t.f <Configure> -width 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4404,7 +4382,7 @@ test bind-22.127 {HandleEventGenerate: options <Key> -width 2i} -setup { set x {} } -body { bind .t.f <Key> "lappend x %k" - event generate .t.f <Key> -width 2i + event generate .t.f <Key> -width 2i } -cleanup { destroy .t.f } -returnCodes error -result {<Key> event doesn't accept "-width" option} @@ -4417,7 +4395,7 @@ test bind-22.128 {HandleEventGenerate: options <Unmap> -window .xyz} -setup { set x {} } -body { bind .t.f <Unmap> "lappend x %W" - event generate .t.f <Unmap> -window .xyz + event generate .t.f <Unmap> -window .xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window path name ".xyz"} @@ -4430,7 +4408,7 @@ test bind-22.129 {HandleEventGenerate: options <Unmap> -window .t.f} -setup { set x {} } -body { bind .t.f <Unmap> "lappend x %W" - event generate .t.f <Unmap> -window .t.f + event generate .t.f <Unmap> -window .t.f return $x } -cleanup { destroy .t.f @@ -4444,7 +4422,7 @@ test bind-22.130 {HandleEventGenerate: options <Unmap> -window xyz} -setup { set x {} } -body { bind .t.f <Unmap> "lappend x %W" - event generate .t.f <Unmap> -window xyz + event generate .t.f <Unmap> -window xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad window name/identifier "xyz"} @@ -4457,7 +4435,7 @@ test bind-22.131 {HandleEventGenerate: options <Unmap> -window [winfo id .t.f]} set x {} } -body { bind .t.f <Unmap> "lappend x %W" - event generate .t.f <Unmap> -window [winfo id .t.f] + event generate .t.f <Unmap> -window [winfo id .t.f] return $x } -cleanup { destroy .t.f @@ -4471,7 +4449,7 @@ test bind-22.132 {HandleEventGenerate: options <Unmap> -window .t.f} -setup { set x {} } -body { bind .t.f <Unmap> "lappend x %W" - event generate .t.f <Unmap> -window .t.f + event generate .t.f <Unmap> -window .t.f return $x } -cleanup { destroy .t.f @@ -4485,7 +4463,7 @@ test bind-22.133 {HandleEventGenerate: options <Map> -window .t.f} -setup { set x {} } -body { bind .t.f <Map> "lappend x %W" - event generate .t.f <Map> -window .t.f + event generate .t.f <Map> -window .t.f return $x } -cleanup { destroy .t.f @@ -4499,7 +4477,7 @@ test bind-22.134 {HandleEventGenerate: options <Reparent> -window .t.f} -setup { set x {} } -body { bind .t.f <Reparent> "lappend x %W" - event generate .t.f <Reparent> -window .t.f + event generate .t.f <Reparent> -window .t.f return $x } -cleanup { destroy .t.f @@ -4513,7 +4491,7 @@ test bind-22.135 {HandleEventGenerate: options <Configure> -window .t.f} -setup set x {} } -body { bind .t.f <Configure> "lappend x %W" - event generate .t.f <Configure> -window .t.f + event generate .t.f <Configure> -window .t.f return $x } -cleanup { destroy .t.f @@ -4527,7 +4505,7 @@ test bind-22.136 {HandleEventGenerate: options <Gravity> -window .t.f} -setup { set x {} } -body { bind .t.f <Gravity> "lappend x %W" - event generate .t.f <Gravity> -window .t.f + event generate .t.f <Gravity> -window .t.f return $x } -cleanup { destroy .t.f @@ -4541,7 +4519,7 @@ test bind-22.137 {HandleEventGenerate: options <Circulate> -window .t.f} -setup set x {} } -body { bind .t.f <Circulate> "lappend x %W" - event generate .t.f <Circulate> -window .t.f + event generate .t.f <Circulate> -window .t.f return $x } -cleanup { destroy .t.f @@ -4555,7 +4533,7 @@ test bind-22.138 {HandleEventGenerate: options <Key> -window .t.f} -setup { set x {} } -body { bind .t.f <Key> "lappend x %W" - event generate .t.f <Key> -window .t.f + event generate .t.f <Key> -window .t.f } -cleanup { destroy .t.f } -returnCodes error -result {<Key> event doesn't accept "-window" option} @@ -4568,7 +4546,7 @@ test bind-22.139 {HandleEventGenerate: options <Key> -x xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %x" - event generate .t.f <Key> -x xyz + event generate .t.f <Key> -x xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad screen distance "xyz"} @@ -4581,7 +4559,7 @@ test bind-22.140 {HandleEventGenerate: options <Key> -x 2i} -setup { set x {} } -body { bind .t.f <Key> "lappend x %x" - event generate .t.f <Key> -x 2i + event generate .t.f <Key> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4595,7 +4573,7 @@ test bind-22.141 {HandleEventGenerate: options <Button> -x 2i} -setup { set x {} } -body { bind .t.f <Button> "lappend x %x" - event generate .t.f <Button> -x 2i + event generate .t.f <Button> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4609,7 +4587,7 @@ test bind-22.142 {HandleEventGenerate: options <ButtonRelease> -x 2i} -setup { set x {} } -body { bind .t.f <ButtonRelease> "lappend x %x" - event generate .t.f <ButtonRelease> -x 2i + event generate .t.f <ButtonRelease> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4623,7 +4601,7 @@ test bind-22.143 {HandleEventGenerate: options <Motion> -x 2i} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %x" - event generate .t.f <Motion> -x 2i + event generate .t.f <Motion> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4637,7 +4615,7 @@ test bind-22.144 {HandleEventGenerate: options <<Paste>> -x 2i} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %x" - event generate .t.f <<Paste>> -x 2i + event generate .t.f <<Paste>> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4651,7 +4629,7 @@ test bind-22.145 {HandleEventGenerate: options <Enter> -x 2i} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %x" - event generate .t.f <Enter> -x 2i + event generate .t.f <Enter> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4665,7 +4643,7 @@ test bind-22.146 {HandleEventGenerate: options <Expose> -x 2i} -setup { set x {} } -body { bind .t.f <Expose> "lappend x %x" - event generate .t.f <Expose> -x 2i + event generate .t.f <Expose> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4679,7 +4657,7 @@ test bind-22.147 {HandleEventGenerate: options <Configure> -x 2i} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %x" - event generate .t.f <Configure> -x 2i + event generate .t.f <Configure> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4693,7 +4671,7 @@ test bind-22.148 {HandleEventGenerate: options <Gravity> -x 2i} -setup { set x {} } -body { bind .t.f <Gravity> "lappend x %x" - event generate .t.f <Gravity> -x 2i + event generate .t.f <Gravity> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4707,7 +4685,7 @@ test bind-22.149 {HandleEventGenerate: options <Reparent> -x 2i} -setup { set x {} } -body { bind .t.f <Reparent> "lappend x %x" - event generate .t.f <Reparent> -x 2i + event generate .t.f <Reparent> -x 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4721,7 +4699,7 @@ test bind-22.150 {HandleEventGenerate: options <Map> -x 2i} -setup { set x {} } -body { bind .t.f <Map> "lappend x %x" - event generate .t.f <Map> -x 2i + event generate .t.f <Map> -x 2i } -cleanup { destroy .t.f } -returnCodes error -result {<Map> event doesn't accept "-x" option} @@ -4734,7 +4712,7 @@ test bind-22.151 {HandleEventGenerate: options <Key> -y xyz} -setup { set x {} } -body { bind .t.f <Key> "lappend x %y" - event generate .t.f <Key> -y xyz + event generate .t.f <Key> -y xyz } -cleanup { destroy .t.f } -returnCodes error -result {bad screen distance "xyz"} @@ -4747,7 +4725,7 @@ test bind-22.152 {HandleEventGenerate: options <Key> -y 2i} -setup { set x {} } -body { bind .t.f <Key> "lappend x %y" - event generate .t.f <Key> -y 2i + event generate .t.f <Key> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4761,7 +4739,7 @@ test bind-22.153 {HandleEventGenerate: options <Button> -y 2i} -setup { set x {} } -body { bind .t.f <Button> "lappend x %y" - event generate .t.f <Button> -y 2i + event generate .t.f <Button> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4775,7 +4753,7 @@ test bind-22.154 {HandleEventGenerate: options <ButtonRelease> -y 2i} -setup { set x {} } -body { bind .t.f <ButtonRelease> "lappend x %y" - event generate .t.f <ButtonRelease> -y 2i + event generate .t.f <ButtonRelease> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4789,7 +4767,7 @@ test bind-22.155 {HandleEventGenerate: options <Motion> -y 2i} -setup { set x {} } -body { bind .t.f <Motion> "lappend x %y" - event generate .t.f <Motion> -y 2i + event generate .t.f <Motion> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4803,7 +4781,7 @@ test bind-22.156 {HandleEventGenerate: options <<Paste>> -y 2i} -setup { set x {} } -body { bind .t.f <<Paste>> "lappend x %y" - event generate .t.f <<Paste>> -y 2i + event generate .t.f <<Paste>> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4817,7 +4795,7 @@ test bind-22.157 {HandleEventGenerate: options <Enter> -y 2i} -setup { set x {} } -body { bind .t.f <Enter> "lappend x %y" - event generate .t.f <Enter> -y 2i + event generate .t.f <Enter> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4831,7 +4809,7 @@ test bind-22.158 {HandleEventGenerate: options <Expose> -y 2i} -setup { set x {} } -body { bind .t.f <Expose> "lappend x %y" - event generate .t.f <Expose> -y 2i + event generate .t.f <Expose> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4845,7 +4823,7 @@ test bind-22.159 {HandleEventGenerate: options <Configure> -y 2i} -setup { set x {} } -body { bind .t.f <Configure> "lappend x %y" - event generate .t.f <Configure> -y 2i + event generate .t.f <Configure> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4859,7 +4837,7 @@ test bind-22.160 {HandleEventGenerate: options <Gravity> -y 2i} -setup { set x {} } -body { bind .t.f <Gravity> "lappend x %y" - event generate .t.f <Gravity> -y 2i + event generate .t.f <Gravity> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4873,7 +4851,7 @@ test bind-22.161 {HandleEventGenerate: options <Reparent> -y 2i} -setup { set x {} } -body { bind .t.f <Reparent> "lappend x %y" - event generate .t.f <Reparent> -y 2i + event generate .t.f <Reparent> -y 2i expr {[winfo pixels .t.f 2i] eq $x} } -cleanup { destroy .t.f @@ -4887,7 +4865,7 @@ test bind-22.162 {HandleEventGenerate: options <Map> -y 2i} -setup { set x {} } -body { bind .t.f <Map> "lappend x %y" - event generate .t.f <Map> -y 2i + event generate .t.f <Map> -y 2i } -cleanup { destroy .t.f } -returnCodes error -result {<Map> event doesn't accept "-y" option} @@ -4900,7 +4878,7 @@ test bind-22.163 {HandleEventGenerate: options <Key> -xyz 1} -setup { set x {} } -body { bind .t.f <Key> "lappend x %k" - event generate .t.f <Key> -xyz 1 + event generate .t.f <Key> -xyz 1 } -cleanup { destroy .t.f } -returnCodes error -result {bad option "-xyz": must be -when, -above, -borderwidth, -button, -count, -data, -delta, -detail, -focus, -height, -keycode, -keysym, -mode, -override, -place, -root, -rootx, -rooty, -sendevent, -serial, -state, -subwindow, -time, -warp, -width, -window, -x, or -y} @@ -5121,9 +5099,13 @@ test bind-25.3 {ParseEventDescription procedure} -setup { } -cleanup { destroy .t.f } -result a -test bind-25.4 {ParseEventDescription} -body { - bind .t <<Shift-Paste>> {puts hi} - bind .t +test bind-25.4 {ParseEventDescription} -setup { + frame .t.f -class Test -width 150 -height 100 +} -body { + bind .t.f <<Shift-Paste>> {puts hi} + bind .t.f +} -cleanup { + destroy .t.f } -result {<<Shift-Paste>>} # Assorted error cases in event sequence parsing @@ -5457,6 +5439,42 @@ test bind-25.49 {modifier names} -setup { destroy .t.f } -result <Extended-Key-Return> +test bind-25.50 {modifier names} -setup { + frame .t.f -class Test -width 150 -height 100 +} -body { + bind .t.f <Button6-a> foo + bind .t.f +} -cleanup { + destroy .t.f +} -result <B6-Key-a> + +test bind-25.51 {modifier names} -setup { + frame .t.f -class Test -width 150 -height 100 +} -body { + bind .t.f <Button7-a> foo + bind .t.f +} -cleanup { + destroy .t.f +} -result <B7-Key-a> + +test bind-25.52 {modifier names} -setup { + frame .t.f -class Test -width 150 -height 100 +} -body { + bind .t.f <Button8-a> foo + bind .t.f +} -cleanup { + destroy .t.f +} -result <B8-Key-a> + +test bind-25.53 {modifier names} -setup { + frame .t.f -class Test -width 150 -height 100 +} -body { + bind .t.f <Button9-a> foo + bind .t.f +} -cleanup { + destroy .t.f +} -result <B9-Key-a> + test bind-26.1 {event names} -setup { @@ -5790,8 +5808,8 @@ test bind-27.1 {button names} -body { bind .t <Expose-1> foo } -returnCodes error -result {specified button "1" for non-button event} test bind-27.2 {button names} -body { - bind .t <Button-10> foo -} -returnCodes error -result {bad event type or keysym "10"} + bind .t <Button-6> foo +} -returnCodes error -result {bad button number "6"} test bind-27.3 {button names} -setup { frame .t.f -class Test -width 150 -height 100 pack .t.f @@ -5862,62 +5880,6 @@ test bind-27.7 {button names} -setup { } -cleanup { destroy .t.f } -result {<Button-5> {button 5}} -test bind-27.8 {button names} -setup { - frame .t.f -class Test -width 150 -height 100 - pack .t.f - focus -force .t.f - update -} -body { - bind .t.f <Button-6> {lappend x "button 6"} - set x [bind .t.f] - event generate .t.f <Button-6> - event generate .t.f <ButtonRelease-6> - set x -} -cleanup { - destroy .t.f -} -result {<Button-6> {button 6}} -test bind-27.9 {button names} -setup { - frame .t.f -class Test -width 150 -height 100 - pack .t.f - focus -force .t.f - update -} -body { - bind .t.f <Button-7> {lappend x "button 7"} - set x [bind .t.f] - event generate .t.f <Button-7> - event generate .t.f <ButtonRelease-7> - set x -} -cleanup { - destroy .t.f -} -result {<Button-7> {button 7}} -test bind-27.10 {button names} -setup { - frame .t.f -class Test -width 150 -height 100 - pack .t.f - focus -force .t.f - update -} -body { - bind .t.f <Button-8> {lappend x "button 8"} - set x [bind .t.f] - event generate .t.f <Button-8> - event generate .t.f <ButtonRelease-8> - set x -} -cleanup { - destroy .t.f -} -result {<Button-8> {button 8}} -test bind-27.11 {button names} -setup { - frame .t.f -class Test -width 150 -height 100 - pack .t.f - focus -force .t.f - update -} -body { - bind .t.f <Button-9> {lappend x "button 9"} - set x [bind .t.f] - event generate .t.f <Button-9> - event generate .t.f <ButtonRelease-9> - set x -} -cleanup { - destroy .t.f -} -result {<Button-9> {button 9}} test bind-28.1 {keysym names} -body { bind .t <Expose-a> foo @@ -6017,7 +5979,7 @@ test bind-29.1 {Tcl_BackgroundError procedure} -setup { while executing "error "This is a test"" (command bound to event)}} - + test bind-29.2 {Tcl_BackgroundError procedure} -setup { proc do {} { event generate .t.f <Button> @@ -6182,7 +6144,7 @@ test bind-31.7 {virtual event user_data field - unshared, asynch} -setup { destroy .t.f } -result {{} {} {TestUserData >b<}} -test bind-32 {-warp, window was destroyed before the idle callback DoWarp} -setup { +test bind-32.1 {-warp, window was destroyed before the idle callback DoWarp} -setup { frame .t.f pack .t.f focus -force .t.f @@ -6194,7 +6156,511 @@ test bind-32 {-warp, window was destroyed before the idle callback DoWarp} -setu update ; # shall simply not crash } -cleanup { } -result {} +test bind-32.2 {detection of double click should not fail} -setup { + pack [frame .t.f] + focus -force .t.f + bind .t.f <Double-Button-1> { set x "Double" } + update + set x {} +} -body { + event generate .t.f <ButtonPress-1> + event generate .t.f <ButtonRelease-1> + # Simulate a lot of intervening exposure events. The old implementation + # that used an event ring overflowed, and the double click was not detected. + # But new implementation should work properly. + for {set i 0} {$i < 1000} {incr i} { + event generate .t.f <Expose> + } + event generate .t.f <ButtonPress-1> + event generate .t.f <ButtonRelease-1> + set x +} -cleanup { + destroy .t.f +} -result {Double} +test bind-32.3 {should trigger best match of modifier states} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Alt-Control-Key-A> { lappend x "Alt-Control" } + bind .t.f <Shift-Control-Key-A> { lappend x "Shift-Control" } + bind .t.f <Shift-Key-A> { lappend x "Shift" } + event generate .t.f <Alt-Control-Key-A> + set x +} -cleanup { + destroy .t.f +} -result {Shift-Control} +test bind-32.4 {should not trigger Double-1} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Double-1> { set x "Double" } + event generate .t.f <1> -time current + after 1000 + event generate .t.f <1> -time current + set x +} -cleanup { + destroy .t.f +} -result {} +test bind-32.5 {should trigger Quadruple-1} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Quadruple-1> { set x "Quadruple" } + bind .t.f <Triple-1> { set x "Triple" } + bind .t.f <Double-1> { set x "Double" } + bind .t.f <1> { set x "Single" } + # Old implementation triggered "Double", but new implementation + # triggers "Quadruple", the latter behavior conforms to other toolkits. + event generate .t.f <Button-1> -time 0 + event generate .t.f <Button-1> -time 400 + event generate .t.f <Button-1> -time 800 + event generate .t.f <Button-1> -time 1200 + set x +} -cleanup { + destroy .t.f +} -result {Quadruple} +test bind-32.6 {problem with sendevent} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + # Old implementation was losing sendevent value + bind .t.f <FocusIn> { set x "sendevent=%E" } + event generate .t.f <FocusIn> -sendevent 1 + set x +} -cleanup { + destroy .t.f +} -result {sendevent=1} +test bind-32.7 {test sequences} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Double-1> { lappend x "Double" } + bind .t.f <1><1><a> { lappend x "11" } + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {Double 11} +test bind-32.8 {test sequences} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <a><1><Double-1><1><a> { lappend x "Double" } + event generate .t.f <a> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {Double} +test bind-32.9 {trigger events for modifier keys} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Any-Key> { set x "Key" } + event generate .t.f <KeyPress> -keysym Caps_Lock + set x +} -cleanup { + destroy .t.f +} -result {Key} +test bind-32.10 {reset key state when destroying window} -setup { + set x {} +} -body { + pack [frame .t.f]; update; focus -force .t.f + bind .t.f <Key-A> { set x "A" } + event generate .t.f <KeyPress-A> + event generate .t.f <KeyPress-A> + destroy .t.f; update + pack [frame .t.f]; update; focus -force .t.f + bind .t.f <Key-A> { set x "A" } + bind .t.f <Double-Key-A> { set x "AA" } + event generate .t.f <KeyPress-A> + destroy .t.f + set x +} -result {A} +test bind-32.11 {match detailed virtual} -setup { + pack [frame .t.f -class Test] + focus -force .t.f + update + set x {} +} -body { + event add <<TestControlButton1>> <Control-Button-1> + bind Test <<TestControlButton1>> { set x "Control-Button-1" } + bind Test <Button-1> { set x "Button-1" } + bind .t.f <Button-1> { set x "Button-1" } + event generate .t.f <Control-ButtonPress-1> + set x +} -cleanup { + destroy .t.f + event delete <<TestControlButton1>> + bind Test <Button-1> {#} +} -result {Control-Button-1} +test bind-32.12 {don't detect repetition when window has changed} -setup { + pack [frame .t.f] + pack [frame .t.g] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Button-1> { set x "1" } + bind .t.f <Double-Button-1> { set x "11" } + event generate .t.f <ButtonPress-1> + event generate .t.g <ButtonPress-1> + event generate .t.f <ButtonPress-1> + set x +} -cleanup { + destroy .t.f + destroy .t.g +} -result {1} +test bind-32.13 {don't detect repetition when window has changed} -setup { + pack [frame .t.f] + pack [frame .t.g] + update + set x {} +} -body { + bind .t.f <Key-A> { set x "A" } + bind .t.f <Double-Key-A> { set x "AA" } + focus -force .t.f; event generate .t.f <KeyPress-A> + focus -force .t.g; event generate .t.g <KeyPress-A> + focus -force .t.f; event generate .t.f <KeyPress-A> + set x +} -cleanup { + destroy .t.f + destroy .t.g +} -result {A} +test bind-32.14 {don't detect repetition when window has changed} -setup { + pack [frame .t.f] + pack [frame .t.g] + update + set x {} +} -body { + bind .t.f <ButtonPress-1> { set x "1" } + bind .t.f <Double-ButtonPress-1> { set x "11" } + focus -force .t.f; event generate .t.f <ButtonPress-1> + focus -force .t.g; event generate .t.g <ButtonPress-1> + focus -force .t.f; event generate .t.f <ButtonPress-1> + set x +} -cleanup { + destroy .t.f + destroy .t.g +} -result {1} +test bind-32.15 {reset button state when destroying window} -setup { + set x {} +} -body { + pack [frame .t.f]; update; focus -force .t.f + bind .t.f <ButtonPress-1> { set x "1" } + event generate .t.f <ButtonPress-1> + event generate .t.f <ButtonPress-1> + destroy .t.f; update + pack [frame .t.f]; update; focus -force .t.f + bind .t.f <ButtonPress-1> { set x "1" } + bind .t.f <Double-ButtonPress-1> { set x "11" } + event generate .t.f <ButtonPress-1> + destroy .t.f + set x +} -result {1} +test bind-33.1 {prefer longest match} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <a><1><1> { lappend x "a11" } + bind .t.f <Double-1> { lappend x "Double" } + event generate .t.f <a> + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {a11} +test bind-33.2 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Double-1> { lappend x "Double" } + bind .t.f <1><1> { lappend x "11" } + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {Double} +test bind-33.3 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <a><Double-1><a> { lappend x "Double" } + bind .t.f <a><1><1><a> { lappend x "11" } + event generate .t.f <a> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {Double} +test bind-33.4 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><1> { lappend x "11" } + bind .t.f <Double-1> { lappend x "Double" } + event generate .t.f <1> -time 0 + event generate .t.f <1> -time 1000 + set x +} -cleanup { + destroy .t.f +} -result {11} +test bind-33.5 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><1> { lappend x "11" } + bind .t.f <Double-ButtonPress> { lappend x "Double" } + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {11} +test bind-33.6 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <a><1><1><1><1><a> { lappend x "1111" } + bind .t.f <a><ButtonPress><Double-ButtonPress><ButtonPress><a> { lappend x "Any-Double-Any" } + event generate .t.f <a> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {1111} +test bind-33.7 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <ButtonPress-1><a> { lappend x "1" } + bind .t.f <ButtonPress><a> { lappend x "Any" } + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {1} +test bind-33.8 {prefer most specific event} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Double-ButtonPress-1><a> { lappend x "1" } + bind .t.f <ButtonPress><ButtonPress><a> { lappend x "Any" } + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <a> + set x +} -cleanup { + destroy .t.f +} -result {1} +test bind-33.9 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><2><2><Double-1> { lappend x "first" } + bind .t.f <1><Double-2><1><1> { lappend x "last" } + event generate .t.f <1> + event generate .t.f <2> + event generate .t.f <2> + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {last} +test bind-33.10 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><Double-2><1><1> { lappend x "first" } + bind .t.f <1><2><2><Double-1> { lappend x "last" } + event generate .t.f <1> + event generate .t.f <2> + event generate .t.f <2> + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {last} +test bind-33.11 {should prefer most specific} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <2><Double-1><Double-2><Double-1><2><2> { lappend x "first" } + bind .t.f <2><1><1><2><2><Double-1><Double-2> { lappend x "last" } + event generate .t.f <2> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <2> + event generate .t.f <2> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <2> + event generate .t.f <2> + set x +} -cleanup { + destroy .t.f +} -result {first} +test bind-33.12 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <Control-1><1> { lappend x "first" } + bind .t.f <1><Control-1> { lappend x "last" } + event generate .t.f <Control-1> + event generate .t.f <Control-1> + set x +} -cleanup { + destroy .t.f +} -result {last} +test bind-33.13 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><Control-1> { lappend x "first" } + bind .t.f <Control-1><1> { lappend x "last" } + event generate .t.f <Control-1> + event generate .t.f <Control-1> + set x +} -cleanup { + destroy .t.f + # Old implementation failed, and returned "first", but this was wrong, + # because both bindings are homogeneous equal, so the most recently defined + # must be preferred. +} -result {last} +test bind-33.14 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <1><ButtonPress><1><ButtonPress> { lappend x "first" } + bind .t.f <ButtonPress><1><ButtonPress><1> { lappend x "last" } + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f +} -result {last} +test bind-33.15 {prefer last in case of homogeneous equal patterns} -setup { + pack [frame .t.f] + focus -force .t.f + update + set x {} +} -body { + bind .t.f <ButtonPress><1><ButtonPress><1> { lappend x "first" } + bind .t.f <1><ButtonPress><1><ButtonPress> { lappend x "last" } + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + event generate .t.f <1> + set x +} -cleanup { + destroy .t.f + # Old implementation failed, and returned "first", but this was wrong, + # because both bindings are homogeneous equal, so the most recently defined + # must be preferred. +} -result {last} + +test bind-34.1 {-warp works relatively to a window} -setup { + toplevel .top +} -body { + # In order to avoid platform-dependent coordinate results due to + # decorations and borders, this test warps the pointer twice + # relatively to a window that moved in the meantime, and checks + # how much the pointer moved + wm geometry .top +200+200 + update + event generate .top <Motion> -x 20 -y 20 -warp 1 + update idletasks ; # DoWarp is an idle callback + set pointerPos1 [winfo pointerxy .t] + wm geometry .top +600+600 + update + event generate .top <Motion> -x 20 -y 20 -warp 1 + update idletasks ; # DoWarp is an idle callback + set pointerPos2 [winfo pointerxy .t] + # from the first warped position to the second one, the mouse + # pointer should have moved the same amount as the window moved + set res 1 + foreach pos1 $pointerPos1 pos2 $pointerPos2 { + if {$pos1 != [expr {$pos2 - 400}]} { + set res 0 + } + } + set res +} -cleanup { + destroy .top +} -result {1} +test bind-34.2 {-warp works relatively to the screen} -setup { +} -body { + # Contrary to bind-32.2, we're directly checking screen coordinates + event generate {} <Motion> -x 20 -y 20 -warp 1 + update idletasks ; # DoWarp is an idle callback + set res [winfo pointerxy .] + event generate {} <Motion> -x 200 -y 200 -warp 1 + update idletasks ; # DoWarp is an idle callback + lappend res {*}[winfo pointerxy .] +} -cleanup { +} -result {20 20 200 200} # cleanup cleanupTests diff --git a/tests/bugs.tcl b/tests/bugs.tcl deleted file mode 100644 index 55e5f84..0000000 --- a/tests/bugs.tcl +++ /dev/null @@ -1,41 +0,0 @@ -# This file is a Tcl script to test out various known bugs that will -# cause Tk to crash. This file ends with .tcl instead of .test to make -# sure it isn't run when you type "source all". We currently are not -# shipping this file with the rest of the source release. -# -# Copyright (c) 1996 Sun Microsystems, Inc. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. - -if {[info procs test] != "test"} { - source defs -} - -test crash-1.0 {imgPhoto} { - image create photo p1 - image create photo p2 - catch {image create photo p2 -file bogus} - p1 copy p2 - label .l -image p1 - destroy .l - set foo "" -} {} - -test crash-1.1 {color} { - . configure -bg rgb:345 - set foo "" -} {} - - - - - - - - - - - - - diff --git a/tests/constraints.tcl b/tests/constraints.tcl index a87499d..c77fb00 100644 --- a/tests/constraints.tcl +++ b/tests/constraints.tcl @@ -190,7 +190,7 @@ testConstraint nonUnixUserInteraction [expr { [testConstraint userInteraction] || ([testConstraint unix] && [testConstraint notAqua]) }] -testConstraint haveDISPLAY [info exists env(DISPLAY)] +testConstraint haveDISPLAY [expr {[info exists env(DISPLAY)] && [testConstraint x11]}] testConstraint altDisplay [info exists env(TK_ALT_DISPLAY)] testConstraint noExceed [expr { ![testConstraint unix] || [catch {font actual "\{xyz"}] diff --git a/tests/ttk/ttk.test b/tests/ttk/ttk.test index d1ac1c2..9f78966 100644 --- a/tests/ttk/ttk.test +++ b/tests/ttk/ttk.test @@ -206,6 +206,7 @@ test ttk-2.8 "bug 3223850: button state disabled during click" -setup { destroy .b set ttk28 {} pack [ttk::button .b -command {set ::ttk28 failed}] + update } -body { bind .b <ButtonPress-1> {after 0 {.b configure -state disabled}} after 1 {event generate .b <ButtonPress-1>} diff --git a/unix/tkUnixPort.h b/unix/tkUnixPort.h index f581719..f8cf91b 100644 --- a/unix/tkUnixPort.h +++ b/unix/tkUnixPort.h @@ -24,6 +24,7 @@ #include <math.h> #include <pwd.h> #include <stdlib.h> +#include <assert.h> #include <string.h> #include <sys/types.h> #include <sys/file.h> diff --git a/win/tkWinInt.h b/win/tkWinInt.h index c118477..aa7b094 100644 --- a/win/tkWinInt.h +++ b/win/tkWinInt.h @@ -201,6 +201,12 @@ MODULE_SCOPE void TkpWinToplevelDetachWindow(TkWindow *winPtr); MODULE_SCOPE int TkpWmGetState(TkWindow *winPtr); /* + * The following is implemented in tkWinPointer.c and also used in tkWinWindow.c + */ + +MODULE_SCOPE void TkSetCursorPos(int x, int y); + +/* * Common routines used in Windows implementation */ MODULE_SCOPE Tcl_Obj * TkWin32ErrorObj(HRESULT hrError); diff --git a/win/tkWinPointer.c b/win/tkWinPointer.c index 251b5b9..896500c 100644 --- a/win/tkWinPointer.c +++ b/win/tkWinPointer.c @@ -336,10 +336,10 @@ XQueryPointer( /* *---------------------------------------------------------------------- * - * XWarpPointer -- + * XWarpPointer, TkpWarpPointer -- * - * Move pointer to new location. This is not a complete implementation of - * this function. + * Move pointer to new location. Note that implementation of XWarpPointer + * is incomplete. * * Results: * None. @@ -350,6 +350,29 @@ XQueryPointer( *---------------------------------------------------------------------- */ +/* + * TkSetCursorPos is a helper function replacing SetCursorPos since this + * latter Windows function appears to have been broken by Microsoft + * since Win10 Falls Creator Update - See ticket [69b48f427e] along with + * several other Internet reports about this breakage. + */ + +void TkSetCursorPos( + int x, + int y) +{ + INPUT input; + + input.type = INPUT_MOUSE; + input.mi.dx = x * (65535.0 / (GetSystemMetrics(SM_CXSCREEN) - 1)); + input.mi.dy = y * (65535.0 / (GetSystemMetrics(SM_CYSCREEN) - 1)); + input.mi.mouseData = 0; + input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + input.mi.time = 0; + input.mi.dwExtraInfo = 0; + SendInput(1, &input, sizeof(input)); +} + int XWarpPointer( Display *display, @@ -365,7 +388,7 @@ XWarpPointer( RECT r; GetWindowRect(Tk_GetHWND(dest_w), &r); - SetCursorPos(r.left+dest_x, r.top+dest_y); + TkSetCursorPos(r.left+dest_x, r.top+dest_y); return Success; } @@ -377,9 +400,9 @@ TkpWarpPointer( RECT r; GetWindowRect(Tk_GetHWND(Tk_WindowId(dispPtr->warpWindow)), &r); - SetCursorPos(r.left + dispPtr->warpX, r.top + dispPtr->warpY); + TkSetCursorPos(r.left + dispPtr->warpX, r.top + dispPtr->warpY); } else { - SetCursorPos(dispPtr->warpX, dispPtr->warpY); + TkSetCursorPos(dispPtr->warpX, dispPtr->warpY); } } @@ -544,6 +567,29 @@ TkpSetCapture( } /* + *---------------------------------------------------------------------- + * + * TkpGetCapture -- + * + * This function requests which window is capturing the mouse. + * + * Results: + * The return value is a pointer to the capture window, if there is + * one, otherwise it is NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tk_Window +TkpGetCapture(void) +{ + return Tk_HWNDToWindow(GetCapture()); +} + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/win/tkWinPort.h b/win/tkWinPort.h index 254f44e..89d91a7 100644 --- a/win/tkWinPort.h +++ b/win/tkWinPort.h @@ -25,6 +25,7 @@ #include <wchar.h> #include <io.h> #include <stdlib.h> +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <malloc.h> diff --git a/win/tkWinWindow.c b/win/tkWinWindow.c index 2a32006..0b883b1 100644 --- a/win/tkWinWindow.c +++ b/win/tkWinWindow.c @@ -765,33 +765,6 @@ XChangeWindowAttributes( /* *---------------------------------------------------------------------- * - * XReparentWindow -- - * - * TODO: currently placeholder to satisfy Xlib stubs. - * - * Results: - * None. - * - * Side effects: - * TODO. - * - *---------------------------------------------------------------------- - */ - -int -XReparentWindow( - Display *display, - Window w, - Window parent, - int x, - int y) -{ - return BadWindow; -} - -/* - *---------------------------------------------------------------------- - * * TkWinSetWindowPos -- * * Adjust the stacking order of a window relative to a second window (or @@ -882,7 +855,7 @@ TkpShowBusyWindow( */ GetCursorPos(&point); - SetCursorPos(point.x, point.y); + TkSetCursorPos(point.x, point.y); } /* @@ -924,7 +897,7 @@ TkpHideBusyWindow( */ GetCursorPos(&point); - SetCursorPos(point.x, point.y); + TkSetCursorPos(point.x, point.y); } /* diff --git a/win/tkWinX.c b/win/tkWinX.c index 4d88dc7..46a651b 100644 --- a/win/tkWinX.c +++ b/win/tkWinX.c @@ -587,6 +587,11 @@ TkpOpenDisplay( */ TkpInitKeymapInfo(tsdPtr->winDisplay); + /* + * Key map info must be available immediately, because of "send event". + */ + TkpInitKeymapInfo(tsdPtr->winDisplay); + return tsdPtr->winDisplay; } @@ -564,7 +564,6 @@ XDrawSegments( } #endif -#if 0 char * XFetchBuffer( Display *display, @@ -580,7 +579,7 @@ XFetchName( Window w, char **window_name_return) { - return (Status) 0; + return Success; } Atom * @@ -592,14 +591,16 @@ XListProperties( return (Atom *) 0; } -void +int XMapRaised( Display *display, Window w) { + return Success; } -void +#if 0 +int XPutImage( Display *display, Drawable d, @@ -612,9 +613,11 @@ XPutImage( unsigned int width, unsigned int height) { + return 0; } +#endif -void +int XQueryTextExtents( Display *display, XID font_ID, @@ -625,9 +628,10 @@ XQueryTextExtents( int *font_descent_return, XCharStruct *overall_return) { + return Success; } -void +int XReparentWindow( Display *display, Window w, @@ -635,32 +639,72 @@ XReparentWindow( int x, int y) { + return BadWindow; } -void -XRotateBuffers( +int +XUndefineCursor( Display *display, - int rotate) + Window w) { + return Success; } -void -XStoreBuffer( +Window +XCreateWindow( Display *display, - _Xconst char *bytes, - int nbytes, - int buffer) + Window parent, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned int border_width, + int depth, + unsigned int clazz, + Visual *visual, + unsigned long value_mask, + XSetWindowAttributes *attributes) { + return 0; } -void -XUndefineCursor( - Display *display, - Window w) +int +XPointInRegion( + Region rgn, + int x, + int y) { + return 0; +} + +int +XUnionRegion( + Region srca, + Region srcb, + Region dr_return) +{ + return 0; +} + +Region +XPolygonRegion( + XPoint *pts, + int n, + int rule) +{ + return 0; +} + +int +XOffsetRegion( + Region rgn, + int dx, + int dy) +{ + return 0; } -#endif + /* * Local Variables: * mode: c @@ -668,3 +712,4 @@ XUndefineCursor( * fill-column: 78 * End: */ + |