Re: gtk_widget_draw()



----- "Benjamin Otte" <otte gnome org> wrote:

> Hi,
> 
> Here's an email detailing my thought process for what I want to do
> with rendering in the master branch following the rendering-cleanup
> merge. It's mostly a brain dump, so treat it as such.
>

So, I read this thread and thought about it a bit (not a lot though,
obviously this whole thing needs a lot of careful thought). Its a
complex area, and while I share your enthusiasm for many of the
changes I think in some places it might oversimplify things.

Anyway, lets start on a positive side with things where we are wildly
in agreement.

Getting rid of GdkPixmap seems like a good think. Its not used a lot
in the current rendering-cleanup branch. The only thing I see that is
slightly problematic is windows backgrounds. I'm not sure what we
should replace this with? One approach is to use X cairo pattern
rather than pixmaps. Pixmap backgrounds are heavily used in themes
atm, but that may change with the new theming api. Also, pixmap
background in windows are mainly used to get nice initial repaint
behaviour (no flashing before expose finished) which is less important
in a modern composited world.

Another obvious thing is folding GdkDrawable into GdkWindow. With the
removal of pixmaps and the gc ops this truly is a useless class.

Disallowing multiple calls to being_paint_region() and using a window
as source while drawing also sounds like pure wins in terms of dropping
complexity with no real loss in useful operations.

Just doing this is a great cleanup and simplification of gdk and the
backends, which is clearly a good step towards further work.

However, the next step, getting rid of the wrappers/impls and
restructuring the window class hierarchy is not really right
imho. Having impls be a separate object type is quite nice for CSW, it
models pretty well the situation where GdkWindow are the client side
things, and GdkWindowImpls are the native part that GdkWindows draw
in, etc. It also matches the dynamic status of "nativeness". I.e. a
window can get a native window when needed rather than having to do
this upfront. If nativeness was encoded in the typesystem things like
that just wouldn't work.

Similarly a problem is encoding things like toplevel/subwindowness in
the typesystem (i.e. GdkSubwindow or GdkToplevel). A subwindow is a
subwindow purely based on where its in the hierarchy, which is easily
changed with gdk_window_reparent. This means windows can go from
toplevels and back at runtime. Even if its possible to perhaps
enforce things like this from the gdk typesystem it is not really
representative to how the actual underlying abstraction (i.e. X
windows or win32 HWND) really work, so its gonna cause a bunch of pain
fighting the system, for a relatively small gain (approximately nobody uses
gdkwindow apis anyway, as its too low-level).

There is still some class hierarchy cleanup that can be done with
the new drawable-free situation though. I.e. We can have something like:

Public classes:
GdkWindow

Internal Classes:
GdkNativeWindow
 |  (replaces GdkWindowImplIface, could maybe be a public type?
 |   maybe not ideal name due to offscreen subclass...)
 |- GdkOffscreenWindow
 |- GdkWindowX11
 |- GdkWindowQuartz
 |- GdkWindowWin32

This part of things has been very concrete stuff, now we get to the
more abstract parts about rendering using cairo only. First of all I
must say your description is kinda handwavy in the details. For
instance, its not at all obvious how multi-window widgets are supposed
to render themselves, or how the window hierarchy is traversed while
rendering.

Secondly, while I think your description of a no-exposes cairo_t-only
rendering system sounds very nice I fear that it is to simple to
handle everything a full featured toolkit will meet in
practice. People will want to put all kinds of weird stuff inside a Gtk
window, including things like: OpenGL rendered stuff, Xv magic,
socket/plug X embedding, embedding things like a flash plugin or old
motif plugins in a browser, etc. There is just no way you can take
things like these and put them transformed on a pdf surface. We have
to expose a more "raw" expose system.

Also, a straight single paint function isn't ideal for e.g printing a
widget, or scaling it up a gui. Its quite common for widgets to have
offscreen pixmaps with stuff that are pre-rendered that are just blasted
out when drawing. These things would generally be allocated when the
widget is realized by making surfaces similar to the window (ie
x pixmaps of the same size/depth). A more general method would have
separate callbacks (somewhat similar to realize/unrealize) for
preparing to draw on some surface/scale factor that let you create
such surfaces for the right target, letting you keep vector info when
things are drawn on e.g. a pdf surface.

Nevertheless, for all "standard" widgets this is a good model that
would be nice to use. So, lets try to make it work.

As a basis, lets say all drawing are done inside a (explicit or
implicit) begin/end_paint pair (i.e. always double buffer, don't allow
direct draw on windows). On X this will draw to a double buffer pixmap
that will be copied to the window, on "double buffered" systems like quartz
it'll draw directly to the window. Additionally we support
gdk_window_scroll() and gdk_window_move_region() to allow efficient
scrolling and moving child windows (native and client side) for
standard scrolling implementations.
Additionally we keep the update region on each impl window just like
now, updating it manually with gdk_window_invalidate_region() as well
as from native expose events.

Now, say you have a complex dialog with lots of widgetry (and no
native subwindows). The only thing in the update region is the area of
two buttons, at the bottom of the window (maybe you moused over
them). How do we repaint this?

At the very least what we need to do is draw all widgets that have an
area inside the subwindows that contains an invalid region. Any
widgets that are containers are responsible for chaining the drawing
of any no-window child widgets of it. Ideally we should also *not*
redraw any other widgets, and the result of all drawing should be
double buffered on a single pixmap that at the end is copied to the
destination window using the invalid region as clip.

At the moment this is done by recursing over the hierarchy for each
window that intersects the invalid region, and then when a child has
been handled we remove that region from the invalid set. If after all
the children are handled we still have invalid area for the window we
find the widget for the window and pass it an expose event with the
remaining invalid area for that child window. Each such expose event
calls begin/end_paint passing in the invalid area as the draw region
which will affect clipping. We also create an initial double buffer
pixmap (the implicit paint) that we reuse parts of when handling the
many widget begin/end pairs.

So, how would this work with cairo? First of all one must notice that
this is very tied to the invalid region handling. This is not right
for general rendering, as you should not have to invalidate a widget
to print it. However, it is also not really right to have the complex
hierarchy trawling and machinery described above implemented by each
widget. So, we need to expose as a public api the above
mechanism. Something like:

gdk_window_draw_hierarchy(GdkWindow *window,
                          cairo_t *cr,
                          cairo_region_t *draw_region)

This would then do all the region manipulation and widget hierarchy
traversal needed to find all the widgets that need to be repainted,
setting the clip and offset on the cairo_t and then calling draw on
each widget.

The invalid region machinery could then use this by getting a cairo_t
for the window, then for each window with an impl that has a non-empty
invalid region we do something like:

 invalid_region = window->invalid_region
 window->invalid_region = empty region
 cairo_clip(invalid_region);
 cairo_push_group()
 gdk_window_draw_hierarchy(window_with_impl, invalid_region)
 // Do moves a late as possible, but before pushing the double buffer pixmap:
 gdk_window_flush_outstanding_moves(window);
 cairo_pop_group()

Additionally we could have a gtk_widget helper function that draws a 
whole widget hierarchy to a cairo_t if you want to e.g. print widgets.

One ugly aspect of this is the handling of no-window widgets, which
currently need to be handled by container widgets propagating
exposes. This is kinda ugly, and having paint be a signal on the
GdkWindow seems like a possible solution. However, this leads to
problems with draw ordering that I don't quite know how to fix
(i.e. each no-window container widget must paint before all no-window
children of it paints). So, I guess we need a similar thing to
gtk_container_propagate_expose that propagates painting with cairo.

We can still use an paint signal for windows though, as this pretty
neatly handles the problem of painting different things on different
subwindows of the same widget. I'm not sure this is enough to fully
replace set_user_data though, as gdk_window_get_user_data is used in some
other places to find the widget related to some random window id we get
from X.

Another problem is how to handle the weird things that don't support
cairo drawing in the above scheme. Like say a gl widget. Maybe we need
to have the whole thing described above be the default implementation for
some signal on native windows, and then let widgets that do other things
for rendering override this signal and do its drawing in some other way. 
Then you can only do "weird stuff" for native window widgets, but thats
generally when you need it anyway.

Anyway, time to stop my mumbling and go to sleep...


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