RECORDER

  Jeffrey S. Shulman

  Last revised on:  July 7, 1984

1. Introduction
  RECORDER  is  a  package  that  allows  you  to record and playback mouse and

keyboard events.  These events are recorded as you perform  them  in  (more  or1
less                                                                            ) real time.  During playback these recorded events arere-performedasif
you were there doing it again.

  RECORDER  performs these feats of magic by redefining the low level mouse and
keyboard  handlers.    Specifically  RECORDER  provides  its  own  versions  of
GETMOUSESTATE  and  \GETSYSBUF  (as  well  as  a  slightly  modified version of
\KEYHANDLER1.)

2. Use
  To use you first load RECORDER.DCOM.  After  this  file  is  loaded  it  will
BKSYSBUF  its initialization function as well as a HARDRESET (this is necessary
in order for the modified \KEYHANDLER1 to take affect.)



2.1. Starting a recording
  To start a recording  session  you  use  the  function  RECORD.START.    This
function  returns  immediately  with the array the recording will be placed in.
This array is a SMALLP array of \RASIZE elements.

  At this point the recording  has  not  actually  started.    You  should  now
position  the  mouse  where  you  want  it to be when the recording starts.  To
actually start  the  recording  you  press  the  CONTROL  and  LEFT  SHIFT  key
simultaneously  (this  fact  also  appears  in  the PROMPTWINDOW after you call
RECORD.START as a reminder.)

  When these keys are pressed the recording starts.  The message  Recording....
will  appear  in  the  PROMPTWINDOW.  Recording proceeds until either you press
CTRL-LSHIFT again or the array fills up.  When either of these events occur the
message stopped. will appear in the PROMPTWINDOW.

  A sync event (described below) is recorded automatically when you  start  and
when you stop a recording.

               

  1
   N.B.  Due  to  the  way  playback occurs interrupts will not be seen in real
time.  For example,  if  you  were  pretty-printing  a  long  function  to  the
typescript  window  and  ↑E'ed  it  in  the  middle, the printing would (during
recording) stop immediately.  However, during playback, the  ↑E  would  not  be
seen until the pretty-printing was finished.
                                       1


  Thus, an example of the sequence of events would be:  

    (SETQ A (RECORD.START))
    position the mouse to the starting position
    Press CTRL-LSHIFT
    perform the desired actions
    Press CTRL-LSHIFT

  This  array  may  be saved on a file using the UGLYVARS File Package command.
The function SQUEEZE.RECORDING, described below, can be used to eliminate  much
of the dead space.



2.2. Playing back a previous recording
  To playback a previous recording you may use the function REPLAY whose single
argument is the array returned by a previous call to RECORD.START.  REPLAY will
set in motion what is needed to playback an old recording and will return after
the playback is finished.

  During  playback  the mouse and keyboard will NOT respond to anything you do.
The only exceptions to this are:

   1. Pressing CTRL-LSHIFT will stop playback and return control to you.

   2. Pressing CTRL-LSHIFT-DELETE  (the  emergency  interrupt)  will  stop
      playback and reset the system.

  You will again receive control of the keyboard and mouse when the playback is
finished.

  If  you typed what was in the previous "How to record" example you would play
it back via:  

    (REPLAY A)

3. Synchronization
  During playback you may wish to perform some "outside" event.    For  example
while the mouse was moving around or when choosing various menu items you might
want  to  print  some  accompanying text.  An easy method of synchronizing just
these kinds of things is provided so that things "happen"  at  the  appropriate
moment.

  During playback this synchronization is accomplished by the function RP.SYNC.
If  while  playback is happening a sync event occurs (more about how to put one
in below) the recording will essentially suspend itself until a call to RP.SYNC
has been performed.  If a call to RP.SYNC occurs before a sync  event  it  will
not return from it until this sync event happens.

  A  sync  event  is automatically recorded at the start and at the end of each
recording.
                                       2


  The  function REPLAY is really a call to PLAYBACK.START followed by two calls
to RP.SYNC (one for the start of the recording and one for the ending.)

  The function PLAYBACK.START (whose single argument in the recording array) is
what really starts playback of a previous recording.  This function  queues  up
the  recording  for  playback  and  returns  immediately.   You should use this
function and provide your own RP.SYNC's if you want to "do" something during  a
playback.

  This should be come clear by an example.  Suppose you wanted to print "I will
now  move  to  menu X", move to the menu, print "Now I will select an item from
this menu" and then select an item from this menu.

  During recording you would move the mouse over to  the  menu,  cause  a  sync
event, and then select the menu item.

  A function that would playback this back correctly would look like:  

    (LAMBDA (RECORDED-ARRAY)
            (PLAYBACK.START RECORDED-ARRAY)
            (printout T "I will now move to menu X" T)
            (RP.SYNC)
            (RP.SYNC)
            (printout T "Now I will select an item from this menu" T)
            (RP.SYNC))

  What happens in this function is:

   1. The  recording  is queued up.  Since the act of making the recording
      puts in a sync event at the start, the mouse does not move.

   2. "I will now move to menu X" is printed.

   3. The first (RP.SYNC) starts the playback.  The mouse begins moving to
      the menu.

   4. The second (RP.SYNC) does not return until  the  second  sync  event
      happens.    This  is  the  one  that was intentionally put in during
      recording.

   5. "Now I will select an item from this  menu"  is  printed  after  the
      second sync event.

   6. The  third  (RP.SYNC)  waits  for  the  last  sync  event  that  was
      automatically recorded when the recording stopped to happen.



3.1. Sync events
  Sync events are caused to occur during recording by pressing  the  sync  key.
This  key's  number  (returned  by  \KEYNAMETONUMBER)  is  bound  to the global
variable \SYNC.KEY.   The  default  is  91  which  is  the  AGAIN  key  on  the
                                       3


Dandelion's keyboard.

  When  this  key  is pressed during recording a sync event is recorded at that
instant.  Feedback is provided by printing a letter S in the PROMPTWINDOW  each
time this key is pressed.

  N.B.:  If you are not careful it is possible to have the wrong number of sync
events  to the number of RP.SYNC function calls.  This could cause the mouse to
freeze waiting for a RP.SYNC function call (which you cannot type  in  yourself
since  the  keyboard  is locked out during playback.)  Should this happen it is
possible to stop playback and regain control via the CTRL-LSHIFT abort.

4. Caveats
  It should go without saying that it is important for the same  exact  set  of
circumstances to exist before any given playback as they did when the recording
was made.

  Little  gotcha's  will  pop up in the unexpected places so it is important to
"set up" before any playback.

  An example of a gotcha are popup menus  that  use  the  MENUOFFSET  field  to
determine  where  they will next appear.  If this is different at playback time
from what it was at record time the results will not be the same  (N.B.:    the
global  menus  WindowMenu, IconWindowMenu and BackgroundMenu are examples menus
that use the MENUOFFSET field.  Since these are the most  commonly  used  menus
that  are  all  set  to NIL (so they will be recreated from their corresponding
Commands list) by both RECORD.START and PLAYBACK.START.)

5. Miscellaneous stuff
  During playback mouse key presses are indicated by the  letter  L,  M  and  R
shown along the bottom of the cursor bitmap (at the obvious positions.)

  The function SQUEEZE.RECORDING is provided to eliminate dead space at the end
of  a  recording array.  Its single argument is an array previously returned by
RECORD.START.  Its value is a new array of minimum size.
                                       i


                               Table of Contents
1. Introduction                                                               0
2. Use                                                                        0
     2.1. Starting a recording                                                0
     2.2. Playing back a previous recording                                   1
3. Synchronization                                                            1
     3.1. Sync events                                                         2
4. Caveats                                                                    3
5. Miscellaneous stuff                                                        3