[gtk+] Update drawing model docs



commit 7806684592db3e9ff4a39e1a30f3acab355ad335
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Nov 10 03:25:48 2013 -0500

    Update drawing model docs
    
    Include material from Alex' blog post about the modern GTK+
    rendering model.

 docs/reference/gtk/drawing-model.xml |  483 ++++++++++++++--------------------
 1 files changed, 192 insertions(+), 291 deletions(-)
---
diff --git a/docs/reference/gtk/drawing-model.xml b/docs/reference/gtk/drawing-model.xml
index ec762e7..1304603 100644
--- a/docs/reference/gtk/drawing-model.xml
+++ b/docs/reference/gtk/drawing-model.xml
@@ -30,175 +30,157 @@
       background color of all widgets with the same method.
     </para>
 
-    <para>
-      Programs that run in a windowing system generally create
-      rectangular regions in the screen called
-      <firstterm>windows</firstterm>.  Traditional windowing systems
-      do not automatically save the graphical content of windows, and
-      instead ask client programs to repaint those windows whenever it
-      is needed.  For example, if a window that is stacked below other
-      windows gets raised to the top, then a client program has to
-      repaint the area that was previously obscured.  When the
-      windowing system asks a client program to redraw part of a
-      window, it sends an <firstterm>exposure event</firstterm> to the
-      program for that window.
-    </para>
-
-    <para>
-      Here, "windows" means "rectangular regions with automatic
-      clipping", instead of "toplevel application windows".  Most
-      windowing systems support nested windows, where the contents of
-      child windows get clipped by the boundaries of their parents.
-      Although GTK+ and GDK in particular may run on a windowing
-      system with no such notion of nested windows, GDK presents the
-      illusion of being under such a system.  A toplevel window may
-      contain many subwindows and sub-subwindows, for example, one for
-      the menu bar, one for the document area, one for each scrollbar,
-      and one for the status bar.  In addition, controls that receive
-      user input, such as clickable buttons, are likely to have their
-      own subwindows as well.
-    </para>
-
-    <para>
-      Generally, the drawing cycle begins when GTK+ receives an
-      exposure event from the underlying windowing system:  if the
-      user drags a window over another one, the windowing system will
-      tell the underlying window that it needs to repaint itself.  The
-      drawing cycle can also be initiated when a widget itself decides
-      that it needs to update its display.  For example, when the user
-      types a character in a <link
-      linkend="GtkEntry"><classname>GtkEntry</classname></link>
-      widget, the entry asks GTK+ to queue a redraw operation for
-      itself.
-    </para>
-
-    <para>
-      The following sections describe how GTK+ decides which widgets
-      need to be repainted, and how widgets work internally in terms
-      of the resources they use from the windowing system.
-    </para>
+    <refsect2 id="drawing model windows">
+      <title>Windows and events</title>
 
-    <para>
-      A <link linkend="GdkWindow"><classname>GdkWindow</classname></link>
-      represents a window from the underlying windowing system on which GTK+
-      is running.  For example, on X11 it corresponds to a
-      <type>Window</type>; on Win32, it corresponds to a <type>HANDLE</type>.
-      The windowing system generates events for these windows.  The GDK
-      interface to the windowing system translates such native events into
-      <link linkend="GdkEvent"><structname>GdkEvent</structname></link>
-      structures and sends them on to the GTK layer.  In turn, the GTK layer
-      finds the widget that corresponds to a particular
-      <classname>GdkWindow</classname> and emits the corresponding event
-      signals on that widget.
-    </para>
+      <para>
+        Programs that run in a windowing system generally create
+        rectangular regions in the screen called
+        <firstterm>windows</firstterm>.  Traditional windowing systems
+        do not automatically save the graphical content of windows, and
+        instead ask client programs to repaint those windows whenever it
+        is needed.  For example, if a window that is stacked below other
+        windows gets raised to the top, then a client program has to
+        repaint the area that was previously obscured.  When the
+        windowing system asks a client program to redraw part of a
+        window, it sends an <firstterm>exposure event</firstterm> to the
+        program for that window.
+      </para>
 
-    <refsect2 id="emission of the draw event">
-      <title>Emission of the draw event</title>
+      <para>
+        Here, "windows" means "rectangular regions with automatic
+        clipping", instead of "toplevel application windows".  Most
+        windowing systems support nested windows, where the contents of
+        child windows get clipped by the boundaries of their parents.
+        Although GTK+ and GDK in particular may run on a windowing
+        system with no such notion of nested windows, GDK presents the
+        illusion of being under such a system.  A toplevel window may
+        contain many subwindows and sub-subwindows, for example, one for
+        the menu bar, one for the document area, one for each scrollbar,
+        and one for the status bar.  In addition, controls that receive
+        user input, such as clickable buttons, are likely to have their
+        own subwindows as well.
+      </para>
 
       <para>
-        When the program needs to redraw a region of a
-        <classname>GdkWindow</classname>, generates an event of
-        type <link
-        linkend="GDK_EVENT_EXPOSE"><constant>GDK_EVENT_EXPOSE</constant></link>
-        for that window, specifying the region to redraw in the process.
+        In practice, most windows in modern GTK+ application are client-side
+        constructs. Only few windows (in particular toplevel windows) are
+        <emphasis>native</emphasis>, which means that they represent a
+        window from the underlying windowing system on which GTK+ is running.
+        For example, on X11 it corresponds to a <type>Window</type>; on Win32,
+        it corresponds to a <type>HANDLE</type>.
       </para>
 
       <para>
-        When generating the event, GDK also sets up double buffering to
-        avoid the flickering that would result from each widget drawing
-        itself in turn.  <xref linkend="double-buffering"/> describes
-        the double buffering mechanism in detail.
+        Generally, the drawing cycle begins when GTK+ receives an
+        exposure event from the underlying windowing system:  if the
+        user drags a window over another one, the windowing system will
+        tell the underlying window that it needs to repaint itself.  The
+        drawing cycle can also be initiated when a widget itself decides
+        that it needs to update its display.  For example, when the user
+        types a character in a <link
+        linkend="GtkEntry"><classname>GtkEntry</classname></link>
+        widget, the entry asks GTK+ to queue a redraw operation for
+        itself.
       </para>
-        
+
       <para>
-        When the GTK+ widget layer receives the event, it finds the widget that
-        corresponds to the window, and causes it to render itself using the
-        widget's #GtkWidget::draw signal. For this purpose it creates a
-        <link linkend="#cairo_t">cairo context</link>. It then clips the context
-        to the area that needs to be drawn. This makes sure that the minimal
-        amount of work is done if only a small part of the widget needs to be
-        repainted. After translating the context so that its (0, 0) coordinate
-        corresponds to the top left corner of the widget, it effectively calls
-        the widget's <function>gtk_widget_draw</function> function.
+        The windowing system generates events for native windows. The GDK
+        interface to the windowing system translates such native events into
+        <link linkend="GdkEvent"><structname>GdkEvent</structname></link>
+        structures and sends them on to the GTK layer.  In turn, the GTK layer
+        finds the widget that corresponds to a particular
+        <classname>GdkWindow</classname> and emits the corresponding event
+        signals on that widget.
       </para>
 
       <para>
-        <function>gtk_widget_draw</function> takes care of drawing the widget
-        to the cairo context. It first checks that the widget actually needs to
-        be drawn. Widgets might for example be empty or outside of the cairo
-        context's clipped area, which would make drawing them not do anything.
-        Usually they will need to be drawn. In this case, the context will be
-        clipped to the widget's allocated size and the
-        <link linkend="GtkWidget::draw">draw signal</link> will be emitted on 
-        the widget which will finally draw the widget.
+        The following sections describe how GTK+ decides which widgets
+        need to be repainted in response to such events, and how widgets
+        work internally in terms of the resources they use from the
+        windowing system.
       </para>
     </refsect2>
 
-    <refsect2 id="window-no-window-widgets">
-      <title>Window and no-window widgets</title>
+    <refsect2 id="frameclock">
+      <title>The frame clock</title>
 
       <para>
-       In principle, each widget could have a
-       <classname>GdkWindow</classname> of its own.  With such a
-       scheme, the drawing cycle would be trivial:  when GDK notifies
-       the GTK layer about an exposure event for a
-       <classname>GdkWindow</classname>, the GTK layer would simply
-       emit the #GtkWidget::draw signal for that widget.  The signal
-       handler would subsequently repaint the widget.  No further
-       work would be necessary; the windowing system would generate
-       exposure events for each window that needs it, and then each
-       corresponding widget would draw itself in turn.
+        All GTK+ applications are mainloop-driven, which means that most
+        of the time the app is idle inside a loop that just waits for
+        something to happen and then calls out to the right place when
+        it does. On top of this GTK+ has a frame clock that gives a
+        “pulse” to the application. This clock beats at a steady rate,
+        which is tied to the framerate of the output (this is synced to
+        the monitor via the window manager/compositor). The clock has
+        several phases:
+        <itemizedlist>
+          <listitem><para>Events</para></listitem>
+          <listitem><para>Update</para></listitem>
+          <listitem><para>Layout</para></listitem>
+          <listitem><para>Paint</para></listitem>
+        </itemizedlist>
+        The phases happens in this order and we will always run each
+        phase through before going back to the start.
       </para>
 
       <para>
-       However, in practice it is convenient to have widgets which do
-       not have a <classname>GdkWindow</classname> of their own, but
-       rather share the one from their parent widget.  Such widgets
-       have called <function>gtk_widget_set_has_window</function> to
-       disable it; this can be tested easily with the <link
-       linkend="gtk-widget-get-has-window"><function>gtk_widget_get_has_window()</function></link>
-       function.  As such, these are called <firstterm>no-window
-       widgets</firstterm>.
+        The Events phase is a long stretch of time between each
+        redraw where we get input events from the user and other events
+        (like e.g. network I/O). Some events, like mouse motion are
+        compressed so that we only get a single mouse motion event per
+        clock cycle.
       </para>
 
       <para>
-       No-window widgets are useful for various reasons:
+        Once the Events phase is over we pause all external events and
+        run the redraw loop. First is the Update phase, where all
+        animations are run to calculate the new state based on the
+        estimated time the next frame will be visible (available via
+        the frame clock). This often involves geometry changes which
+        drives the next phase, Layout. If there are any changes in
+        widget size requirements we calculate a new layout for the
+        widget hierarchy (i.e. we assign sizes and positions). Then
+        we go to the Paint phase where we redraw the regions of the
+        window that need redrawing.
       </para>
 
-      <itemizedlist>
-       <listitem>
-         <para>
-           Some widgets may want the parent's background to show through, even
-           when they draw on parts of it.  For example, consider a theme that
-           uses textured backgrounds, such as gradients or repeating
-           patterns.  If each widget had its own window, and in turn its own
-           gradient background, labels would look bad because there would be a
-           visible break with respect to their surroundings.  <xref
-             linkend="figure-windowed-label"/> shows this undesirable effect.
-         </para>
-
-         <figure id="figure-windowed-label">
-           <title>Windowed label vs. no-window label</title>
-
-           <graphic fileref="figure-windowed-label.png" format="png"/>
-         </figure>
-       </listitem>
-
-       <listitem>
-         <para>
-           Reducing the number of windows creates less traffic between GTK+ and
-           the underlying windowing system, especially when getting events.
-         </para>
-       </listitem>
-      </itemizedlist>
+      <para>
+        If nothing requires the Update/Layout/Paint phases we will
+        stay in the Events phase forever, as we don’t want to redraw
+        if nothing changes. Each phase can request further processing
+        in the following phases (e.g. the Update phase will cause there
+        to be layout work, and layout changes cause repaints).
+      </para>
 
       <para>
-       On the other hand, widgets that would benefit from having a "hard"
-       clipping region may find it more convenient to create their own
-       windows.  Also, widgets which want to receive events resulting from
-       user interaction may find it convenient to use windows of their own as
-       well.  Widgets may have more than one window if they want to
-       define different regions for capturing events.
+        There are multiple ways to drive the clock, at the lowest level
+        you can request a particular phase with
+        gdk_frame_clock_request_phase() which will schedule a clock beat
+        as needed so that it eventually reaches the requested phase.
+        However, in practice most things happen at higher levels:
+        <itemizedlist>
+          <listitem><para>
+            If you are doing an animation, you can use
+            gtk_widget_add_tick_callback() which will cause a regular
+            beating of the clock with a callback in the Update phase
+            until you stop the tick.
+          </para></listitem>
+          <listitem><para>
+            If some state changes that causes the size of your widget
+            to change you call gtk_widget_queue_resize() which will
+            request a Layout phase and mark your widget as needing
+            relayout.
+          </para></listitem>
+          <listitem><para>
+            If some state changes so you need to redraw some area of
+            your widget you use the normal gtk_widget_queue_draw()
+            set of functions. These will request a Paint phase and
+            mark the region as needing redraw.
+          </para></listitem>
+        </itemizedlist>
+        There are also a lot of implicit triggers of these from the
+        CSS layer (which does animations, resizes and repaints as needed).
       </para>
     </refsect2>
 
@@ -206,123 +188,73 @@
       <title>Hierarchical drawing</title>
 
       <para>
-       When the GTK layer receives an exposure event from GDK, it
-       finds the widget that corresponds to the window which received
-       the event.  By definition, this corresponds to a widget that
-       has the <constant>GTK_NO_WINDOW</constant> flag turned
-       <emphasis>off</emphasis> (otherwise, the widget wouldn't own
-       the window!).  First this widget paints its background, and
-       then, if it is a container widget, it tells each of its
-       <constant>GTK_NO_WINDOW</constant> children to paint
-       themselves.  This process is applied recursively for all the
-       <constant>GTK_NO_WINDOW</constant> descendants of the original
-       widget.
+        During the Paint phase we will send a single expose event to
+        the toplevel window. The event handler will create a cairo
+        context for the window and emit a GtkWidget::draw() signal
+        on it, which will propagate down the entire widget hierarchy
+        in back-to-front order, using the clipping and transform of
+        the cairo context. This lets each widget draw its content at
+        the right place and time, correctly handling things like
+        partial transparencies and overlapping widgets.
+      </para>
+
+      <para>
+        When generating the event, GDK also sets up double buffering to
+        avoid the flickering that would result from each widget drawing
+        itself in turn.  <xref linkend="double-buffering"/> describes
+        the double buffering mechanism in detail.
+      </para>
+        
+      <para>
+        Normally, there is only a single cairo context which is used in
+        the entire repaint, rather than one per GdkWindow. This means you
+        have to respect (and not reset) existing clip and transformations
+        set on it.
       </para>
 
       <para>
-       Note that this process does not get propagated to widgets
-       which have windows of their own, that is, to widgets which
-       have the <constant>GTK_NO_WINDOW</constant> flag turned off.
-       If such widgets require redrawing, then the windowing system
-       will already have sent exposure events to their corresponding
-       windows.  As such, there is no need to
-       <firstterm>propagate</firstterm> the exposure to them on the
-       GTK+ side.
+        Most widgets, including those that create their own GdkWindows have
+        a transparent background, so they draw on top of whatever widgets
+        are below them. This was not the case in GTK+ 2 where the theme set
+        the background of most widgets to the default background color. (In
+        fact, transparent GdkWindows used to be impossible.)
       </para>
 
       <para>
-       <xref
-       linkend="figure-hierarchical-drawing"/> shows how a simple toplevel window would
-       paint itself when it contains only <constant>GTK_NO_WINDOW</constant> descendants:
-
-       <orderedlist>
-         <listitem>
-           <para>
-             The outermost, thick rectangle is a toplevel <link
-               linkend="GtkWindow"><classname>GtkWindow</classname></link>,
-             which is not a <constant>GTK_NO_WINDOW</constant> widget &mdash;
-             as such, it does receive its exposure event as it comes from GDK.
-             First the <classname>GtkWindow</classname> would paint its own
-             background.  Then, it would ask its only child to paint itself,
-             numbered 2.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The dotted rectangle represents a <link
-               linkend="GtkVBox"><classname>GtkVBox</classname></link>, which
-             has been made the sole child of the
-             <classname>GtkWindow</classname>.  Boxes are just layout
-             containers that do not paint anything by themselves, so this
-             <classname>GtkVBox</classname> would draw nothing, but rather ask
-             its children to draw themselves.  The children are numbered 3 and
-             6.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The thin rectangle is a <link
-               linkend="GtkFrame"><classname>GtkFrame</classname></link>,
-             which has two children:  a label for the frame, numbered 4, and
-             another label inside, numbered 5.  First the frame would draw its
-             own beveled box, then ask the frame label and its internal child to
-             draw themselves.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The frame label has no children, so it just draws its text:  "Frame&nbsp;Label".
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The internal label has no children, so it just draws its text:  "This
-             is some text inside the frame!".
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The dotted rectangle represents a <link
-               linkend="GtkHBox"><classname>GtkHBox</classname></link>.  Again,
-             this does not draw anything by itself, but rather asks its children
-             to draw themselves.  The children are numbered 7 and 9.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             The thin rectangle is a <link
-               linkend="GtkButton"><classname>GtkButton</classname></link> with
-             a single child, numbered 8.  First the button would draw its
-             beveled box, and then it would ask its child to draw itself.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             This is a text label which has no children, so it just draws its
-             own text:  "Cancel".
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             Similar to number 7, this is a button with a single child, numbered
-             10.  First the button would draw its beveled box, and then it would
-             ask its child to draw itself.
-           </para>
-         </listitem>
-         <listitem>
-           <para>
-             Similar to number 8, this is a text label which has no children,
-             so it just draws its own text:  "OK".
-           </para>
-         </listitem>
-       </orderedlist>
+        The whole rendering hierarchy is captured in the call stack, rather
+        than having multiple separate draw emissions, so you can use effects
+        like e.g. cairo_push/pop_group() which will affect all the widgets
+        below you in the hierarchy. This makes it possible to have e.g.
+        partially transparent containers.
       </para>
+    </refsect2>
 
-      <figure id="figure-hierarchical-drawing">
-       <title>Hierarchical drawing order</title>
+    <refsect2 id="scrolling drawing model">
+      <title>Scrolling</title>
 
-       <graphic fileref="figure-hierarchical-drawing.png" format="png"/>
-      </figure>
+      <para>
+        Traditionally, GTK+ has used self-copy operations to implement
+        scrolling with native windows. With transparent backgrounds, this
+        no longer works. Instead, we just mark the entire affected area for
+        repainting when these operations are used. This allows (partially)
+        transparent backgrounds, and it also more closely models modern
+        hardware where self-copy operations are problematic (they break the
+        rendering pipeline).
+      </para>
+
+      <para>
+        Since the above causes some overhead, we introduce a caching mechanism.
+        Containers that scroll a lot (GtkViewport, GtkTextView, GtkTreeView,
+        etc) allocate an offscreen image during scrolling and render their
+        children to it (which is possible since drawing is fully hierarchical).
+        The offscreen image is a bit larger than the visible area, so most of
+        the time when scrolling it just needs to draw the offscreen in a
+        different position. This matches contemporary graphics hardware much
+        better, as well as allowing efficient transparent backgrounds.
+        In order for this to work such containers need to detect when child
+        widgets are redrawn so that it can update the offscreen. This can be
+        done with the new gdk_window_set_invalidate_handler() function.
+      </para>
     </refsect2>
 
   </refsect1>
@@ -331,16 +263,6 @@
     <title>Double buffering</title>
 
     <para>
-      When the GTK layer receives an exposure event from GDK, it first finds
-      the <literal>!<constant>GTK_NO_WINDOW</constant></literal> widget that
-      corresponds to the event's window.  Then, it emits the
-      #GtkWidget::draw signal for that
-      widget.  As described above, that widget will first draw its background,
-      and then ask each of its <constant>GTK_NO_WINDOW</constant> children to
-      draw themselves.
-    </para>
-
-    <para>
       If each of the drawing calls made by each subwidget's
       <literal>draw</literal> handler were sent directly to the
       windowing system, flicker could result.  This is because areas may get
@@ -352,26 +274,6 @@
       when all drawing operations are done.
     </para>
 
-    <!-- FIXME: figure with a timeline of non-double-buffered and
-         double-buffered paints:
-
-         onscreen:
-         [garbage]
-         [background]
-         [button-frame]
-         [icon]
-         [label]
-
-
-         onscreen:             offscreen:
-         [garbage]
-                               [background]
-                               [button-frame]
-                               [icon]
-                               [label]
-         [final result]
-    -->
-
     <para>
       Two basic functions in GDK form the core of the double-buffering
       mechanism:  <link
@@ -396,14 +298,11 @@
       </para>
 
       <para>
-       To make this easier, most GTK+ widgets have the
-       <constant>GTK_DOUBLE_BUFFERED</constant> <link
-       linkend="GtkWidgetFlags">widget flag</link> turned on by
-       default.  When GTK+ encounters such a widget, it automatically
-       calls <function>gdk_window_begin_paint_region()</function>
-       before emitting the #GtkWidget::draw signal for the widget, and
+       To make this easier, GTK+ normally calls
+        <function>gdk_window_begin_paint_region()</function>
+        before emitting the #GtkWidget::draw signal, and
        then it calls <function>gdk_window_end_paint()</function>
-       after the signal has been emitted.  This is convenient for
+       after the signal has been emitted. This is convenient for
        most widgets, as they do not need to worry about creating
        their own temporary drawing buffers or about calling those
        functions.
@@ -411,10 +310,12 @@
 
       <para>
        However, some widgets may prefer to disable this kind of
-       automatic double buffering and do things on their own.  To do
-        this, call the
-        <function>gtk_widget_set_double_buffered()</function> function
-       in your widget's constructor.
+       automatic double buffering and do things on their own.
+        To do this, call the
+        <function>gtk_widget_set_double_buffered()</function>
+        function in your widget's constructor. Double buffering
+        can only be turned off for widgets that have a native
+        window.
       </para>
 
       <example id="disabling-double-buffering">


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