Tk Source Code

View Ticket
Login
Ticket UUID: 796462af2424c8063099441088d5dde24e1f84d0
Title: Images don't display in "a simple interface for displaying images"
Type: Bug Version: core-8-6-branch
Submitter: kevin_walzer Created on: 2018-10-21 02:15:56
Subsystem: 76. Widget Tour Assigned To: marc_culler
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2018-11-20 22:47:35
Resolution: Fixed Closed By: marc_culler
    Closed on: 2018-11-20 22:47:35
Description:
In the widget demo, "A simple user interface for viewing images," the images don't display on macOS 10.14.. I double-click on the image file in the listbox and it's supposed to be displayed in the label next to it. Images in other demos display just fine, so this is a case of dynamically changing an image in a widget in response to a (non-virtual) double-click event. This is likely in response to the various changes we are making to get 10.14 working on Mojave; documenting here as a symptom to look at.
User Comments: marc_culler (claiming to be Marc Culler) added on 2018-11-20 22:47:35:
Closing this ticket since the problem was fixed and merged some time ago.

marc_culler added on 2018-11-12 14:32:06:
I did some more experiments to determine the behavior of the clickCount.
I was wrong about how it works.  It turns out that the clickCount gets reset
to 1 any time that the state of the modifier keys changes.  Here is a sample
where I am clicking the left mouse button repeatedly with my right hand and
pressing and releasing various modifier keys with my left hand.  (Next, I am
going to learn how to juggle!)

[ ] LeftMouseDown clickCount= 1
[ ] LeftMouseDown clickCount= 2
[ ] LeftMouseDown clickCount= 3
[ ] LeftMouseDown clickCount= 4
[ ] LeftMouseDown clickCount= 5
[ Alt ] LeftMouseDown clickCount= 1
[ Alt ] LeftMouseDown clickCount= 2
[ Alt ] LeftMouseDown clickCount= 3
[ Alt ] LeftMouseDown clickCount= 4
[ ] LeftMouseDown clickCount= 1
[ ] LeftMouseDown clickCount= 2
[ ] LeftMouseDown clickCount= 3
[ Shift ] LeftMouseDown clickCount= 1
[ Shift ] LeftMouseDown clickCount= 2
[ Shift ] LeftMouseDown clickCount= 3
[ ] LeftMouseDown clickCount= 1
[ ] LeftMouseDown clickCount= 2
[ ] LeftMouseDown clickCount= 3

gcramer added on 2018-11-12 10:20:34:
I decided to implement this feature in a different way: I will use the information [theEvent clickCount] only for the decision whether this is a double (triple, quadruple) click. And the thing with intervening changes of modifier flags, or intervening key press events, will be handled with the platform independent logic. I think it's also useful to regard [theEvent isARepeat] for key events, in a similar way.

Nevertheless it's seems to be useful to add a comment how the original Mac behavior with intervening events works, in this way the algorithmic flow becomes more transparent, and this makes testing easier.

marc_culler added on 2018-11-10 05:23:27:
The macOS build does not use X.org header files.  It uses X11 header files
copied from some old version of X11 which are embedded in the distribution.
Some of the structs in those headers have already been extended to add platform-
specific fields.  So I don't think there is any problem with adding a field to
the Xevent struct for the mac port.

I will do some testing to verify how the modifier keys affect mouse button
events.  But I am pretty sure that they are independent.  I think that each
mouse button event simply records the state of each modifier key at the time
of the button press.  There are also seperate keypress events which are
generated when a modifier key is pressed or released.

gcramer added on 2018-11-02 19:21:48:

> Maybe the simplest thing to do would be to add a clickCount element in the Xevent structure.

This would be fine, but seems not to be possible under X11, because the X.org header files are providing the XEvent structure definitions. And so far as I see the X.org files are also used under Mac.

> I don't know what the correct response to 5 rapid clicks would be.

The interpretation is <Single>+<Double>+<Triple>+<Quadruple>+<Quadruple>.

> I don't know what you mean by clicking without pressing the button.

I have to be more precise: I need information about the behavior of the click count when either the modifier states is changing, or the button state is changing, between rapid clicks. Furthermore I need the information what happens when a key press is intervening rapid clicks. And about repeated key pressing: what happens if a button press is intervening a repeated key press?


marc_culler (claiming to be Marc Culler) added on 2018-10-30 18:35:16:
Maybe the simplest thing to do would be to add a clickCount element in the
Xevent structure.  Presumably some platforms would not be able to use it, but
others might.  The pattern matcher could ignore it if it is <= 1 and use it
otherwise.

I don't know what the correct response to 5 rapid clicks would be.
Does Tk interpret that as 4 double-clicks or 3 triple-clicks or both or
neither?

marc_culler (claiming to be Marc Culler) added on 2018-10-30 18:23:30:
I don't understand why you say that there are only 24 bits available for
storing modifier flags.  I know that X11 only specifies 24 possible modifiers.
But the XEvent struct is storing those 24 bits in a 32-bit int.  Why can't the
other 8 bits be used?

marc_culler (claiming to be Marc Culler) added on 2018-10-30 17:41:04:
I don't know what you mean by clicking without pressing the button.  But I
think I can tell you how the clickCount works.  There is a double-click
time interval which the user can set with her preferences.  Let's call it Δ.
When the left mouse button is pressed the Apple window manager checks
whether an event of type NSLeftMouseDown occurred in almost the same place
no more than Δ seconds earlier.  If so, it posts an event of type
NSLeftMouseDown with its clickCount set to be 1 plus the clickCount
of the previous event of type NSLeftMouseDown.  If not, it posts an event
of type NSLeftMouseDown with a clickCount set to 1.

If I add a print statement in tkMacOSXMouseEvent.c and click the left
button repeatedly I get output like this:
LeftMouseDown: clickCount = 1
LeftMouseDown: clickCount = 2
LeftMouseDown: clickCount = 3
LeftMouseDown: clickCount = 4
LeftMouseDown: clickCount = 5
LeftMouseDown: clickCount = 6
LeftMouseDown: clickCount = 7
LeftMouseDown: clickCount = 8
LeftMouseDown: clickCount = 9
I don't think there is any limit to how large the clickCount can be other
than the endurance of your finger.

The clickCount is not available for all NSEvents, but it seems that at least
an event of type NSKeyDown, NSLeftMouseDown, NSRightMouseDown or
NSOtherMouseDown has a clickCount attribute.

fvogel added on 2018-10-29 21:13:17:
Re: "Francois' idea": that was not my idea, it was Marc's idea.
Assigning to him, temporarily at least, so that he can answer your questions.

Marc: don't hesitate to bounce back if you wish.

gcramer added on 2018-10-29 14:24:40:
Yes, I've fixed the event ring overflow problem, probably that will fix this problem.

About Francois' idea to use the clickCount information: this has some advantages, of course, but then I need more information about the handling.

1) When doing fast clicks, say 5 times, first 3 with mouse button 1 pressed, and the last 2 without mouse button pressed, what is [theEvent clickCount] responding for the 4th click: 4 or 1?

2) We have to allow setting of the count, e.g. "event generate <ButtonPress-1> -count 4" generates a button press event with clickCount 4. (Currently -count is only allowed for Expose events.)

3) Field state must be restricted to a 24 bit value, this means that "event generate <ButtonPress-1> -state xxx" should not allow values > 0x00ffffff. Otherwise we have a clash with clickCount information. (Because of the fact that the maximal click count is 4, it's also possible to use only the two highest bits, this means a restriction to 0x3fffffff.)

fvogel added on 2018-10-28 19:19:09:

Marc's analysis does in the same direction of Gregor's in [6e8afe516d].

Gregor's fix completely removes the event ring, which should result in the whole problem to vanish. We have not yet tested this fix on all platforms, and for the mac we would probably like to check if that implementation is taking full advantage of the extra event information available on a mac.


marc_culler (claiming to be Marc Culler) added on 2018-10-26 15:22:32:
Typo: should have been 5238 not 5298, in case anyone is checking my arithmetic.

marc_culler (claiming to be Marc Culler) added on 2018-10-26 15:16:49:

I analyzed this pretty carefully by adding debugging code and I can explain what is happening. I have a proposal for a future fix.

First, what is happening. There are three points:

1. Q: How does Tk decide if the user has double-clicked the mouse? A: It maintains an "event ring" containing the last 30 events. When a mouse button event arrives, the bind pattern matcher searches through the event ring looking for a prior mouse button event with almost the same x and y values and almost the same timestamp. If it finds one, the double-click bindings fire. Otherwise the single-click bindings fire.

2. Q: What happens when you double click in the "simple interface" demo's listbox? A: When the first mouse button event arrives, the listview processes it by marking that list entry as the active item and redrawing it with a blue background. This causes the listview to post an idle task for it to redraw itself. But the click on the window also causes the apple window manager to call the drawRect method of the window's content view. That causes expose events to be added to the queue, and then all of the idle events get processed, including handling virtual events generated by the listview. Then the second click event arrives.

3. Q: What goes wrong? A: The listview processing generates enough events to push the first click out of the event ring before the second click arrives. When I monitor the event queue with the debugging tools in my latest commit I see e.g. that the first button click has serial number 3188 and the second has serial number 3310. The same experiment with the "Colors" demo gives serial numbers 5298 and 5290. The differences are 122 and 42 respectively. I don't think those numbers correspond exactly to the number of events, but still it suggests that double-clicking in the "simple interface" generates about 3 times as many events as double clicking in the "Colors". To validate my theory, I tried changing the size of the event ring from 30 to 45. After that, the "simple interface" demo works fine.

Now, how to fix it. One way to "fix it" in the sense of making the "simple interface" demo work would be to simply increase the size of the event ring. I have done that in check-in [831734f0]

But I think that is not the best solution. In the code there are comments saying that the ring really can't be smaller than 30, because there are simple examples where combinations of keys and modifiers can easily generate 20 events in the ring. Evidently, the ring has been made as small as possible. The obvious reason for that is that the larger the ring is, the slower the pattern matching is, and we don't want to make Tk sluggish.

The truth is that searching for double-clicks this way is not at all robust. Flooding of the event ring is a well known problem and is specifically addressed in the release notes for 8.6.7. Moreover, at least on the Mac, there is a better way. A Mac mouse event has a field called clickCount which is set to 1 normally, but is set to 2 if the mouse click is the second click of a double-click and set to 3 if it is the third click of a triple-click. Moreover, the time threshold for double-clicking is controllable in the user preferences. Currently we ignore this clickCount data. But an XEvent has a 32 bit state field of which only 24 bits are used (for indicating things like which modifier keys were down at click time). I would propose to use one more bit to indicate a double click and maybe another one for a triple click. Then the pattern matcher could shortcut its search of the event ring by checking this new bit (or bits). If the double-click bit is set on a new event, the pattern matcher should fire the double-click bindings and skip its current check for nearby clicks. If the bit is not set it should just search the event ring for nearby clicks, as it does now. I think this would not be hard to implement and would not require any big changes in the code.


fvogel added on 2018-10-21 14:44:13:

Works with macOS 10.12 using core-8-6-branch at its current tip, i.e. at [e89731623c].

Therefore a Mojave-specific issue, and not a regression introduced for older macOSes by the recent fixes for Mojave.