-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdrawing-model.html
More file actions
338 lines (303 loc) · 15.5 KB
/
Copy pathdrawing-model.html
File metadata and controls
338 lines (303 loc) · 15.5 KB
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
<!DOCTYPE html>
<html lang="en">
<head>
<base href=".">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The GTK+ Drawing Model</title>
<link rel="stylesheet" href="assets/css/custom_bootstrap.css" type="text/css">
<link rel="stylesheet" href="assets/css/bootstrap-toc.min.css" type="text/css">
<link rel="stylesheet" href="assets/css/frontend.css" type="text/css">
<link rel="stylesheet" href="assets/css/jquery.mCustomScrollbar.min.css">
<link rel="stylesheet" href="assets/css/prism.css" type="text/css">
<script src="assets/js/mustache.min.js"></script>
<script src="assets/js/jquery.js"></script>
<script src="assets/js/scrollspy.js"></script>
<script src="assets/js/bootstrap.js"></script>
<script src="assets/js/typeahead.jquery.min.js"></script>
<script src="assets/js/search.js"></script>
<script src="assets/js/isotope.pkgd.min.js"></script>
<script src="assets/js/compare-versions.js"></script>
<script src="assets/js/jquery.mCustomScrollbar.concat.min.js"></script>
<script src="assets/js/bootstrap-toc.min.js"></script>
<script src="assets/js/jquery.touchSwipe.min.js"></script>
<script src="assets/js/anchor.min.js"></script>
<script src="assets/js/tag_filtering.js"></script>
<script src="assets/js/language_switching.js"></script>
<script src="assets/js/lines_around_headings.js"></script>
<script src="assets/js/prism-core.js"></script>
<script src="assets/js/prism-autoloader.js"></script>
<script src="assets/js/prism_autoloader_path_override.js"></script>
<script src="assets/js/trie.js"></script>
</head>
<body class="no-script
" data-spy="scroll" data-target="#toc" data-offset="70">
<script>
$('body').removeClass('no-script');
</script>
<nav class="navbar navbar-fixed-top navbar-default" id="topnav">
<div class="container-fluid">
<div class="navbar-right">
<a id="toc-toggle">
<span class="glyphicon glyphicon-menu-right"></span>
<span class="glyphicon glyphicon-menu-left"></span>
</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-wrapper" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<form action="" class="navbar-form pull-right" id="navbar-search-form">
<div class="form-group has-feedback">
<input type="text" class="form-control input-sm" name="search" id="sidenav-lookup-field" placeholder="search" disabled>
<span class="glyphicon glyphicon-search form-control-feedback" id="search-mgn-glass"></span>
</div>
</form>
</div>
<div class="navbar-header">
<a id="sidenav-toggle">
<span class="glyphicon glyphicon-menu-right"></span>
<span class="glyphicon glyphicon-menu-left"></span>
</a>
<a id="home-link" href="index.html" class="hotdoc-navbar-brand">
<img src="assets/images/home.svg" alt="Home">
</a>
</div>
<div class="navbar-collapse collapse" id="navbar-wrapper">
<ul class="nav navbar-nav" id="menu">
</ul>
<div class="hidden-xs hidden-sm navbar-text navbar-center">
</div>
</div>
</div>
</nav>
<main>
<div data-extension="gi-extension" data-hotdoc-in-toplevel="True" data-hotdoc-project="gtk-4.0.0" data-hotdoc-ref="drawing-model.html" class="page_container" id="page-wrapper" data-hotdoc-meta-gi-languages="['csharp']" data-hotdoc-meta-gi-language="c">
<script src="assets/js/utils.js"></script>
<div class="panel panel-collapse oc-collapsed" id="sidenav" data-hotdoc-role="navigation">
<script src="assets/js/navigation.js"></script>
<script src="assets/js/sitemap.js"></script>
</div>
<div id="body">
<div id="main">
<div id="page-description" data-hotdoc-source="drawing-model.markdown" data-hotdoc-role="main">
<h1 id="overview-of-the-drawing-model">Overview of the drawing model</h1>
<p>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.</p>
<h2 id="windows-and-events">Windows and events</h2>
<p>Programs that run in a windowing system generally create rectangular
regions in the screen called windows. 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 exposure event to the program for
that window.</p>
<p>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.</p>
<p>In practice, most windows in modern GTK+ application are client-side
constructs. Only few windows (in particular toplevel windows) are
<em>native</em>, 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 <code>Window</code>; on Win32, it corresponds to a <code>HANDLE</code>.</p>
<p>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 <a href="gtkentry-h.html#GtkEntry" data-gi-href-csharp="gtkentry-h.html#GtkEntry" data-gi-title-csharp="Gtk.Entry"><code>GtkEntry</code></a>
widget, the entry asks GTK+ to queue a redraw operation for itself.</p>
<p>The windowing system generates events for native windows. The GDK
interface to the windowing system translates such native events into
<a href="">GdkEvent</a> structures and sends them on to the GTK layer. In turn, the
GTK layer finds the widget that corresponds to a particular <code>GdkWindow</code>
and emits the corresponding event signals on that widget.</p>
<p>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.</p>
<h2 id="the-frame-clock">The frame clock</h2>
<p>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:</p>
<ul>
<li>
<p>Events</p>
</li>
<li>
<p>Update</p>
</li>
<li>
<p>Layout</p>
</li>
<li>
<p>Paint</p>
</li>
</ul>
<p>The phases happens in this order and we will always run each phase
through before going back to the start.</p>
<p>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.</p>
<p>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.</p>
<p>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).</p>
<p>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:</p>
<ul>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
</ul>
<p>There are also a lot of implicit triggers of these from the CSS layer
(which does animations, resizes and repaints as needed).</p>
<h2 id="hierarchical-drawing">Hierarchical drawing</h2>
<p>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.</p>
<p>When generating the event, GDK also sets up double buffering to avoid
the flickering that would result from each widget drawing itself in
turn. <a href="drawing-model.html#double-buffering">Double buffering</a>
describes the double buffering mechanism in detail.</p>
<p>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.</p>
<p>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.)</p>
<p>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.</p>
<h2 id="scrolling">Scrolling</h2>
<p>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).</p>
<h1 id="double-buffering">Double buffering</h1>
<p>If each of the drawing calls made by each subwidget's <code>draw</code> 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 double buffering 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.</p>
<p>Two basic functions in GDK form the core of the double-buffering
mechanism:
<a href="drawing-model.html#gdk_window_begin_paint_region"><code>gdk_window_begin_paint_region()</code></a> and
<a href="drawing-model.html#gdk_window_end_paint"><code>gdk_window_end_paint()</code></a>. The first function
tells a <code>GdkWindow</code> 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.</p>
<h2 id="automatic-double-buffering">Automatic double buffering</h2>
<p>It would be inconvenient for all widgets to call
<code>gdk_window_begin_paint_region()</code> and <code>gdk_window_end_paint()</code> at the
beginning and end of their draw handlers.</p>
<p>To make this easier, GTK+ normally calls
<code>gdk_window_begin_paint_region()</code> before emitting the #GtkWidget::draw
signal, and then it calls <code>gdk_window_end_paint()</code> 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.</p>
<p>However, some widgets may prefer to disable this kind of automatic
double buffering and do things on their own. To do this, call the
<code>gtk_widget_set_double_buffered()</code> function in your widget's
constructor. Double buffering can only be turned off for widgets that
have a native window.</p>
<pre><code>static void
my_widget_init (MyWidget *widget)
{
...
gtk_widget_set_double_buffered (widget, FALSE);
...
}
</code></pre>
<p>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 <em>not</em> 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.</p>
<p>Even if you turn off double buffering on a widget, you can still call
<code>gdk_window_begin_paint_region()</code> and <code>gdk_window_end_paint()</code> by hand
to use temporary drawing buffers.</p>
</div>
</div>
<div id="search_results">
<p>The results of the search are</p>
</div>
<hr>
<div id="footer">
</div>
</div>
<div id="toc-column">
<div class="edit-button">
</div>
<div id="toc-wrapper">
<nav id="toc"></nav>
</div>
</div>
</div>
</main>
</body>
<script src="assets/js/navbar_offset_scroller.js"></script>
</html>