forked from AuroraMiddleware/gtk
dd105f3db8
It's not used anymore.
428 lines
17 KiB
XML
428 lines
17 KiB
XML
<?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>GdkWindow</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 GdkWindow. 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 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>
|
||
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_window_begin_paint_region"><function>gdk_window_begin_paint_region()</function></link>
|
||
and <link
|
||
linkend="gdk_window_end_paint"><function>gdk_window_end_paint()</function></link>.
|
||
The first function tells a <classname>GdkWindow</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_window_begin_paint_region()</function> and
|
||
<function>gdk_window_end_paint()</function> at the beginning
|
||
and end of their draw handlers.
|
||
</para>
|
||
|
||
<para>
|
||
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
|
||
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_window_begin_paint_region()</function> and
|
||
<function>gdk_window_end_paint()</function> by hand to use
|
||
temporary drawing buffers.
|
||
</para>
|
||
</refsect2>
|
||
</refsect1>
|
||
|
||
<refsect1 id="app-paintable-widgets">
|
||
<title>App-paintable widgets</title>
|
||
|
||
<para>
|
||
Generally, applications use the pre-defined widgets in GTK+ and
|
||
they do not draw extra things on top of them (the exception
|
||
being <classname>GtkDrawingArea</classname>). However,
|
||
applications may sometimes find it convenient to draw directly
|
||
on certain widgets like toplevel windows or event boxes. When
|
||
this is the case, GTK+ needs to be told not to overwrite your
|
||
drawing afterwards, when the window gets to drawing its default
|
||
contents.
|
||
</para>
|
||
|
||
<para>
|
||
<classname>GtkWindow</classname> and
|
||
<classname>GtkEventBox</classname> are the two widgets that allow
|
||
turning off drawing of default contents by calling
|
||
<function>gtk_widget_set_app_paintable()</function>. If you call
|
||
this function, they will not draw their contents and let you do
|
||
it instead.
|
||
</para>
|
||
|
||
<para>
|
||
Since the #GtkWidget::draw signal runs user-connected handlers
|
||
<emphasis>before</emphasis> the widget's default handler, what
|
||
usually happens is this:
|
||
</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para>
|
||
Your own draw handler gets run. It paints something
|
||
on the window or the event box.
|
||
</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>
|
||
The widget's default draw handler gets run. If
|
||
<function>gtk_widget_set_app_paintable()</function> has not
|
||
been called to turn off widget drawing (this
|
||
is the default), <emphasis>your drawing will be
|
||
overwritten</emphasis>. An app paintable widget will not
|
||
draw its default contents however and preserve your drawing
|
||
instead.
|
||
</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>
|
||
The draw handler for the parent class gets run.
|
||
Since both <classname>GtkWindow</classname> and
|
||
<classname>GtkEventBox</classname> are descendants of
|
||
<classname>GtkContainer</classname>, their no-window
|
||
children will be asked to draw themselves recursively, as
|
||
described in <xref linkend="hierarchical-drawing"/>.
|
||
</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
|
||
<formalpara>
|
||
<title>Summary of app-paintable widgets</title>
|
||
|
||
<para>
|
||
Call <function>gtk_widget_set_app_paintable()</function> if you
|
||
intend to draw your own content directly on a
|
||
<classname>GtkWindow</classname> and
|
||
<classname>GtkEventBox</classname>. You seldom need to draw
|
||
on top of other widgets, and
|
||
<classname>GtkDrawingArea</classname> ignores this flag, as it
|
||
<emphasis>is</emphasis> intended to be drawn on.
|
||
</para>
|
||
</formalpara>
|
||
</refsect1>
|
||
</refentry>
|
||
|
||
<!--
|
||
Local variables:
|
||
mode: xml
|
||
sgml-parent-document: ("gtk-docs.sgml" "book" "part" "refentry")
|
||
End:
|
||
-->
|