Re: client side windows borkage on Gdk/Quartz

On Sun, Apr 25, 2010 at 5:53 AM, Paul Davis <paul linuxaudiosystems com> wrote:
> I have to do bit more investigation (e.g. ripping out code and adding the
> old approach back), but I'd appreciate it if either Richard or whoever may
> have worked with him on this issue could comment. Does my description make
> sense, and is it sensible that forgetting that Quartz was already recursing
> over children could have led to this?

DISCLAIMER: I was not involved in the initial port of the Quartz
backend to the client side window code.

Below my view on the issue, based on my understanding of things.

> So far, so good. What's the problem? Well, the Cocoa code that we use is a
> call to "displayIfNeeded" on the top level NSView in the NSWindow. This does
> a recursive descent over all the NSViews it contains, checking whether or
> not they need to be updated, and if so, calling their "drawRect" methods.

As far as I understand, there's only that top level NSView in GDK
applications now in the case of client side windows, that does not
contain any other NSViews.  So for each NSWindow there will only be a
single NSView for which the drawRect: method will be called.

> What does "drawRect" do? Well, a bit of housekeeping, and then it calls
> _gdk_window_process_updates_recurse(). Still no obvious problem. Except wait
> a second ... this takes the GdkWindow associated with the NSView, and does
> ... you guessed it ... recursive descent over all GdkWindows it has a
> children, generating exposes for each one. So what? well, the "GdkWindow"
> associated with the NSView is the GdkWindow corresponding to the top level
> window! Every single "drawRect" of an NSView causes a recursive descent
> across the entire GdkWindow heirarchy of the top level window!! Arrgh!
> Yikes!

That is because the NSView will be the content view, so it corresponds
to the toplevel GdkWindow.  Again note that drawRect will only be
called once per NSWindow (since there is only one NSView per

The call to _gdk_window_process_updates_recurse()  in the drawRect
method was added in this commit:

commit 65b5644cda073928f6f9005db3e4bb27c589c165
Author: Richard Hult <richard imendio com>
Date:   Mon Jan 26 20:29:51 2009 +0100

    Call _gdk_window_process_updates_recurse() in drawRect instead of
creating our own expose events

Likely a required change because in the past an NSView mapped to each
GdkWindow, which is no longer the case.  The drawRect of the NSView of
a GdkWindow has to take into account all GdkWindows that are

> In Ardour, this means that we spend up to 0.5 seconds in a the idle "redraw"
> callback every time we need to redraw an NSView...

If this function really takes that much time, we need to start
thinking of a smart solution here.  Richard has also mentioned to me
the possibility that the update handling could be very slow in cases
with many subwindows and thus would need a re-visit.

> In older GDK/GTK code, the Quartz backend generated an expose for the
> GdkWindow corresponding precisely to the NSView, but apparently after the
> introduction.of client side windows, somebody decided that the relationship
> between GdkWindows and NSViews as represented by the existing Quartz backend
> was wrong, and that when drawRect was called for *this* NSView, we still had
> to do a recursive descent through all child GdkWindows of the affected
> GdkWindow. This ignores the fact that Quartz is already doing this for us.

How I understand things, the goal of the client side windows work was
to generate and manage most windows "client side", that means no
native window for that will be created.  For the Quartz backend that
meant that no NSView would be created any more for each GdkWindow;
since the NSView mimicked the native window in this setup.

So yes, we have to do a recursive descent through child GdkWindows of
the affected GdkWindow, since these do not all have NSViews coupled to
them.  In this case, Quartz is not already doing that for us, since
there were no NSViews created for these.

Hope this helps.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]