home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / books / progem / hooks.11 < prev    next >
Text File  |  1986-11-01  |  27KB  |  519 lines

  1.       Permission to reprint or excerpt is granted only if the following
  2.       line appears at the top of the article:
  3.  
  4.        ANTIC PUBLISHING INC., COPYRIGHT 1986.  REPRINTED BY PERMISSION.
  5.  
  6.  
  7.  
  8.       PROFESSIONAL GEM  by Tim Oren
  9.       Column #11 - GEM Hooks and Hacks, An Insider's AES Tricks
  10.  
  11.  
  12.            Welcome  to  the  eleventh episode of ST PRO  GEM,  which  is
  13.       devoted to exploring some of the little documented,  but powerful,
  14.       features  of GEM.   Like the authors of most complex systems,  the
  15.       GEM  programmers left behind a set of "hooks",  powerful  features
  16.       which would aid them in enhancing the system later.  I am going to
  17.       lay  out  a number of these methods which have served me  well  in
  18.       making  creative use of the AES.   You will find that most of them
  19.       concern  the object and form libraries,  since I was most involved
  20.       in those parts of GEM.  There are probably many more useful tricks
  21.       waiting  to be found in other parts of GEM,  so if you happen onto
  22.       one, please let me know in the Feedback!   Before  you  begin,  be
  23.       sure to pick up the download for this column:  GMCL11.C  in DL3 of
  24.       PCS-57.
  25.  
  26.            POWERFUL   OBJECTS.    The  first  four  tricks  all  involve
  27.       augmenting  standard  AES  objects.   This is a powerful technique
  28.       for two reasons.  First, you can  take  advantage  of the  regular
  29.       AES  object  and  form  libraries  to draw and handle most of your
  30.       objects,  so that  your  program need only process the exceptions.
  31.       Second, you can use the RCS  to  copy  the  special  objects  into
  32.       multiple  dialogs  or resources.   These  four tricks are Extended
  33.       Object  Types,  User-defined  Objects,  TOUCHEXIT,  and  INDIRECT.
  34.       Let's look at each of them in turn.
  35.  
  36.            EXTENDED OBJECT TYPES.  If you look at the AES Object Library
  37.       documentation,  you  will  notice  that the values for the OB_TYPE
  38.       field in an object are  all 32 or less.   This means that a number
  39.       of bits are unused in  this  field.   In fact,  the AES completely
  40.       ignores the top byte of  the OB_TYPE field.   In addition, the RCS
  41.       ignores the top byte, but it  also  preserves  its  value  when an
  42.       object is read, written, or copied.
  43.  
  44.            This  gives  you one byte per object to use as you  see  fit.
  45.       Since  the  processing of an object or dialog is (so far)  in  the
  46.       hands of the AES,  the best uses of Extended Types are in flagging
  47.       methods for initializing an object or handling its value when  the
  48.       dialog  completes.
  49.  
  50.            For  example,  you  might  have  several  dialogs  containing
  51.       editable numeric fields.   The Extended Type of each numeric field
  52.       could be set to the index of the corresponding value in an  array.
  53.       Then your application's dialog initialization code could scan  the
  54.       object  tree  for  such objects,  pick up  the  appropriate  value
  55.       from the array and convert it to ASCII,  storing the result in the
  56.       resource's  string  area.   When the dialog was finished,  another
  57.       pass could be made to reconvert the ASCII to binary and store away
  58.       the results in the array.   (Note that the map_tree() utility from
  59.       column #5 will scan an entire resource tree.)
  60.  
  61.            Another  application  is  to assign  uniform  codes  to  exit
  62.       buttons  in  dialogs.   If you give every "OK" button an  Extended
  63.       Type  of one,  and every "Cancel" button an Extended Type of  two,
  64.       then  you  needn't  worry about naming every  exit  object.   Just
  65.       examine  the Extended Type of the object returned by form_do,  and
  66.       proceed accordingly.
  67.  
  68.            The catch, of course, is that you have to find a way to enter
  69.       the  Extended  Type code in the first place.   Version 1.0 of  the
  70.       RCS, as shipped with the Atari developer's kit, makes no provision
  71.       for this.  So you have your choice of two methods for creating the
  72.       first  object with each Extended Type code.
  73.  
  74.            First,  you can dump out a C source of a resource, insert the
  75.       new type code by hand,  and regenerate the resource with STCREATE.
  76.       Alternately,  you could carefully modify the binary resource using
  77.       SID.   You will probably want to reread the AES object manual,  ST
  78.       PRO GEM #4 and #5,  and use the C source as a guide when doing so.
  79.       In both cases, you should make things easy on yourself by creating
  80.       a  one  dialog resource with only a single object other  than  the
  81.       root.   Version  2.0  of  the RCS will let you directly  enter  an
  82.       Extended Type, but it has not yet been released for the ST by DRI.
  83.  
  84.            Once  you have created a prototype extended object by  either
  85.       method, you can use the RCS to propogate it.  The safest way is to
  86.       use the MERGE option to add the modified tree to the resource  you
  87.       are building.  Then copy the prototype object via the clipboard to
  88.       your dialog(s), deleting the extra tree when you are done.  If you
  89.       are  using several different extended objects,  you can use  MERGE
  90.       and clipboard copies to get them all into one tree which will then
  91.       become your own object library.
  92.  
  93.            The  second way of using RCS is easier,  but more  dangerous.
  94.       If you want to try the following technique,  BACK UP YOUR RCS DISK
  95.       FIRST!   Put  simply,  the RCS does not care what is in its dialog
  96.       partbox. It will make copies of anything that it finds there! This
  97.       gives you the option of using the RCS on ITS OWN RESOURCE in order
  98.       to add your customized objects to the partbox.
  99.  
  100.            To do this, open RCS.RSC from the RCS.  Since there is no DEF
  101.       file,  you will get a collection of question mark icons.   Use the
  102.       NAME option to make TREE5 into a DIALOG.    Open it,  and you will
  103.       see the dialog partbox.
  104.  
  105.            Now you can use the MERGE technique described above to insert
  106.       your  customized  objects.   Then SAVE the modified resource,  and
  107.       rerun the RCS.  Your new objects should now appear in the partbox.
  108.       If  you added several,  you may have to stretch the partbox to see
  109.       them  all.   You can now make copies of the new objects just  like
  110.       any other part.  (Note: DO NOT modify the alert or menu partboxes,
  111.       you will probably crash the RCS.)
  112.  
  113.            USER-DEFINED  OBJECTS.   The one type of object which was not
  114.       discussed in the  earlier articles  on  AES objects was G_USERDEF,
  115.       the programmer defined object.  This  is  the  hook  for  creating
  116.       objects  with  other  appearances  beyond  those  provided  by the
  117.       standard  AES.   By  the  way,  you should note that the G_PROGDEF
  118.       and APPLBLK mnemonics  used  in  the  AES documents are incorrect;
  119.       the actual  names  as  used  defined  OBDEFS.H  are  G_USERDEF and
  120.       USERBLK.
  121.  
  122.            The  RCS does not support the creation of G_USERDEF  objects,
  123.       since  it  has  no idea how they will be drawn  by  your  program.
  124.       Instead,  you  must insert a dummy object into your resource where
  125.       you  want  the  G_USERDEF  to appear,  and  patch  it  after  your
  126.       application performs its resource load.
  127.  
  128.            You   must  replace  the  object's  existing   OB_TYPE   with
  129.       G_USERDEF,  though you may still use the upper byte as an Extended
  130.       Type.   You  must  also  change the OB_SPEC field to be  a  32-bit
  131.       pointer  to  a USERBLK structure.   An USERBLK is simply two  LONG
  132.       (32-bit)  fields.   The  first is the address of the drawing  code
  133.       associated  with  the  user  defined object.   The  second  is  an
  134.       arbitrary 32-bit value assigned to the object by your application.
  135.  
  136.            You can designate objects for conversion to G_USERDEFs in the
  137.       normal fashion by assigning them names which are referenced one by
  138.       one in your initialization code.   You can also combine two tricks
  139.       by using the Extended Type field as a designator for objects to be
  140.       converted to G_USERDEF.  Each tree can then be scanned for objects
  141.       to  be converted.   There is a short code segment in the  download
  142.       which demonstrates this technique.
  143.  
  144.            My  usual  convention  is to define new  drawing  objects  as
  145.       variants  of  existing objects,  using the Extended Type field  to
  146.       designate the particular variation.   Thus an Extended Type of one
  147.       might designate a G_BUTTON with rounded corners,  while a value of
  148.       two  could  flag  a G_STRING of boldface text.   When  using  this
  149.       technique,  the  RCS can be used to build a rough facsimile of the
  150.       dialog  by inserting the basic object type as  placeholders.   The
  151.       existing  OB_SPEC  information can then be copied  to  the  second
  152.       position in the USERBLK when the object is initialized.
  153.  
  154.            One final note before moving on:  There is no reason that the
  155.       USERBLK  cannot be extended beyond two fields.   You might want to
  156.       add  extra words to store more information related to drawing  the
  157.       object, such as its original type.
  158.  
  159.            The  AES will call your drawing code whenever  the  G_USERDEF
  160.       needs  to be drawn.   This occurs when you make an objc_draw  call
  161.       for its tree,  or when an objc_change occurs for that object.   If
  162.       your user-defined object is in a menu drop-drop, then your drawing
  163.       code will be called any time the user exposes that menu.
  164.  
  165.            Before  getting  into the details of the AES  to  application
  166.       calling  sequence,  some  warnings are in order.   First,  remember
  167.       that your drawing code will execute in the AES' context, using its
  168.       stack.   Therefore,  be careful not to overuse the stack with deep
  169.       recursion, long parameter lists, or large dynamic arrays.  Second,
  170.       the  AES  is NOT re-entrant,  so you may not make ANY calls to  it
  171.       from within a G_USERDEF procedure.   You may,  of course, call the
  172.       VDI.   Finally,  realize  that drawing code associated with a menu
  173.       object may be called by the AES at any time.   Exercise great care
  174.       in  sharing  data  space between such code and  the  rest  of  the
  175.       application!
  176.  
  177.            When your drawing code is called by the AES, the stack is set
  178.       up  as if a normal procedure call had occured.   There will be one
  179.       parameter  on the stack:  a 32-bit pointer to a PARMBLK structure.
  180.       This structure lies in the AES' data space, so do not write beyond
  181.       its end!
  182.  
  183.            The  PARMBLK contains 15 words.   The first two are the  long
  184.       address of the object tree being drawn,  and the third word is the
  185.       number of the G_USERDEF object.   You may need these values if the
  186.       same  drawing  code is used for more than one object  at  a  time.
  187.       Words  four  and five contain the previous  and  current  OB_STATE
  188.       values of the object.  If these values are different, your drawing
  189.       code  is  being  called in response  to  an  objc_change  request.
  190.       Otherwise, the active AES call is an objc_draw.
  191.  
  192.            Words six through nine contain the object's rectangle on  the
  193.       screen.   Remember  that  you cannot call objc_offset  within  the
  194.       drawing code,  so you will need these values!  The next four words
  195.       contain the clipping rectangle specified in the active objc_change
  196.       or objc_draw call.   You should set the VDI clip rectangle to this
  197.       value before doing any output to the screen.
  198.  
  199.            The last two words in the PARMBLK contain a copy of the extra
  200.       32-bit parameter from the object's USERBLK.   If you have followed
  201.       the  method of copying an OB_SPEC into this location,  these words
  202.       will be your pointer to a string, or BITBLK, or whatever.
  203.  
  204.            When  your drawing routine is done,  it should return a  zero
  205.       value  to  the AES.   This is a "magic" value;  anything else will
  206.       stop the drawing operation.
  207.  
  208.            The download contains a sample drawing routine which  defines
  209.       one extended drawing object,  a rounded rectangle button.  You can
  210.       use  this procedure as a starting point for your own User  Defined
  211.       objects.
  212.  
  213.            PUT ANYTHING YOU WANT ON THE DESKTOP!    In ST PRO GEM #2,  I
  214.       described the use  of  the WF_NEWDESK wind_set  call to substitute
  215.       your own object tree for  the  normal  green  desktop  background.
  216.       If the tree you supply contains User Defined objects, you can draw
  217.       whatever you want on the  desktop!  Some  of  the things you might
  218.       try are free  hand  drawings  imported  in  metafile  format  from
  219.       EasyDraw,  or whole  screen bit images generated by Degas.  If you
  220.       do the latter, you will have to store  the entire image off screen
  221.       and blit parts of it to the display as requested.
  222.  
  223.            In  any case,  remember that your desktop drawing code can be
  224.       called any time that a window is moved,  so exercise the same care
  225.       as with a menu drawer.   Also, be aware that making the WF_NEWDESK
  226.       call  does  not force an immediate redraw of the desktop.   To  do
  227.       that, do a form_dial(3) call for the entire desktop rectangle.
  228.  
  229.            THE TOUCHEXIT FLAG. The TOUCHEXIT attribute is an alternative
  230.       to  the  more  usual  EXIT.   When  the TOUCHEXIT bit is set in an
  231.       object's OB_FLAG word,  the  form_do routine will exit immediately
  232.       when the mouse button is pressed with the cursor over the  object.
  233.       Your  code  can immediately take control of the mouse and display,
  234.       without  waiting  for  the release of the button.  This method  is
  235.       used  for  generating effects such as slider bars within otherwise
  236.       normal dialogs.
  237.  
  238.            The  easiest  way to code a TOUCHEXIT handler is to  place  a
  239.       loop  around the form_do call.   If the object number returned  is
  240.       TOUCHEXIT,  then the animation procedure is called,  followed by a
  241.       resumption   of  the  form_do  (WITHOUT  recalling  form_dial   or
  242.       objc_draw!).   If the object returned is a normal EXIT, the dialog
  243.       is  complete and control flows to the cleanup code.
  244.  
  245.            There is one idiosyncrasy of TOUCHEXIT which should be noted.
  246.       When the AES "notices" that the mouse button has been pressed over
  247.       a TOUCHEXIT,  it immediately retests the button state.   If it has
  248.       already  been  released,  it  waits to see if  a  double click  is
  249.       performed.  If so, the object number returned by form_do will have
  250.       its  high  bit set.   If you don't care about double clicks,  your
  251.       code should mask off this flag.   However, you may want to use the
  252.       double click to denote some enhanced action.  For example, the GEM
  253.       file selector uses a double click on one of the file name  objects
  254.       to indicate a selection plus immediate exit.
  255.  
  256.            THE INDIRECT FLAG.  If the INDIRECT bit is set in an object's
  257.       OB_STATE  word,  the  AES interprets the 32-bit OB_SPEC field as a
  258.       pointer to the memory  location  in which the ACTUAL OB_SPEC is to
  259.       be found.   Like  User  Defined  objects,  this  capability is not
  260.       supported by the RCS, so  you  have to set up the INDIRECT bit and
  261.       alter the OB_SPEC at run time.
  262.  
  263.            The value of INDIRECT is that you can use it to associate  an
  264.       AES  object with other data or code.   The general technique is to
  265.       set  up  a table with a spare 32-bit location  at  its  beginning.
  266.       Then,  when initializing the application's resource,  you move the
  267.       true  OB_SPEC  into  this location,  set the  INDIRECT  flag,  and
  268.       replace the OB_SPEC field with a pointer to the table.  The object
  269.       behaves normally during drawing and form handling. However, if you
  270.       receive  its  number  from  form_do  or  objc_find,  you  have  an
  271.       immediate  pointer to the associated table,  without having to  go
  272.       through a lookup procedure.
  273.  
  274.            This  technique works well in programs like the GEM  Desktop.
  275.       Each  G_ICON object is set up with INDIRECT.   Its OB_SPEC goes to
  276.       the  beginning of a data area defining the associated  file.   The
  277.       blank  location at the beginning of file table is filled  up  with
  278.       the former OB_SPEC, which points to a ICONBLK.
  279.  
  280.            You  can also combine INDIRECT with TOUCHEXIT  when  creating
  281.       objects  that  must change when they are clicked by a  user.   For
  282.       instance,  a  color  selection  box  might be linked  to  a  table
  283.       containing  the various OB_SPEC values through which  the  program
  284.       will cycle.   Each time the user clicked on the box, the TOUCHEXIT
  285.       routine would advance the table pointer,  copy the next value into
  286.       the  dummy OB_SPEC location at the front of the table,  and redraw
  287.       the object in its new appearance.
  288.  
  289.            A  programmer  who wanted to follow a  truly  object-oriented
  290.       "Smalltalkish" approach could use the INDIRECT method to bind  AES
  291.       drawing  object to tables of associated procedures  or  "methods".
  292.       For instance, one procedure could be flagged for use when the user
  293.       clicked  on the object,  one when the object was dragged,  one for
  294.       double-click,  and  so on.   If the table structure was capable of
  295.       indicating  that  the true method was stored in another  table,  a
  296.       rudimentary form of class inheritance could be obtained.
  297.  
  298.            INSTANT CO-ROUTINES.     We turn to the AES event and message
  299.       system for this  trick.    While  some  languages  like  Modula  2
  300.       provide a method for implementing co-routines,  there  is  no such
  301.       capability in C.  However, we can effectively fake it by using the
  302.       AES  event library.
  303.  
  304.            As  already  seen in an earlier column,  an  application  can
  305.       write a message to its own event queue using the appl_write  call.
  306.       Usually, this is a redraw message, but there is nothing to prevent
  307.       you from using this facility to send messages from one routine  in
  308.       your program to another.  To set up co-routines using this method,
  309.       they  would  be  coded  as separate  procedures  called  from  the
  310.       application's main event loop.
  311.  
  312.            When  one  of the co-routines wanted to call  the  other,  it
  313.       would  post  a message containing the request and  any  associated
  314.       parameters into the application's queue and then return.  The main
  315.       loop  would find the message and make the appropriate call to  the
  316.       second co-routine.  If it was necessary to then re-enter the first
  317.       co-routine  at  the  calling point,  the  original  message  could
  318.       contain an imbedded reply message to be sent back when the request
  319.       was  complete.   A  simple switch structure could then be used  to
  320.       resume at the appropriate point.
  321.  
  322.            There  are two potential problems in using this method.   The
  323.       first is the limited capacity of the application event queue.  The
  324.       queue  contains  eight entries.    While the AES  economizes  this
  325.       space  by  merging redraws and multiple events,  it  cannot  merge
  326.       messages.   Because  of this limit,  you must be extremely careful
  327.       when  one  message received has the potential to generate  two  or
  328.       more  messages sent.   Unless this situation is carefully managed,
  329.       you  can get a sort of "cancer" which will overflow the queue  and
  330.       probably crash your application.
  331.  
  332.            The second danger involves race conditions.   Message sent by
  333.       the  application  are posted to the end of the  queue.   If  other
  334.       events  have occurred,  such as mouse clicks or keyboard  presses,
  335.       they will be seen and processed ahead of the application generated
  336.       message.   This  implies  that you cannot use this method  if  the
  337.       program must complete its action before a new user generated event
  338.       can be processed.
  339.  
  340.            THAT'S ALL FOR NOW.   Hopefully  these hints  will  keep  you
  341.       profitably occupied for  a  while.   ST PRO GEM number twelve will
  342.       return to the topic of user interfaces.   Reaction  to  the  first
  343.       article on this  subject was mostly  positive,  but a lot of folks
  344.       wanted to see real code  as  well.   In response to your feedback,
  345.       there will also be code for implemented your own "mouse sensitive"
  346.       objects which highlight when the cursor touches  them.  This  will
  347.       be presented as part of an alternate form manager.
  348.  
  349.            UPDATE: ATARI ST.  I have recently gotten more information on
  350.      some topics mentioned in earlier articles.   These notes will bring
  351.      you up to date:
  352.  
  353.            A number of developers reported that they were unable to  get
  354.       the  self-redraw  technique described in ST PRO GEM  #2  to  work.
  355.       This is usually due to a bug in the appl_init binding in Alcyon C.
  356.       The  value  returned from the function,  which would  normally  be
  357.       assigned  to gl_apid,  is coming back as garbage.   To work around
  358.       the problem,  declare EXTERN WORD gl_apid;  in your program and DO
  359.       NOT  assign the value from appl_init.   The binding WILL make  the
  360.       assignment.  A tip of the hat to Russ Wetmore for this report.
  361.  
  362.            The   last  column  mentioned  that  turning  off  the   clip
  363.       rectangle  while drawing graphics text will speed things  up.   It
  364.       turns  out that the VDI will also run at the non-clipped speed  if
  365.       the  ENTIRE  string  to  be written is  within  the  current  clip
  366.       rectangle.  To compound the problem, there is a one-off bug in the
  367.       detection  algorithm  for  the right  edge.   That  is,  the  clip
  368.       rectangle  has to be one pixel BEYOND the right edge of  the  text
  369.       for the fast write to work.
  370.  
  371.            The Feedback in ST PRO GEM #10 mentioned that there are known
  372.       bugs  in  the  Alcyon C floating point  library.   In  fact,  this
  373.       library  has been replaced with a new,  debugged version in recent
  374.       shipments  of the Toolkit.   If you need to use floating point and
  375.       have  run into this bug,  you should be able to get an update from
  376.       Atari.   Also,  check  the  Atari  Developer's SIG (PCS-57) for  a
  377.       possible download.
  378.  
  379.            In addition, it turns out there is an undocumented feature in
  380.       Alcyon  C  which allows you to imbed assembly code  in-line.   Try
  381.       using:
  382.  
  383.            asm(".....");
  384.  
  385.       where the dots are replaced with an assembly instruction.  You get
  386.       one instruction per asm(),  one asm() per line.  Thanks to Leonard
  387.       Tramiel for both of the above tidbits.
  388.  
  389.  
  390.  
  391.  
  392. >>>>>>>>>>>>>>> Sample code for initializing User Objects <<<<<<<<<<<<<<<<
  393.  
  394. GLOBAL    USERBLK extobjs[MAX_OBJS];    /* APPLBLK defined in OBDEFS.H    */
  395. GLOBAL    WORD    n_extobjs;        /* Set MAX_OBJS to total user   */
  396.                     /* objects in resource        */
  397.  
  398.     VOID
  399. obj_init()                /* Scan whole resource for user   */
  400.     {                /* objects.  Uses map_tree()      */
  401.     LONG    tree, obspec;        /* from GEMCL5.C          */
  402.     WORD    itree, i, obj;
  403.  
  404.     n_extobjs = 0;            /* Replace TREE0 with your first  */
  405.                     /* tree, TREEN with the last      */
  406.     for (itree = TREE0; itree <= TREEN; itree++)
  407.         {
  408.         rsrc_gaddr(R_TREE, itree, &tree);
  409.         map_tree(tree, ROOT, NIL, fix_obj);
  410.         }
  411.     }
  412.  
  413.     WORD
  414. fix_obj(tree, obj)            /* Code to check and fix up    */
  415.     LONG    tree;            /* a user defined object    */
  416.     WORD    obj;
  417.     {
  418.     WORD    hibyte;
  419.  
  420.     hibyte = LWGET(OB_TYPE(obj)) & 0xff00;        /* check extended */
  421.     if (!hibyte)                    /* type - if none */
  422.         return (TRUE);                /* ignore object  */
  423.     extobjs[n_extobjs].ub_code = dr_code;        /* set drawcode   */
  424.     extobjs[n_extobjs].ub_parm = LLGET(OB_SPEC(obj)); /* copy obspec  */
  425.     LLSET(OB_SPEC(obj), ADDR(&extobjs[n_extobjs]));   /* point obspec */
  426.     LWSET(OB_TYPE(obj), G_USERDEF | hibyte);    /* to userblk &   */
  427.     n_extobjs++;                    /* patch type     */
  428.     return (TRUE);
  429.     }
  430.  
  431.     
  432. >>>>>>>>>>>>>>>>>>>>>> Sample User Object Drawing Code <<<<<<<<<<<<<<<<<<<<
  433. >>>>>>>>>>>>>>>>>>>>>> Implements Rounded Box based    <<<<<<<<<<<<<<<<<<<<
  434. >>>>>>>>>>>>>>>>>>>>>> on G_BOX type               <<<<<<<<<<<<<<<<<<<<
  435.  
  436.     WORD
  437. dr_code(pb)                /* Sample user object drawing   */
  438.     PARMBLK    *pb;            /* code.  Caution: NOT portable */
  439.     {                /* to Intel small data models   */
  440.     LONG    tree, obspec;
  441.     WORD    slct, flip, type, ext_type, flags;
  442.     WORD    pxy[4];
  443.     WORD    bgc, interior, style, bdc, width, chc;
  444.     
  445.     tree = pb->pb_tree;
  446.     obspec = LLGET(pb->pb_parm);    /* original obspec from USERBLK  */
  447.     ext_type = LHIBT(LWGET(OB_TYPE(pb->pb_obj)));
  448.     slct = SELECTED & pb->pb_currstate;
  449.     flip = SELECTED & (pb->pb_currstate ^ pb->pb_prevstate);
  450.     set_clip(TRUE, &pb->pb_xc);    /* These two routines in GEMCL9.C */
  451.     grect_to_array(&pb->pb_x, pxy);    
  452.  
  453.     switch (ext_type) {
  454.         case 1:            /* Rounded box       */
  455.                     /* Crack color word  */
  456.             get_colrwd(obspec, &bgc, &style, &interior,
  457.                 &bdc, &width, &chc);
  458.                     /* For select effect, use char color */
  459.             if (slct)    /* In place of background         */
  460.                 bgc = chc;
  461.                     /* Fill in background             */
  462.             rr_fill(MD_REPLACE, (width? 0: 1), bgc, interior, 
  463.                 style, pxy);
  464.                     /* Do perimeter if needed         */
  465.                     /* rr_perim is in GEMCL9.C         */
  466.             if (width && !flip)
  467.                 {
  468.                 pxy[0] -= width; pxy[2] += width; 
  469.                 rr_perim(MD_REPLACE,bdc,FIS_SOLID,width,pxy);
  470.                 }
  471.             break;
  472.         default:        /* Add more types here            */
  473.             break;
  474.         }
  475.     return (0);
  476.     }
  477.  
  478.     VOID                /* Cracks the obspec color word    */
  479. get_colrwd(obspec, bgc, style, interior, bdc, width, chc)
  480.     LONG    obspec;
  481.     WORD    *bgc, *style, *interior, *bdc, *width, *chc, *chmode;
  482.     {
  483.     WORD    colorwd;
  484.  
  485.     colorwd = LLOWD(obspec);
  486.     *bgc = colorwd & 0xf;
  487.     *style = (colorwd & 0x70) >> 4;
  488.     if ( !(*style) )
  489.         *interior = 0;
  490.     else if (*style == 7)
  491.         *interior = 1;
  492.     else if (colorwd & 0x80)    /* HACK: Uses character writing mode */
  493.         *interior = 3;        /* bit to select alternate interior  */
  494.     else                /* styles!                 */
  495.         *interior = 2;
  496.     *bdc = (colorwd & 0xf000) >> 12;
  497.  
  498.     *width = LHIWD(obspec) & 0xff;
  499.     if (*width > 127)
  500.         *width = 256 - *width;
  501.  
  502.     if (*width && !(*width & 0x1))        /* VDI only renders odd */
  503.         (*width)--;            /* widths!        */
  504.  
  505.     *chc = (colorwd & 0x0f00) >> 8;        /* used for select effect */
  506.     }
  507.  
  508.     VOID                /* Fill a rounded rectangle    */
  509. rr_fill(mode, perim, color, interior, style, pxy)
  510.     WORD    mode, perim, color, style, interior, *pxy;
  511.     {
  512.     vswr_mode(vdi_handle, mode);
  513.     vsf_color(vdi_handle, color);
  514.     vsf_style(vdi_handle, style);
  515.     vsf_interior(vdi_handle, interior);
  516.     vsf_perimeter(vdi_handle, perim);
  517.     v_rfbox(vdi_handle, pxy);
  518.     }
  519.