diff options
Diffstat (limited to 'generic/tkBind.c')
-rw-r--r-- | generic/tkBind.c | 290 |
1 files changed, 160 insertions, 130 deletions
diff --git a/generic/tkBind.c b/generic/tkBind.c index e8827b3..3416f52 100644 --- a/generic/tkBind.c +++ b/generic/tkBind.c @@ -59,7 +59,9 @@ /* * 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. + * equivalent sequences. However it is logical to give <Double-1> higher precedence + * since it is more specific. Indeed <Double-1> includes time and space requirements, + * which is not the case for <1><1>. * This is achieved by setting PREFER_MOST_SPECIALIZED_EVENT to 1. */ @@ -135,17 +137,10 @@ typedef struct { * 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); +TK_ARRAY_DEFINE(PSModMaskArr, unsigned); typedef struct PSEntry { TK_DLIST_LINKS(PSEntry); /* Makes this struct a double linked list; must be first entry. */ @@ -201,7 +196,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. */ - ModMask curModMask; /* Containing the current modifier mask. */ + unsigned 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 *). */ @@ -263,7 +258,7 @@ typedef struct { typedef struct { 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 + unsigned 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 @@ -414,7 +409,7 @@ static Tcl_HashTable nameTable; /* keyArray hashed by keysym name. */ typedef struct { const char *name; /* Name of modifier. */ - ModMask mask; /* Button/modifier mask value, such as Button1Mask. */ + unsigned mask; /* Button/modifier mask value, such as Button1Mask. */ unsigned flags; /* Various flags; see below for definitions. */ } ModInfo; @@ -460,10 +455,20 @@ static const ModInfo modArray[] = { {"Button9", Button9Mask, 0}, {"Mod1", Mod1Mask, 0}, {"M1", Mod1Mask, 0}, +#ifdef MAC_OSX_TK {"Command", Mod1Mask, 0}, +#elif defined (_WIN32) + {"Command", ControlMask, 0}, +#else + {"Command", META_MASK, 0}, +#endif {"Mod2", Mod2Mask, 0}, {"M2", Mod2Mask, 0}, +#ifdef MAC_OSX_TK {"Option", Mod2Mask, 0}, +#else + {"Option", ALT_MASK, 0}, +#endif {"Mod3", Mod3Mask, 0}, {"M3", Mod3Mask, 0}, {"Mod4", Mod4Mask, 0}, @@ -719,7 +724,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, int create, - int allowVirtual, EventMask *maskPtr); + int allowVirtual, unsigned *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); @@ -735,8 +740,7 @@ static PatSeq * MatchPatterns(TkDisplay *dispPtr, Tk_BindingTable bindPtr, PSLi static int NameToWindow(Tcl_Interp *interp, Tk_Window main, Tcl_Obj *objPtr, Tk_Window *tkwinPtr); static unsigned ParseEventDescription(Tcl_Interp *interp, const char **eventStringPtr, - TkPattern *patPtr, EventMask *eventMaskPtr); -static void DoWarp(ClientData clientData); + TkPattern *patPtr, unsigned *eventMaskPtr); static PSList * GetLookupForEvent(LookupTables* lookupPtr, const Event *eventPtr, Tcl_Obj *object, int onlyConsiderDetailedEvents); static void ClearLookupTable(LookupTables *lookupTables, ClientData object); @@ -767,8 +771,8 @@ static int TestNearbyCoords(int lhs, int rhs) { return Abs(lhs - rhs) <= NEARBY_ static int IsSubsetOf( - ModMask lhsMask, /* this is a subset */ - ModMask rhsMask) /* of this bit field? */ + unsigned lhsMask, /* this is a subset */ + unsigned rhsMask) /* of this bit field? */ { return (lhsMask & rhsMask) == lhsMask; } @@ -798,8 +802,10 @@ static unsigned GetButtonNumber( const char *field) { + unsigned button; assert(field); - return (field[0] >= '1' && field[0] <= '9' && field[1] == '\0') ? field[0] - '0' : 0; + button = (field[0] >= '1' && field[0] <= '9' && field[1] == '\0') ? field[0] - '0' : 0; + return (button > 3) ? (button + 4) : button; } static Time @@ -854,6 +860,20 @@ CountSpecialized( return sndCount - fstCount; } +int +IsKeyEventType( + unsigned eventType) +{ + return eventType == KeyPress || eventType == KeyRelease; +} + +int +IsButtonEventType( + unsigned eventType) +{ + return eventType == ButtonPress || eventType == ButtonRelease; +} + static int MatchEventNearby( const XEvent *lhs, /* previous button event */ @@ -861,7 +881,7 @@ MatchEventNearby( { assert(lhs); assert(rhs); - assert(lhs->type == ButtonPress || lhs->type == ButtonRelease); + assert(IsButtonEventType(lhs->type)); assert(lhs->type == rhs->type); /* assert: lhs->xbutton.time <= rhs->xbutton.time */ @@ -873,16 +893,16 @@ MatchEventNearby( static int MatchEventRepeat( - const XEvent *lhs, /* previous key event */ - const XEvent *rhs) /* current key event */ + const XKeyEvent *lhs, /* previous key event */ + const XKeyEvent *rhs) /* current key event */ { assert(lhs); assert(rhs); - assert(lhs->type == KeyPress || lhs->type == KeyRelease); + assert(IsKeyEventType(lhs->type)); assert(lhs->type == rhs->type); - /* assert: lhs->xkey.time <= rhs->xkey.time */ - return TestNearbyTime(rhs->xkey.time, lhs->xkey.time); + /* assert: lhs->time <= rhs->time */ + return lhs->keycode == rhs->keycode && TestNearbyTime(lhs->time, rhs->time); } static void @@ -941,11 +961,10 @@ ClearList( static PSEntry * FreePatSeqEntry( - PSList *pool, + TCL_UNUSED(PSList *), PSEntry *entry) { PSEntry *next = PSList_Next(entry); - (void)pool; PSModMaskArr_Free(&entry->lastModMaskArr); ckfree(entry); @@ -961,13 +980,13 @@ ResolveModifiers( if (dispPtr->metaModMask) { if (modMask & META_MASK) { - modMask &= ~(ModMask)META_MASK; + modMask &= ~META_MASK; modMask |= dispPtr->metaModMask; } } if (dispPtr->altModMask) { if (modMask & ALT_MASK) { - modMask &= ~(ModMask)ALT_MASK; + modMask &= ~ALT_MASK; modMask |= dispPtr->altModMask; } } @@ -977,7 +996,7 @@ ResolveModifiers( static int ButtonNumberFromState( - ModMask state) + unsigned state) { if (!(state & ALL_BUTTONS)) { return 0; } if (state & Button1Mask) { return 1; } @@ -1632,7 +1651,7 @@ Tk_CreateBinding( * existing binding will always be replaced. */ { PatSeq *psPtr; - EventMask eventMask; + unsigned eventMask; char *oldStr; char *newStr; @@ -2157,7 +2176,7 @@ Tk_BindEvent( TkDisplay *dispPtr; TkDisplay *oldDispPtr; Event *curEvent; - TkWindow *winPtr = (TkWindow *) tkwin; + TkWindow *winPtr = (TkWindow *)tkwin; BindInfo *bindInfoPtr; Tcl_InterpState interpState; LookupTables *physTables; @@ -2213,7 +2232,7 @@ Tk_BindEvent( * Ignore the event completely if it is an Enter, Leave, FocusIn, or * FocusOut event with detail NotifyInferior. The reason for ignoring * these events is that we don't want transitions between a window and its - * children to visible to bindings on the parent: this would cause + * children to be 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. * @@ -2297,7 +2316,7 @@ Tk_BindEvent( switch (eventPtr->type) { case KeyPress: case KeyRelease: - if (MatchEventRepeat(&curEvent->xev, eventPtr)) { + if (MatchEventRepeat(&curEvent->xev.xkey, &eventPtr->xkey)) { if (curEvent->xev.xkey.keycode == eventPtr->xkey.keycode) { ++curEvent->countDetailed; } else { @@ -2447,7 +2466,6 @@ Tk_BindEvent( LookupTables *virtTables = &bindInfoPtr->virtualEventTable.lookupTables; PatSeq *matchPtr = matchPtrArr[k]; PatSeq *mPtr; - PSList *psl[2]; /* * Note that virtual events cannot promote. @@ -2521,13 +2539,13 @@ Tk_BindEvent( switch (patPtr->eventType) { case ButtonPress: case ButtonRelease: - if (curEvent->xev.type == KeyPress || curEvent->xev.type == KeyRelease) { + if (IsKeyEventType(curEvent->xev.type)) { RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); } break; case KeyPress: case KeyRelease: - if (curEvent->xev.type == ButtonPress || curEvent->xev.type == ButtonRelease) { + if (IsButtonEventType(curEvent->xev.type)) { RemoveListEntry(&bindPtr->lookupTables.entryPool, psEntry); } break; @@ -2725,8 +2743,8 @@ static int CompareModMasks( const PSModMaskArr *fstModMaskArr, const PSModMaskArr *sndModMaskArr, - ModMask fstModMask, - ModMask sndModMask) + unsigned fstModMask, + unsigned sndModMask) { int fstCount = 0; int sndCount = 0; @@ -2750,11 +2768,11 @@ CompareModMasks( assert(PSModMaskArr_Size(fstModMaskArr) == PSModMaskArr_Size(sndModMaskArr)); for (i = PSModMaskArr_Size(fstModMaskArr) - 1; i >= 0; --i) { - ModMask fstModMask = *PSModMaskArr_Get(fstModMaskArr, i); - ModMask sndModMask = *PSModMaskArr_Get(sndModMaskArr, i); + unsigned fstiModMask = *PSModMaskArr_Get(fstModMaskArr, i); + unsigned sndiModMask = *PSModMaskArr_Get(sndModMaskArr, i); - if (IsSubsetOf(fstModMask, sndModMask)) { ++sndCount; } - if (IsSubsetOf(sndModMask, fstModMask)) { ++fstCount; } + if (IsSubsetOf(fstiModMask, sndiModMask)) { ++sndCount; } + if (IsSubsetOf(sndiModMask, fstiModMask)) { ++fstCount; } } } @@ -2784,8 +2802,9 @@ MatchPatterns( PSEntry *psEntry; PatSeq *bestPtr; PatSeq *bestPhysPtr; - ModMask bestModMask; + unsigned bestModMask; const PSModMaskArr *bestModMaskArr = NULL; + int i, isModKeyOnly = 0; assert(dispPtr); assert(bindPtr); @@ -2800,6 +2819,26 @@ MatchPatterns( bestPhysPtr = NULL; window = curEvent->xev.xany.window; + /* + * Modifier key events interlaced between patterns parts of a + * sequence shall not prevent a sequence from ultimately + * matching. Example: when trying to trigger <a><Control-c> + * from the keyboard, the sequence of events actually seen is + * <a> then <Control_L> (possibly repeating if the key is hold + * down), and finally <Control-c>. At the time <Control_L> is + * seen, we shall keep the <a><Control-c> pattern sequence in + * the promotion list, otherwise it is impossible to trigger + * it from the keyboard. See bug [16ef161925]. + */ + if (IsKeyEventType(curEvent->xev.type)) { + for (i = 0; i < dispPtr->numModKeyCodes; ++i) { + if (dispPtr->modKeyCodes[i] == curEvent->xev.xkey.keycode) { + isModKeyOnly = 1; + break; + } + } + } + for (psEntry = PSList_First(psList); psEntry; psEntry = PSList_Next(psEntry)) { if (patIndex == 0 || psEntry->window == window) { PatSeq* psPtr = psEntry->psPtr; @@ -2814,6 +2853,12 @@ MatchPatterns( : VirtPatIsBound(bindPtr, psPtr, object, physPtrPtr)) { TkPattern *patPtr = psPtr->pats + patIndex; + /* ignore modifier key events, and KeyRelease events if the current event + * is of a different type (e.g. a Button event) + */ + psEntry->keepIt = isModKeyOnly || \ + ((patPtr->eventType != (unsigned) curEvent->xev.type) && curEvent->xev.type == KeyRelease); + if (patPtr->eventType == (unsigned) curEvent->xev.type && (curEvent->xev.type != CreateNotify || curEvent->xev.xcreatewindow.parent == window) @@ -2824,12 +2869,13 @@ MatchPatterns( * 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); + unsigned modMask = ResolveModifiers(dispPtr, patPtr->modMask); + unsigned curModMask = ResolveModifiers(dispPtr, bindPtr->curModMask); psEntry->expired = 1; /* remove it from promotion list */ + psEntry->keepIt = 0; /* don't keep matching patterns */ - if ((modMask & ~curModMask) == 0) { + if (IsSubsetOf(modMask, curModMask)) { unsigned count = patPtr->info ? curEvent->countDetailed : curEvent->countAny; if (patIndex < PSModMaskArr_Size(psEntry->lastModMaskArr)) { @@ -3476,7 +3522,7 @@ DeleteVirtualEventTable( * already defined, the new definition augments those that already exist. * * Results: - * The return value is TCL_ERROR if an error occured while creating the + * The return value is TCL_ERROR if an error occurred 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. * @@ -3820,7 +3866,7 @@ HandleEventGenerate( Tk_Window tkwin; Tk_Window tkwin2; TkWindow *mainPtr; - EventMask eventMask; + unsigned eventMask; Tcl_Obj *userDataObj; int synch; int warp; @@ -4004,6 +4050,9 @@ HandleEventGenerate( return TCL_ERROR; } if (flags & BUTTON) { + if (number >= Button4) { + number += (Button8 - Button4); + } event.general.xbutton.button = number; } else { badOpt = 1; @@ -4358,17 +4407,6 @@ HandleEventGenerate( } /* - * Now we have constructed the event, inject it into the event handling - * code. - */ - - if (synch) { - Tk_HandleEvent(&event.general); - } else { - Tk_QueueWindowEvent(&event.general, pos); - } - - /* * We only allow warping if the window is mapped. */ @@ -4377,11 +4415,6 @@ HandleEventGenerate( Tk_Window warpWindow = Tk_IdToWindow(dispPtr->display, event.general.xmotion.window); - if (!(dispPtr->flags & TK_DISPLAY_IN_WARP)) { - Tcl_DoWhenIdle(DoWarp, dispPtr); - dispPtr->flags |= TK_DISPLAY_IN_WARP; - } - if (warpWindow != dispPtr->warpWindow) { if (warpWindow) { Tcl_Preserve(warpWindow); @@ -4394,6 +4427,31 @@ HandleEventGenerate( dispPtr->warpMainwin = mainWin; dispPtr->warpX = event.general.xmotion.x; dispPtr->warpY = event.general.xmotion.y; + + /* + * Warping with respect to a window will be done when Tk_handleEvent + * below will run the event handlers and in particular TkPointerEvent. + * This allows to make grabs and warping work together robustly, that + * is without depending on a precise sequence of events. + * Warping with respect to the whole screen (i.e. dispPtr->warpWindow + * is NULL) is run directly here. + */ + + if (!dispPtr->warpWindow) { + TkpWarpPointer(dispPtr); + XForceScreenSaver(dispPtr->display, ScreenSaverReset); + } + } + + /* + * Now we have constructed the event, inject it into the event handling + * code. + */ + + if (synch) { + Tk_HandleEvent(&event.general); + } else { + Tk_QueueWindowEvent(&event.general, pos); } } @@ -4466,46 +4524,47 @@ NameToWindow( /* *------------------------------------------------------------------------- * - * DoWarp -- + * TkDoWarpWrtWin -- * - * Perform Warping of X pointer. Executed as an idle handler only. + * Perform warping of mouse pointer with respect to a window. * * Results: * None * * Side effects: - * X Pointer will move to a new location. + * Mouse pointer moves to a new location. * *------------------------------------------------------------------------- */ -static void -DoWarp( - ClientData clientData) +void +TkDoWarpWrtWin( + TkDisplay *dispPtr) { - TkDisplay *dispPtr = (TkDisplay *)clientData; - - assert(clientData); + assert(dispPtr); /* - * 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 - * one needs to guard against window destruction in the meantime. - * Finally, the case warpWindow == NULL is special in that it means - * the whole screen. + * A NULL warpWindow means warping with respect to the whole screen. + * We want to warp here only if we're warping with respect to a window. */ - if (!dispPtr->warpWindow || - (Tk_IsMapped(dispPtr->warpWindow) && Tk_WindowId(dispPtr->warpWindow) != None)) { - TkpWarpPointer(dispPtr); - XForceScreenSaver(dispPtr->display, ScreenSaverReset); - } - if (dispPtr->warpWindow) { - Tcl_Release(dispPtr->warpWindow); - dispPtr->warpWindow = NULL; + + /* + * Warping with respect to a window can only be done if the window is + * mapped. This was checked in HandleEvent. The window needs to be + * still mapped at the time the present code is executed. Also + * one needs to guard against window destruction in the meantime, + * which could have happened as a side effect of an event handler. + */ + + if (Tk_IsMapped(dispPtr->warpWindow) && Tk_WindowId(dispPtr->warpWindow) != None) { + TkpWarpPointer(dispPtr); + XForceScreenSaver(dispPtr->display, ScreenSaverReset); + } + Tcl_Release(dispPtr->warpWindow); + dispPtr->warpWindow = NULL; } - dispPtr->flags &= ~TK_DISPLAY_IN_WARP; } /* @@ -4592,7 +4651,7 @@ FindSequence( * 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 + unsigned *maskPtr) /* *maskPtr is filled in with the event types on which this * pattern sequence depends. */ { unsigned patsBufSize = 1; @@ -4606,8 +4665,8 @@ FindSequence( int isNew; unsigned count; unsigned maxCount = 0; - EventMask eventMask = 0; - ModMask modMask = 0; + unsigned eventMask = 0; + unsigned modMask = 0; PatternTableKey key; assert(lookupTables); @@ -4781,10 +4840,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. */ - EventMask *eventMaskPtr) /* Filled with event mask of matched event. */ + unsigned *eventMaskPtr) /* Filled with event mask of matched event. */ { const char *p; - EventMask eventMask = 0; + unsigned eventMask = 0; unsigned count = 1; assert(eventStringPtr); @@ -4957,7 +5016,7 @@ ParseEventDescription( "NON_BUTTON"); } #if SUPPORT_ADDITIONAL_MOTION_SYNTAX - patPtr->modMask |= TkGetButtonMask(button); + patPtr->modMask |= Tk_GetButtonMask(button); p = SkipFieldDelims(p); while (*p && *p != '>') { p = SkipFieldDelims(GetField(p, field, sizeof(field))); @@ -4967,7 +5026,7 @@ ParseEventDescription( patPtr, 0, Tcl_ObjPrintf("bad button number \"%s\"", field), "BUTTON"); } - patPtr->modMask |= TkGetButtonMask(button); + patPtr->modMask |= Tk_GetButtonMask(button); } patPtr->info = ButtonNumberFromState(patPtr->modMask); #endif @@ -5097,7 +5156,7 @@ GetPatternObj( assert(patPtr->name); Tcl_AppendPrintfToObj(patternObj, "<<%s>>", patPtr->name); } else { - ModMask modMask; + unsigned modMask; const ModInfo *modPtr; /* @@ -5145,16 +5204,16 @@ GetPatternObj( } case ButtonPress: case ButtonRelease: - assert(patPtr->info <= Button9); - Tcl_AppendPrintfToObj(patternObj, "-%d", (int) patPtr->info); + assert(patPtr->info <= 13); + Tcl_AppendPrintfToObj(patternObj, "-%u", (unsigned) ((patPtr->info > 7) ? (patPtr->info - 4) : patPtr->info)); break; #if PRINT_SHORT_MOTION_SYNTAX case MotionNotify: { - ModMask mask = patPtr->modMask; + unsigned mask = patPtr->modMask; while (mask & ALL_BUTTONS) { - int button = ButtonNumberFromState(mask); - Tcl_AppendPrintfToObj(patternObj, "-%d", button); - mask &= ~TkGetButtonMask(button); + unsigned button = ButtonNumberFromState(mask); + Tcl_AppendPrintfToObj(patternObj, "-%u", (button > 7) ? (button - 4) : button); + mask &= ~Tk_GetButtonMask(button); } break; } @@ -5276,35 +5335,6 @@ TkpGetBindingXEvent( /* *---------------------------------------------------------------------- * - * TkpCancelWarp -- - * - * This function cancels an outstanding pointer warp and - * is called during tear down of the display. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -TkpCancelWarp( - TkDisplay *dispPtr) -{ - assert(dispPtr); - - if (dispPtr->flags & TK_DISPLAY_IN_WARP) { - Tcl_CancelIdleCall(DoWarp, dispPtr); - dispPtr->flags &= ~TK_DISPLAY_IN_WARP; - } -} - -/* - *---------------------------------------------------------------------- - * * TkpDumpPS -- * * Dump given pattern sequence to stdout. |