TIP 532: Re-implementation of event loop processing

Login
Author:         Gregor Cramer <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        09-Jan-2019
Post-History:  
Keywords:       Tk, bind, event, event loop
Tcl-Version:    8.6
Tk-Branch:      bug6e8afe516d
Votes-For:      DKF, BG, KBK, JN, FV, SL
Votes-Against:  none
Votes-Present:  DGP, AK

Abstract

Current implementation of event loop processing suffers from a limited event ring. This limitation is causing unexpected behavior. Moreover some issues in event loop handling are known.

Increasing the event size of the event ring can solve the problems caused by event ring overflow locally, but not in general. Therefore the author decided to re-implement the event processing, the revised implementation is working as if the event ring is unlimited. Moreover new implementation is more efficient (in time), and all known issues in event handling have been eliminated.

This project (re-implementing the event loop handling) has been started with error report Severe bugs in binding (tkBind.c).

Rationale

The following problems, caused by event ring overflow, have been solved:

  1. Sometimes double-clicks with mouse will not be detected, nothing happens although this event is bound (see test case bind-32.2). This problem occurs often in application Scidb, because this application is using tooltips heavily, causing a lot of intervening expose events.

  2. Immediately after startup of application Scidb (same with applications Scid, and Scid vs PC) it's not possible to open the menu via a shortcut like <Alt-m>. This event will be gobbled, because of many intervening events, causing an event ring overflow.

  3. After switching a tab pane in a notebook window the tab is losing the focus sometimes. This has been observed in applications Scid, and Scid vs PC.

Moreover the following issues have been solved:

  1. It's possible to bind an event like <Quadruple-1>, but it's nearly impossible to trigger this event (with mousepad). Even a triple-click is not so easy. This behavior is user-unfriendly, and it seems that it is caused by an erroneous implementation.

  2. If a statement like event generate . <1> is executed, and after some time (> 500 ms) this statement is executed again, then it's likely that a double-click event will be triggered, even if a single-click event is expected, because the triggering of double-click events has to fit time requirements (due to manual; see test case bind-32.4).

  3. event generate . <FocusIn> -sendevent 1 is not working, the argument of sendevent gets lost (test case bind-32.6).

  4. See following code:

    bind . <Double-1> { lappend x "Double" }  
    bind . <1><1> { lappend x "11" }  
    event generate . <1> -x 0 -y 0 -time 0  
    event generate . <1> -x 0 -y 0 -time 0
    set x
    

    This gives the result 11, but Double is expected, because the time (and space) constraints for a double click are fulfilled. With other words, the legacy implementation is not preferring the most specialized event. But it should, because the manual says (man bind):

    If more than one binding matches a particular event and they have the same tag, then the most specific binding is chosen and its script is evaluated.

    And the sequence <Double-1> is more specific than <1><1> because of time and space requirements (in <Double-1>). Note that constant PREFER_MOST_SPECIALIZED_EVENT=1 has to be set when compiling to enable this new feature.

  5. Legacy implementation cannot handle homogeneous equal sequences properly, see this script:

    bind . <1><Control-1> { lappend x "first" }
    bind . <Control-1><1> { lappend x "last" }
    event generate . <Control-1>
    event generate . <Control-1>
    set x
    

    Manual (man bind) says:

    If these tests fail to determine a winner, then the most recently registered sequence is the winner.

    In this script there is no winner, so the later defined one has to be chosen, and revised implementation is doing this.

Legacy code also suffers from causing memory holes, revised implementation is tested to be memory friendly.

The revised implementation supports an additional syntax for binding motion events (if constant SUPPORT_ADDITIONAL_MOTION_SYNTAX=1 is set when compiling). E.g., the following bindings

bind . <B2-Motion> { ... }  
bind . <B1-B2-Motion> { ... }

can be expressed in a different way:

bind . <Motion-2> { ... }  
bind . <Motion-1-2> { ... }

The additional syntax is easier to remember, because button press/release events will also be expressed in the latter form, for example bind . <ButtonPress-1>. The former syntax (<B2-Motion>) form will still be supported.

Specification

The whole handling in file general/tkBind.c has been re-implemented. This implementation is passing all test cases in tests/bind.test. Note that legacy implementation is failing in some (of the new) test cases.

In Issue (1), the legacy implementation is computing the time difference of nth click with first click, and tests whether it is less than 500 ms. But this seems to be an implementation bug. Revised implementation computes the difference of nth and (n+1)th clicks. This behavior also conforms better to the behavior of other toolkits. With new implementation the use of quadruple clicks (and triple clicks) is unproblematic. See also test case bind-32.6.

Issue (2) is only a minor bug, and there exists a work-around. But the author decided to eliminate this design bug, with revised implementation option -time is recognizing new special value current, and is using the current event time in this case. This extension is fully backward compatible. See also test case bind-32.4.

For the fix of issue (3) the author decided that non-zero values (given with option -send_event) will be converted to 1. This is conform to the manual, see man bind (search for Sendevent), see also lines 3287ff in legacy file generic/tkBind.c.

The fix of issue (4) is not fully backwards compatible. But in the author's opinion this is not a real problem, nobody is using sequences like <1><1>, it is not expected that applications have to be adjusted.

Fix of issue (5) is correcting a major bug, see test case bind-33.13.

Implementation

Please refer to the bug6e8afe516d branch of the core Tcl repository.

The event ring has been removed. Revised implementation is working with promoted event bindings, and remembers the latest event per event type. This technique works as if we have an infinite event ring, so no overflow is possible.

Based on tests the performance in time is better than with legacy implementation. This result is expected, because a triple-nested loop, executed for each incoming event, has been changed to a quasi-double-nested loop (only in very seldom cases it is still triple-nested). Furthermore the traversed lists are shorter than with legacy implementation, because the event ring, always containing 30 items (or even 45 on Mac), has been eliminated. Only unbinding a tag is a bit slower than before. Memory consumption did not change significantly.

Branch bug6e8afe516d has PREFER_MOST_SPECIALIZED_EVENT=0 and SUPPORT_ADDITIONAL_MOTION_SYNTAX=0. It targets 8.6 and is intended to be merged into core-8-6-branch.

Branch bug6e8afe516d-87 has PREFER_MOST_SPECIALIZED_EVENT=1 and SUPPORT_ADDITIONAL_MOTION_SYNTAX=1. It targets 8.7 and is intended to be merged into trunk.

Backwards Compatibility

Fix of issue (4) is not fully backwards compatible (more details in section Rationale).

Moreover fix of issue (5) is not backwards compatible, but here erroneous behavior has been corrected.

Beside these two exceptions the revised implementation is fully backwards compatible, even if the additional syntax style for motion bindings is enabled.

If constant PREFER_MOST_SPECIALIZED_EVENT=1 is defined (prefer most specific binding in term of most high-ordered patterns), then the guidance in manual of bind, how to choose the most specific binding, has to be changed:

(a) An event pattern that specifies a specific button or key is more specific than one that does not.

(b) 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.

(c) If the modifiers specified in one pattern are a subset of the modifiers in another pattern, then the pattern with more modifiers is more specific.

(d) A virtual event whose physical pattern matches the sequence is less specific than the same physical pattern that is not associated with a virtual event.

(e) Given a sequence that matches two or more virtual events, one of the virtual events will be chosen, but the order is undefined.

Rule (b) has been exchanged, because the old rule (a longer sequence is more specific than a shorter sequence) is now obsolete.

Copyright

This document has been placed in the public domain.