summaryrefslogtreecommitdiff
path: root/docs/reference/gtk/drawing-model.xml
blob: c19fffc2b6c21e3cf96ce98aa1b35fe221cdbe6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<refentry id="chap-drawing-model">
<refmeta>
<refentrytitle>The GTK+ Drawing Model</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>GTK Library</refmiscinfo>
</refmeta>

<refnamediv>
<refname>The GTK+ Drawing Model</refname>
<refpurpose>
    The GTK+ drawing model in detail
</refpurpose>
</refnamediv>


  <refsect1 id="drawing-overview">
    <title>Overview of the drawing model</title>

    <para>
      This chapter describes the GTK+ drawing model in detail.  If you
      are interested in the procedure which GTK+ follows to draw its
      widgets and windows, you should read this chapter; this will be
      useful to know if you decide to implement your own widgets.  This
      chapter will also clarify the reasons behind the ways certain
      things are done in GTK+; for example, why you cannot change the
      background color of all widgets with the same method.
    </para>

    <refsect2 id="drawing model windows">
      <title>Windows and events</title>

      <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>
        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>
        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 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>GdkSurface</classname> and emits the corresponding event
        signals on that widget.
      </para>

      <para>
        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="frameclock">
      <title>The frame clock</title>

      <para>
        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>
        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>
        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>

      <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>
        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>

    <refsect2 id="hierarchical-drawing">
      <title>Hierarchical drawing</title>

      <para>
        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 GdkSurface. This means you
        have to respect (and not reset) existing clip and transformations
        set on it.
      </para>

      <para>
        Most widgets, including those that create their own GdkSurfaces 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 GdkSurfaces used to be impossible.)
      </para>

      <para>
        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>

    <refsect2 id="scrolling drawing model">
      <title>Scrolling</title>

      <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>
    </refsect2>

  </refsect1>

  <refsect1 id="double-buffering">
    <title>Double buffering</title>

    <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
      redrawn repeatedly:  the background, then decorative frames, then text
      labels, etc.  To avoid flicker, GTK+ employs a <firstterm>double
	buffering</firstterm> system at the GDK level.  Widgets normally don't
      know that they are drawing to an off-screen buffer; they just issue their
      normal drawing commands, and the buffer gets sent to the windowing system
      when all drawing operations are done.
    </para>

    <para>
      Two basic functions in GDK form the core of the double-buffering
      mechanism:  <link
      linkend="gdk_surface_begin_paint_region"><function>gdk_surface_begin_paint_region()</function></link>
      and <link
      linkend="gdk_surface_end_paint"><function>gdk_surface_end_paint()</function></link>.
      The first function tells a <classname>GdkSurface</classname> to
      create a temporary off-screen buffer for drawing.  All
      subsequent drawing operations to this window get automatically
      redirected to that buffer.  The second function actually paints
      the buffer onto the on-screen window, and frees the buffer.
    </para>

    <refsect2 id="automatic-double-buffering">
      <title>Automatic double buffering</title>

      <para>
	It would be inconvenient for all widgets to call
	<function>gdk_surface_begin_paint_region()</function> and
	<function>gdk_surface_end_paint()</function> at the beginning
	and end of their draw handlers.
      </para>

      <para>
	To make this easier, GTK+ normally calls
        <function>gdk_surface_begin_paint_region()</function>
        before emitting the #GtkWidget::draw signal, and
	then it calls <function>gdk_surface_end_paint()</function>
	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.
      </para>

      <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. Double buffering
        can only be turned off for widgets that have a native
        window.
      </para>

      <example id="disabling-double-buffering">
	<title>Disabling automatic double buffering</title>

	<programlisting>
static void
my_widget_init (MyWidget *widget)
{
  ...

  gtk_widget_set_double_buffered (widget, FALSE);

  ...
}
	</programlisting>
      </example>

      <para>
	When is it convenient to disable double buffering?  Generally,
	this is the case only if your widget gets drawn in such a way
	that the different drawing operations do not overlap each
	other.  For example, this may be the case for a simple image
	viewer:  it can just draw the image in a single operation.
	This would <emphasis>not</emphasis> be the case with a word
	processor, since it will need to draw and over-draw the page's
	background, then the background for highlighted text, and then
	the text itself.
      </para>

      <para>
	Even if you turn off double buffering on a widget, you
	can still call
	<function>gdk_surface_begin_paint_region()</function> and
	<function>gdk_surface_end_paint()</function> by hand to use
	temporary drawing buffers.
      </para>
    </refsect2>
  </refsect1>

</refentry>

<!--
Local variables:
mode: xml
sgml-parent-document: ("gtk-docs.sgml" "book" "part" "refentry")
End:
-->