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 99540 - missing refresh when moving a window on top of another
missing refresh when moving a window on top of another
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: Win32
2.0.x
Other Windows
: Normal normal
: ---
Assigned To: Tor Lillqvist
gtk-bugs
: 104678 107312 135453 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2002-11-25 18:37 UTC by Arnaud Charlet
Modified: 2011-02-04 16:16 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
patch that fixes this bug (758 bytes, patch)
2003-03-28 13:35 UTC, Arnaud Charlet
none Details | Review
Fixes Window move and size repaint bug (4.22 KB, patch)
2003-08-01 18:26 UTC, Herman Bloggs
none Details | Review
This is an update to my previous patch for the gtk-2-2 branch. (3.78 KB, patch)
2003-08-15 20:02 UTC, Herman Bloggs
none Details | Review
Simple patch, doesn't work well enough (893 bytes, patch)
2003-10-12 23:17 UTC, Tor Lillqvist
none Details | Review
Herman's patch modified a bit, use also timer, bounded loop (3.97 KB, patch)
2003-10-13 01:04 UTC, Tor Lillqvist
none Details | Review

Description Arnaud Charlet 2002-11-25 18:37:42 UTC
Under Windows, if you put a top level window on top of another top
level window, if you move the first window, no painting will occur until
the move is done, unlike what other Windows (or X11 for that matter)
applications do.

Things get worse when using e.g. a windows native open file dialog on
top of a Gtk+ top level window: nothing gets refreshed until the
open file dialog is closed.

Arno
Comment 1 Arnaud Charlet 2003-01-24 16:57:10 UTC
Any news, or possible code to explore to address this issue ?
If you have some possible paths to explore, I'd be willing to provide
some help.

Arno
Comment 2 Tor Lillqvist 2003-01-24 21:53:42 UTC
No news ;-) Very probably this could be quite easy to fix, number-of-
code-lines-changed-wise, but it would have to be debugged rather 
well. I don't have any ideas right now, and pushed the target 
milestone forward... 
Comment 3 Tor Lillqvist 2003-01-29 02:23:51 UTC
*** Bug 104678 has been marked as a duplicate of this bug. ***
Comment 4 Arnaud Charlet 2003-03-28 13:34:59 UTC
Here is the analysis and possible resolution of this bug:
The WM_PAINT events to redraw the underlying windows are properly sent
and are also properly handled by gdk. The problem is that the redrawing
of these windows is queued, and won't occur until too late.

The patch attached fixes (or works around) this issue, by forcing
a refresh immediately when receiving the event.

Arno
Comment 5 Arnaud Charlet 2003-03-28 13:35:55 UTC
Created attachment 15257 [details] [review]
patch that fixes this bug
Comment 6 Tor Lillqvist 2003-03-28 21:55:16 UTC
Hmm. Can it be this simple. Owen, are you reading this? Does calling 
gdk_window_process_updates() from the low-level Windows message 
handler seem right to you? 
Comment 7 Owen Taylor 2003-04-25 14:52:12 UTC
I'm not sure - I don't really understand the reported problem
here - the GTK+ main loop should be idle as the other window
is dragged on top, right? So, process_updates() should be
happening right after the WM_PAINT event handler returns?

The only thing I could imagine is that Windows simply
doesn't schedule tasks other than the task being dragged
around other than sending the WM_PAINT messages, but that
seems a little hard to believe for a modern operating
system.

In general, note that the GTK+ scheme of:

 gdk_window_invalidate_region()
 gdk_window_process_updates()
 expose event sent when idle

Is very closely modelled on how Win32 works; and the
receipt of a WM_PAINT message on Win32 corresponds 
very closely to gdk_window_process_updates() in GDK.

There is certainly some argument to be made that we should  
get rid of the duplication and actually *use* the Win32 system 
and not track invalid regions separately inside of GDK for the 
Win32 port. But that would be a pretty major change.
Comment 8 Tor Lillqvist 2003-04-25 23:16:15 UTC
The problem occurs when you move a window of the *same* application 
across another of its windows.
Comment 9 Owen Taylor 2003-04-26 14:35:33 UTC
That doesn't really answer my question - if the application
is getting WM_PAINT messages, why doesn't the idle get
run and the window repainted?
Comment 10 Owen Taylor 2003-04-28 19:56:23 UTC
*** Bug 107312 has been marked as a duplicate of this bug. ***
Comment 11 Owen Taylor 2003-04-28 20:26:42 UTC
No news to anybody who understands win32, but for outsiders
like me, the problem is presumably that during move/resize
the window procedure is being called for resize/repaint
events, but control never returns to the message loop /
main loop.

A fairly radical solution that comes to mind is to have
a single helper thread that a) creates windows b) runs 
the message loop which is *separate* from the main thread(s)
where the application runs. Then the move/resize blocks
only that thread, and the main thread looks a lot more like X.

Maybe not crazy? I think you need this to get threaded
GTK+ apps to work on Win32 anyways.

I don't think *just* calling gdk_process_updates() directly
in response to WM_PAINT message is going to work; it seems
to me that you are going to at a minimum need to also
call the event function when you get WM_SIZE message,
since if you handle the paints out of order from the
resizes, the results would be random.

As to *when* it is safe to call process_updates() or the
event function direction from the window procedure...
it should be safe in the case of:

 gdk_event_dispatch()
   _gdk_events_queue()
     DispatchMessage()
       real_window_procedure()

Or:

 gdk_event_dispatch()
   _gdk_events_queue()
     DispatchMessage()
       real_window_procedure()
          DefWindowProc
             real_window_procedure()

[ The second being my guess as to what is going on here ]

But not safe if you have:

 gdk_event_get()
  _gdk_events_queue()
     DispatchMessage()
       real_window_procedure()

Or:

 gdk_sync()
   DispatchMessage()
     real_window_procedure()

Or:

 gdk_random_function()
   Win32RandomFunction()
     real_window_procedure()

[ I don't know if this is possible ]

So, approx algorithm would be, in gdk_event_dispatch():

 gboolean old_safe_to_call_event_func = safe_to_call_event_func;
 safe_to_call_event_func = TRUE;

 [ Do stuff ]

 safe_to_call_event_func = old_safe_to_call_event_func;

And then when calling the event_func:

 gboolean old_safe_to_call_event_func = safe_to_call_event_func;
 safe_to_call_event_func = FALSE;

 (*_gdk_event_func) (&event, _gdk_event_data);

 safe_to_call_event_func = old_safe_to_call_event_func;
Comment 12 Herman Bloggs 2003-08-01 18:26:41 UTC
Created attachment 18832 [details] [review]
Fixes Window move and size repaint bug
Comment 13 Herman Bloggs 2003-08-15 20:02:19 UTC
Created attachment 19245 [details] [review]
This is an update to my previous patch for the gtk-2-2 branch.
Comment 14 Tor Lillqvist 2003-10-12 23:15:05 UTC
Here is another, much simpler patch, actually suggested already back 
in November 2001 by Hans Breuer on the gimpwin-dev list... This was 
suggested as a fix to the problem that any timeout activity going on 
(like testgtk's progress bar or previews) doesn't happen while a 
window is being moved and/or resized. But it also takes care of the 
problem described in this bug.

Unfortunately, this simple patch doesn't work well enough. It 
introduces an irritating nondeterminism; sometimes when you let go of 
a window's title bar after having moved the window, the window still 
is "glued" to the cursor, is still being moved around. You have to 
click once or twice more on the title bar to "release" it.
Comment 15 Tor Lillqvist 2003-10-12 23:17:26 UTC
Created attachment 20665 [details] [review]
Simple patch, doesn't work well enough
Comment 16 Tor Lillqvist 2003-10-13 00:07:01 UTC
Upon closer inspection, the simple patch doesn't completely solve the 
problem it is claimed to solve: If you keep the mouse absolutely 
still while moving a window, nothing happens, no timeouts fire, the 
progress bar doesn't grow. No Windows messages are sent or posted 
while keeping the mouse absolutely still during a move, which of 
course explains why nothing happens. Hmm, maybe a timer should be 
kept running while resizing, to get WM_TIMER messages, which would 
then be handled similarily as WM_MOVING?

Herman's patch again does indeed solve the problem of this bug 
report, updating a window that is getting uncovered while moving or 
resizing another window of the same application. But it has the same 
problem as the simple patch, keeping the mouse absolutely still 
prevents any timeout-based updates to fire.

Also, as Herman's patch uses an unlimited while g_main_context_pending
() loop, it suffers from the problem described on the gimpwin-dev 
list: For instance testgtk's preview color won't move, and if you Alt-
Tab to the main testgtk window, you get GLib-WARNING **: 
g_main_context_prepare() called recursively from within a source's 
check() or prepare() member.
Comment 17 Tor Lillqvist 2003-10-13 01:02:32 UTC
Using a timer does seem to help. The arbitrary_limit seems to work 
even if as low as one. A patch follows that seems to fix all problems 
mentioned. Please test...
Comment 18 Tor Lillqvist 2003-10-13 01:04:51 UTC
Created attachment 20668 [details] [review]
Herman's patch modified a bit, use also timer, bounded loop
Comment 19 Tor Lillqvist 2003-12-14 02:05:13 UTC
Patch applied to gtk-2-2 (already some time ago) and a moment ago to 
HEAD:

2003-10-25  Tor Lillqvist  <tml@iki.fi>

* gdk/win32/gdkevents-win32.c 
(handle_stuff_while_moving_or_resizing): New function, to
dispatch the main loop (once).

(resize_timer_proc): New function, set to be called by an inerval
timer during resizes/moves. Calls 
handle_stuff_while_moving_or_resizing().

(gdk_event_translate): 	On WM_ENTERSIZEMOVE, set a flag, and start an 
interval timer that
calls resize_timer_proc() at regular intervals. On
WM_EXITSIZEMOVE, kill the timer.

On WM_WINDOWPOSCHANGED, generate a configure event if necessary,
and dispatch the main loop (by calling
handle_stuff_while_moving_or_resizing()). Fixes #99540, idea by
Herman Bloggs.
Comment 20 Tor Lillqvist 2004-03-01 03:24:11 UTC
*** Bug 135453 has been marked as a duplicate of this bug. ***