Re: gtk_widget_draw()



On Mon, Aug 16, 2010 at 11:23 PM, Alexander Larsson <alexl redhat com> wrote:
> [...]
>
> Just doing this is a great cleanup and simplification of gdk and the
> backends, which is clearly a good step towards further work.
>
Great. This is what I've been hacking on in the rendering-cleanup
branch (yes, you cannot make it die, even by merging it) since it was
merged last time. I do not inted to touch any GdkWindow internals in
that branch until it's merged again. The branch right now focusses on
getting rid of GdkPixmap and providing suitable replacements. More on
that in a separate mail though.

> 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.
>
The biggest problem for me when reading the GdkWindow code is that I
have no idea what a window is. So I have a hard time understanding the
code and if what it does is sane.
- Does a GdkWindow mirror a window provided by the windowing system?
Obviously no, because offscreen windows do not own a windowing system
window and csw windows do grab onto the native parent's window. So the
relationship between GdkWindows and X Windows is 1:N (where N can be
0).
- Does a GdkWindow have a matching Cairo surface? Well, kinda, but csw
windows share the surface with of the native window, so when drawing
to it, we need to clip before we draw.
- Is a GdkWindow an implementation detail or available in the public
API? Again, it depends. If you have a GdkWindowImplFoo, it's private,
otherwise it's public. Can I even call the same (internal or public)
functions on these two types of windows?
- Does a GdkWindow describe a window hierarchy? Again, kinda. Mostly
you can restack and reparent windows fine, but stuffing a toplevel
into an offscreen window does not work at all.
- What's a GdkWindowType? When I restack a TOPLEVEL into a CHILD, will
it change type? Should it?
- Who's responsible for what? Who decides when to repaint a window? Is
it the same for offscreen windows? Who creates damage events?
- Can windows be translucent? Should they be?
- Is a GdkWindow the only way to get enter/leave events to widgets? Do
widget authors know these thing?
- What do we do with event masks? I learned yesterday that widgets
without EXPOSURE_MASK don't get their background cleared. Is that
documented? Or should that even be the case? Do we even need event
masks?
- Shoud every widget have its own window now that we have lightweight
client-side windows? Or should almost no widget have a window because
we can synthethically create all the events widgets need? Do we have a
list for widget authors somewhere for when they want to create their
own window?

Note that I don't describe the current state of things in GTK2, I can
look that up in the code if I have to. I'm more interested in how
things should be.

> 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.
>
But there are operations that do not make sense at all on a
non-toplevel window. Like setting the icon or the title. And I'd argue
that it's confusing for widget implementors that they need to think
about this stuff when creating a window for their widget.
For offscreen windows, a lot more of the window operations don't make sense.

> 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.
>
I think in retrospect, that that is mostly related to the question
outlined above about what a GdkWindow is and what it does. It's hard
to be concrete if you have no idea about the underlying design. ;)

> 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.
>
We can have a very simple solution to most of these problems I
believe. Something like:
gboolean gdk_cairo_is_system_repaint (cairo_t *cr);
That function would return TRUE if the cairo_t was created by GDK for
repainting and FALSE if it just was a random call to
gtk_widget_paint().
You could just return a black area for where your OpenGL stuff would
be. Or you could use it for optimized drawing of a cached result: If
it's TRUE, just cairo_paint() the cache, otherwise rerender the full
contents.
Also, for the xv and OpenGL cases you outlined, you probably want to
create an X window outside of GDK (you definitely want in GStreamer,
and I'm prettty sure I'd want to for GL, too) and handle it manually.
In that case it's not GDK's responsibility to get something drawn. And
it would be up to the widgets to draw it when doing a non-system
draw() call.

> 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?
>
So here's pseudocode for the important functions for redraw handling
that exist in my mind. There's probably some corner cases that we need
to think about, but in general, i think it works.

def expose_event (native_window, invalid_region):
  cairo_create (get_surface (native_window));
  cairo_clip (invalid_region)
  cairo_push_group() /* for double-buffering */
  render_window(native_window)
  cairo_pop_group_to_source()
  cairo_set_operator(SOURCE)
  cairo_paint()

def render_window (native_window, cr):
  foreach (subwindow of native_window):
    cairo_save()
    cairo_clip(get_region (subwindow))
    if (! everything_clipped ()):
      cairo_translate(subwindow.x,subwindow.y)
      gtk_widget_draw (get_widget (subwindow))
    cairo_restore()

def gtk_widget_draw(widget, cr):
  cairo_save ()
  cairo_clip (widget.allocation)
  if (! everything_clipped ()):
    widget->class->draw ()
  cairo_restore ()

As you've probably figured out, the everything_clipped() function does
all the magic here. It replaces the current cairo_region_intersect()
and does basically the same thing:
In render_window() it figures out if the window actually overlaps the
area to repaint and skips windows that don't.
In gtk_widget_draw() it does the same for widgets. If we wanted, we
could also use gdk_cairo_is_system_repaint() and check if
widget->window is identical, but I don't think that's even necessary.

Benjamin


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