summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgcramer <remarcg@gmx.net>2019-01-13 10:13:47 (GMT)
committergcramer <remarcg@gmx.net>2019-01-13 10:13:47 (GMT)
commitf094a8c8a291d30c8013117f93b1a90b664986bd (patch)
tree41d24eb99aa1b81e8cd430e58f2f1c842b172b08
parent7a6e5802ad2316efd38f519c23ea346c7d36b0ec (diff)
downloadtk-f094a8c8a291d30c8013117f93b1a90b664986bd.zip
tk-f094a8c8a291d30c8013117f93b1a90b664986bd.tar.gz
tk-f094a8c8a291d30c8013117f93b1a90b664986bd.tar.bz2
(1) Return type of Tk_CreateBinding changed to 'Mask'. (2) Fix of issue with homogeneous equal sequences. New test cases bind-32.12 and bind-32.13.
-rw-r--r--doc/BindTable.32
-rw-r--r--generic/tk.decls2
-rw-r--r--generic/tkArray.h40
-rw-r--r--generic/tkBind.c275
-rw-r--r--generic/tkCanvas.c6
-rw-r--r--generic/tkCmds.c2
-rw-r--r--generic/tkDecls.h4
-rw-r--r--generic/tkTextTag.c8
-rw-r--r--tests/bind.test31
9 files changed, 257 insertions, 113 deletions
diff --git a/doc/BindTable.3 b/doc/BindTable.3
index 5130bfc..18a9ea2 100644
--- a/doc/BindTable.3
+++ b/doc/BindTable.3
@@ -19,7 +19,7 @@ Tk_BindingTable
.sp
\fBTk_DeleteBindingTable(\fIbindingTable\fB)\fR
.sp
-unsigned long
+Mask
\fBTk_CreateBinding(\fIinterp, bindingTable, object, eventString, script, append\fB)\fR
.sp
int
diff --git a/generic/tk.decls b/generic/tk.decls
index 9ceb3af..f33ebc3 100644
--- a/generic/tk.decls
+++ b/generic/tk.decls
@@ -163,7 +163,7 @@ declare 32 {
Tk_Window Tk_CoordsToWindow(int rootX, int rootY, Tk_Window tkwin)
}
declare 33 {
- unsigned long Tk_CreateBinding(Tcl_Interp *interp,
+ Mask Tk_CreateBinding(Tcl_Interp *interp,
Tk_BindingTable bindingTable, ClientData object,
const char *eventStr, const char *script, int append)
}
diff --git a/generic/tkArray.h b/generic/tkArray.h
index 266644d..44123e7 100644
--- a/generic/tkArray.h
+++ b/generic/tkArray.h
@@ -5,7 +5,7 @@
* 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 by Gregor Cramer.
+ * 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.
@@ -28,7 +28,7 @@
* MyArray_Append(&arr, MakePair(1, 2));
* MyArray_Append(&arr, MakePair(2, 3));
* for (i = 0; i < MyArray_Size(arr); ++i) {
- * const Pair *p = MyArray_Get(arr, i);
+ * Pair *p = MyArray_Get(arr, i);
* printf("%d -> %d\n", p->key, p->value);
* ckfree(p);
* }
@@ -49,7 +49,6 @@
* for (i = 0; i < MyArray_Size(arr); ++i) {
* const Pair *p = MyArray_Get(arr, i);
* printf("%d -> %d\n", p->key, p->value);
- * ckfree(p);
* }
* MyArray_Free(&arr);
* assert(arr == NULL);
@@ -205,7 +204,7 @@ AT##_BufferSize(size_t numElems) \
\
__TK_ARRAY_UNUSED \
static int \
-AT##_IsEmpty(AT *arr) \
+AT##_IsEmpty(const AT *arr) \
{ \
assert(!arr || arr->size != 0xdeadbeef); \
return !arr || arr->size == 0u; \
@@ -247,7 +246,8 @@ __TK_ARRAY_UNUSED \
static ElemType * \
AT##_Front(AT *arr) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(!AT##_IsEmpty(arr)); \
return &arr->buf[0]; \
} \
@@ -256,7 +256,8 @@ __TK_ARRAY_UNUSED \
static ElemType * \
AT##_Back(AT *arr) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(!AT##_IsEmpty(arr)); \
return &arr->buf[arr->size - 1]; \
} \
@@ -288,7 +289,8 @@ __TK_ARRAY_UNUSED \
static void \
AT##_Clear(AT *arr, size_t from, size_t to) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(to <= AT##_Capacity(arr)); \
assert(from <= to); \
memset(arr->buf + from, 0, AT##_BufferSize(to - from)); \
@@ -311,10 +313,11 @@ __TK_ARRAY_UNUSED \
static void \
AT##_SetSize(AT *arr, size_t newSize) \
{ \
- assert(arr); \
assert(newSize <= AT##_Capacity(arr)); \
assert(!arr || arr->size != 0xdeadbeef); \
- arr->size = newSize; \
+ if (arr) { \
+ arr->size = newSize; \
+ } \
} \
\
__TK_ARRAY_UNUSED \
@@ -340,16 +343,18 @@ AT##_PopBack(AT *arr) \
\
__TK_ARRAY_UNUSED \
static ElemType * \
-AT##_Get(AT *arr, size_t at) \
+AT##_Get(const AT *arr, size_t at) \
{ \
+ assert(arr); \
assert(at < AT##_Size(arr)); \
- return &arr->buf[at]; \
+ 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; \
} \
@@ -410,7 +415,7 @@ AT##_BufferSize(size_t numElems) \
\
__TK_ARRAY_UNUSED \
static int \
-AT##_IsEmpty(AT *arr) \
+AT##_IsEmpty(const AT *arr) \
{ \
assert(!arr || arr->size != 0xdeadbeef); \
return !arr || arr->size == 0; \
@@ -436,7 +441,8 @@ __TK_ARRAY_UNUSED \
static ElemType * \
AT##_Front(AT *arr) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(!AT##_IsEmpty(arr)); \
return arr->buf[0]; \
} \
@@ -445,7 +451,8 @@ __TK_ARRAY_UNUSED \
static ElemType * \
AT##_Back(AT *arr) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(!AT##_IsEmpty(arr)); \
return arr->buf[arr->size - 1]; \
} \
@@ -493,7 +500,8 @@ __TK_ARRAY_UNUSED \
static void \
AT##_Clear(AT *arr, size_t from, size_t to) \
{ \
- assert(!arr || arr->size != 0xdeadbeef); \
+ assert(arr); \
+ assert(arr->size != 0xdeadbeef); \
assert(to <= AT##_Capacity(arr)); \
assert(from <= to); \
memset(arr->buf + from, 0, AT##_BufferSize(to - from)); \
@@ -546,6 +554,7 @@ __TK_ARRAY_UNUSED \
static ElemType * \
AT##_Get(const AT *arr, size_t at) \
{ \
+ assert(arr); \
assert(at < AT##_Size(arr)); \
return arr->buf[at]; \
} \
@@ -554,6 +563,7 @@ __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; \
} \
diff --git a/generic/tkBind.c b/generic/tkBind.c
index 43e4655..ff00cb0 100644
--- a/generic/tkBind.c
+++ b/generic/tkBind.c
@@ -7,7 +7,7 @@
* 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 by Gregor Cramer.
+ * 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.
@@ -108,22 +108,24 @@ enum { true = (bool) 1, false = (bool) 0 };
#ifdef SUPPORT_DEBUGGING
# undef SUPPORT_DEBUGGING
#endif
-#define SUPPORT_DEBUGGING 0
+#define SUPPORT_DEBUGGING 1
/*
* Test validity of PSEntry items.
*/
-# define TEST_PSENTRY(psPtr) psPtr->added != (int) 0xdeadbeef
-# define MARK_PSENTRY(psPtr) psPtr->added = 0xdeadbeef
+# 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 {
- unsigned long info; /* This either corresponds to xkey.keycode, or to xbutton.button,
+ 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;
@@ -148,10 +150,18 @@ typedef struct {
struct PatSeq; /* forward declaration */
+/* We need this array for bookkeeping the last matching modifier mask per pattern. */
+TK_ARRAY_DEFINE(PSModMaskArr, Mask);
+
typedef struct PSEntry {
TK_DLIST_LINKS(PSEntry); /* Makes this struct a double linked list; must be first entry. */
- struct PatSeq* psPtr; /* Pointer to pattern sequence. */
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:30; /* 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
@@ -159,7 +169,11 @@ typedef struct PSEntry {
unsigned keepIt:1; /* Whether to keep this entry, even if expired. */
} PSEntry;
-TK_DLIST_DEFINE(PSList, PSEntry);/* defining the whole PSList_* stuff (list of PSEntry items) */
+/* 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.
@@ -193,8 +207,7 @@ typedef struct Tk_BindingTable_ {
/* Containing the most recent event for every event type. */
PromArr *promArr; /* Contains the promoted pattern sequences. */
Event *curEvent; /* Pointing to most recent event. */
- unsigned long curModStateMask;
- /* Containing the current modifier mask. */
+ Mask 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 *). */
@@ -249,16 +262,16 @@ typedef struct {
* 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.
+ * For technical reasons we do not use 'union Detail', although this would
+ * be possible, instead 'info' and 'name' are both included.
*/
typedef struct {
unsigned eventType; /* Type of X event, e.g. ButtonPress. */
unsigned count; /* Multi-event count, e.g. double-clicks, triple-clicks, etc. */
- unsigned long modStateMask; /* Mask of modifiers that must be present (zero means no modifiers
+ Mask modMask; /* Mask of modifiers that must be present (zero means no modifiers
* are required). */
- unsigned long info; /* Additional information that must match event. Normally this is zero,
+ 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
@@ -299,7 +312,9 @@ typedef struct PatSeq {
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. */
- bool added; /* Is this pattern sequence already added to lookup table? */
+ 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. */
@@ -315,13 +330,18 @@ typedef struct PatSeq {
* the same object (NULL for end of list). Needed to implement
* Tk_DeleteAllBindings. In a virtual event table it has no meaning. */
} ptr;
- DEBUG(bool owned); /* For debugging purposes. */
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;
/*
+ * Compute memory size of struct PatSeq with given pattern size.
+ * The caller must be sure that pattern size is greater than zero.
+ */
+#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:
*/
@@ -692,7 +712,7 @@ static void ExpandPercents(TkWindow *winPtr, const char *before, Event *eventPt
unsigned scriptCount, Tcl_DString *dsPtr);
static PatSeq * FindSequence(Tcl_Interp *interp, LookupTables *lookupTables,
ClientData object, const char *eventString, bool create,
- bool allowVirtual, unsigned long *maskPtr);
+ bool allowVirtual, Mask *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);
@@ -708,13 +728,13 @@ static PatSeq * MatchPatterns(TkDisplay *dispPtr, Tk_BindingTable bindPtr, PSLi
static bool NameToWindow(Tcl_Interp *interp, Tk_Window main,
Tcl_Obj *objPtr, Tk_Window *tkwinPtr);
static unsigned ParseEventDescription(Tcl_Interp *interp, const char **eventStringPtr,
- TkPattern *patPtr, unsigned long *eventMaskPtr);
+ TkPattern *patPtr, Mask *eventMaskPtr);
static void DoWarp(ClientData clientData);
static PSList * GetLookupForEvent(LookupTables* lookupPtr, const Event *eventPtr,
Tcl_Obj *object, bool onlyConsiderDetailedEvents);
static void ClearLookupTable(LookupTables *lookupTables, ClientData object);
static void ClearPromotionLists(Tk_BindingTable bindPtr, ClientData object);
-static PSEntry * MakeListEntry(PSList *pool, PatSeq *psPtr);
+static PSEntry * MakeListEntry(PSList *pool, PatSeq *psPtr, bool needModMasks);
static void RemovePatSeqFromLookup(LookupTables *lookupTables, PatSeq *psPtr);
static void RemovePatSeqFromPromotionLists(Tk_BindingTable bindPtr, PatSeq *psPtr);
static PatSeq * DeletePatSeq(PatSeq *psPtr);
@@ -740,8 +760,8 @@ static bool TestNearbyCoords(int lhs, int rhs) { return Abs(lhs - rhs) <= NEARBY
static bool
IsSubsetOf(
- unsigned long lhsMask, /* this is a subset */
- unsigned long rhsMask) /* of this bit field? */
+ Mask lhsMask, /* this is a subset */
+ Mask rhsMask) /* of this bit field? */
{
return (lhsMask & rhsMask) == lhsMask;
}
@@ -783,7 +803,7 @@ CurrentTimeInMilliSecs()
return ((Time) now.sec)*1000 + ((Time) now.usec)/1000;
}
-static unsigned long
+static Info
GetInfo(
const PatSeq *psPtr,
unsigned index)
@@ -794,6 +814,7 @@ GetInfo(
return psPtr->pats[index].info;
}
+#if PREFER_MOST_SPECIALIZED_EVENT
static unsigned
GetCount(
const PatSeq *psPtr,
@@ -804,6 +825,7 @@ GetCount(
return psPtr->pats[index].count;
}
+#endif
static bool
MatchEventNearby(
@@ -859,6 +881,9 @@ RemoveListEntry(
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);
}
@@ -887,6 +912,17 @@ ClearList(
}
}
+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,
@@ -978,22 +1014,34 @@ SetupPatternKey(
static PSEntry *
MakeListEntry(
PSList *pool,
- PatSeq *psPtr)
+ PatSeq *psPtr,
+ bool 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 = false;
@@ -1182,7 +1230,8 @@ TkBindInit(
assert(sizeof(XEvent) >= sizeof(XVirtualEvent));
/* type of TkPattern.info is well defined? */
- assert(sizeof(unsigned long) >= sizeof(KeySym));
+ 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));
@@ -1465,7 +1514,7 @@ Tk_DeleteBindingTable(
ClearPromotionLists(bindPtr, NULL);
PromArr_Free(&bindPtr->promArr);
DEBUG(countEntryItems -= PSList_Size(&bindPtr->lookupTables.entryPool));
- PSList_Clear(&bindPtr->lookupTables.entryPool);
+ PSList_Traverse(&bindPtr->lookupTables.entryPool, FreePatSeqEntry);
/*
* Clean up the rest of the information associated with the binding table.
@@ -1525,7 +1574,7 @@ InsertPatSeq(
psList = Tcl_GetHashValue(hPtr);
}
- psEntry = MakeListEntry(&lookupTables->entryPool, psPtr);
+ psEntry = MakeListEntry(&lookupTables->entryPool, psPtr, false);
PSList_Append(psList, psEntry);
psPtr->added = true;
}
@@ -1554,7 +1603,7 @@ InsertPatSeq(
*--------------------------------------------------------------
*/
-unsigned long
+Mask
Tk_CreateBinding(
Tcl_Interp *interp, /* Used for error reporting. */
Tk_BindingTable bindPtr, /* Table in which to create binding. */
@@ -1567,7 +1616,7 @@ Tk_CreateBinding(
* existing binding will always be replaced. */
{
PatSeq *psPtr;
- unsigned long eventMask;
+ Mask eventMask;
char *oldStr;
char *newStr;
@@ -1576,11 +1625,14 @@ Tk_CreateBinding(
assert(eventString);
assert(script);
+ psPtr = FindSequence(interp, &bindPtr->lookupTables, object, eventString,
+ !!*script, true, &eventMask);
+
if (!*script) {
+ assert(!psPtr || psPtr->added);
/* Silently ignore empty scripts -- see SF#3006842 */
- return 1;
+ return eventMask;
}
- psPtr = FindSequence(interp, &bindPtr->lookupTables, object, eventString, true, true, &eventMask);
if (!psPtr) {
return 0;
}
@@ -2040,8 +2092,8 @@ IsBetterMatch(
if (!fstMatchPtr) { return true; }
for (i = patIndex; i >= 0; --i) {
- unsigned long fstInfo = GetInfo(fstMatchPtr, i);
- unsigned long sndInfo = GetInfo(sndMatchPtr, i);
+ Info fstInfo = GetInfo(fstMatchPtr, i);
+ Info sndInfo = GetInfo(sndMatchPtr, i);
if (sndInfo && !fstInfo) { return true; }
if (!sndInfo && fstInfo) { return false; }
@@ -2123,9 +2175,9 @@ Tk_BindEvent(
}
if (flags & KEY_BUTTON_MOTION_VIRTUAL) {
- bindPtr->curModStateMask = eventPtr->xkey.state;
+ bindPtr->curModMask = eventPtr->xkey.state;
} else if (flags & CROSSING) {
- bindPtr->curModStateMask = eventPtr->xcrossing.state;
+ bindPtr->curModMask = eventPtr->xcrossing.state;
}
dispPtr = ((TkWindow *) tkwin)->dispPtr;
@@ -2150,10 +2202,13 @@ Tk_BindEvent(
bindInfoPtr->lastCurrentTime = CurrentTimeInMilliSecs();
bindInfoPtr->lastEventTime = eventPtr->xcrossing.time;
}
- /* fallthru */
+ if (eventPtr->xcrossing.detail == NotifyInferior) {
+ return;
+ }
+ break;
case FocusIn:
case FocusOut:
- if (eventPtr->xcrossing.detail == NotifyInferior) {
+ if (eventPtr->xfocus.detail == NotifyInferior) {
return;
}
break;
@@ -2598,7 +2653,7 @@ VirtPatIsBound(
const TkPattern *virtPatPtr = psPtr->pats;
if (physPatPtr->info || !virtPatPtr->info) {
- if (IsSubsetOf(virtPatPtr->modStateMask, physPatPtr->modStateMask)) {
+ if (IsSubsetOf(virtPatPtr->modMask, physPatPtr->modMask)) {
return false; /* we cannot surpass this match */
}
}
@@ -2639,22 +2694,62 @@ Compare(
if (!fstMatchPtr) { return +1; }
- assert(patIndex < fstMatchPtr->numPats);
- assert(patIndex < sndMatchPtr->numPats);
+ assert(patIndex == fstMatchPtr->numPats - 1);
+ assert(patIndex == sndMatchPtr->numPats - 1);
for (i = patIndex; i >= 0; --i) {
- unsigned long fstInfo = GetInfo(fstMatchPtr, i);
- unsigned long sndInfo = GetInfo(sndMatchPtr, i);
+ Info fstInfo = GetInfo(fstMatchPtr, i);
+ Info sndInfo = GetInfo(sndMatchPtr, i);
if (sndInfo && !fstInfo) { return +1; }
if (!sndInfo && fstInfo) { return -1; }
}
- if (patIndex == fstMatchPtr->numPats - 1 && patIndex == sndMatchPtr->numPats - 1) {
- return (int) sndMatchPtr->count - (int) fstMatchPtr->count;
+ return (int) sndMatchPtr->count - (int) fstMatchPtr->count;
+}
+
+/* helper function */
+static bool
+CompareModMasks(
+ const PSModMaskArr *fstModMaskArr,
+ const PSModMaskArr *sndModMaskArr,
+ Mask fstModMask,
+ Mask sndModMask)
+{
+ int fstCount = 0;
+ int sndCount = 0;
+ int i;
+
+ if (PSModMaskArr_IsEmpty(fstModMaskArr)) {
+ if (!PSModMaskArr_IsEmpty(sndModMaskArr)) {
+ for (i = PSModMaskArr_Size(sndModMaskArr) - 1; i >= 0; --i) {
+ if (*PSModMaskArr_Get(sndModMaskArr, i)) {
+ ++sndCount;
+ }
+ }
+ }
+ } else if (PSModMaskArr_IsEmpty(sndModMaskArr)) {
+ for (i = PSModMaskArr_Size(fstModMaskArr) - 1; i >= 0; --i) {
+ if (*PSModMaskArr_Get(fstModMaskArr, i)) {
+ ++fstCount;
+ }
+ }
+ } else {
+ assert(PSModMaskArr_Size(fstModMaskArr) == PSModMaskArr_Size(sndModMaskArr));
+
+ for (i = PSModMaskArr_Size(fstModMaskArr) - 1; i >= 0; --i) {
+ Mask fstModMask = *PSModMaskArr_Get(fstModMaskArr, i);
+ Mask sndModMask = *PSModMaskArr_Get(sndModMaskArr, i);
+
+ if (IsSubsetOf(fstModMask, sndModMask)) { ++sndCount; }
+ if (IsSubsetOf(sndModMask, fstModMask)) { ++fstCount; }
+ }
}
- return (int) GetCount(sndMatchPtr, patIndex) - (int) GetCount(fstMatchPtr, patIndex);
+ if (IsSubsetOf(fstModMask, sndModMask)) { ++sndCount; }
+ if (IsSubsetOf(sndModMask, fstModMask)) { ++fstCount; }
+
+ return fstCount - sndCount;
}
static PatSeq *
@@ -2675,7 +2770,8 @@ MatchPatterns(
PSEntry *psEntry;
PatSeq *bestPtr;
PatSeq *bestPhysPtr;
- unsigned long bestModMask;
+ Mask bestModMask;
+ const PSModMaskArr *bestModMaskArr;
assert(dispPtr);
assert(bindPtr);
@@ -2714,48 +2810,48 @@ MatchPatterns(
* cannot be done in ParseEventDescription, otherwise this function would
* be the better place.
*/
- unsigned long modMask = ResolveModifiers(dispPtr, patPtr->modStateMask);
- unsigned long curModStateMask = ResolveModifiers(dispPtr, bindPtr->curModStateMask);
+ Mask modMask = ResolveModifiers(dispPtr, patPtr->modMask);
+ Mask curModMask = ResolveModifiers(dispPtr, bindPtr->curModMask);
psEntry->expired = true; /* remove it from promotion list */
- if ((modMask & ~curModStateMask) == 0) {
+ if ((modMask & ~curModMask) == 0) {
unsigned count = patPtr->info ? curEvent->countDetailed : curEvent->countAny;
+ if (patIndex < PSModMaskArr_Size(psEntry->lastModMaskArr)) {
+ PSModMaskArr_Set(psEntry->lastModMaskArr, patIndex, &modMask);
+ }
+
/*
* This pattern is finally matching.
*/
if (psPtr->numPats == patIndex + 1) {
if (patPtr->count <= count) {
- int cmp = Compare(bestPtr, psPtr, patIndex);
-
/*
* This is also a final pattern.
* We always prefer the pattern with better match.
*
- * In case of equal modifier masks:
- * this match has same mask as best match:
- * choose this match if number is higher
- * otherwise:
- * choose this match if modifier states is not a subset
- * of those in best match.
*/
- if (cmp > 0
- || (cmp == 0
- && (modMask == bestModMask
- ? bestPtr->number < psPtr->number
- : !IsSubsetOf(modMask, bestModMask)))) {
+ int cmp = Compare(bestPtr, psPtr, patIndex);
+
+ 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 {
- psEntry->keepIt = true; /* don't remove it from promotion list */
DEBUG(psEntry->expired = false);
+ psEntry->keepIt = true; /* don't remove it from promotion list */
}
} else if (psSuccList) {
/*
@@ -2766,16 +2862,20 @@ MatchPatterns(
PSEntry *psNewEntry;
assert(!patPtr->name);
- psNewEntry = MakeListEntry(&bindPtr->lookupTables.entryPool, psPtr);
+ 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 = false);
psEntry->count += 1;
psEntry->keepIt = true; /* don't remove it from promotion list */
- DEBUG(psEntry->expired = false);
}
}
}
@@ -3355,7 +3455,7 @@ DeleteVirtualEventTable(
ClearLookupTable(&vetPtr->lookupTables, NULL);
DEBUG(countEntryItems -= PSList_Size(&vetPtr->lookupTables.entryPool));
- PSList_Clear(&vetPtr->lookupTables.entryPool);
+ PSList_Traverse(&vetPtr->lookupTables.entryPool, FreePatSeqEntry);
}
/*
@@ -3711,7 +3811,7 @@ HandleEventGenerate(
Tk_Window tkwin;
Tk_Window tkwin2;
TkWindow *mainPtr;
- unsigned long eventMask;
+ Mask eventMask;
Tcl_Obj *userDataObj;
bool synch;
bool warp;
@@ -3798,7 +3898,7 @@ HandleEventGenerate(
return TCL_OK;
}
if (flags & KEY_BUTTON_MOTION_VIRTUAL) {
- event.general.xkey.state = pat.modStateMask;
+ event.general.xkey.state = pat.modMask;
if ((flags & KEY) && event.general.xany.type != MouseWheelEvent) {
TkpSetKeycodeAndState(tkwin, pat.info, &event.general);
} else if (flags & BUTTON) {
@@ -4490,7 +4590,7 @@ FindSequence(
* True means create. */
bool allowVirtual, /* False means that virtual events are not allowed in the sequence.
* True otherwise. */
- unsigned long *maskPtr) /* *maskPtr is filled in with the event types on which this
+ Mask *maskPtr) /* *maskPtr is filled in with the event types on which this
* pattern sequence depends. */
{
unsigned patsBufSize = 1;
@@ -4504,13 +4604,14 @@ FindSequence(
bool isNew;
unsigned count;
unsigned maxCount = 0;
- unsigned long eventMask = 0;
+ Mask eventMask = 0;
+ Mask modMask = 0;
PatternTableKey key;
assert(lookupTables);
assert(eventString);
- psPtr = ckalloc(sizeof(PatSeq) + (patsBufSize - 1)*sizeof(TkPattern));
+ psPtr = ckalloc(PATSEQ_MEMSIZE(patsBufSize));
/*
*------------------------------------------------------------------
@@ -4522,7 +4623,7 @@ FindSequence(
if (numPats >= patsBufSize) {
unsigned pos = patPtr - psPtr->pats;
patsBufSize += patsBufSize;
- psPtr = ckrealloc(psPtr, sizeof(PatSeq) + (patsBufSize - 1)*sizeof(TkPattern));
+ psPtr = ckrealloc(psPtr, PATSEQ_MEMSIZE(patsBufSize));
patPtr = psPtr->pats + pos;
}
@@ -4548,6 +4649,7 @@ FindSequence(
}
totalCount += count;
+ modMask |= patPtr->modMask;
}
/*
@@ -4570,7 +4672,7 @@ FindSequence(
return NULL;
}
if (patsBufSize > numPats) {
- psPtr = ckrealloc(psPtr, sizeof(PatSeq) + (numPats - 1)*sizeof(TkPattern));
+ psPtr = ckrealloc(psPtr, PATSEQ_MEMSIZE(numPats));
}
patPtr = psPtr->pats;
@@ -4615,6 +4717,7 @@ FindSequence(
psPtr->count = totalCount;
psPtr->number = lookupTables->number++;
psPtr->added = false;
+ psPtr->modMaskUsed = (modMask != 0);
psPtr->script = NULL;
psPtr->nextSeqPtr = Tcl_GetHashValue(hPtr);
psPtr->hPtr = hPtr;
@@ -4676,10 +4779,10 @@ ParseEventDescription(
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. */
+ Mask *eventMaskPtr) /* Filled with event mask of matched event. */
{
const char *p;
- unsigned long eventMask = 0;
+ Mask eventMask = 0;
unsigned count = 1;
assert(eventStringPtr);
@@ -4791,7 +4894,7 @@ ParseEventDescription(
break;
}
modPtr = Tcl_GetHashValue(hPtr);
- patPtr->modStateMask |= modPtr->mask;
+ patPtr->modMask |= modPtr->mask;
if (modPtr->flags & MULT_CLICKS) {
unsigned i = modPtr->flags & MULT_CLICKS;
@@ -4852,7 +4955,7 @@ ParseEventDescription(
"NON_BUTTON");
}
#if SUPPORT_ADDITIONAL_MOTION_SYNTAX
- patPtr->modStateMask |= ButtonNumberToMask(button);
+ patPtr->modMask |= ButtonNumberToMask(button);
p = SkipFieldDelims(p);
while (*p && *p != '>') {
p = SkipFieldDelims(GetField(p, field, sizeof(field)));
@@ -4862,9 +4965,9 @@ ParseEventDescription(
patPtr, 0,
Tcl_ObjPrintf("bad button number \"%s\"", field), "BUTTON");
}
- patPtr->modStateMask |= ButtonNumberToMask(button);
+ patPtr->modMask |= ButtonNumberToMask(button);
}
- patPtr->info = ButtonNumberFromState(patPtr->modStateMask);
+ patPtr->info = ButtonNumberFromState(patPtr->modMask);
#endif
} else {
return FinalizeParseEventDescription(
@@ -4879,7 +4982,7 @@ ParseEventDescription(
patPtr, 0,
Tcl_NewStringObj("no event type or button # or keysym", -1), "UNMODIFIABLE");
} else if (patPtr->eventType == MotionNotify) {
- patPtr->info = ButtonNumberFromState(patPtr->modStateMask);
+ patPtr->info = ButtonNumberFromState(patPtr->modMask);
}
p = SkipFieldDelims(p);
@@ -4981,7 +5084,7 @@ GetPatternObj(
*/
if (patPtr->eventType == KeyPress
&& patPtr->count == 1
- && patPtr->modStateMask == 0
+ && patPtr->modMask == 0
&& patPtr->info < 128
&& isprint(UCHAR(patPtr->info))
&& patPtr->info != '<'
@@ -4992,7 +5095,7 @@ GetPatternObj(
assert(patPtr->name);
Tcl_AppendPrintfToObj(patternObj, "<<%s>>", patPtr->name);
} else {
- unsigned modStateMask;
+ unsigned modMask;
const ModInfo *modPtr;
/*
@@ -5009,16 +5112,16 @@ GetPatternObj(
case 4: Tcl_AppendToObj(patternObj, "Quadruple-", 10); break;
}
- modStateMask = patPtr->modStateMask;
+ modMask = patPtr->modMask;
#if PRINT_SHORT_MOTION_SYNTAX
if (patPtr->eventType == MotionNotify) {
- modStateMask &= ~ALL_BUTTONS_MASK;
+ modMask &= ~ALL_BUTTONS_MASK;
}
#endif
- for (modPtr = modArray; modStateMask; ++modPtr) {
- if (modPtr->mask & modStateMask) {
- modStateMask &= ~modPtr->mask;
+ for (modPtr = modArray; modMask; ++modPtr) {
+ if (modPtr->mask & modMask) {
+ modMask &= ~modPtr->mask;
Tcl_AppendPrintfToObj(patternObj, "%s-", modPtr->name);
}
}
@@ -5045,7 +5148,7 @@ GetPatternObj(
break;
#if PRINT_SHORT_MOTION_SYNTAX
case MotionNotify: {
- unsigned mask = patPtr->modStateMask;
+ unsigned mask = patPtr->modMask;
while (mask & ALL_BUTTONS_MASK) {
unsigned button = ButtonNumberFromState(mask);
Tcl_AppendPrintfToObj(patternObj, "-%u", button);
diff --git a/generic/tkCanvas.c b/generic/tkCanvas.c
index 414c6fb..bb8f138 100644
--- a/generic/tkCanvas.c
+++ b/generic/tkCanvas.c
@@ -977,7 +977,7 @@ CanvasWidgetCmd(
if (objc == 5) {
int append = 0;
- unsigned long mask;
+ Mask mask;
const char *argv4 = Tcl_GetString(objv[4]);
if (argv4[0] == 0) {
@@ -1027,7 +1027,7 @@ CanvasWidgetCmd(
result = TCL_ERROR;
goto done;
}
- if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
+ if (mask & (Mask) ~(ButtonMotionMask|Button1MotionMask
|Button2MotionMask|Button3MotionMask|Button4MotionMask
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
@@ -4747,7 +4747,7 @@ CanvasBindProc(
XEvent *eventPtr) /* Pointer to X event that just happened. */
{
TkCanvas *canvasPtr = clientData;
- int mask;
+ Mask mask;
Tcl_Preserve(canvasPtr);
diff --git a/generic/tkCmds.c b/generic/tkCmds.c
index a64d2e1..b998dfd 100644
--- a/generic/tkCmds.c
+++ b/generic/tkCmds.c
@@ -200,7 +200,7 @@ Tk_BindObjCmd(
if (objc == 4) {
int append = 0;
- unsigned long mask;
+ Mask mask;
const char *sequence = Tcl_GetString(objv[2]);
const char *script = Tcl_GetString(objv[3]);
diff --git a/generic/tkDecls.h b/generic/tkDecls.h
index 64c32cd..c455030 100644
--- a/generic/tkDecls.h
+++ b/generic/tkDecls.h
@@ -148,7 +148,7 @@ EXTERN Tk_TextLayout Tk_ComputeTextLayout(Tk_Font font, const char *str,
EXTERN Tk_Window Tk_CoordsToWindow(int rootX, int rootY,
Tk_Window tkwin);
/* 33 */
-EXTERN unsigned long Tk_CreateBinding(Tcl_Interp *interp,
+EXTERN Mask Tk_CreateBinding(Tcl_Interp *interp,
Tk_BindingTable bindingTable,
ClientData object, const char *eventStr,
const char *script, int append);
@@ -909,7 +909,7 @@ typedef struct TkStubs {
void (*tk_ConfigureWindow) (Tk_Window tkwin, unsigned int valueMask, XWindowChanges *valuePtr); /* 30 */
Tk_TextLayout (*tk_ComputeTextLayout) (Tk_Font font, const char *str, int numChars, int wrapLength, Tk_Justify justify, int flags, int *widthPtr, int *heightPtr); /* 31 */
Tk_Window (*tk_CoordsToWindow) (int rootX, int rootY, Tk_Window tkwin); /* 32 */
- unsigned long (*tk_CreateBinding) (Tcl_Interp *interp, Tk_BindingTable bindingTable, ClientData object, const char *eventStr, const char *script, int append); /* 33 */
+ Mask (*tk_CreateBinding) (Tcl_Interp *interp, Tk_BindingTable bindingTable, ClientData object, const char *eventStr, const char *script, int append); /* 33 */
Tk_BindingTable (*tk_CreateBindingTable) (Tcl_Interp *interp); /* 34 */
Tk_ErrorHandler (*tk_CreateErrorHandler) (Display *display, int errNum, int request, int minorCode, Tk_ErrorProc *errorProc, ClientData clientData); /* 35 */
void (*tk_CreateEventHandler) (Tk_Window token, unsigned long mask, Tk_EventProc *proc, ClientData clientData); /* 36 */
diff --git a/generic/tkTextTag.c b/generic/tkTextTag.c
index 13216ca..40ea7ab 100644
--- a/generic/tkTextTag.c
+++ b/generic/tkTextTag.c
@@ -273,7 +273,7 @@ TkTextTagCmd(
if (objc == 6) {
int append = 0;
- unsigned long mask;
+ Mask mask;
const char *fifth = Tcl_GetString(objv[5]);
if (fifth[0] == 0) {
@@ -292,7 +292,7 @@ TkTextTagCmd(
if (mask == 0) {
return TCL_ERROR;
}
- if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
+ if (mask & (Mask) ~(ButtonMotionMask|Button1MotionMask
|Button2MotionMask|Button3MotionMask|Button4MotionMask
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
@@ -1460,7 +1460,7 @@ TkTextBindProc(
if (eventPtr->type == ButtonPress) {
textPtr->flags |= BUTTON_DOWN;
} else if (eventPtr->type == ButtonRelease) {
- int mask;
+ Mask mask;
switch (eventPtr->xbutton.button) {
case Button1:
@@ -1482,7 +1482,7 @@ TkTextBindProc(
mask = 0;
break;
}
- if ((eventPtr->xbutton.state & AnyButtonMask) == (unsigned) mask) {
+ if ((eventPtr->xbutton.state & AnyButtonMask) == mask) {
textPtr->flags &= ~BUTTON_DOWN;
repick = 1;
}
diff --git a/tests/bind.test b/tests/bind.test
index 2d46f9b..b6087b1 100644
--- a/tests/bind.test
+++ b/tests/bind.test
@@ -6527,11 +6527,42 @@ test bind-33.11 {should prefer most specialized} -setup {
event generate .t.f <2>
set x
} -cleanup {
+ destroy .t.f
# This test case shows that old implementation has an issue, because
# in my opinion it is expected that first one is matching, this binding
# is more specialized. But new implementation will be conform to old,
# and so "last" is the expected result.
+} -result {last}
+test bind-33.12 {prefer last in case of homogeneous 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 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 fails, and returns "first", but this is wrong,
+ # because both bindings are homogeneous equal, so the latter must
+ # be preferred.
} -result {last}