home *** CD-ROM | disk | FTP | other *** search
/ Falcon 030 Power 2 / F030_POWER2.iso / ST_STE / MAGS / ICTARI03.ARJ / ictari.03 / C / GEM_TUT / GEM002.TXT < prev   
Text File  |  1993-05-12  |  20KB  |  414 lines

  1.                            by Tim Oren
  2.  
  3.                      Topic: Windows, part II
  4.                              10/21/85
  5.  
  6.  
  7.                             EXCELSIOR!
  8.  
  9.   In this installment, we continue the exploration of GEM's window
  10. manager by finding out how to process the messages received by an
  11. application when it has a window defined on the screen.
  12.  
  13.   Also, beginning with this column, sample C code demonstrating the
  14. techniques discussed will be available on SIG*ATARI in DL5.  This
  15. will allow you to download the code without interference by the CIS
  16. text-formatter used by ANTIC ONLINE output.
  17.  
  18.   The file for this column is GEMCL2.XMO.  All references  to non-GEM
  19. routines in this column refer to this file.  Please note that  these
  20. files will not contain entire programs.  Instead, they consist of
  21. small pieces of utility code which you may copy and modify in your
  22. own  programs.
  23.  
  24.                         REDRAWING WINDOWS
  25.  
  26.   One of the most misunderstood parts of GEM is the correct method for
  27. drawing within a window.  Most requests for redrawing are generated
  28. by the GEM system, and arrive as messages (read with evnt_multi)
  29. which contain the handle of the window, and the screen rectangle
  30. which is  "dirty" and needs to be redrawn.
  31.  
  32.   Screen areas may become dirty as a result of windows being closed,
  33. sized down, or moved, thus "exposing" an area underneath.  The
  34. completion of  a dialog, or closing of a desk accessory may also free
  35. up a screen area which needs to be redrawn.  When GEM detects the
  36. presence of a dirty rectangle,  it checks its list of open windows,
  37. and sends the application a redraw message  for each of its windows
  38. which intersects the dirty area.
  39.  
  40.                           CAVEAT EMPTOR
  41.  
  42.   GEM does not "clip" the rectangle which it sends to  the application;
  43. that is, the rectangle may not lie entirely within the  portion of
  44. the window which is exposed on the screen.  It is the job of the
  45. application to determine in what portion of the rectangle it may
  46. safely draw.   This is done by examining the "rectangle list"
  47. associated with the window.
  48.  
  49.   A rectangle list is maintained by GEM for each active window.  It
  50. contains the portions of the window's interior which are exposed,
  51. i.e., topmost, on the screen and within which the app may draw.
  52.  
  53.   Let's consider an example to make this clear.  Suppose an app has
  54. opened two windows, and there are no desk accessory windows open. The
  55. window which is topmost will  always have only one rectangle in its
  56. list.  If the two are separate on the  screen, then the second window
  57. will also have one rectangle.  If they overlap,  then the top window
  58. will "break" the rectangle of the bottom one.  If the  overlap is at
  59. a corner, two rectangles will be generated for the bottom window.  If
  60. the overlap is on a side only, then three rectangles are required to
  61. cover  the exposed portion of the bottom window.  Finally, if the
  62. first window is  entirely within the second, it requires four
  63. rectangles in the list to tile the second window.
  64.  
  65.   Try working out a few rectangle examples with pencil and paper to
  66. get the feel of it.  You will see that the possible combinations with
  67. more  than two windows are enormous.  This, by the way, is the reason
  68. that GEM does  not send one message for each rectangle on the list:
  69. With multiple windows,  the number of messages generated would
  70. quickly fill up the application's message queue.
  71.  
  72.   Finally, note that every app MUST use this method, even if it only
  73. uses a single window, because there may be desk accessories with
  74. their own  windows in the system at the same time.  If you do not use
  75. the rectangle lists, you may overwrite an accessory's window.
  76.  
  77.                           INTO THE BITS
  78.  
  79.   First, we should note that the message type for a  redraw request is
  80. WM_REDRAW, which is stored in msg[0], the first location of the
  81. message returned by evnt_multi.  The window handle is stored in
  82. msg[3].  These locations are the same for all of the message types
  83. being discuss.  The rectangle which needs to be redrawn is stored in
  84. msg[4] through msg[7].
  85.  
  86.   Now let's examine the sample redraw code in more detail. The redraw
  87. loop is bracketed with mouse off and mouse on calls.  If you forget
  88. to do  this, the mouse pointer will be over-written if it is within
  89. the window and  the next movement of the mouse will leave a
  90. rectangular blotch on the screen  as a piece of the "old" screen is
  91. incorrectly restored.
  92.  
  93.   The other necessary step is to set the window update flag.  This
  94. prevents the menu manager from dropping a menu on top of the screen
  95. portion being redrawn.  You must release this flag at the end of the
  96. redraw, or the you will be unable to use any menus afterwards.
  97.  
  98.   The window rectangles are retrieved using a get-first, get-next
  99. scheme which will be familiar if you have used the GEM DOS or PC-DOS
  100. wildcard file calls.  The end of the rectangle list has been reached
  101. when both the width and height returned are zero.  Since some part of
  102. a  window might be off-screen (unless you have clamped its position -
  103. see below), the retrieved rectangle is intersected with the desktop's
  104. area,  and then with the screen area for which a redraw was
  105. requested.
  106.  
  107.   Now you have the particular area of the screen in which it is  legal
  108. to draw.  Unless there is only one window in your application, you
  109. will have to test the handle in the redraw request to figure out what
  110. to  put in the rectangle.
  111.  
  112.   Depending on the app, you may be drawing an AES  object tree, or
  113. executing VDI calls, or some combination of the two.  In  the AES
  114. case, the computed rectangle is used to specify the bounds of the
  115. objc_draw.  For VDI work, the rectangle is used to set the clipping
  116. area before executing the VDI calls.
  117.  
  118.                         A SMALL CONFESSION
  119.  
  120.   At the beginning of this discussion, I  deliberately omitted one
  121. class of redraws: those initiated by the application  itself.
  122.  
  123.   In some cases a part of the screen must be redrawn immediately to
  124. give feedback to the user following a keystroke, button, or mouse
  125. action.   In these cases, the application could call do_redraw
  126. directly, without  waiting for a message.
  127.  
  128.   The only time you can bypass do_redraw, and draw  without walking the
  129. rectangle list, is when you can be sure that the target  window is on
  130. top, and that the figure being drawn is entirely contained  within
  131. it.
  132.  
  133.   In many cases, however, an application initiated redraw happens
  134. because of a computed change, for instance, a spreadsheet update, and
  135. its timing is not crucial.  In this instance, you may wish to have
  136. the  app send ITSELF a redraw request.
  137.  
  138.   The main advantage of this approach  is that the AES is smart enough
  139. to see if there is already a redraw request for the same window in
  140. the queue, and, if so, to merge the requests by  doing a union of
  141. their rectangles.  In this fashion, the "blinky" appearance of
  142. multiple redraws is avoided, without the need to include logic for
  143. merging redraws within the program.
  144.  
  145.   A utility routine for sending the "self-redraw" is included in the
  146. down-load for this article.
  147.  
  148.                      WINDOW CONTROL REQUESTS
  149.  
  150.   An application is notified by the AES, via the message system, when
  151. the user manipulates one of the window control points.  Remember that
  152. you must have specified each control point when the window was
  153. created, or will not receive the associated control message.
  154.  
  155.   The most important thing to understand about window control is that
  156. the change which the user requested does not take place until the
  157. application forwards it to the AES.  While this makes for a little
  158. extra work,  it gives the program a chance to intervene and validate
  159. or modify the request to suit.
  160.  
  161.   A second thing to keep in mind is that not all window updates cause a
  162. redraw request to be generated for the window, because the AES
  163. attempts to save time with raster moves on the screen.
  164.  
  165.   Now let's look at each window control request in detail.  The
  166. message code for a window move is WM_MOVED.  If you are willing to
  167. accept  any such request, just do:
  168.  
  169.   wind_set(wh, WF_CXYWH, msg[4], msg[5], msg[6], msg[7]);
  170.  
  171.   (Remember that wh, the window handle, is always in msg[3]).
  172.  
  173.   The AES will not request a redraw of the window following this call,
  174. unless the window is being moved from a location which is partially
  175. "off-screen". Instead, it will do a "blit" (raster copy) of the
  176. window and its contents to the new location without intervention by
  177. the app.
  178.  
  179.   There are two constraints which you may often wish to apply to  the
  180. user's move request.  The first is to force the new location to lie
  181. entirely within the desktop, rather than partially off-screen.  You
  182. can do this with the rc_constrain utility by executing:
  183.  
  184.   rc_constrain(&full, &msg[4]);
  185.  
  186. before making the wind_set call.  (Full is assumed to contain the
  187. desktop dimensions.)
  188.  
  189.   The second common constraint is to "snap" the x-dimension location of
  190. the new location to a word boundary.  This operation will speed up
  191. GEM's "blit" because no shifting or masking will need to be done
  192. when moving the window.  To perform this operation, use align()
  193. before the  wind_set call:
  194.  
  195.   msg[4] = align(msg[4], 16);
  196.  
  197.   The message code for a window size request is WM_SIZED.  Again, if
  198. you are willing to accept any request, you can just "turn it around"
  199. with the same wind_set call as given for WM_MOVED.
  200.  
  201.   Actually, GEM enforces a couple of constraints on sizing.  First, the
  202. window may not be sized off screen.  Second, there is a minimum
  203. window size which is dependent on the window components specified
  204. when it was created.  This prevents features like scroll arrows from
  205. being squeezed into oblivion.
  206.  
  207.   The most common application constraint on sizing is to snap the size
  208. to horizontal words (as above) and/or vertical character lines.  In
  209. the latter case, the vertical dimension of the output font is used
  210. with align().
  211.  
  212.   Also,  be aware that the size message which you receive specifies the
  213. EXTERNAL  dimensions of the window.  To assure an "even" size for the
  214. INTERNAL dimensions, you must make a wind_calc call to compute them,
  215. use align() on the computed values, back out the corresponding
  216. external dimensions with the reverse wind_calc, and then make the
  217. wind_set call with this set of values.
  218.  
  219.   A window resize will only cause a redraw request for the window if
  220. the size is being increased in at least one dimension.  This is
  221. satisfactory for most applications, but if you must "reshuffle" the
  222. window after a  size-down, you should send yourself a redraw (as
  223. described above) after you make the wind_set call.  This will
  224. guarantee that the display is updated correctly.  Also note that the
  225. sizing or movement of one window may cause redraw requests to be
  226. generated for other windows which are uncovered by the change.
  227.  
  228.   The window full request, with code WM_FULLED, is actually a toggle.
  229. If the window is already at its full size (as specified in the
  230. wind_create), then this is a request to shrink to its previous size.
  231. If the window is currently small, then the request is to grow to full
  232. size.
  233.  
  234.   Since the AES records the current, previous, and maximum window size,
  235. you can use  wind_get calls to determine which situation pertains.
  236. The hndl_full utility in the down-load (modified from Doodle), shows
  237. how to do this.
  238.  
  239.   The "zoom box" effects when changing size are optional, and can be
  240. removed  to speed things up.  Again, if the window's size is
  241. decreasing, no redraw is  generated, so you must send yourself one if
  242. necessary.  You should not have  to perform any constraint or "snap"
  243. operations here, since (presumably) the  full and previous sizes have
  244. had these checks applied to them already.
  245.  
  246.   The WM_CLOSED message is received when the close box is clicked. What
  247. action you perform depends on the application.  If you want to remove
  248. the window, use wind_close as described in the last column.  In many
  249. applications, however, the close message may indicate that a file is
  250. to be saved, or a directory or editing level is to be closed.  In
  251. these cases, the message is used to trigger this action before or
  252. instead of the wind_close.  (Folders on the Desktop are an example of
  253. this situation.)
  254.  
  255.   The WM_TOPPED message indicates that the AES wants to bring the
  256. indicated window to the "top" and make it active.  This happens if
  257. the user clicks within a window which is not on top, or if the
  258. currently topped window is closed by its application or desk
  259. accessory.  Normally, the application should respond to this message
  260. with:
  261.  
  262.   wind_set(wh, WF_TOP, 0, 0);
  263.  
  264. and allow the process to complete.
  265.  
  266.   In a few instances, a window may be used in an output only mode, such
  267. as a status display, with at least one other window present for
  268. input.  In this case, a WM_TOPPED message for the status window may
  269. be ignored.  In all other cases, you must handle the WM_TOPPED
  270. message even if your application has only one window: Invocation of a
  271. desk accessory could always place another window on top.  If you fail
  272. to do so, subsequent redraws for your window may not be processed
  273. correctly.
  274.  
  275.                       WINDOW SLIDER MESSAGES
  276.  
  277.   If you specify all of the slider bar parts for your window, you may
  278. receive up to five different message types for each of the two sets
  279. of sliders.  To simplify things a little, I will discuss everything
  280. in terms of the vertical (right hand side) sliders.  If you are also
  281. using the horizontal sliders, the same techniques will work, just use
  282. the alternate mnemonics.
  283.  
  284.   The WM_VSLID message indicates that the user has dragged the slider
  285. bar within its box, indicating a new relative position within the
  286. document.  Along with the window handle, this message includes the
  287. relative position between 1 and 1000 in msg[4].
  288.  
  289.   Recall from last column's discussion that this interval corresponds
  290. to the "freedom of movement" of the slider. If you want to accept the
  291. user's request, just make the call:
  292.  
  293.   wind_set(wh, WF_VSLIDE, msg[4], 0, 0, 0);
  294.  
  295.   (Corresponding horizontal mnemonics are WM_HSLID and WF_HSLIDE).
  296.  
  297.   Note that this wind_set call will not cause a redraw message to be
  298. sent.  You must update the display to reflect the new scrolled
  299. position, either by executing a redraw directly, or by sending
  300. yourself a message.
  301.  
  302.   If the document within the window has some structure, you may not
  303. wish to accept all slider positions.  Instead you may want to force
  304. the scroll position to the nearest text line (for instance).  Using
  305. terms defined in the last column, you may convert the slider position
  306. to "document units" with:
  307.  
  308.   top_wind = msg[4] * (total_doc - seen_doc) / 1000 + top_doc
  309.  
  310.   (This will probably require 32-bit arithmetic).
  311.  
  312.   After rounding off or otherwise modifying the request, convert it
  313. back to slider units and make the WF_VSLIDE request.
  314.  
  315.   The other four slider requests all share one message code:
  316. WM_ARROWED.  They are distinguished by sub-codes stored in msg[4]:
  317. WA_UPPAGE, WA_DNPAGE, WA_UPLINE, and WA_DNLINE.  These are produced
  318. by clicking above and below the slider, and on the up and down
  319. arrows, respectively.  (I have no idea why sub-codes were used in
  320. this one instance.)  The corresponding horizontal slider codes are:
  321. WA_LFPAGE, WA_RTPAGE, WA_LFLINE, and WA_RTLINE.
  322.  
  323.   What interpretation you give to these requests will depend on  the
  324. application.  In the most common instance, text documents, the
  325. customary method is to change the top of window position (top_wind)
  326. by  one line for a WA_UPLINE or WA_DNLINE, and by seen_doc (the
  327. number of lines in the window) for a WA_UPPAGE or WA_DNPAGE.
  328.  
  329.   After making the change, compute a new slider position, and make the
  330. wind_set call as given above.  If the document's length is not an
  331. even multiple of "lines" or "pages" you will have to be careful that
  332. incrementing or  decrementing top_wind does not exceed its range of
  333. freedom: top_doc to (top_doc + total_doc - seen_doc).
  334.  
  335.   If you have such an odd size document, you will also have to make a
  336. decision on whether to violate the line positioning rule so that the
  337. slider may be put at its bottom-most position, or to follow the rule
  338. but make it impossible to get the slider to the extreme of its range.
  339.  
  340.                            A COMMON BUG
  341.  
  342.   It is easy to forget that user clicks are not the only things that
  343. affect slider position.  If the window size changes as a result of a
  344. WM_SIZED or WM_FULLED message, the app must also  update its sliders
  345. (if they are present).  This is a good reason to keep the top of
  346. window information in "document units".
  347.  
  348.   You can just redo the position calculation with the new "seen_doc"
  349. value, and call  wind_set.  Also remember that changing the size of
  350. the underlying document  (adding or deleting a bottom line, for
  351. instance) must also cause the sliders  to be adjusted.
  352.  
  353.                       DEPT. OF DIRTY TRICKS
  354.  
  355.   There are two remaining window calls which are useful to advanced
  356. programmers.  They require techniques which I have not yet discussed,
  357. so you may need to file them for future reference.
  358.  
  359.   The AES maintains a quarter-screen sized buffer which is used to save
  360. the area under alerts and menu drop-downs.  It is occasionally useful
  361. for the application to gain access to this buffer for its own use in
  362. saving screen areas with raster copies.  To do so, use:
  363.  
  364.   wind_get(0, WF_SCREEN, &loaddr, &hiaddr, &lolen, &hilen);
  365.  
  366.   Hiaddr and loaddr are the top and bottom 16-bits (respectively) of
  367. the  32-bit address of the buffer.  Hilen and lolen are the two
  368. halves of  its length.
  369.  
  370.   Due to a preculiarity of the binding you have to reassemble  these
  371. pieces before using them.  (The actual value of WF_SCREEN is 17; this
  372. does not appear in some versions of the GEMDEFS.H file.)
  373.  
  374.   If you use this buffer, you MUST prevent menus from dropping down by
  375. using either the BEG_UPDATE or BEG_MCTRL wind_update calls.  Failure
  376. to do so will result in your data being destroyed.  Remember to use
  377. the matching wind_update: END_UPDATE or END_MCTRL, when you are done.
  378.  
  379.   The other useful call enables you to replace the system's desktop
  380. definition with a resource of your choosing.  The call:
  381.  
  382.   wind_set(0, WF_NEWDESK, tree, 0, 0);
  383.  
  384. where tree is the 32-bit address of the object tree, will cause the
  385. AES to draw your definition instead of the usual gray or green
  386. background. Not only that, it will continue to redraw this tree with
  387. no intervention on your part.
  388.  
  389.   Obviously, the new definition must be carefully built to fit the
  390. desktop area exactly or garbage will be left around the edges.  For
  391. the truly sophisticated, a user-defined object could be used in this
  392. tree, with the result that your application's code would be entered
  393. from the AES whenever the desktop was redrawn.  This would allow you
  394. to put VDI pictures or complex images onto the desktop background.
  395.  
  396.                         A SIN OF OMISSION
  397.  
  398.   In the last column, I neglected to mention that strings whose
  399. addresses are passed in the WF_NAME and WF_INFO  wind_set calls must
  400. be allocated in a static data area.  Since the AES remembers the
  401. addresses (not the characters), a disaster may result if the storage
  402. has been reused when the window manager next attempts to draw the
  403. window title area.
  404.  
  405.                           COMING SOON...
  406.  
  407.   This concludes our tour of GEM's basic window management techniques.
  408. There have been some unavoidable glimpses of paths not yet taken
  409. (forward references), but we will return in time.
  410.  
  411.   On our next excursion, we will take a look at techniques for handling
  412. simple dialog boxes, and start exploring the mysteries of resources
  413. and object trees.
  414.