GNOME Bugzilla – Bug 99540
missing refresh when moving a window on top of another
Last modified: 2011-02-04 16:16:03 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
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
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...
*** Bug 104678 has been marked as a duplicate of this bug. ***
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
Created attachment 15257 [details] [review] patch that fixes this bug
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?
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.
The problem occurs when you move a window of the *same* application across another of its windows.
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?
*** Bug 107312 has been marked as a duplicate of this bug. ***
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;
Created attachment 18832 [details] [review] Fixes Window move and size repaint bug
Created attachment 19245 [details] [review] This is an update to my previous patch for the gtk-2-2 branch.
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.
Created attachment 20665 [details] [review] Simple patch, doesn't work well enough
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.
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...
Created attachment 20668 [details] [review] Herman's patch modified a bit, use also timer, bounded loop
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.
*** Bug 135453 has been marked as a duplicate of this bug. ***