After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 152035 - Gtk+ Menus lose focus on first click in Win32
Gtk+ Menus lose focus on first click in Win32
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: Win32
2.4.x
Other Windows
: Normal normal
: ---
Assigned To: gtk-win32 maintainers
gtk-bugs
Depends on:
Blocks: 152744
 
 
Reported: 2004-09-07 04:49 UTC by Todd A. Fisher
Modified: 2004-12-22 21:47 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Todd A. Fisher 2004-09-07 04:49:05 UTC
1. run testgtk
2. run the menus test
3. click on test\nline2 menu item.  

The first time I click on the menu item it closes immediately.  The second
time it remains open.  I've also noticed that in other cases the menu refuses
to stay open unless I hold the mouse button down and drag it over the open menu
window.
Comment 1 Tor Lillqvist 2004-09-08 22:09:01 UTC
What's interesting is that in testgtk's menus test, clicking on the "foo" menu 
item does work as intended. One difference between "foo" and the "test\nline2" 
and "table" menu items is that "foo" is much shorter. The other ones would 
extend below the screen, and become scrollable. This might have something to do 
with this.

Anyway, I don't think this can be classified as "high" priority and "major" 
severity. After all, it's just one extra click.
Comment 2 Tor Lillqvist 2004-09-08 22:10:26 UTC
Forgot to ask, have you checked that this happens only on Win32?
Comment 3 Soren Sandmann Pedersen 2004-09-09 00:51:20 UTC
It doesn't happen for me on X11.
Comment 4 Todd A. Fisher 2004-09-09 01:39:32 UTC
It is only one click in the testgtk program, but when used with GtkEmbedTest in
mozilla with the following patch applied
http://bugzilla.mozilla.org/show_bug.cgi?id=256560
It is quite a few more clicks then just one or even two.

That said, the gtkmozembed patch isn't necessarily the best way to get mozembed
working in win32; however it is possible there are other cases with similar
symptoms.

Still, I'm curious what changed to cause this problem with the menus?
Comment 5 Todd A. Fisher 2004-09-19 03:27:48 UTC
Has anyone else been able to confirm this bug in win32? I had been using the
menu  drop shadow patch from http://www.xfce.org/gtkmenu-shadow/ . I removed
that patch and my menus are working fine now.  So, I believe this bug can be
closed unless anyone else has been experiencing similar problems in win32.
Comment 6 Robert Ögren 2004-09-19 13:06:45 UTC
To me it looks like Tor confirmed this bug. And I can reproduce it as well using
testgtk with the steps you described, with a very recent GTK+ 2.4, and I don't
use that drop shadow patch.

There have been changes to the menu code a few months ago, see for example bug
#141169. Maybe that caused the problem, or uncovered a bug in the Win32 GDK backend.
Comment 7 Robert Ögren 2004-11-06 18:39:33 UTC
The problem seems to be how the event timestamps are generated on win32, by
_gdk_win32_get_next_tick() in gdkevents-win32.c. That function seems to make
sure that each time stamp is unique (strictly increasing), but the problem is
that when a large menu is realized a lot of window messages are received
(WM_CREATE, WM_SIZE, WM_PAINT etc, for each item), which makes the timestamp for
the button release event so large that it satisfies the  (event->time -
menu_shell->activate_time) > MENU_SHELL_TIMEOUT  condition in
gtk_menu_shell_button_release for the menu bar, and therefore the menu is
immediately deactivated. This also explains why the problem shows up for the
"test\nline2" and "table" menus but not for "foo" or "Help" - the first two
menus contain a lot of items while the last two contain much fewer and therefore
fewer messages are produced.

Removing the "++" from _gdk_win32_get_next_tick solves the menu problem
described in this bug and makes the tick numbers only nondecreasing but I don't
know if it causes other problems.

Looking at the CVS log for gdkevents-win32.c shows it was introduced in rev. 1.94:

        (_gdk_win32_get_next_tick): New function. Used to make sure
        timestamps of events are always increasing, both in events
        generated from the window procedure and in events gotten via
        PeekMessage(). Not sure whether this is actually useful, but it
        seemed as a good idea.

Another observation is that _gdk_win32_get_next_tick might not be completely
safe if someone manages to keep hir Windows system running for 50 days so that
the tick count wraps around. Maybe that is not a very common problem :)
Comment 8 Tor Lillqvist 2004-11-10 22:11:46 UTC
Fix committed to HEAD and gtk-2-4. Please open a separate bug report for the 
GetTickCount() wraparound issue if you feel strongly about it ;-) But X11 has 
the same issue, hasn't it?

2004-11-10  Tor Lillqvist  <tml@iki.fi>

* gdk/win32/gdkevents-win32.c (_gdk_win32_get_next_tick): 
Event timestamps don't have to be unique. As long as they are
nondecreasing we should be fine. Solves problems with for instance
long menus not staying up on first click. (#152035, Robert Ögren)
Comment 9 Matthias Clasen 2004-11-11 12:54:22 UTC
X11 doesn't have a wraparound problem. It has a well-defined "circular" time. It
always interprets one half of the 32-bit timestamp space as future and the other
half as past, the split being at the current server time.
Comment 10 Tor Lillqvist 2004-11-11 13:12:45 UTC
Well, isn't "circular time" just an euphemism for (unsigned) wraparound? 
Exactly the same can be said of Windows timestamps (tick counts)? Or am I 
missing something? Does GTK take the "circular time" into account?
Comment 11 Robert Ögren 2004-11-11 18:48:37 UTC
I'm sorry for being unclear, what I was thinking of was that
_gdk_win32_get_next_tick will not let the time stamps wrap around and instead
the returned values will get stuck at a high value. Some additional conditions
would be needed in the if statement that decides whether to accept the new
timestamp or use the current. Something like

  if (((suggested_tick <= cur_tick) && (cur_tick - suggested_tick < (1 << 31)))
      || (suggested_tick - cur_tick > (1 << 31)))
    return cur_tick;
  else
    return cur_tick = suggested_tick;

This is completely untested of course ;)

(I can open a new bug report for this, but there are much worse bugs that are
more important to fix, such as #137551.)
Comment 12 Tor Lillqvist 2004-11-11 21:11:20 UTC
Robert: Yes, and  the GetTickCount() should perhaps be changed into 
GetMessageTime(). Not that it should make any huge difference. GetMessageTime() 
might be faster, if the timestamp of the last message that GetMessageTime() 
fetches is cached in userspace, while GetTimeCount() presumably dives into the 
kernel? (Or is using Unixish thinking like that wrong on Windows?)
Comment 13 Robert Ögren 2004-11-11 23:01:22 UTC
Using GetMessageTime might yield more accurate timestamps on some events, but
then the whole _gdk_win32_get_next_tick business should probably be scrapped and
the users have to deal with timestamps in any order, if that is even a problem. 

I guess that reducing kernel-mode transitions is never wrong, but the problem is
that in general you don't know how many such transitions a Windows API call will
cause. The implementation of the API can of course vary between different
Windows versions. So it is probably much better to spend development time on
improving algorithms and such, and reducing the amount of API calls, preferably
after some profiling to see which things are worth bothering with :)

I conducted a quick study of GetMessageTime and GetTickCount, and on my Win XP
box your guess seems to be wrong at least wrt kernel mode transitions ;)
As you can see from the test below, GetTickCount just reads from some global
memory (presumably mapped shared and readonly in all processes) and does a few
calculations while GetMessageTime does a sysenter. I haven't actually timed the
functions on the other hand, that is left as an exercise for the reader. The
sysenter instruction is actually supposed to be pretty fast. See for example the
following article for some gory details:

http://www.codeguru.com/Cpp/W-P/system/devicedriverdevelopment/article.php/c8223/

or http://makeashorterlink.com/?T552523C9

Test:

$ cat msgtime.c
#include <windows.h>
#include <stdio.h>
int main(void)
{
  int a = GetTickCount();
  int b = GetMessageTime();
  printf("%d %d\n", a, b);
  return 0;
}

$ gcc -o msgtime msgtime.c

$ gdb ./msgtime
(gdb) br main
(gdb) r
Breakpoint 1, 0x0040129d in main ()
(gdb) disp /i $eip
nexti
[some init crap omitted]

0x004012b5 in main ()
1: x/i $eip  0x4012b5 <main+30>:        call   0x401860 <GetTickCount@0>
(gdb) stepi
0x00401860 in GetTickCount@0 ()
1: x/i $eip  0x401860 <GetTickCount@0>: jmp    *0x4040d0
(gdb) 
0x7c8092ac in _libuser32_a_iname ()
1: x/i $eip  0x7c8092ac <_libuser32_a>: mov    $0x7ffe0000,%edx
(gdb) 
0x7c8092b1 in _libuser32_a_iname ()
1: x/i $eip  0x7c8092b1 <_libuser32_a>: mov    (%edx),%eax
(gdb) 
0x7c8092b3 in _libuser32_a_iname ()
1: x/i $eip  0x7c8092b3 <_libuser32_a>: mull   0x4(%edx)
(gdb) 
0x7c8092b6 in _libuser32_a_iname ()
1: x/i $eip  0x7c8092b6 <_libuser32_a>: shrd   $0x18,%edx,%eax
(gdb) 
0x7c8092ba in _libuser32_a_iname ()
1: x/i $eip  0x7c8092ba <_libuser32_a>:        ret    
(gdb) 
0x004012ba in main ()
1: x/i $eip  0x4012ba <main+35>:        mov    %eax,0xfffffffc(%ebp)
(gdb) 

0x004012bd in main ()
1: x/i $eip  0x4012bd <main+38>:        call   0x4012f0 <GetMessageTime@0>
(gdb) 
0x004012f0 in GetMessageTime@0 ()
1: x/i $eip  0x4012f0 <GetMessageTime@0>:       jmp    *0x404120
(gdb) 
0x77d3c210 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c210 <_libuser32_a>:        push   $0xb
(gdb) 
0x77d3c212 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c212 <_libuser32_a>:        call   0x77d3c617
(gdb) 
0x77d3c617 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c617 <_libuser32_a>:        mov    $0x11b3,%eax
(gdb) 
0x77d3c61c in _libuser32_a_iname ()
1: x/i $eip  0x77d3c61c <_libuser32_a>:        mov    $0x7ffe0300,%edx
(gdb) 
0x77d3c621 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c621 <_libuser32_a>:        call   *(%edx)
(gdb) 
0x7c90eb8b in _libuser32_a_iname ()
1: x/i $eip  0x7c90eb8b <_libuser32_a>:        mov    %esp,%edx
(gdb) 
0x7c90eb8d in _libuser32_a_iname ()
1: x/i $eip  0x7c90eb8d <_libuser32_a>:        sysenter 
(gdb) 
0x77d3c623 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c623 <_libuser32_a>:        ret    $0x4
(gdb) 
0x77d3c217 in _libuser32_a_iname ()
1: x/i $eip  0x77d3c217 <_libuser32_a>:        ret    
(gdb) 
(etc)


I don't know if we can get any more off-topic than this, and now I need some
sleep ;)