Professional GEM - Part II - Windows

From Atari Wiki
Revision as of 13:04, 13 September 2006 by Zorro 2 (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search


        Professional GEM                                                7


                                    P�PA�AR�RT�T -�- I�II�I

                                 W�Wi�in�nd�do�ow�ws�s       



        E�EX�XC�CE�EL�LS�SI�IO�OR�R         

             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.  


        R�RE�ED�DR�RA�AW�WI�IN�NG�G 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.  


        C�CA�AV�VE�EA�AT�T E�EM�MP�PT�TO�OR�R        

             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.  


        


        Professional GEM             Part II                            8



             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.  


        I�IN�NT�TO�O T�TH�HE�E B�BI�IT�TS�S       

             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


        


        Professional GEM             Part II                            9


        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�A S�SM�MA�AL�LL�L C�CO�ON�NF�FE�ES�SS�SI�IO�ON�N       

             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  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.  

             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


        


        Professional GEM             Part II                           10


        utility  routine for sending the "self-redraw" is included in the
        down-load for this article.  


        W�WI�IN�ND�DO�OW�W C�CO�ON�NT�TR�RO�OL�L R�RE�EQ�QU�UE�ES�ST�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.  

             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: 


        


        Professional GEM             Part II                           11



                           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


        


        Professional GEM             Part II                           12


        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.  


        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.  

























        

Back to Professional_GEM