Tk Source Code

View Ticket
Login
Ticket UUID: 8b49e9cfa66887f66baed045e44cad2bfb82c751
Title: Hang in event generate in Tk9.0 Aqua with a combobox target
Type: Bug Version: 9.0
Submitter: marc_culler Created on: 2024-09-16 23:39:02
Subsystem: 69. Events Assigned To: marc_culler
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2024-09-20 16:16:45
Resolution: Works For Me Closed By: marc_culler
    Closed on: 2024-09-20 16:16:45
Description:
Compare the results of running this script on macOS using tclsh8.6 and
tclsh9.0:

package require Tk
pack [ttk::combobox .cb] -padx 20 -pady 20
after 2000
puts "open"
event generate .cb <Button-1>
event generate .cb <ButtonRelease-1>
puts "done"
after 2000
puts "close"
event generate .cb <Button-1>
event generate .cb <ButtonRelease-1>
puts "done"

With tclsh9.0 nothing is printed after "open" - the script hangs in the
call to event generate.  However, a mouse or key event will cause the
script to continue.

There are other differences too.  With 8.6 the window does not get focus
when the combobox opens, but it does get focus when it closes.  With
9.0 the window gets focus when the combobox opens (but then it hangs).
User Comments: marc_culler (claiming to be Marc Culler) added on 2024-09-20 16:16:45:
I think this can be closed.  I will think about implementing menu unpost for
macOS.

marc_culler (claiming to be Marc Culler) added on 2024-09-17 23:49:39:
I can now explain this.  (And I am responsible, and it is more or less
by design.)

The difference between 8.6 and 9.0 is that in Tk 9.0 a macOS combobox uses
a native macOS menu instead of a Tk window.  This is better in many ways,
but mainly it makes the Tk combobox look like a macOS combobox.  This
means that clicking on the combobox button causes a call to "menu post"
which in turn causes a native popup menu to appear.  When a macOS popup
menu appears the Apple window manager enters a separate event loop.  This
means that no Apple events are received by Tk until the menu is dismissed,
which happens if there is any key event or a mouse button event which may
or may not select a menu item.

When a menu pops up Tk starts a separate thread running a Tk event loop,
so timer events and generated events do get processed while the menu is
on the screen.  In particular, if a <ButtonRelease> event is generated
by an after command that event does get processed by Tk.  However, there
is nothing that a handler for that event can do to dismiss the menu.  This
is not an Apple issue.  It is a design issue with Tk.  It was decided
that macOS and Windows would not support the menu unpost command.  The
manual says: "This subcommand does not work on Windows and the Macintosh,
as those platforms have their own way of unposting menus."

In fact, macOS does provide a way of dismissing a menu by calling the
[NSMenu cancelTracking] method.  So it would be possible to implement
menu unpost on macOS. If that command were implemented the combobox Post
proc could bind it to, say, <ButtonRelease-1>. Then the combobox menu
could be dismissed by actually clicking the combobox button (as it is now)
or by generating a <ButtonRelease> event.

marc_culler (claiming to be Marc Culler) added on 2024-09-17 21:20:34:
Thanks, François.  The key issue, as far as I am concerned, is that the
call to event generate does not return until some unrelated event occurs.
That just should not happen.  The Xevent should be added to to the queue
and the command should complete immediately.  It should not be blocking
for anything.

I can create the hang in wish, so I will try sampling the process while
it is waiting and see if I can figure out which function is blocked.

fvogel added on 2024-09-17 20:02:01:
I'm not reproducing this on Windows when I paste the provided script in a tclsh90.

A few ideas:

- Don't we need an update after 'pack', so that the widget gets drawn on screen before simulating clicks in it?

- I believe the events are not distributed to the widget unless it has focus. What happens if 'focus -force .cb' is added just before the first 'after 2000'?

- It it hangs then it loops somewhere in the source code. Can you break execution during the hang and provide the call stack?