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 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
|
<html>
<head>
<LINK REL="stylesheet" HREF="styles.css" TYPE="text/css">
<title>FOX-Toolkit</title>
<!-- HTML Copyright 2001 Paul Laufer -->
</head>
<body bgcolor=#ffffff link=#990033 vlink=#4a73ad alink=#ed004f text=#000000>
<!--header-->
<table align=center border=0 cellpadding=0 cellspacing=0 width=100% >
<tr><td bgcolor=silver colspan=5 align=right height=50><img src=art/oul_grey.gif align=left valign=top width=8 height=8><img src=art/foxlogo.png valign=bottom alt="FOX Toolkit" height=50 width=500 border=0 ></td>
<td bgcolor=#557faa valign=top align=right><img src=art/our.gif width=8 height=8></td>
</tr>
<!-- end header -->
<tr>
<td bgcolor=#557faa colspan=2 valign=top align=left> </td>
<td bgcolor=#557faa colspan=3><font color=#ffffff size=+1><center>
<!-- Page Title -->
Documentation: Automatic GUI Updating
<!-- End Page Title -->
</center></font></td>
<td bgcolor=#557faa valign=top align=right> </td>
</tr>
<tr>
<td bgcolor=#557faa colspan=2> </td>
<td bgcolor=#ffffff valign=top align=left><img src=art/iul.gif width=8 height=8></td>
<td bgcolor=#ffffff> </td>
<td bgcolor=#ffffff valign=top align=right><img src=art/iur.gif width=8 height=8></td>
<td bgcolor=#557faa width=15> </td>
</tr>
<tr>
<td width=8 bgcolor=#557faa> </td>
<td valign=top bgcolor=#557faa link=#ffffff width=150>
<!-- start navbar content -->
<a href=fox.html><font color=#ffffff>Home</font></a><br>
<a href=news.html><font color=#ffffff>News</font></a><br>
<a href=download.html><font color=#ffffff>Download</font></a><br>
<a href=goals.html><font color=#ffffff>Goals & Approach</font></a><br>
<a href=doc.html><font color=#ffffff>Documentation</font></a><br>
<a href=faq.html><font color=#ffffff>FAQ</font></a><br>
<a href=rex.html><font color=#ffffff>FXRex</font></a><br>
<a href=screenshots.html><font color=#ffffff>Screenshots</font></a><br>
<br>
<a href=adie.html><font color=#ffffff>Adie</font></a><br>
<a href=pathfinder.html><font color=#ffffff>PathFinder</font></a><br>
<a href=calc.html><font color=#ffffff>FOX Calculator</font></a><br>
<br>
<a href=projects.html><font color=#ffffff>Projects</font></a><br>
<br>
<a href='http://fxpy.sourceforge.net'><font color=#ffffff>FXPy</font></a><br>
<a href='http://fxruby.sourceforge.net'><font color=#ffffff>FXRuby</font></a><br>
<a href='http://eiffelfox.sourceforge.net'><font color=#ffffff>EiffelFox</font></a><br>
<a href='http://eevolved.com/foxhole/'><font color=#ffffff>The FOX Hole</font></a><br>
<a href='http://takahr.dhis.portside.net/cgi-bin/rwiki.cgi?cmd=view;name=FOX+FAQ'><font color=#ffffff>Japanese Docs</font></a><br>
<br>
<center>
<a href="http://www.eff.org/br"><img SRC="art/freespeach.gif" border=0></a>
<p>
<a href="http://www.slashdot.org"><img SRC="art/slingerzbutton1.gif" border=0></a>
</center>
<!-- end navbar content -->
</td>
<td bgcolor=#ffffff> </td>
<td valign=top>
<!-- start main window content -->
<center><img src='art/foxstart.png'>
<BR><B>Documentation: Automatic GUI Updating</B>
</center>
<p>
<b>What is Automatic GUI Updating?</b>
<hr>
<P>Traditionally, Controls such as Buttons and Sliders have been used to
provide notifications to the application code that a certain command is
to be performed, or some value has changed. For example, if one moves
a Slider a little bit, a notification message informs the application that
a new value is available; at the same time, the Slider's position will
give a <I>visual cue</I> about the value being transmitted to the application
code.
<P>But suppose one were to add a TextField to control the same variable.
If we type into the TextField, the new value will be transmitted to the
application just fine, but since the slider was not moved, the visual position
of the slider now no longer represents the value that the program is actually
working with.
<P>Traditionally, GUI developers have solved this problem roughly like
this:
<BR>
<UL>
<LI>
When receiving a message from the Slider Control, the program accepts the
new value, and then turns around and sets the new value in the TextField.</LI>
<BR>
<LI>
When receiving a message from the TextField, the program similarly accepts
the new value from the TextField and now updates the Slider.</LI>
</UL>
The above pattern seems eminently reasonable. Up till now, this was
how it was done [although certain toolkits didn't make even
this simple approach very easy!].
<P>However, now imagine a large program being implemented by several developers,
and a graphical user interface that has hundreds, perhaps even thousands
of Controls. All these controls manipulate a bunch of data
in the application program.
<BR>We can see the problem with this approach: how is developer <I>A</I>
supposed to know that the Dialog panel being designed by developer <I>B</I>
is to reflect the new values for the variables being modified by developer
<I>A</I>'s
code? Clearly, this problem can grow into a combinatorial nighmare.
<P>The GUI Updating facility implemented in FOX can largely eliminate this
problem. In a nutshell, the idea is that in FOX, the GUI Controls
are <B><I>bi-directional</I></B>:
<BR>
<UL>
<LI>
A Control can <B><I>notify</I></B> the application program
to inform it that the Control has been given a new value by the user. One
could call this a ``<B><I>push</I></B>.''</LI>
<BR>
<LI>
A Control can also <B><I>interrogate</I></B> the application about the
current state of the application and its data structures, so that the Control
may properly reflect this graphically. This one could call a ``<B><I>pull</I></B>.''</LI>
</UL>
Why is this good? Because it <B><I>compartmentalizes</I></B> large
scale GUI design, and <B><I>simplifies coding. </I></B>In the
above example, developer <I>B</I> wouldn't even need to <I>talk</I> to
developer <I>A</I>. He would simply implement [for each Control in
his Dialog panel] not only the ``command'' messages which notify
his routines about user inputs, but also the ``update'' messages by which
the Controls ask for the values they should be displaying.
<P>Coding complexity is reduced because instead of N command messages each
updating M Controls [for a total of N x M combinations], the developer
would only have to implement N command messages and M update messages [just
N + M combinations]. Complexity is also reduced because <I>command
handlers</I> just perform their operation on the application data structures,
and <I>update handlers</I> simply update the corresponding controls given
the state in which they found these data structures.
<P>A common use of the GUI Updating mechanism is the <B><I>disabling</I></B>
or ``<B><I>graying-out</I></B>'' of controls when they're not applicable
under certain conditions. For example, a <B><FONT FACE="Arial,Helvetica">Save
File </FONT></B>Button may be made unavailable when the application hasn't
loaded a file yet. Other common uses include hiding or showing
of Controls in the GUI based on context or which ``mode'' an application
is in.
<p>
<p>
<b>GUI Updating Example</b>
<hr>
An example of GUI Updating is given in the ScribbleApp program.
The ScribbleApp program allows lines to be drawn in some canvas.
To clear the canvas, the users invokes the <B><FONT FACE="Arial,Helvetica"><U>C</U>lear</FONT></B>
Button. The <B><FONT FACE="Arial,Helvetica"><U>C</U>lear </FONT></B>Button
is to be available only when something has been scribbled; otherwise, it
is to be grayed out or desensitized. One can accomplish this in FOX
as follows:
<BR>
<CENTER><TABLE BORDER CELLSPACING=0 COLS=1 WIDTH="90%" BGCOLOR="#FFF8E1" NOSAVE >
<TR>
<TD>
<PRE><TT><FONT COLOR="#000000"></FONT></TT></PRE>
<PRE><TT><FONT COLOR="#000000">// Construct the Clear Button</FONT></TT></PRE>
<PRE><TT><FONT COLOR="#000000">new FXButton(buttonFrame,"&Clear",NULL,</FONT></TT></PRE>
<PRE><TT><FONT COLOR="#000000"> app,ScribbleApp::ID_CLEAR,</FONT></TT></PRE>
<PRE><TT><FONT COLOR="#000000"> FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,</FONT></TT></PRE>
<PRE><TT><FONT COLOR="#000000"> 0,0,0,0,10,10,5,5);</FONT></TT></PRE>
</TD>
</TR>
</TABLE></CENTER>
<P>This constructs a new Button object, which will send a message ID_CLEAR
to the application object (app).
<P>In the application object, we catch <B><I>two</I></B> message types
from the Clear button:
<BR>
<CENTER><TABLE BORDER CELLSPACING=0 COLS=1 WIDTH="90%" BGCOLOR="#FFF8E1" NOSAVE >
<TR NOSAVE>
<TD NOSAVE>
<PRE><FONT FACE="Courier New,Courier"></FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">// Message Map for the Scribble App class</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">FXDEFMAP(ScribbleApp) ScribbleAppMap[]={</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXMAPFUNC(SEL_LEFTBUTTONPRESS, ScribbleApp::ID_MOUSE, ScribbleApp::onMouseDown),</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXMAPFUNC(SEL_LEFTBUTTONRELEASE, ScribbleApp::ID_MOUSE, ScribbleApp::onMouseUp),</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXMAPFUNC(SEL_MOTION, ScribbleApp::ID_MOUSE, ScribbleApp::onMouseMove),</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"><FONT COLOR="#000000"> FXMAPFUNC(</FONT><FONT COLOR="#FF6666">SEL_COMMAND</FONT><FONT COLOR="#000000">, ScribbleApp::</FONT><FONT COLOR="#FF6666">ID_CLEAR</FONT><FONT COLOR="#000000">, ScribbleApp::</FONT><FONT COLOR="#FF6666">onCmdClear</FONT><FONT COLOR="#000000">),</FONT></FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"><FONT COLOR="#000000"> FXMAPFUNC(</FONT><FONT COLOR="#FF6666">SEL_UPDATE</FONT><FONT COLOR="#000000">, ScribbleApp::</FONT><FONT COLOR="#FF6666">ID_CLEAR</FONT><FONT COLOR="#000000">, ScribbleApp::</FONT><FONT COLOR="#FF6666">onUpdClear</FONT><FONT COLOR="#000000">),</FONT></FONT></PRE>
<FONT FACE="Courier New,Courier"> };</FONT>
<BR><FONT FACE="Courier New,Courier"></FONT> </TD>
</TR>
</TABLE></CENTER>
<P>The SEL_COMMAND message indicates that the Clear button has been pressed;
its action will be to clear the drawing canvas:
<BR>
<BR>
<CENTER><TABLE BORDER CELLSPACING=0 COLS=1 WIDTH="90%" BGCOLOR="#FFF8E1" NOSAVE >
<TR>
<TD>
<PRE><FONT FACE="Courier New,Courier"></FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">// Handle the clear message</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">long ScribbleApp::<FONT COLOR="#FF6666">onCmdClear</FONT>(FXObject*,FXSelector,void*){</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXDC *dc=canvas->DC();
// Erase the canvas
dc->begin(canvas);
dc->setForeground(FXRGB(255,255,255));
dc->fillRectangle(0,0,canvas->getWidth(),canvas->getHeight());
dc->end();</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> dirty=0;</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> return 1;</FONT></PRE>
<FONT FACE="Courier New,Courier"> }</FONT>
<BR><FONT FACE="Courier New,Courier"></FONT> </TD>
</TR>
</TABLE></CENTER>
<P>The SEL_UPDATE message is sent when the Clear Button updates itself.
The GUI-Update handler determines whether the Clear Button should be sensitized
or not depending on whether any drawing has taken place in the canvas;
this is kept track of through a flag variable <B><FONT FACE="Courier New,Courier">dirty</FONT></B>:
<BR>
<CENTER><TABLE BORDER CELLSPACING=0 COLS=1 WIDTH="90%" BGCOLOR="#FFF8E1" NOSAVE >
<TR NOSAVE>
<TD NOSAVE>
<PRE><FONT FACE="Courier New,Courier"></FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">// Handle the GUI Update from the Clear button</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">long ScribbleApp::<FONT COLOR="#FF6666">onUpdClear</FONT>(FXObject* sender,FXSelector,void*){</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXButton* button=(FXButton*)sender;</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> // Button is available when canvas is dirty only</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> dirty ? button->enable() : button->disable();</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> return 1;</FONT></PRE>
<FONT FACE="Courier New,Courier"> }</FONT></TD>
</TR>
</TABLE></CENTER>
<P>Note that in this case we know the origin of the message (the <I>sender</I>)
to be of the type FXButton, so we can simply <I>cast</I> the sender object
down to the appropriate type.
<BR>In general however, we may not always know [the only thing we know
is that the sender is of type FXObject]. In that a case, the GUI
Update handler should <I>send a message back
</I>to the sender.
We can safely do this since all FOX objects are derived from FXObject,
and FXObject's can be sent messages. This leads to the following
code:
<BR>
<CENTER><TABLE BORDER CELLSPACING=0 COLS=1 WIDTH="90%" BGCOLOR="#FFF8E1" NOSAVE >
<TR>
<TD>
<PRE><FONT FACE="Courier New,Courier"></FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">// Update sender</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier">long ScribbleApp::<FONT COLOR="#FF6666">onUpdClear</FONT>(FXObject* sender,FXSelector,void*){</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> FXuint msg=dirty ? ID_ENABLE : ID_DISABLE;</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> sender->handle(this,MKUINT(msg,SEL_COMMAND),NULL);</FONT></PRE>
<PRE><FONT FACE="Courier New,Courier"> return 1;</FONT></PRE>
<FONT FACE="Courier New,Courier"> }</FONT></TD>
</TR>
</TABLE></CENTER>
<P>Many FOX Widgets understand the ID_ENABLE, and ID_DISABLE messages;
if however, a message is sent to a sender that doesn't, nothing bad will
happen as no message handler will be associated with the message.
<p>
<p>
<b>When is GUI Updating Performed?</b>
<hr>
There are two sides to this question:- under what conditions can a Control
be updated, and when is the updating actually done.
<P>With regard to the first part, a Control can be <I>updated</I> whenever
it is <I>not currently being manipulated</I> by the user. In other
words, a Control is normally in a ``<B><I>pull</I></B>'' mode, in that
it tries to interrogate the application to determine the value it needs
to display graphically. As soon as the user starts to manipulate
the Control, it switches to a ``<B><I>push</I></B>'' mode, in which the
Control becomes an <I>input</I> of new values to the application.
<P>As far as the second part of the question goes, the FOX library performs
the GUI Updates when there isn't much else to do, that is to say, the system
is about to block and wait for events from the user. Furthermore,
the GUI Updates are only performed when previous events have been dispatched
and handled.
<P>This is why it is important to return <B>1</B> or <B>0</B> in your message
handlers.:
<BR>
<UL>
<LI>
If you return <B>1</B>, the message is considered handled, and a GUI Update
pass will be performed by FOX.</LI>
<LI>
If you return <B>0</B>, the message is considered unhandled, and since
unhandled messages should not have changed the application's state, no
GUI Update is performed.</LI>
</UL>
For increased efficiency, the system checks for new events between each
GUI Update message, to prevent ``event-deafness'' for extended periods
of time. Even so, it is important to restrict your GUI Update message
handlers to small routines, and to not perform any major computations in
GUI Update message handlers.
<p>
<p>
<b>Automatic Gray Out or Hide</b>
<hr>
FOX also has the option to automatically gray out or hide certain Widgets.
Both options work very similar, and differ only visually. When automatic
grayout is in effect, the Widget will be automatically grayed out (disabled,
or desensitized to user inputs), when one of the following is true:
<BR>
<OL>
<LI>
The Widget's target is NULL.</LI>
<LI>
The Widget's target does not handle the Widget's SEL_UPDATE message.</LI>
</OL>
Why is this useful? If a Widget's target is an object that performs
some sort of message <B><I>delegation</I></B> (for example, FXMDIClient
and FXMDIChild do this), then the ability to handle a certain SEL_UPDATE
message may depend on the delegate object that is in effect at the time
the update message is sent. If the particular delegate in effect
does not handle the update message, there is no handler to make a Widget
assume the correct state.
<P>With automatic gray out, however, the absence of a handler for the SEL_UPDATE
message can be turned into an action to gray out the Widget instead.
This will keep the GUI consistent even in the absence of update message
handlers.
<P>The automatic gray out technique is of particular significance when
using MDI (Multiple Document Interface) widgets as both FXMDIClient and
FXMDIChild perform message delegation. Messages from the pulldown
menus are typically sent to the FXMDIClient, and then subsequently forwarded
by the FXMDIClient to the active FXMDIChild (Sometimes, there are no FXMDIChild
windows, and the message can not be forwarded and then the message handler
returns 0).
<P>As automatic gray out of Widgets will cause a gray out if no handler
for the SEL_UPDATE message is found, it is imperative that the SEL_UPDATE
must always be handled when the Widget should be sensitive. The update
message handler does not necessarily have to do anything, it just needs
to return 1.
<P>To handle this common situation, FXWindow defines just such a message
handler for you: <B>FXWindow::onUpdYes()<TT> </TT></B>will do nothing
but show and sensitize the Widget that is the sender of the message, and
return a 1. You can simply append this to your message map as in:
<BR>
<BLOCKQUOTE><TT>FXMAPFUNC(SEL_UPDATE,ID_MYMENU,<FONT COLOR="#000000">FXWindow::onUpdYes</FONT>)</TT></BLOCKQUOTE>
That will take care of it. Of course if the update message should
do something, you should write your own handler and make it return 1.
<p>
<p>
<b>Delayed Repaint/Layout</b>
<hr>Procrastination is Good Thing [even though my parents always told me
otherwise :-)]. The motto is <I>the fastest thing you can do is nothing
at all.</I> Indeed, my lowly Pentium Classic can do ``<B><I>nothing</I></B>
''as fast as the fastest supercomputer!
<P>All this seems pretty obvious, but what does it mean for GUI systems?
It means we should try to <B>avoid</B> doing the two most expensive things
that GUI systems have to do:
<BR>
<OL>
<LI>
<B>Drawing onto the screen</B>. Drawing generates X protocol,
and causes context switching between client and display server, and is
therefore very expensive.</LI>
<BR>
<LI>
<B>Layout of widgets</B>. Layout involves recursing up and down the
Widget Tree, and computing a bunch of stuff such as sizes of List contents,
etc. In addition, layout also causes lots of stuff to be redrawn!</LI>
</OL>
So it is clear that these two are to be avoided at all cost. Here's
how FOX does this:
<BR>
<OL>
<LI>
Expose or repaint events are stacked up until the system has processed
all user input events. Moreover, exposed regions are compounded so
that in most cases only one repaint is necessary.</LI>
<BR>
<LI>
Layout is performed during the GUI Update phase; in other words, layout
is delayed similarly to repainting so that only a single big layout remains
to be done at the end [I believe FOX is unique in this; having scrutinized
many systems, I have not found any that incorporate this feature; I think
it is therefore safe to say that I invented this...].</LI>
<BR>
<LI>
Size-caching is performed by certain [complex] Widgets to avoid potentially
expensive size computations; for example, resizing a ScrollWindow does
not necessarily change the size of the content.</LI>
</OL>
So how well does this all work? <B><I>It Really Rocks</I></B>!
The <B><I>delayed painting</I></B> is important, as it prevents stacking
up huge piles of expose events when for example dragging [solid-, or opaque-dragging]
windows over FOX applications. By NOT doing the unnecessary work,
the system actually catches up more quickly, and never falls behind more
than one repaint.
<P>The <B><I>delayed layout</I></B> is responsible for the extremely fast
startup times of FOX applications. As hundreds of Widgets are being
added during construction of an application's GUI, recalculating layout
after each addition really kills startup performance.
<BR>Delayed layout benefits performance each time many GUI Widgets are
added or removed, or if layout hints are changed with widespread effects.
It also makes opaque resizing [supported by a few X Window Managers] quite
fast.
<P>Several advanced GUI systems have added special freeze/thaw semantics
to temporarily suspend layout calculations. In FOX, this feature
is automatic, and application-wide in effect.
<P>As the delayed layout is performed as part of the GUI Update process,
GUI Update message handlers should avoid making changes to Controls that
could cause layout computation, as these changes will not be handled till
the next GUI Update cycle.
<P>One more speed-feature related to use of Brian Paul's excellent Mesa
graphics library. With Mesa, double buffering is implemented using
a software backbuffer. Thus, after having drawn this back buffer,
an expose event can repaint the dirty area simply by blitting back this
back buffer, instead of re-rendering OpenGL commands [which can be very
expensive indeed!].
<P>Hence, FOX distinguishes between ``synthetic'' repaint events and ``non-synthetic''
ones. Synthetic expose events are those that originate from within
the application. When receiving a synthetic expose event, the OpenGL
will have to be regenerated; for non-synthetic expose events, the back
buffer can be blitted back.
<P>
<!-- end main window content -->
</td>
<td bgcolor=#ffffff> </td>
<td bgcolor=#557faa width=15> </td>
</tr>
<tr>
<td colspan=2 bgcolor="#557faa" align=center>
</td>
<td bgcolor=#ffffff valign=bottom align=left><img src=art/ill.gif width=8 height=8></td>
<td bgcolor=#ffffff> </td>
<td bgcolor=#ffffff valign=bottom align=right><img src=art/ilr.gif width=8 height=8></td>
<td bgcolor=#557faa width=15> </td>
</tr>
<tr>
<td valign=bottom align=left bgcolor=#557faa><img src=art/oll.gif width=8 height=8></td>
<td colspan=4 bgcolor=#557faa> </td>
<td valign=bottom align=right bgcolor=#557faa><img src=art/olr.gif width=8 height=8></td>
</tr>
</table>
<address>Copyright 1997-2002 <a href=mailto:jeroen@fox-toolkit.org>Jeroen van der Zijp</a></address>
<!-- Created: Mon Apr 10 11:20:32 CEST 2000 -->
<!-- hhmts start -->
<!-- hhmts end -->
</body>
</html>
|