
       [42m                             [42m
       [42m    ARexx Ports Revisited    [40m   [33;3mby John Collett[0m
       [42m                             [40m


         A follow-up to a tutorial in MD28b with the title :
         'ARexx : First Steps in Inter-Process Communication'


  [32m <> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <>
[0m

    In the previous article I described how simple acts of communication
    can be carried out between various pairs of processes, in different
    combinations :

          A text editor  :  A CLI/Shell window  :  An ARexx program

    Because of lack of space, cowardice, or ignorance, there were at
    least two of the pairings which did not get the treatment they
    deserved, and I made a mental note to return to the topic at a later
    date.  That date has arrived, and here is further discussion of IPC
    in the following situations :

      [32m  1  Shell window        ------------>     Text Editor [31m

      [32m  2  ARexx Program #1    ------------>     ARexx Program #2[31m


   [42m  1  Shell window  ->  Text Editor    [40m

    One or two of my regular readers (there [4mmust[0m be that many) will
    know that I am writing this using the QED text editor.  I've been using
    it for years, I still think it's good, and have no wish to change,
    however glowing the praise I read of other bigger and more complex
    systems.

    Before starting any session with QED, I could write a single-line but
    complete ARexx program in a CLI window : rx 'say show("P",,"0a"x)'
    For those of you still coming to terms with ARexx (yes, this is
    another ARexx tutorial in disguise), it means :

           [32mrx    : Run an ARexx program.
           [32msay   : Display the results on this screen.
           [32mshow  : Show me ..
           [32m"P"   : .. what ports are open.
           [32m,,    : (two commas) I am not interested in any particular port.
           [32m"0a"x : Display the output in a column.

    Even immediately after logging on, this action produces a list of
    seven entries, the details of which need not concern us here.

    The first time I use QED in any work session, it is copied into RAM:.
    It needs a stack of 10000, so at its launch a little batch file does
    two jobs : increases the stack size, and runs the program with the
    command  'run >NIL: -PMyport RAM:QED'. The argument '-PMyport'
    assigns an ARexx port named Myport to the QED process. Such an
    allocation of a port in QED is optional, but it generates no
    overheads, and means that I can always do the kind of thing I am
    going to be talking about.

    Once QED is running, I can resize its window to make room, repeat the
    little 'show("P")' exercise in a CLI window below it, and observe the
    results.  The list will now contain [4meight[0m entries.  The extra
    one is 'MYPORT' - as expected, but it's nice to have it confirmed.

    Using the 'show("P")' instruction while any program is running is a
    handy way of finding its ARexx port name, if is has one.  This method
    may be easier than hunting through the docs, and the information you
    obtain by it is certainly correct.  Merrill Callaway, for example, in
    his book [4mThe ARexx CookBook[0m (Whitestone, Albuquerque, 1992,
    p.7-18 and p.7-23 respectively) states that, among other problems,
    the port names as given in the docs relating to 'Electric Thesaurus'
    (SoftWood, Inc., Phoenix) and 'Proper Grammar' (ditto) were incorrect
    in both cases.

    Try the show() function before and then while your Text Editor is
    running.  If no additional port name is listed, then it hasn't got an
    ARexx port available.  If there is no way of adding one (like the
    '-PMyport' I mentioned) then, face it, you need a better editor.

    Now what ARexx ports are all about is IPC (Inter-Process Communication,
    in case you'd forgotten).  With two visible processes running - a QED
    window open on the top half of my screen, and a CLI window below - what
    can I do in one that will have an effect on the other?  I gave some
    examples in the earlier article, and the following is an extension to
    that discussion. It doesn't matter that the examples I use here are
    trivial and will certainly never be needed.  In any case, macros within
    QED have such enormous potential that, once you are in QED, you are
    unlikely to need to use any [4mseparate[0m ARexx process to communicate
    with external tools such as a thesaurus.  My main concern here is the
    principle of IPC and how it works.

    In the file I am now writing, that is, the text you are now reading, I
    am going to insert some empty lines.  Then, working from my CLI
    window, I'll put something into them via 'Myport'.  Here we go.

            [32mThese words were typed in my CLI window.
            [32mThe sum of 123 and 456 is 579
            [32mToday's date is 2 June 1993
            [32mThe time is 7:20AM

    Now I'm back in my QED window again.  Those four lines, of which I have
    just adjusted the position and colour but not changed the content in
    any way, were produced by the following entries in my CLI window:

     [32mrx "address 'MYPORT' 'TEXT These words were typed in my CLI window.'"
     [32mrx "address 'MYPORT' 'TEXT The sum of 123 and 456 is' 123+456"
     [32mrx "address 'MYPORT' 'TEXT Today''s date is 'date('n')"
     [32mrx "address 'MYPORT' 'TEXT The time is 'time('c')"

    I trust you are impressed by my working hours.  What ARexx did with
    those four lines was to send straight to QED, via Myport, anything
    it didn't recognize as an ARexx instruction, and QED dealt with those
    pieces (all the 'TEXT ..' entries) in its own way.  The rest, the
    pieces ARexx did recognise (the addition, date() and time() in this
    case) were recognised as ARexx instructions, interpreted, and the
    results sent through the specified port.

    Each of the four lines above beginning with 'rx' is a complete and
    separate ARexx program run in something resembling what we used to call
    'immediate mode' back in the days of Basic.  All four lines could of
    course have been combined into one pre-written program containing just
    the four 'address' entries.  Such a program is only a token indication
    of what could be done in this way.

    QED makes it very easy, since it looks after most of the knotty
    problems concerning ports.  One of the purposes of what you have read
    so far was to provide a gentle introduction, but the next section is a
    bit tougher.

   [42m  2  ARexx Programs #1 and #2  [40m

    What I particularly wanted to check out and report on in this second
    phase was sending a message from one ARexx program (in a plain Shell
    console) to another one in which the following have been carried out :

    [42m 1 [40m The library 'rexxarplib.library' has been added to the active
        Library List using 'addlib()'
    [42m 2 [40m A Host and associated Port have been created using the function
        'CreateHost()'
    [42m 3 [40m A window has been opened using 'OpenWindow()'.

    The existence of the Port should make it possible to create and
    manipulate Intuition-type features [4mfrom another program[0m.
    As you see, this is somewhat more ambitious than what was covered in
    the earlier article, or in the first part of this one.

    I have two programs, called 'Crayon' and 'SketchPad'. 'Crayon' sends
    messages to  'SketchPad', but 'SketchPad' has to be run first.  It sits
    and waits for messages from 'Crayon'.  If the latter is run first, it
    has nothing to send its messages to, and you get a polite reminder.
    Before we examine the listings in detail, including of course the 'port'
    handling, there are certain aspects of the programs that call for
    explanation.

    [42m  The screen layout  [40m

   [42m (1) [40m Running from icons will be described later, but for now we'll
        do it from CLI.  Start with a small CLI window bottom left.
        It just needs to be big enough to enter :  rx SketchPad
        SketchPad will open (2) and (3), size and position as required.
   [42m (2) [40m Enter : rx Crayon
        Then enter messages you wish to send, as described below.
        It could have been arranged to get 'SketchPad' to run 'Crayon'
        for you automatically, but I have left it so that you have to type
        in the 'rx Crayon' yourself, in order to underline the idea that
        'Crayon' is a [4mseparate[0m process.  When you finish, the
        closing of the 'Crayon' window will also be up to you.
   [42m (3) [40m No user entries required.

         [32;41m                              [31;40m
         [32;41m  (3) This is the             [31;40m    Behind the scenes, as you
         [32;41m  window which will           [31;40m    run 'Crayon', a file will
         [32;41m  display the results         [31;40m    be constructed containing
         [32;41m  of the commands you         [31;40m    a record of the messages
         [32;41m  enter in (2)                [31;40m    which are sent from (2)
         [32;41m                              [31;40m    to (3).
         [32;41m                              [31;40m
         [32;41m                              [31;40m
         [32;41m                              [31;40m

         [32;41m (1)     [40m [41m (2)                [40m
         [41m         [40m [41m                    [40m


    [42m  Saving the script of a program session  [40m

    For all sorts of reasons, it seemed a good idea to keep a record of
    commands sent via 'Crayon', and a file called 'crayonscript' is
    automatically opened for that purpose. It overwrites any existing file
    of that name, so if you are likely to want to look at its contents
    later or to use it for the 'script' command as explained in the next
    paragraph, rename 'crayonscript' as 'script' or copy it to a new file
    of that name as soon as possible.

    If 'SCRIPT' or 'script' is entered in the 'Crayon' program, the text of
    any existing file called 'script' is interpreted, so you can re-run any
    previous session if you saved its 'crayonscript' as just described.
    Meanwhile, in a text editor, you may have made adjustments to entries
    which determine, say, colours or positions of elements on a screen.  Or
    you may have made more dramatic changes such as inserting a 'do' loop
    with incrementing x and y values for drawing sets of lines.  You can
    see the effect of your alterations by entering 'script'.

    Just to show you what such a loop can do, I have included one small
    example in the 'Crayon' program itself.  Give the entry 'Web' to see
    how it performs.

    [42m  Saving (a part of) the screen  [40m

    There needs to be some way of saving what you produce.  'Rexxarplib'
    includes a LoadIff() function.  Pity there isn't a SaveIff() as well.
    Of the methods I tried, 'ClipIt' was easily the best (my source was
    MegaDisc's UT86).  I tried various versions of 'Snap', but found its
    behaviour a bit erratic. 'ScreenSave' (from Fish Disk 185) was
    unbelievably cumbersome, and though it worked, it generated odd error
    messages.

    'ClipIt' is invoked from 'Crayon' by the entry 'Clip', and is expected
    to be in the current directory (or in C:). It allows you to select an
    area from the screen, has a nicely designed requester, and performs
    well. To pick out the area to be saved, you press the 'Alt' key, then
    click and drag the mouse between diagonal corners of the intended
    rectangle.  A copy appears at once upper left on your screen, and it
    can be saved via the ClipIt requester.  I won't give any more details,
    since it's all pretty intuitive.

    Perhaps I should mention that I always have AutoCLI running, and my
    normal settings include a pointer-locking option so that I can draw
    straight lines more easily in DPaint, for example.  Holding down 'Ctrl'
    while moving the mouse restricts it to a vertical movement; holding
    down Alt restricts it to horizontal.  But in ClipIt you need to scan
    over a rectangle with the Alt key depressed.  I have learned to get
    around it with a particular arrangement of before-and-after clicks, but
    I could also press LeftAmiga/Help to get at the AutoCLI options, and
    turn off the Pointer Lock.

                        -----------------------

    Now we're ready to consider the two programs in detail. It takes the
    form of an explanatory listing of the steps in the programs. To help
    you find your way, I will number the steps, and add the numbers as
    comments to the program listings which are attached to the end of
    this article.

    /* SketchPad */

   [42m 1 [40m  Set up at least some error checking, in particular to save you
    from the results of entering incorrect arguments to any of the
    functions you send from 'Crayon', which is easily done. You get a
    message such as 'Wrong number of arguments' and can immediately try
    again.

   [42m 2 [40m  Use 'show("L")' to check whether required libraries have been
    added to the active list, and add them if necessary.

   [42m 3 [40m  Use the 'CreateHost()' function which will enable you later
    on to open an Intuition-type window. The important point is that
    this is the moment at which the required message ports are created.
    'Ports' is in the plural, because what we need (if I understand it
    correctly, though documentation on this area is generally not very
    clear) is one port for sending and one for receiving.  Willy Langeveld,
    author of 'rexxarplib', refers to the ports as 'controlport' and
    'notifyport'.  In the programs below I have egoistically called them
    JCHOST and JCPORT.

    A warning based on experience :  When either of them is needed as an
    argument, it is remarkably easy to type in the wrong one.

   [42m 4 [40m  The creation of the ports is not instantaneous, and it
    is not a good idea to continue until you are sure that they exist.
    'WaitForPort' is provided for this purpose, and should be in your
    Rexxc: directory or its equivalent.  If you haven't got it, construct a
    loop 'do until show("P",JCHOST)', that is, until the question 'Does
    there exist a Port named JCHOST?' returns a positive reply.  If it
    never does, you've gone wrong somewhere, so you might like to build in
    a counter to set a maximum number of tries, just to be on the safe
    side, with perhaps a 'delay(10)' in the loop, and an 'exit' to allow
    you to escape.

   [42m 5 [40m  Before opening the window, you need to define which of the
    standard window gadgets you wish it to contain.  I have included all
    four in the 'flags' setting here, just for convenience and the added
    security they give, but none of them need be used in what we are doing.
    As I have said elsewhere, I like the 'BORDERLESS' option, but have not
    used it here.

   [42m 6 [40m  The 'idcmp' settings declare what kinds of messages the
    window should expect to receive.  We shall see later on that 'Crayon'
    will be sending strings of characters, so 'VANILLAKEY' needs to be set
    in order to receive them.  If an empty string is sent, it is
    interpreted as 'CLOSEWINDOW', so that also needs to be set.

    (Have I got this right?  My impression is that the 'flags' and 'idcmp'
    settings do not interact in a consistent way.  The flag 'WINDOWCLOSE'
    will provide a 'Close' gadget, which will not work unless you also
    include 'CLOSEWINDOW' in the idcmp list.  But the other gadget flags
    such as 'WINDOWDRAG' will work even if you do not include them in the
    idcmp settings.)

   [42m 7 [40m   Open the window to your required dimensions.

   [42m 8 [40m   Open the port.  Activate the window.

   [42m 9 [40m   Now that the window and port are set up, it is safe to run
    'Crayon' from another Shell window.  For your convenience, that window
    is now opened, with size and position as required.  Closing it is up to
    you.

   [42m 10 [40m   The 'start:' label lets the program come back to this point
    so that you can try again if you make a syntax error such as 'Wrong
    number of arguments'.

   [42m 11 [40m  A 'do forever' loop starts. It will normally end only
    when the CLOSEWINDOW message is received.

   [42m 12 [40m  Waitpkt() and getpkt() : the program waits for a packet
    to arrive, and then opens it to see if it contains anything.

   [42m 13 [40m  If the packet is not empty, then the program has to 'do'
    something about it.

   [42m 14 [40m  It has to read the message using 'getarg()' and acknowledge
    its receipt using 'reply()'.  This is more than common courtesy.
    Apparently a 'reply()' is expected, and the system may be upset by its
    failure to arrive.

   [42m 15 [40m  If the message was not 'CLOSEWINDOW', then it is to be
    interpreted.  'Interpret' means "Execute ARexx instructions contained
    in a string", that is, "Carry them out as you would any piece of ARexx
    coding."  If we get this far - the message has been sent, received,
    opened, acknowledged, and read - then our instructions stand a good
    chance of being carried out.  A string to be interpreted may contain
    just one tiny instruction, a great long list, a complete program, an
    instruction to run another program ... and so on.  'Interpret' is a
    [4mvery[0m powerful instruction.

   [42m 16 [40m  Loop ends. Program exits.

   [42m 17 [40m  Some error trapping procedures, probably not enough.


   /* Crayon - to send instructions to 'SketchPad' */

   [42m 18 [40m  If the required port has not been created by running 'SketchPad,
    display a reminder to the user, and exit.

   [42m 19 [40m  Open 'crayonscript' to store a record of the current session.

   [42m 20 [40m  Arrange for a small prompt (not really necessary), and then
    start a loop to obtain user inputs via 'parse pull', until the input
    is an empty string (just pressing 'Return').

   [42m 21 [40m  Store the entry in 'crayonscript'.

   The following items are in a 'select .. when' set :

   [42m 22 [40m  When the input is empty, send 'CLOSEWINDOW' to 'SketchPad',
    and exit.

   [42m 23 [40m  When the entry is 'Clip', save a part of the screen.

   [42m 24 [40m  When the entry is 'Script', execute a stored 'script'.
    Don't make such an entry if no file called 'script' exists!

   [42m 25 [40m  A sample of a pattern of lines drawn by executing a loop.

   [42m 26 [40m  This is the 'otherwise' option in 'select'.  Anything that
    has not been caught in items 22 to 25, comes through to here.  This is
    likely to be by far the most common type of entry, and purists would
    say that it should be the first in the list, to save the program having
    to make all the preceding 'when' discriminations.  Yes, but putting in
    the 'otherwise' spot has made it very easy to define!

   [42m 27 [40m   'Crayon' is designed to pass 'rexxarplib' function calls
    to 'SketchPad'.  For example, if you wanted to have a line drawn from
    100,50 to 300,150 by the Move() and Draw() functions,  'SketchPad'
    would need to receive the following instruction(s) :

          [42mcall Move(JCHOST,100,50) ; call Draw(JCHOST,300,150)[40m

    'Crayon' makes it a bit easier.  You only need to enter

          [42mMove 100,50[40m  and then  [42mDraw 300,150[40m

    Parsing and concatenation in 'Crayon' convert these shorter and
    therefore more convenient entries into the complete forms.  The
    'address' instruction is used to send the result to the waiting port.

   [42m 28 [40m  The entry has been parsed and concatenated as 'str', and an
    early check confirmed that JCPORT was ready and waiting.  All we have
    to do now is send the message.

    Remember the "address MYPORT 'TEXT '" shown in the first part of this
    article?  The equivalent needed here is simply 'address JCPORT str'


    [42m  A few more examples  [40m

        What you need to send ..          .. and what you enter

    For a colour change
        [32mcall SetAPen(JCPORT,3)                SetAPen 3[31m
    A filled rectangle
        [32mcall RectFill(JCPORT,20,20,100,300)   RectFill 20,20,100,300[31m
    An ellipse
        [32mcall DrawEllipse(JCPORT,100,50,40,80) DrawEllipse 100,50,40,80[31m
    A text string
        [32mcall Text(JCPORT,'This text')         Text 'This text'[31m
    Text with line breaks
        [32mcall WindowText(JCPORT,'One\two')     WindowText 'One\two'[31m

    And so on through the range of Intuition-type features available from
    'rexxarplib.library' : menus, gadgets, requesters, fonts, etc.

        ---------------------------------------------------------
            [This is a strategically-placed commercial break]
        If you want a complete rundown on 'rexxarplib', with all
        the docs, and demos of nearly everything, get a copy of
        'SixtyPlus' on UT344 from MegaDisc.
        ---------------------------------------------------------


    [42m  Notes on setting up  [40m

    Copy the two patches below, from the initial comment line in
    'SketchPad' and 'Crayon' to the 'exit' line in each case, to files
    called 'SketchPad.rexx' and 'Crayon.rexx' - though the '.rexx' suffix
    isn't obligatory.  If you are likely to want to use 'ClipIt', put it
    in the same directory as these two programs.

    If you would rather click on an icon than open a CLI window to run the
    programs, then check that the icon type is 'Project', enter the default
    tool as 'rx' (no quotes), and insert an appropriate entry in 'Tool
    Types' :  CONSOLE=CON:0/200/248/55/SketchPad/CLOSE

    You won't need to click on the 'Crayon' icon, but if you really want
    to do so, set 'rx' as its Default Tool and a Tool Type to :
              CONSOLE=CON:250/200/380/55/Crayon/CLOSE

    A click on the 'SketchPad' icon will open the three windows as shown in
    an earlier sketch.  You will still need to enter 'rx Crayon' in the
    bottom-right window (it is a [4mseparate[0m process).  The window
    will be active, and waiting for you to type in your entry, but it might
    be a good idea to check that your mouse pointer is over the 'Crayon'
    window, even though you are not about to use the mouse. If the pointer
    is over the big output window .. well, try it and see, once you are
    familiar with the recommended procedures.

    [42m  Finally, the complete programs  [40m

     /* SketchPad : A window to receive instructions from 'Crayon'  */

    signal on error                                           /*  1 */
    signal on syntax
    if ~show('L', "rexxarplib.library") then do               /*  2 */
     check = addlib('rexxsupport.library',0,-30,0)
     check = addlib('rexxarplib.library',0,-30,0)
     end
    address AREXX '"call CreateHost(JCHOST, JCPORT)"'         /*  3 */
    if ~show('Ports','JCHOST') then                           /*  4 */
        address command 'WaitForPort JCHOST'
    flags = 'WINDOWCLOSE+WINDOWDRAG+WINDOWDEPTH+WINDOWSIZING' /*  5 */
    idcmp = 'CLOSEWINDOW+VANILLAKEY'                          /*  6 */
    call OpenWindow(JCHOST,0,0,640,190,idcmp,flags)           /*  7 */
    call OpenPort(JCPORT) ; call ActivateWindow(JCHOST)       /*  8 */
    address command 'newshell CON:250/200/380/55/Crayon/CLOSE'/*  9 */
    start:                                                    /* 10 */
    do forever                                                /* 11 */
     call waitpkt(JCPORT) ; p = getpkt(JCPORT)                /* 12 */
     if p ~== NULL() then                                     /* 13 */
      do
       i = getarg(p) ;  t = reply(p, 0)                       /* 14 */
       if i = 'CLOSEWINDOW' then
         do ; call CloseWindow(JCHOST) ; exit ; end
       interpret i                                            /* 15 */
     end  /* of 'if p etc. then do' */
    end   /* of 'do forever     */                            /* 16 */
    exit
    syntax:                                                   /* 17 */
       say errortext(RC) ; signal start ;  return
    error:
       call CloseWindow(JCHOST) ; say 'Error at line ' sigl ; exit


    /* Crayon - a program to send instructions to 'SketchPad' */

      if ~ show("P",JCPORT) then do                           /* 18 */
        say "JCPORT not available." ; say "Is SketchPad running?" ; exit
        end
      op = open(cs,'crayonscript','w')                        /* 19 */
      w = writeln(cs,'/*  Script  */')
      options prompt '->'                                     /* 20 */
      do forever
        parse pull str
        w = writeln(cs,str)                                   /* 21 */
        select
          when str = '' then do                               /* 22 */
            address JCPORT "CLOSEWINDOW" ; leave ; end
          when UPPER(str) = 'CLIP' then                       /* 23 */
            address command 'run >NIL: ClipIt'
          when UPPER(str) = 'SCRIPT' then interpret script    /* 24 */
          when UPPER(str) = 'WEB' then                        /* 25 */
            do y = 10 to 180 by 5
              x = 20 + y * 2
              call Move(JCHOST,20,y) ; call Draw(JCHOST,x,180)
              end
          otherwise do                                        /* 26 */
            parse var str command params                      /* 27 */
            params = strip(params)
            str = 'call ' || command || '(JCHOST,' || params || ')'
            address JCPORT str                                /* 28 */
            end    /* of 'otherwise' */
          end      /* of 'select'    */
        end        /* of 'do forever */
        cl = close(cs)
        exit


    [33;3mHamilton, N.Z.  June, 1993[0m

                     ------------------------------


  [32m <> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <%> 34 <>
[0m

