OPLLPC.TXT, accessing LPC functionality from OPL
================================================

Most of the macro playback facilities available in KMAC are also
available to Opl programs.  Opl programs can enhance these KMAC
facilities in numerous ways, limited only by the ingenuity of the
programmer.

A first example of calling LPC from Opl
---------------------------------------
You will need:
    a copy of LPC.DYL in a \DYL\ directory
    a copy of KMACLIB.OPO in an \OPO\ directory.

Then type the following lines into an Opl program, say TKMACLIB.OPL:

    PROC tkmaclib:
      GLOBAL lpcHand%,lpoHand%
      LOCAL pname$(64)
      LOCAL ret%
      LOADM "kmaclib"
      lpOpen:
      WHILE 1
        dInit "Goto process"
        dEdit pname$, "Process name", 20
        IF dialog=0 :BREAK :ENDIF
        ret%=lpWrite:(%g,pname$)
        IF ret%=-33
          gIPRINT "No such process"
        ELSEIF ret%<0
          gIPRINT err$(ret%)
        ELSE
          lpWrite:(%F,"")
          lpWrite:(%I,"Hello "+pname$)
          lpWrite:(%P,"20")
          lpWrite:(%B,"")
        ENDIF
      ENDWH
      lpClose:
    ENDP

Translate and run this program and see what it does.

Essentially, the user supplies the name of a process to go to.  This
could be "SYSTEM" or "Data.dbf" or whatever.  Then a sequence of 5
lpWrite: commands issue equivalents of the following KMAC commands:

   g - to go to the named process, if possible
   F - to make that process foreground
   I - to display an information message in that process
   P - to pause (in this case, for 2 seconds)
   B - to send that process into background again.

The Opl program handles any error from the "g" command.

KMAC commands not allowed in Opl programs
-----------------------------------------
Not all KMAC commands make sense in Opl programs - for example the
"s" single stepping commands, or the "r" return command.

The way to tell if a KMAC command can be used in an lpWrite:
statement in an Opl program is to consult the "Level" field for that
command in the KMAC.DBF syntax database.  Any command whose "Level"
includes "KMAC" explicitly CANNOT be used inside an Opl program.

Thus the following are excluded:
    d, e, l, m, q, r, x, s, and h
    J and j - though these can be duplicated using lpSend:
    N and Q - though an Opl program can run dialogs of its own!

Armed with the above information, anyone familiar with both
    the Opl language, and
    KMAC commands
should be able to create many Opl programs that contain LPC commands.

Launching an Opl program from KMAC
----------------------------------
An Opl program can be started from KMAC (as part of a normal macro)
by means of the "o" command.  This takes as parameter the name of the
.OPO program to run.  See EGKMA.TXT for an example.

If you wish to delay your KMAC macro until the Opl program completes,
follow the "o" command with a "w" command on the following line.

Note that an Opl program does not "inherit" any setting from the .KMA
macro that launched it.  In particular, it starts in a state in which
it is not attached to any application, and it needs to attach to an
application, by one means or another, before issuing any lpWrite: or
lpSend: instructions that would communicate with an attached process.

Using KMACLIB.OPO
-----------------
The above .OPL program relies on KMACLIB.OPO to encapsulate access to
LPC for it.  See the file KMACLIB.OPL for the source to the
procedures lpOpen:, lpWrite:, lpSend:, and lpClose:. 

Note that any Opl program that relies upon KMACLIB.OPO must declare
the GLOBAL variables lpcHand% and lpoHand% - to take, respectively,
the handle of the LPC.DYL category, and the handle of the created
LPCON class instance.

Using lpSend:
-------------
See EGKMA.TXT for an example of using lpSend: to good effect.

Whereas lpWrite: always in fact uses the LPC_ACTION method of the
LPCON class in LPC, lpSend: allows access to any of the (more than
40) different methods of LPCON.  Incidentally, whereas the LPC_ACTION
method is guaranteed never to "leave", the other methods of LPCON
invariably DO "leave" on error - which is why lpWrite: uses the Opl
SEND keyword, whereas lpSend: has to use the ENTERSEND keyword.

In order to use lpSend:, you need to know the method numbers of the
LPCON class in LPC, as well as the parameters to these methods.  To
find out about these, you ought to consult the source code for
LPC.DYL (available separately).

Summary of methods of LPCON class
---------------------------------
In summary terms, here are the method numbers of the various methods,
together with brief comments on the workings of these methods:

(the following is effectively an extract from the category definition
file, LPC.CAT)

0  LPC_DESTROY         Free buffers then root_destroy
1  LPC_INIT            Set user status word, if any
! next group of methods are asynchronous
2  LPC_ACTION          Interpret command and act on it
3  LPC_KEY             Send key with modifier
4  LPC_STRING          Send string of keys
5  LPC_PAUSE           Pause (in client) for specified time
6  LPC_MESSAGE         Cause message to be displayed
7  LPC_DISP_MENU       Display menu at given position
8  LPC_CLIENT_POS      Move process to specified client position
9  LPC_GROUND_STATE    Bring application to ground state
10 LPC_ALLCOUNT        Alloc count in attached process
11 LPC_SELF_CHECK      Self test attached process
12 LPC_DIALOG          Run remote dialog
13 LPC_GET_KEY         Get next key press to record
14 LPC_EXEC            Execute program and attach to it
15 LPC_WAIT            Wait for completion of last img/opl launched
16 LPC_BRING_SEND      Bring data then send it as a string
17 LPC_NAME_SEND       Format name then send it as a string
! next group are synchronous but have async interfaces via lpc_action
18 LPC_STORE_PID       Store current pid for later use
19 LPC_RESTORE_PID     Restore current pid from earlier use
20 LPC_ATTACH          Try to attach to specified process
21 LPC_ATTACH_FORE     Attach to foreground process
22 LPC_ATTACH_FNAME    Try to attach to process specified by file
23 LPC_ZAP             Kill all specified processes
24 LPC_KILL            Kill attached process
25 LPC_TERMINATE       Terminate attached process
26 LPC_CLOSEDOWN       Closedown message to attached process
27 LPC_EXEC_IMG        Execute image program
28 LPC_EXEC_OPL        Execute Opl program
29 LPC_HOOK_DYL        Application should launch and hook DYL
30 LPC_CONVERT         Convert selected stored characters to zero
31 LPC_BEEP            Beep (as in Opl)
! next group of methods are synchronous set or sense methods
32 LPC_BRING           Sense link paste data
33 LPC_RECORD          Turn recording state on or off
34 LPC_YIELD           Yield CPU to any other processes that need it
35 LPC_SET_PID         Set attached pid
36 LPC_SENSE_PID       Sense attached pid
37 LPC_SENSE_NAME      Sense formatted name of process
38 LPC_TEST_PID        Test whether could attach to given pid
39 LPC_STORE           Store text buffer for later use
40 LPC_PROCESS_INFO    Return menu/dialog/locked/filter/busy/start state
41 LPC_COUNT_DIALOGS   Return count of dialogs
42 LPC_SENSE_IOSEM     Return value of io semaphore
43 LPC_SENSE_AMSTART   Return value of appman.start
44 LPC_CANCEL          Cancel current activity

Synchronous and asynchronous access to LPC
------------------------------------------
You may notice that the lpc_init method takes a parameter that
indicates whether access to LPC is going to be synchronous (if this
parameter is NULL) or asynchronous (in which case this parameter is
the address of a status word to be signalled when the message
completes).

By design, access to LPC from KMAC is asynchronous, but access to LPC
from Opl is expected to be synchronous.
