Professional GEM - Part II - Windows: Difference between revisions

From Atari Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
  +
{{Professional GEM}}
<pre>
 
   
  +
==Excelsior==
  +
  +
In this installment, we continue the exploration of GEM's window manager by finding out how to process the messages received by an application when it has a window defined on the screen.
   
  +
Also, beginning with this column, sample [[C]] code demonstrating the techniques discussed will be available on SIG*ATARI in DL5. This will allow you to download the code without interference by the CIS text-formatter used by ANTIC ONLINE output.
Professional GEM 7
 
   
  +
The file for this column is GEMCL2.XMO. All references to non-GEM routines in this column refer to this file. Please note that these files will not contain entire programs. Instead, they consist of small pieces of utility code which you may copy and modify in your own programs.
   
P�PA�AR�RT�T -�- I�II�I
 
   
  +
==Redrawing windows==
W�Wi�in�nd�do�ow�ws�s
 
   
  +
One of the most misunderstood parts of GEM is the correct method for drawing within a window. Most requests for redrawing are generated by the GEM system, and arrive as messages (read with ''evntmulti'') which contain the handle of the window, and the
  +
screen rectangle which is "dirty" and needs to be redrawn.
   
  +
Screen areas may become dirty as a result of windows being closed, sized down, or moved, thus "exposing" an area underneath. The completion of a dialog, or closing of a desk accessory may also free up a screen area which needs to be redrawn. When GEM detects the presence of a dirty rectangle, it checks its list of open windows, and sends the application a redraw message for each of its windows which intersects the dirty area.
   
E�EX�XC�CE�EL�LS�SI�IO�OR�R
 
   
  +
==Caveat Emptor==
In this installment, we continue the exploration of GEM's
 
window manager by finding out how to process the messages
 
received by an application when it has a window defined on the
 
screen.
 
   
  +
GEM does not "clip" the rectangle which it sends to the application; that is, the rectangle may not lie entirely within the portion of the window which is exposed on the screen. It is the job of the application to determine in what portion of the rectangle it may safely draw. This is done by examining the "rectangle list" associated with the window.
Also, beginning with this column, sample C code
 
demonstrating the techniques discussed will be available on
 
SIG*ATARI in DL5. This will allow you to download the code
 
without interference by the CIS text-formatter used by ANTIC
 
ONLINE output.
 
   
  +
A rectangle list is maintained by GEM for each active window. It contains the portions of the window's interior which are exposed, i.e., topmost, on the screen and within which the app may draw.
The file for this column is GEMCL2.XMO. All references to
 
non-GEM routines in this column refer to this file. Please note
 
that these files will not contain entire programs. Instead,
 
they consist of small pieces of utility code which you may copy
 
and modify in your own programs.
 
   
  +
Let's consider an example to make this clear. Suppose an app has opened two windows, and there are no desk accessory windows open. The window which is topmost will always have only one rectangle in its list. If the two are separate on the screen, then the second window will also have one rectangle. If they overlap, then the top window will "break" the rectangle of the bottom one. If the overlap is at a corner, two rectangles will be generated for the bottom window. If the overlap is on a side only, then three rectangles are required to cover the exposed portion of the bottom window. Finally, if the first window is entirely within the second, it requires four rectangles in the list to tile the second window.
   
  +
Try working out a few rectangle examples with pencil and paper to get the feel of it. You will see that the possible combinations with more than two windows are enormous. This, by the way, is the reason that GEM does not send one message for each rectangle on the list: With multiple windows, the number of messages generated would quickly fill up the application's message queue.
R�RE�ED�DR�RA�AW�WI�IN�NG�G W�WI�IN�ND�DO�OW�WS�S
 
   
  +
Finally, note that every app '''must''' use this method, even if it only uses a single window, because there may be desk accessories with their own windows in the system at the same time. If you do not use the rectangle lists, you may overwrite an accessory's window.
One of the most misunderstood parts of GEM is the correct
 
method for drawing within a window. Most requests for redrawing
 
are generated by the GEM system, and arrive as messages (read
 
with evntmulti) which contain the handle of the window, and the
 
screen rectangle which is "dirty" and needs to be redrawn.
 
   
Screen areas may become dirty as a result of windows being
 
closed, sized down, or moved, thus "exposing" an area
 
underneath. The completion of a dialog, or closing of a desk
 
accessory may also free up a screen area which needs to be
 
redrawn. When GEM detects the presence of a dirty rectangle,
 
it checks its list of open windows, and sends the application a
 
redraw message for each of its windows which intersects the
 
dirty area.
 
   
  +
==Into the bits==
   
  +
First, we should note that the message type for a redraw request is <tt>WMREDRAW</tt>, which is stored in <tt>msg[0]</tt>, the first location of the message returned by <tt>evntmulti</tt>. The window handle is stored in <tt>msg[3]</tt>. These locations are the same for all of the message types being discuss. The rectangle which needs to be redrawn is stored in <tt>msg[4]</tt> through <tt>msg[7]</tt>.
C�CA�AV�VE�EA�AT�T E�EM�MP�PT�TO�OR�R
 
   
  +
Now let's examine the sample redraw code in more detail. The redraw loop is bracketed with mouse off and mouse on calls. If you forget to do this, the mouse pointer will be over-written if it is within the window and the next movement of the mouse will leave a rectangular blotch on the screen as a piece of the "old" screen is incorrectly restored.
GEM does not "clip" the rectangle which it sends to the
 
application; that is, the rectangle may not lie entirely within
 
the portion of the window which is exposed on the screen. It
 
is the job of the application to determine in what portion of
 
the rectangle it may safely draw. This is done by examining
 
the "rectangle list" associated with the window.
 
   
  +
The other necessary step is to set the window update flag. This prevents the menu manager from dropping a menu on top of the screen portion being redrawn. You must release this flag at the end of the redraw, or the you will be unable to use any menus afterwards.
   
  +
The window rectangles are retrieved using a get-first, get-next scheme which will be familiar if you have used the GEM DOS or PC-DOS wildcard file calls. The end of the rectangle list has been reached when both the width and height returned are zero. Since some part of a window might be off-screen (unless you have clamped its position - see below), the retrieved rectangle is intersected with the desktop's area, and then with the screen area for which a redraw was requested.
 
   
  +
Now you have the particular area of the screen in which it is legal to draw. Unless there is only one window in your application, you will have to test the handle in the redraw request to figure out what to put in the rectangle.
   
  +
Depending on the app, you may be drawing an AES object tree, or executing VDI calls, or some combination of the two. In the AES case, the computed rectangle is used to specify the bounds of the objcdraw. For VDI work, the rectangle is used to set the clipping area before executing the VDI calls.
Professional GEM Part II 8
 
   
   
  +
==A small confession==
   
  +
At the beginning of this discussion, I deliberately omitted one class of redraws: those initiated by the application itself. In some cases a part of the screen must be redrawn immediately to give feedback to the user following a keystroke, button, or mouse action. In these cases, the application could call doredraw directly,without waiting for a message.
A rectangle list is maintained by GEM for each active
 
window. It contains the portions of the window's interior which
 
are exposed, i.e., topmost, on the screen and within which the
 
app may draw.
 
   
  +
The only time you can bypass <tt>doredraw</tt>, and draw without walking the list, is when you can be sure that the target window is on top, and that the figure being drawn is entirely contained within it.
Let's consider an example to make this clear. Suppose an
 
app has opened two windows, and there are no desk accessory
 
windows open. The window which is topmost will always have only
 
one rectangle in its list. If the two are separate on the
 
screen, then the second window will also have one rectangle. If
 
they overlap, then the top window will "break" the rectangle of
 
the bottom one. If the overlap is at a corner, two rectangles
 
will be generated for the bottom window. If the overlap is on a
 
side only, then three rectangles are required to cover the
 
exposed portion of the bottom window. Finally, if the first
 
window is entirely within the second, it requires four
 
rectangles in the list to tile the second window.
 
   
  +
In many cases, however, an application initiated redraw happens because of a computed change, for instance, a spreadsheet update, and its timing is not crucial. In this instance, you may wish to have the app send ITSELF a redraw request.
Try working out a few rectangle examples with pencil and
 
paper to get the feel of it. You will see that the possible
 
combinations with more than two windows are enormous. This, by
 
the way, is the reason that GEM does not send one message for
 
each rectangle on the list: With multiple windows, the number
 
of messages generated would quickly fill up the application's
 
message queue.
 
   
  +
The main advantage of this approach is that the AES is smart enough to see if there is already a redraw request for the same window in the queue, and, if so, to merge the requests by doing a union of their rectangles. In this fashion, the "blinky" appearance of multiple redraws is avoided, without the need to include logic for merging redraws within the program. A utility routine for sending the "self-redraw" is included in the download for this article.
Finally, note that every app MUST use this method, even if
 
it only uses a single window, because there may be desk
 
accessories with their own windows in the system at the same
 
time. If you do not use the rectangle lists, you may overwrite
 
an accessory's window.
 
   
   
  +
==Window control requests==
I�IN�NT�TO�O T�TH�HE�E B�BI�IT�TS�S
 
   
  +
An application is notified by the [[AES]], via the message system, when the user manipulates one of the window control points. Remember that you must have specified each control point when the window was created, or will not receive the associated control message.
First, we should note that the message type for a redraw
 
request is WMREDRAW, which is stored in msg[0], the first
 
location of the message returned by evntmulti. The window
 
handle is stored in msg[3]. These locations are the same for
 
all of the message types being discuss. The rectangle which
 
needs to be redrawn is stored in msg[4] through msg[7].
 
   
  +
The most important thing to understand about window control is that the change which the user requested does not take place until the application forwards it to the AES. While this makes for a little extra work, it gives the program a chance to intervene and validate or modify the request to suit.
Now let's examine the sample redraw code in more detail.
 
The redraw loop is bracketed with mouse off and mouse on calls.
 
If you forget to do this, the mouse pointer will be
 
over-written if it is within the window and the next movement
 
of the mouse will leave a rectangular blotch on the screen as a
 
piece of the "old" screen is incorrectly restored.
 
   
  +
A second thing to keep in mind is that not all window updates cause a redraw request to be generated for the window, because the AES attempts to save time with raster moves on the screen. Now let's look at each window control request in detail. The message code for a window move is <tt>WMMOVED</tt>. If you are willing to accept any such request, just do:
The other necessary step is to set the window update flag.
 
This prevents the menu manager from dropping a menu on top of
 
   
  +
<pre>
  +
windset(wh, WFCXYWH, msg[4], msg[5], msg[6], msg[7]);
  +
</pre>
   
  +
(Remember that <tt>wh</tt>, the window handle, is always in <tt>msg[3])</tt>.
 
   
  +
The AES will not request a redraw of the window following this call, unless the window is being moved from a location which is partially "off-screen". Instead, it will do a "blit" (raster copy) of the window and its contents to the new location without intervention by the app.
   
  +
There are two constraints which you may often wish to apply to the user's move request. The first is to force the new location to lie entirely within the desktop, rather than partially off-screen. You can do this with the rcconstrain utility by executing:
Professional GEM Part II 9
 
   
  +
<pre>
  +
rcconstrain(&full, &msg[4]);
  +
</pre>
   
  +
before making the windset call. (Full is assumed to contain the desktop dimensions.)
the screen portion being redrawn. You must release this flag at
 
the end of the redraw, or the you will be unable to use any
 
menus afterwards.
 
   
  +
The second common constraint is to "snap" the x-dimension location of the new location to a word boundary. This operation will speed up GEM's "blit" because no shifting or masking will need to be done when moving the window. To perform this operation, use <tt>align()</tt> before the windset call:
The window rectangles are retrieved using a get-first,
 
get-next scheme which will be familiar if you have used the GEM
 
DOS or PC-DOS wildcard file calls. The end of the rectangle
 
list has been reached when both the width and height returned
 
are zero. Since some part of a window might be off-screen
 
(unless you have clamped its position - see below), the
 
retrieved rectangle is intersected with the desktop's area, and
 
then with the screen area for which a redraw was requested.
 
   
  +
<pre>
Now you have the particular area of the screen in which it
 
  +
msg[4] = align(msg[4], 16);
is legal to draw. Unless there is only one window in your
 
  +
</pre>
application, you will have to test the handle in the redraw
 
request to figure out what to put in the rectangle.
 
   
  +
The message code for a window size request is WMSIZED. Again, if you are willing to accept any request, you can just "turn it around" with the same windset call as given for <tt>WMMOVED</tt>.
Depending on the app, you may be drawing an AES object
 
tree, or executing VDI calls, or some combination of the two.
 
In the AES case, the computed rectangle is used to specify the
 
bounds of the objcdraw. For VDI work, the rectangle is used to
 
set the clipping area before executing the VDI calls.
 
   
  +
Actually, GEM enforces a couple of constraints on sizing. First, the window may not be sized off screen. Second, there is a minimum window size which is dependent on the window components specified when it was created. This prevents features like scroll arrows from being squeezed into oblivion. The most common application constraint on sizing is to snap the size to horizontal words (as above) and/or vertical character lines. In the latter case, the vertical dimension of the output font is used with align().
   
  +
Also, be aware that the size message which you receive specifies the EXTERNAL dimensions of the window. To assure an "even" size for the INTERNAL dimensions, you must make a windcalc call to compute them, use align() on the computed values, back out the corresponding external dimensions with the reverse windcalc, and then make the windset call with this set of values.
A�A S�SM�MA�AL�LL�L C�CO�ON�NF�FE�ES�SS�SI�IO�ON�N
 
   
  +
A window resize will only cause a redraw request for the window if the size is being increased in at least one dimension. This is satisfactory for most applications, but if you must "reshuffle" the window after a size-down, you should send yourself a redraw (as described above) after you make the windset call. This will guarantee that the display is updated correctly. Also note that the sizing or movement of one window may cause redraw requests to be generated for other windows which are uncovered by the change.
At the beginning of this discussion, I deliberately
 
omitted one class of redraws: those initiated by the
 
application itself. In some cases a part of the screen must be
 
redrawn immediately to give feedback to the user following a
 
keystroke, button, or mouse action. In these cases, the
 
application could call doredraw directly, without waiting for a
 
message.
 
   
  +
The window full request, with code WMFULLED, is actually a toggle. If the window is already at its full size (as specified in the windcreate), then this is a request to shrink to its previous size. If the window is currently small, then the request is to grow to full size.
The only time you can bypass doredraw, and draw without
 
walking the rectangle list, is when you can be sure that the
 
target window is on top, and that the figure being drawn is
 
entirely contained within it.
 
   
  +
Since the AES records the current, previous, and maximum window size, you can use windget calls to determine which situation pertains. The hndlfull utility in the down-load (modified from Doodle), shows how to do this.
In many cases, however, an application initiated redraw
 
happens because of a computed change, for instance, a
 
spreadsheet update, and its timing is not crucial. In this
 
instance, you may wish to have the app send ITSELF a redraw
 
request.
 
   
  +
The "zoom box" effects when changing size are optional, and can be removed to speed things up. Again, if the window's size is decreasing, no redraw is generated, so you must send yourself one if necessary. You should not have to perform any constraint or "snap" operations here, since (presumably) the full and previous sizes have had these checks applied to them already.
The main advantage of this approach is that the AES is
 
smart enough to see if there is already a redraw request for the
 
same window in the queue, and, if so, to merge the requests by
 
doing a union of their rectangles. In this fashion, the
 
"blinky" appearance of multiple redraws is avoided, without the
 
need to include logic for merging redraws within the program. A
 
   
  +
The <tt>WMCLOSED</tt> message is received when the close box is clicked. What action you perform depends on the application. If you want to remove the window, use windclose as described in the last column. In many applications, however, the close message may indicate that a file is to be saved, or a directory or editing level is to be closed. In these cases, the message is used to trigger this action before or instead of the windclose. (Folders on the Desktop are an example of this situation.)
   
  +
The <tt>WMTOPPED</tt> message indicates that the AES wants to bring the indicated window to the "top" and make it active. This happens if the user clicks within a window which is not on top, or if the currently topped window is closed by its application or desk accessory. Normally, the application should respond to this message with:
 
   
  +
<pre>
  +
windset(wh, WFTOP, 0, 0);
  +
</pre>
   
  +
and allow the process to complete.
Professional GEM Part II 10
 
   
  +
In a few instances, a window may be used in an output only mode, such as a status display, with at least one other window present for input. In this case, a WMTOPPED message for the status window may be ignored. In all other cases, you must handle the WMTOPPED message even if your application has only one window: Invocation of a desk accessory could always place another window on top. If you fail to do so, subsequent redraws for your window may not be processed correctly.
   
utility routine for sending the "self-redraw" is included in the
 
down-load for this article.
 
   
  +
==Window slider messages==
   
  +
If you specify all of the slider bar parts for your window, you may receive up to five different message types for each of the two sets of sliders. To simplify things a little, I will discuss everything in terms of the vertical (right hand side) sliders. If you are also using the horizontal sliders, the same techniques will work, just use the alternate mnemonics.
W�WI�IN�ND�DO�OW�W C�CO�ON�NT�TR�RO�OL�L R�RE�EQ�QU�UE�ES�ST�TS�S
 
   
  +
The <tt>WMVSLID</tt> message indicates that the user has dragged the slider bar within its box, indicating a new relative position within the document. Along with the window handle, this message includes the relative position between 1 and 1000 in <tt>msg[4]</tt>.
An application is notified by the AES, via the message
 
system, when the user manipulates one of the window control
 
points. Remember that you must have specified each control
 
point when the window was created, or will not receive the
 
associated control message.
 
   
  +
Recall from last column's discussion that this interval corresponds to the "freedom of movement" of the slider. If you want to accept the user's request, just make the call:
The most important thing to understand about window control
 
is that the change which the user requested does not take place
 
until the application forwards it to the AES. While this makes
 
for a little extra work, it gives the program a chance to
 
intervene and validate or modify the request to suit.
 
   
  +
<pre>
A second thing to keep in mind is that not all window
 
  +
windset(wh, WFVSLIDE, msg[4], 0, 0, 0);
updates cause a redraw request to be generated for the window,
 
  +
</pre>
because the AES attempts to save time with raster moves on the
 
screen. Now let's look at each window control request in
 
detail. The message code for a window move is WMMOVED. If you
 
are willing to accept any such request, just do:
 
   
  +
(Corresponding horizontal mnemonics are <tt>WMHSLID</tt> and <tt>WFHSLIDE</tt>).
windset(wh, WFCXYWH, msg[4], msg[5], msg[6], msg[7]);
 
   
  +
Note that this windset call will not cause a redraw message to be sent. You must update the display to reflect the new scrolled position, either by executing a redraw directly, or by sending yourself a message.
   
  +
If the document within the window has some structure, you may not wish to accept all slider positions. Instead you may want to force the scroll position to the nearest text line (for instance). Using terms defined in the last column, you may convert the slider position to "document units" with:
(Remember that wh, the window handle, is always in msg[3]).
 
   
  +
<pre>
The AES will not request a redraw of the window following this
 
  +
topwind = msg[4] * (totaldoc - seendoc) / 1000 + topdoc
call, unless the window is being moved from a location which is
 
  +
</pre>
partially "off-screen". Instead, it will do a "blit" (raster
 
copy) of the window and its contents to the new location without
 
intervention by the app.
 
   
  +
(This will probably require 32-bit arithmetic).
There are two constraints which you may often wish to apply
 
to the user's move request. The first is to force the new
 
location to lie entirely within the desktop, rather than
 
partially off-screen. You can do this with the rcconstrain
 
utility by executing:
 
   
  +
After rounding off or otherwise modifying the request, convert it back to slider units and make the WFVSLIDE request.
rcconstrain(&full, &msg[4]);
 
   
  +
The other four slider requests all share one message code: <tt>WMARROWED</tt>. They are distinguished by sub-codes stored in <tt>msg[4]</tt>: <tt>WAUPPAGE</tt>, <tt>WADNPAGE</tt>, <tt>WAUPLINE</tt>, and <tt>WADNLINE</tt>. These are produced by clicking above and below the slider, and on the up and down arrows, respectively. (I have no idea why sub-codes were used in this one instance.) The corresponding horizontal slider codes are: <tt>WALFPAGE</tt>, <tt>WARTPAGE</tt>, <tt>WALFLINE</tt>, and <tt>WARTLINE</tt>.
before making the windset call. (Full is assumed to contain the
 
desktop dimensions.)
 
   
  +
What interpretation you give to these requests will depend on the application. In the most common instance, text documents, the customary method is to change the top of window position (topwind) by one line for a <tt>WAUPLINE</tt> or <tt>WADNLINE</tt>, and by seendoc (the number of lines in the window) for a <tt>WAUPPAGE</tt> or <tt>WADNPAGE</tt>.
The second common constraint is to "snap" the x-dimension
 
location of the new location to a word boundary. This operation
 
will speed up GEM's "blit" because no shifting or masking will
 
need to be done when moving the window. To perform this
 
operation, use align() before the windset call:
 
   
  +
After making the change, compute a new slider position, and make the windset call as given above. If the document's length is not an even multiple of "lines" or "pages" you will have to be careful that incrementing or decrementing <tt>topwind</tt> does not exceed its range of freedom:
   
  +
<tt>topdoc to (topdoc + totaldoc - seendoc)</tt>.
 
   
  +
If you have such an odd size document, you will also have to make a decision on whether to violate the line positioning rule so that the slider may be put at its bottom-most position, or to follow the rule but make it impossible to get the slider to the extreme of its range.
   
Professional GEM Part II 11
 
   
  +
==A common bug==
   
  +
It is easy to forget that user clicks are not the only things that affect slider position. If the window size changes as a result of a <tt>WMSIZED</tt> or <tt>WMFULLED</tt> message, the app must also update its sliders (if they are present). This is a good reason to keep the top of window information in "document units".
   
  +
You can just redo the position calculation with the new <tt>seendoc</tt> value, and call windset. Also remember that changing the size of the underlying document (adding or deleting a bottom line, for instance) must also cause the sliders to be adjusted.
msg[4] = align(msg[4], 16);
 
   
The message code for a window size request is WMSIZED. Again,
 
if you are willing to accept any request, you can just "turn it
 
around" with the same windset call as given for WMMOVED.
 
   
  +
==Dept. of dirty tricks==
Actually, GEM enforces a couple of constraints on sizing.
 
First, the window may not be sized off screen. Second, there is
 
a minimum window size which is dependent on the window
 
components specified when it was created. This prevents
 
features like scroll arrows from being squeezed into oblivion.
 
The most common application constraint on sizing is to snap the
 
size to horizontal words (as above) and/or vertical character
 
lines. In the latter case, the vertical dimension of the output
 
font is used with align().
 
   
  +
There are two remaining window calls which are useful to advanced programmers. They require techniques which I have not yet discussed, so you may need to file them for future reference.
Also, be aware that the size message which you receive
 
specifies the EXTERNAL dimensions of the window. To assure an
 
"even" size for the INTERNAL dimensions, you must make a
 
windcalc call to compute them, use align() on the computed
 
values, back out the corresponding external dimensions with the
 
reverse windcalc, and then make the windset call with this set
 
of values.
 
   
  +
The AES maintains a quarter-screen sized buffer which is used to save the area under alerts and menu drop-downs. It is occasionally useful for the application to gain access to this buffer for its own use in saving screen areas with raster copies. To do so, use:
A window resize will only cause a redraw request for the
 
window if the size is being increased in at least one
 
dimension. This is satisfactory for most applications, but if
 
you must "reshuffle" the window after a size-down, you should
 
send yourself a redraw (as described above) after you make the
 
windset call. This will guarantee that the display is updated
 
correctly. Also note that the sizing or movement of one window
 
may cause redraw requests to be generated for other windows
 
which are uncovered by the change.
 
   
  +
<pre<
The window full request, with code WMFULLED, is actually a
 
  +
windget(0, WFSCREEN, &loaddr, &hiaddr, &lolen, &hilen);
toggle. If the window is already at its full size (as specified
 
  +
</pre>
in the windcreate), then this is a request to shrink to its
 
previous size. If the window is currently small, then the
 
request is to grow to full size.
 
   
  +
<tt>Hiaddr</tt> and <tt>loaddr</tt> are the top and bottom 16-bits (respectively) of the 32-bit address of the buffer. Hilen and lolen are the two halves of its length.
Since the AES records the current, previous, and maximum
 
window size, you can use windget calls to determine which
 
situation pertains. The hndlfull utility in the down-load
 
(modified from Doodle), shows how to do this.
 
   
  +
Due to a preculiarity of the binding you have to reassemble these pieces before using them. (The actual value of <tt>WFSCREEN</tt> is 17; this does not appear in some versions of the <tt>GEMDEFS.H</tt> file.)
The "zoom box" effects when changing size are optional, and
 
can be removed to speed things up. Again, if the window's size
 
is decreasing, no redraw is generated, so you must send yourself
 
one if necessary. You should not have to perform any
 
constraint or "snap" operations here, since (presumably) the
 
full and previous sizes have had these checks applied to them
 
   
  +
If you use this buffer, you MUST prevent menus from dropping down by using either the <tt>BEGUPDATE</tt> or <tt>BEGMCTRL</tt> <tt>windupdate</tt> calls. Failure to do so will result in your data being destroyed. Remember to use the matching <tt>windupdate</tt>: <tt>ENDUPDATE</tt> or <tt>ENDMCTRL</tt>, when you are done.
   
  +
The other useful call enables you to replace the system's desktop definition with a resource of your choosing. The call:
 
   
  +
<pre>
  +
windset(0, WFNEWDESK, tree, 0, 0);
  +
</pre>
   
  +
where tree is the 32-bit address of the object tree, will cause the AES to draw your definition instead of the usual gray or green background. Not only that, it will continue to redraw this tree with no intervention on your part.
Professional GEM Part II 12
 
   
  +
Obviously, the new definition must be carefully built to fit the desktop area exactly or garbage will be left around the edges. For the truly sophisticated, a user-defined object could be used in this tree, with the result that your application's code would be entered from the AES whenever the desktop was redrawn. This would allow you to put VDI pictures or complex images onto the desktop background.
   
already.
 
   
  +
==A sin of omission==
The WMCLOSED message is received when the close box is
 
clicked. What action you perform depends on the application. If
 
you want to remove the window, use windclose as described in the
 
last column. In many applications, however, the close message
 
may indicate that a file is to be saved, or a directory or
 
editing level is to be closed. In these cases, the message is
 
used to trigger this action before or instead of the windclose.
 
(Folders on the Desktop are an example of this situation.)
 
   
  +
In the last column, I neglected to mention that strings whose addresses are passed in the <tt>WFNAME</tt> and <tt>WFINFO</tt> <tt>windset</tt> calls must be allocated in a static data area. Since the AES remembers the addresses (not the characters), a disaster may result if the storage has been reused when the window manager next attempts to draw the window title area.
The WMTOPPED message indicates that the AES wants to bring
 
the indicated window to the "top" and make it active. This
 
happens if the user clicks within a window which is not on top,
 
or if the currently topped window is closed by its application
 
or desk accessory. Normally, the application should respond to
 
this message with:
 
   
windset(wh, WFTOP, 0, 0);
 
   
  +
==Coming soon==
and allow the process to complete.
 
   
  +
This concludes our tour of GEM's basic window management techniques. There have been some unavoidable glimpses of paths not yet taken (forward references), but we will return in time.
In a few instances, a window may be used in an output only
 
mode, such as a status display, with at least one other window
 
present for input. In this case, a WMTOPPED message for the
 
status window may be ignored. In all other cases, you must
 
handle the WMTOPPED message even if your application has only
 
one window: Invocation of a desk accessory could always place
 
another window on top. If you fail to do so, subsequent redraws
 
for your window may not be processed correctly.
 
 
 
W�WI�IN�ND�DO�OW�W S�SL�LI�ID�DE�ER�R M�ME�ES�SS�SA�AG�GE�ES�S
 
 
If you specify all of the slider bar parts for your window,
 
you may receive up to five different message types for each of
 
the two sets of sliders. To simplify things a little, I will
 
discuss everything in terms of the vertical (right hand side)
 
sliders. If you are also using the horizontal sliders, the same
 
techniques will work, just use the alternate mnemonics.
 
 
The WMVSLID message indicates that the user has dragged the
 
slider bar within its box, indicating a new relative position
 
within the document. Along with the window handle, this message
 
includes the relative position between 1 and 1000 in msg[4].
 
 
Recall from last column's discussion that this interval
 
corresponds to the "freedom of movement" of the slider. If you
 
want to accept the user's request, just make the call:
 
 
windset(wh, WFVSLIDE, msg[4], 0, 0, 0);
 
 
 
 
 
 
 
Professional GEM Part II 13
 
 
 
(Corresponding horizontal mnemonics are WMHSLID and WFHSLIDE).
 
 
Note that this windset call will not cause a redraw message to
 
be sent. You must update the display to reflect the new
 
scrolled position, either by executing a redraw directly, or by
 
sending yourself a message.
 
 
If the document within the window has some structure, you
 
may not wish to accept all slider positions. Instead you may
 
want to force the scroll position to the nearest text line (for
 
instance). Using terms defined in the last column, you may
 
convert the slider position to "document units" with:
 
 
topwind = msg[4] * (totaldoc - seendoc) / 1000 + topdoc
 
 
(This will probably require 32-bit arithmetic).
 
 
After rounding off or otherwise modifying the request, convert
 
it back to slider units and make the WFVSLIDE request.
 
 
The other four slider requests all share one message code:
 
WMARROWED. They are distinguished by sub-codes stored in
 
msg[4]: WAUPPAGE, WADNPAGE, WAUPLINE, and WADNLINE. These are
 
produced by clicking above and below the slider, and on the up
 
and down arrows, respectively. (I have no idea why sub-codes
 
were used in this one instance.) The corresponding horizontal
 
slider codes are: WALFPAGE, WARTPAGE, WALFLINE, and WARTLINE.
 
 
What interpretation you give to these requests will depend
 
on the application. In the most common instance, text
 
documents, the customary method is to change the top of window
 
position (topwind) by one line for a WAUPLINE or WADNLINE, and
 
by seendoc (the number of lines in the window) for a WAUPPAGE or
 
WADNPAGE.
 
 
After making the change, compute a new slider position, and
 
make the windset call as given above. If the document's length
 
is not an even multiple of "lines" or "pages" you will have to
 
be careful that incrementing or decrementing topwind does not
 
exceed its range of freedom: topdoc to (topdoc + totaldoc -
 
seendoc).
 
 
If you have such an odd size document, you will also have
 
to make a decision on whether to violate the line positioning
 
rule so that the slider may be put at its bottom-most position,
 
or to follow the rule but make it impossible to get the slider
 
to the extreme of its range.
 
 
 
A�A C�CO�OM�MM�MO�ON�N B�BU�UG�G
 
 
It is easy to forget that user clicks are not the only
 
 
 
 
 
 
Professional GEM Part II 14
 
 
 
things that affect slider position. If the window size changes
 
as a result of a WMSIZED or WMFULLED message, the app must also
 
update its sliders (if they are present). This is a good reason
 
to keep the top of window information in "document units".
 
 
You can just redo the position calculation with the new
 
"seendoc" value, and call windset. Also remember that changing
 
the size of the underlying document (adding or deleting a
 
bottom line, for instance) must also cause the sliders to be
 
adjusted.
 
 
 
D�DE�EP�PT�T.�. O�OF�F D�DI�IR�RT�TY�Y T�TR�RI�IC�CK�KS�S
 
 
There are two remaining window calls which are useful to
 
advanced programmers. They require techniques which I have not
 
yet discussed, so you may need to file them for future
 
reference.
 
 
The AES maintains a quarter-screen sized buffer which is
 
used to save the area under alerts and menu drop-downs. It is
 
occasionally useful for the application to gain access to this
 
buffer for its own use in saving screen areas with raster
 
copies. To do so, use:
 
 
windget(0, WFSCREEN, &loaddr, &hiaddr, &lolen, &hilen);
 
 
Hiaddr and loaddr are the top and bottom 16-bits (respectively)
 
of the 32-bit address of the buffer. Hilen and lolen are the
 
two halves of its length.
 
 
Due to a preculiarity of the binding you have to
 
reassemble these pieces before using them. (The actual value
 
of WFSCREEN is 17; this does not appear in some versions of the
 
GEMDEFS.H file.)
 
 
If you use this buffer, you MUST prevent menus from
 
dropping down by using either the BEGUPDATE or BEGMCTRL
 
windupdate calls. Failure to do so will result in your data
 
being destroyed. Remember to use the matching windupdate:
 
ENDUPDATE or ENDMCTRL, when you are done.
 
 
The other useful call enables you to replace the system's
 
desktop definition with a resource of your choosing. The call:
 
 
windset(0, WFNEWDESK, tree, 0, 0);
 
 
where tree is the 32-bit address of the object tree, will cause
 
the AES to draw your definition instead of the usual gray or
 
green background. Not only that, it will continue to redraw this
 
tree with no intervention on your part.
 
 
 
 
 
 
 
Professional GEM Part II 15
 
 
 
 
Obviously, the new definition must be carefully built to
 
fit the desktop area exactly or garbage will be left around the
 
edges. For the truly sophisticated, a user-defined object could
 
be used in this tree, with the result that your application's
 
code would be entered from the AES whenever the desktop was
 
redrawn. This would allow you to put VDI pictures or complex
 
images onto the desktop background.
 
 
 
A�A S�SI�IN�N O�OF�F O�OM�MI�IS�SS�SI�IO�ON�N
 
 
In the last column, I neglected to mention that strings
 
whose addresses are passed in the WFNAME and WFINFO windset
 
calls must be allocated in a static data area. Since the AES
 
remembers the addresses (not the characters), a disaster may
 
result if the storage has been reused when the window manager
 
next attempts to draw the window title area.
 
 
 
C�CO�OM�MI�IN�NG�G S�SO�OO�ON�N
 
 
This concludes our tour of GEM's basic window management
 
techniques. There have been some unavoidable glimpses of paths
 
not yet taken (forward references), but we will return in time.
 
 
On our next excursion, we will take a look at techniques
 
for handling simple dialog boxes, and start exploring the
 
mysteries of resources and object trees.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
</pre>
 
   
  +
On our next excursion, we will take a look at techniques for handling simple dialog boxes, and start exploring the mysteries of resources and object trees.
Back to [[Professional_GEM]]
 

Latest revision as of 20:39, 12 October 2006

Professional GEM
Part I -- Windows
In the beginningOpen sesameCleaning upThose fat slidersComing up nextFeedback
Part II -- Windows
ExcelsiorRedrawing windowsCaveat emptorInto the bitsA small confessionWindow control requestWindow slider messagesA common bugDept. of dirty tricksA sin of omissionComing soon
Part III -- The dialog handler
A meaningful dialogDefining termsBug alert!A handy trickClean upRecapButton ButtonWho's got the button?Coming up nextDispell gremlins
Part IV -- Resource structure
A maze of twisty little passagesPutting it to workLetters, we get lettersStraw poll!Stay tuned!
Part V -- Resource tree structures
How GEM does itThought experimentsA treewalker of our own
Part VI -- Raster operations
Seasons greetingsDefining termsMonochrome vs. colorStandard vs. device-specific formatEven-word vs. fringesMFDB'sLet's operateTransform formCopy raster opaqueCopy raster transparentThe mode parameterReplace modeErase modeXor modeTransparent modeReverse transparent modeThe problem of colorOptimizing raster operationsAvoid merged copiesMove to corresponding pixelsAvoid fringesUse another methodFeedback resultsThe next questionComing up soon
Part VII -- Menu structures
Happy new yearMenu basicsMenu structuresUsing the menuGetting fancyCheck please?Now you see it now you don'tLunch and dinner menusDo it yourselfMake prettyThat's it for now!
Part VIII -- User interfaces
And now for something completely different!Credit where it's dueFingertipsMusclesEyesShort-term memoryChunkingThink!Are we not men?Of modes and bandwidthTo do is to be!Amen...
Part IX -- VDI Graphics: Lines and solids
A bit of historyThe line forms on the leftSolidsTo be continued
Appendices
Main page

Excelsior

In this installment, we continue the exploration of GEM's window manager by finding out how to process the messages received by an application when it has a window defined on the screen.

Also, beginning with this column, sample C code demonstrating the techniques discussed will be available on SIG*ATARI in DL5. This will allow you to download the code without interference by the CIS text-formatter used by ANTIC ONLINE output.

The file for this column is GEMCL2.XMO. All references to non-GEM routines in this column refer to this file. Please note that these files will not contain entire programs. Instead, they consist of small pieces of utility code which you may copy and modify in your own programs.


Redrawing windows

One of the most misunderstood parts of GEM is the correct method for drawing within a window. Most requests for redrawing are generated by the GEM system, and arrive as messages (read with evntmulti) which contain the handle of the window, and the screen rectangle which is "dirty" and needs to be redrawn.

Screen areas may become dirty as a result of windows being closed, sized down, or moved, thus "exposing" an area underneath. The completion of a dialog, or closing of a desk accessory may also free up a screen area which needs to be redrawn. When GEM detects the presence of a dirty rectangle, it checks its list of open windows, and sends the application a redraw message for each of its windows which intersects the dirty area.


Caveat Emptor

GEM does not "clip" the rectangle which it sends to the application; that is, the rectangle may not lie entirely within the portion of the window which is exposed on the screen. It is the job of the application to determine in what portion of the rectangle it may safely draw. This is done by examining the "rectangle list" associated with the window.

A rectangle list is maintained by GEM for each active window. It contains the portions of the window's interior which are exposed, i.e., topmost, on the screen and within which the app may draw.

Let's consider an example to make this clear. Suppose an app has opened two windows, and there are no desk accessory windows open. The window which is topmost will always have only one rectangle in its list. If the two are separate on the screen, then the second window will also have one rectangle. If they overlap, then the top window will "break" the rectangle of the bottom one. If the overlap is at a corner, two rectangles will be generated for the bottom window. If the overlap is on a side only, then three rectangles are required to cover the exposed portion of the bottom window. Finally, if the first window is entirely within the second, it requires four rectangles in the list to tile the second window.

Try working out a few rectangle examples with pencil and paper to get the feel of it. You will see that the possible combinations with more than two windows are enormous. This, by the way, is the reason that GEM does not send one message for each rectangle on the list: With multiple windows, the number of messages generated would quickly fill up the application's message queue.

Finally, note that every app must use this method, even if it only uses a single window, because there may be desk accessories with their own windows in the system at the same time. If you do not use the rectangle lists, you may overwrite an accessory's window.


Into the bits

First, we should note that the message type for a redraw request is WMREDRAW, which is stored in msg[0], the first location of the message returned by evntmulti. The window handle is stored in msg[3]. These locations are the same for all of the message types being discuss. The rectangle which needs to be redrawn is stored in msg[4] through msg[7].

Now let's examine the sample redraw code in more detail. The redraw loop is bracketed with mouse off and mouse on calls. If you forget to do this, the mouse pointer will be over-written if it is within the window and the next movement of the mouse will leave a rectangular blotch on the screen as a piece of the "old" screen is incorrectly restored.

The other necessary step is to set the window update flag. This prevents the menu manager from dropping a menu on top of the screen portion being redrawn. You must release this flag at the end of the redraw, or the you will be unable to use any menus afterwards.

The window rectangles are retrieved using a get-first, get-next scheme which will be familiar if you have used the GEM DOS or PC-DOS wildcard file calls. The end of the rectangle list has been reached when both the width and height returned are zero. Since some part of a window might be off-screen (unless you have clamped its position - see below), the retrieved rectangle is intersected with the desktop's area, and then with the screen area for which a redraw was requested.

Now you have the particular area of the screen in which it is legal to draw. Unless there is only one window in your application, you will have to test the handle in the redraw request to figure out what to put in the rectangle.

Depending on the app, you may be drawing an AES object tree, or executing VDI calls, or some combination of the two. In the AES case, the computed rectangle is used to specify the bounds of the objcdraw. For VDI work, the rectangle is used to set the clipping area before executing the VDI calls.


A small confession

At the beginning of this discussion, I deliberately omitted one class of redraws: those initiated by the application itself. In some cases a part of the screen must be redrawn immediately to give feedback to the user following a keystroke, button, or mouse action. In these cases, the application could call doredraw directly,without waiting for a message.

The only time you can bypass doredraw, and draw without walking the list, is when you can be sure that the target window is on top, and that the figure being drawn is entirely contained within it.

In many cases, however, an application initiated redraw happens because of a computed change, for instance, a spreadsheet update, and its timing is not crucial. In this instance, you may wish to have the app send ITSELF a redraw request.

The main advantage of this approach is that the AES is smart enough to see if there is already a redraw request for the same window in the queue, and, if so, to merge the requests by doing a union of their rectangles. In this fashion, the "blinky" appearance of multiple redraws is avoided, without the need to include logic for merging redraws within the program. A utility routine for sending the "self-redraw" is included in the download for this article.


Window control requests

An application is notified by the AES, via the message system, when the user manipulates one of the window control points. Remember that you must have specified each control point when the window was created, or will not receive the associated control message.

The most important thing to understand about window control is that the change which the user requested does not take place until the application forwards it to the AES. While this makes for a little extra work, it gives the program a chance to intervene and validate or modify the request to suit.

A second thing to keep in mind is that not all window updates cause a redraw request to be generated for the window, because the AES attempts to save time with raster moves on the screen. Now let's look at each window control request in detail. The message code for a window move is WMMOVED. If you are willing to accept any such request, just do:

 windset(wh, WFCXYWH, msg[4], msg[5], msg[6], msg[7]);

(Remember that wh, the window handle, is always in msg[3]).

The AES will not request a redraw of the window following this call, unless the window is being moved from a location which is partially "off-screen". Instead, it will do a "blit" (raster copy) of the window and its contents to the new location without intervention by the app.

There are two constraints which you may often wish to apply to the user's move request. The first is to force the new location to lie entirely within the desktop, rather than partially off-screen. You can do this with the rcconstrain utility by executing:

 rcconstrain(&full, &msg[4]);

before making the windset call. (Full is assumed to contain the desktop dimensions.)

The second common constraint is to "snap" the x-dimension location of the new location to a word boundary. This operation will speed up GEM's "blit" because no shifting or masking will need to be done when moving the window. To perform this operation, use align() before the windset call:

 msg[4] = align(msg[4], 16);

The message code for a window size request is WMSIZED. Again, if you are willing to accept any request, you can just "turn it around" with the same windset call as given for WMMOVED.

Actually, GEM enforces a couple of constraints on sizing. First, the window may not be sized off screen. Second, there is a minimum window size which is dependent on the window components specified when it was created. This prevents features like scroll arrows from being squeezed into oblivion. The most common application constraint on sizing is to snap the size to horizontal words (as above) and/or vertical character lines. In the latter case, the vertical dimension of the output font is used with align().

Also, be aware that the size message which you receive specifies the EXTERNAL dimensions of the window. To assure an "even" size for the INTERNAL dimensions, you must make a windcalc call to compute them, use align() on the computed values, back out the corresponding external dimensions with the reverse windcalc, and then make the windset call with this set of values.

A window resize will only cause a redraw request for the window if the size is being increased in at least one dimension. This is satisfactory for most applications, but if you must "reshuffle" the window after a size-down, you should send yourself a redraw (as described above) after you make the windset call. This will guarantee that the display is updated correctly. Also note that the sizing or movement of one window may cause redraw requests to be generated for other windows which are uncovered by the change.

The window full request, with code WMFULLED, is actually a toggle. If the window is already at its full size (as specified in the windcreate), then this is a request to shrink to its previous size. If the window is currently small, then the request is to grow to full size.

Since the AES records the current, previous, and maximum window size, you can use windget calls to determine which situation pertains. The hndlfull utility in the down-load (modified from Doodle), shows how to do this.

The "zoom box" effects when changing size are optional, and can be removed to speed things up. Again, if the window's size is decreasing, no redraw is generated, so you must send yourself one if necessary. You should not have to perform any constraint or "snap" operations here, since (presumably) the full and previous sizes have had these checks applied to them already.

The WMCLOSED message is received when the close box is clicked. What action you perform depends on the application. If you want to remove the window, use windclose as described in the last column. In many applications, however, the close message may indicate that a file is to be saved, or a directory or editing level is to be closed. In these cases, the message is used to trigger this action before or instead of the windclose. (Folders on the Desktop are an example of this situation.)

The WMTOPPED message indicates that the AES wants to bring the indicated window to the "top" and make it active. This happens if the user clicks within a window which is not on top, or if the currently topped window is closed by its application or desk accessory. Normally, the application should respond to this message with:

 windset(wh, WFTOP, 0, 0);

and allow the process to complete.

In a few instances, a window may be used in an output only mode, such as a status display, with at least one other window present for input. In this case, a WMTOPPED message for the status window may be ignored. In all other cases, you must handle the WMTOPPED message even if your application has only one window: Invocation of a desk accessory could always place another window on top. If you fail to do so, subsequent redraws for your window may not be processed correctly.


Window slider messages

If you specify all of the slider bar parts for your window, you may receive up to five different message types for each of the two sets of sliders. To simplify things a little, I will discuss everything in terms of the vertical (right hand side) sliders. If you are also using the horizontal sliders, the same techniques will work, just use the alternate mnemonics.

The WMVSLID message indicates that the user has dragged the slider bar within its box, indicating a new relative position within the document. Along with the window handle, this message includes the relative position between 1 and 1000 in msg[4].

Recall from last column's discussion that this interval corresponds to the "freedom of movement" of the slider. If you want to accept the user's request, just make the call:

 windset(wh, WFVSLIDE, msg[4], 0, 0, 0);

(Corresponding horizontal mnemonics are WMHSLID and WFHSLIDE).

Note that this windset call will not cause a redraw message to be sent. You must update the display to reflect the new scrolled position, either by executing a redraw directly, or by sending yourself a message.

If the document within the window has some structure, you may not wish to accept all slider positions. Instead you may want to force the scroll position to the nearest text line (for instance). Using terms defined in the last column, you may convert the slider position to "document units" with:

 topwind = msg[4] * (totaldoc - seendoc) / 1000 + topdoc

(This will probably require 32-bit arithmetic).

After rounding off or otherwise modifying the request, convert it back to slider units and make the WFVSLIDE request.

The other four slider requests all share one message code: WMARROWED. They are distinguished by sub-codes stored in msg[4]: WAUPPAGE, WADNPAGE, WAUPLINE, and WADNLINE. These are produced by clicking above and below the slider, and on the up and down arrows, respectively. (I have no idea why sub-codes were used in this one instance.) The corresponding horizontal slider codes are: WALFPAGE, WARTPAGE, WALFLINE, and WARTLINE.

What interpretation you give to these requests will depend on the application. In the most common instance, text documents, the customary method is to change the top of window position (topwind) by one line for a WAUPLINE or WADNLINE, and by seendoc (the number of lines in the window) for a WAUPPAGE or WADNPAGE.

After making the change, compute a new slider position, and make the windset call as given above. If the document's length is not an even multiple of "lines" or "pages" you will have to be careful that incrementing or decrementing topwind does not exceed its range of freedom:

topdoc to (topdoc + totaldoc - seendoc).

If you have such an odd size document, you will also have to make a decision on whether to violate the line positioning rule so that the slider may be put at its bottom-most position, or to follow the rule but make it impossible to get the slider to the extreme of its range.


A common bug

It is easy to forget that user clicks are not the only things that affect slider position. If the window size changes as a result of a WMSIZED or WMFULLED message, the app must also update its sliders (if they are present). This is a good reason to keep the top of window information in "document units".

You can just redo the position calculation with the new seendoc value, and call windset. Also remember that changing the size of the underlying document (adding or deleting a bottom line, for instance) must also cause the sliders to be adjusted.


Dept. of dirty tricks

There are two remaining window calls which are useful to advanced programmers. They require techniques which I have not yet discussed, so you may need to file them for future reference.

The AES maintains a quarter-screen sized buffer which is used to save the area under alerts and menu drop-downs. It is occasionally useful for the application to gain access to this buffer for its own use in saving screen areas with raster copies. To do so, use:

<pre<

windget(0, WFSCREEN, &loaddr, &hiaddr, &lolen, &hilen);

Hiaddr and loaddr are the top and bottom 16-bits (respectively) of the 32-bit address of the buffer. Hilen and lolen are the two halves of its length.

Due to a preculiarity of the binding you have to reassemble these pieces before using them. (The actual value of WFSCREEN is 17; this does not appear in some versions of the GEMDEFS.H file.)

If you use this buffer, you MUST prevent menus from dropping down by using either the BEGUPDATE or BEGMCTRL windupdate calls. Failure to do so will result in your data being destroyed. Remember to use the matching windupdate: ENDUPDATE or ENDMCTRL, when you are done.

The other useful call enables you to replace the system's desktop definition with a resource of your choosing. The call:

 windset(0, WFNEWDESK, tree, 0, 0);

where tree is the 32-bit address of the object tree, will cause the AES to draw your definition instead of the usual gray or green background. Not only that, it will continue to redraw this tree with no intervention on your part.

Obviously, the new definition must be carefully built to fit the desktop area exactly or garbage will be left around the edges. For the truly sophisticated, a user-defined object could be used in this tree, with the result that your application's code would be entered from the AES whenever the desktop was redrawn. This would allow you to put VDI pictures or complex images onto the desktop background.


A sin of omission

In the last column, I neglected to mention that strings whose addresses are passed in the WFNAME and WFINFO windset calls must be allocated in a static data area. Since the AES remembers the addresses (not the characters), a disaster may result if the storage has been reused when the window manager next attempts to draw the window title area.


Coming soon

This concludes our tour of GEM's basic window management techniques. There have been some unavoidable glimpses of paths not yet taken (forward references), but we will return in time.

On our next excursion, we will take a look at techniques for handling simple dialog boxes, and start exploring the mysteries of resources and object trees.