HELP PWMINPUT                                   Ben Rubinstein, Feb 1987
                                             Revised: Nic Ford, Apr 1987
                                        Revised: Gareth Palmer, Sep 1989

Handling user keyboard and mouse input in the PWM.

Keywords: PWM, mouse, input, events

    <procedure|word:H> -> pwm_eventhandler(<window-id>, <word|false:E>)
    pwm_eventhandler(<window-id>, <word|false:E>) -> <procedure|word:H>

    pwm_force_input(<window-id>)

    pwm_get_oneinput() -> <string>

    pwm_inputcatcher(<vector>)

    <procedure|false:H> -> pwm_itemhandler(<window-id>,
                                             <word|list|false:E>,
                                             <integer|list|false:B>,
                                             <vector|list|false:A>)
    pwm_itemhandler(<window-id>,
                      <word|list|false:E>,
                      <integer|list|false:B>,
                      <vector|list|false:A>) -> <procedure:H>

    pwm_reset_input()

    pwm_prompt_user(<integer:C>, <string:T>) -> <string>

    pwm_quit_window(<vector>, <word|false:V>)

    pwm_wait_inevent(<window-id|list:W>, <boolean>)


Before reading this file you should have read HELP *PWM, *PWMGENERAL
and *PWMWINDOWS. After reading this file, you may wish to read HELP
*PWMWINIO, which discusses a technique for achieving character-stream
input on a user window using the facilities described here.

== CONTENTS - (Use <ENTER> g to access required sections) ==

 -- Introduction: how input is multiplexed
 -- Catching input on user windows
 -- Distributing mouse events in parts of windows
 -- Special facilities

-- Introduction: how input is multiplexed -----------------------------

The PWM manages a number of windows for POPLOG, interfacing to the host
workstation's window manager to take care of tasks like refreshing
windows, catching and responding to user requests to open, close or
resize them, and so on.  It also allows POPLOG to maintain the notion of
a single input and output stream, redirecting output to different
windows in response to periodic requests from POPLOG, and combining
input events on all windows into a single stream of input in which all
events are sent in chronological order.  This absolves POPLOG from the
need to keep track of many sources of input and ensures that it responds
to different events in the correct order, which would otherwise be
difficult since POPLOG is often busy for extended periods compiling
files etc.

Most of the input which the PWM passes to POPLOG consists of single
characters, the result of keyboard actions from the user.  Interspersed
within the input stream, however, are messages from the PWM encoded as
escape sequences.  These fall into three categories:

    1)  Rather than accompanying each character of input with an
    indication of the window from which it came, POPLOG and the PWM
    maintain the notion of a current input source, the window into which
    the user is directing input.  When the user selects a
    different window for input, the first input event from the new
    window is preceded in the input stream by a message from the PWM
    to tell POPLOG that the following input is coming from a particular
    window.  POPLOG's record of this is the variable -pwminputsource-,
    which generally contains the window identifier record of the window
    from which the last input event occurred.

    2)  Some input events cannot be encoded as a single character: in
    particular, mouse events (e.g. the user pressing a button on the
    mouse) fall into this category.  When such an event occurs, the PWM
    sends a message giving details of which button was pressed and the
    location in the window of the mouse at the moment the button was
    pressed.  Like character input, these events are assumed to have
    happened in the "current" window, i.e. -pwminputsource-.

    3)  There are also events that can occur in the life of a window
    without the user having selected that window for input.  For
    example, if the user asks for a window to be closed (using the
    mouse) this action is handled by the PWM: but it also sends a
    message to POPLOG informing it that the window has been closed.
    Similarly if the user employs the mouse to stretch or resize a
    window, the PWM takes care of the various tasks needed to change the
    size of the window, but then sends a message to POPLOG informing it
    that the window has changed size, so that POPLOG can take any
    necessary actions (for example, to refresh the window).  A
    slightly different example is when the user employs the mouse to ask
    that a window be quitted or destroyed; in this case the PWM takes no
    action at all on it's own initiative, but simply informs POPLOG of the
    request.  These messages contain within them the identity of the
    window to which the event relates.

An appreciation of this model is necessary to understand how input can
be used from PWM windows, and some special problems.  As an example of
the problems, POPLOG occasionally purges the input stream (i.e. it
forces any input waiting in it to be thrown away without reading it);
for example it does this in a -setpop-, as part of the default response
to a mishap or interrupt.  In the context of a normal terminal, this
just means that some keyboard input from the user will be discarded, and
the user can normally see that this has happened and repeat any commands
which in the light of the mishap they still want to give.  But in the
context of POPLOG connected to a PWM, the PWM cannot know that POPLOG
has failed to read some of its messages.  (It is for this reason that
you were warned in HELP *PWMWINDOW to be wary with functions like
-pwm_window_height-).

It is also necessary to appreciate a general point about programming for
WIMP-style workstations.  Conventional programs on other types of
machine tend to assume that they have control over the user's use of the
computer. The program decides what to do: when it wants input from the
user, it stops and asks the user to type something.  Except at the
points that it prompts the user for input, the user has no control over
what is happening: in many systems, he or she can't even stop the
program to do something else with the computer, except by asking the
program to kindly hand over control to another program (or by taking
some extreme action which may kill the program entirely).

The usual style on a WIMP-type workstation such as the Sun or HP Bobcat
is somewhat different.  Here the user is in control.  If a program
running in one window demands some information from the user, he or she
does not have to give it at once.  Instead the user can simply move the
mouse to select another window in which some other program is running,
and use that for a while until he or she wants to talk to the first
program again.

The same interface design extends to a well-written program running
within a window as between windows. Rather than determing at all times
what is to happen next, the program should be event-driven: that is,
its basic behaviour, after some setup actions, should be to sit in a
loop waiting for an event of some kind (which might be the user pressing
a mouse button, or requesting that a window be quit, or typing some
characters) and be prepared to respond to whatever the event is as
swiftly as possible, before returning to the main loop.

Although POPLOG has a fundamental handicap in supporting this model,
(because as noted above it's response to some events can be a time-
consuming one) POPLOG's interface to the PWM is designed to support
such.  The programmer can assign procedures to catch different kinds of
events on a particular window.  The main POPLOG loops (the top-level
compiler loops of pop11, Clisp and Prolog, and the VED editing loop) are
all capable of recognising the PWM messages referred to above, and will
call the assigned trap procedure for an event on a user window.

Thus the normal mode of interaction is a very "open" one, in which the
user can interact simultaneously with the compiler, the editor, and one
or more "programs" in different windows, switching at will between them
(with the unfortunate caveat that if one program, including the POPLOG
compiler or VED, is taking a long time to complete its response to an
event then no other event will be processed until it has finished).

You are encouraged to use this model. It is possible to reconstruct a
more traditional programming style 'on top of' it and one way of
doing so is discussed in HELP *PWMWINIO. However, if you are forced to
adopt a more traditional approach directly, there are some limited
facilities to assist this, but since it is not the style for which the
interface was designed the result is generally less tidy.

-- Catching input on user windows -------------------------------------

The basic mechanism for catching input on user windows is based around
the notion of an event.  An event is represented as a four element
vector, in which the first element is always a word, the name of this
type of event, and the remaining elements are details (in effect
arguments) some of which may be irrelevant to some event types.  The
events that can be trapped by user programs are listed below, with the
interpretation of their 'arguments', where "-" indicates that the value
of this element of the vector is not relevant.

  Event name  Args: #1  #2  #3  Meaning:
  ----------  --------  --  --  ---------
  "opened"        -   -   -   the window has been opened
  "closed"        -   -   -   the window has been closed
  "elevator"        -   H   V   horizontal or vertical elevator moved
  "press"          B   X   Y   mouse button B pressed at X, Y
  "release"      B   X   Y   mouse button B released at X,Y
  "move"            B   X   Y   mouse moved to X,Y with button B down
  "mousexit"        B   -   -   mouse left window with button B down
  "quitrequest"  -   -   -   user requested the window be destroyed
  "resized"      -   W   H   window has been resized to W, H
  "character"      C   -   -   character with ASCII code C typed into window

Notes:
1.  Elevators ("scroll bars") have not yet been implemented, so the
    "elevator" event will not be recieved in the current version.

2.  The arguments X and Y in the "press", "release" and "move" events,
    and the arguments W and H in the "resized" event, will be in pixel
    units in a graphics window, in character units in a text window.

3.  The "move" event is only sent when it has been specifically
    requested that intermediate mouse positions should be reported.
    See HELP *PWM_TRACK_MOUSE for details of how this is done.


The traps for each combination of event and window can be accessed
through the procedure -pwm_eventhandler-.

    <procedure|word:H> -> pwm_eventhandler(<window-id>, <word|false:E>)
    pwm_eventhandler(<window-id>, <word|false:E>) -> <procedure|word:H>

The word E is one of the event names listed above: false indicates all
events.  If a procedure is given for the argument H (for 'handler') then
when an event of the specified kind occurs on the specified window, the
procedure will be called with the event vector as described above as
the single argument. If H is word, then whenever an appropriate event is
received the value of that variable will be examined: if it is a
procedure the procedure will be called as before, otherwise a mishap
will be generated.  Giving a word as argument is thus slightly less
efficient and robust, but is useful while debugging since the latest
version of the procedure will be used, (otherwise one must remember to
repeat the call to -pwm_eventhandler- whenever the procedure is
recompiled: and it is easy to forget to do this).

When a window is first made the procedure -pwm_inputcatcher- is assigned
to catch all events.  The default definition of this kills the window
immediately in response to a "quitrequest" event: apart from this its
only action for all events is to report them by printing a message.
This procedure can be redefined (e.g. if you don't want to have windows
killed by default).

Although as mentioned in the introduction there are some messages sent
by the PWM which do not apply to the current input window, the procedure
assigned to handle a particular event can always discover the window
to which the event applies by checking the variable -pwminputsource-:
all user handler procedures are called in an environment in which this
variable is locally defined to the window to which the event relates.

When you first assign a catcher for a particular event, a null catcher
is assigned to all other events (i.e. no action is taken on them, not
even printing a message) except for the "quitrequest" event, which again
has a default procedure assigned which kills the window.  So you must
explicitly assign something to catch this event if you want control over
the killing of windows.

A useful procedure for this event is -pwm_quit_window-.  This takes two
arguments, as:

    pwm_quit_window(<vector>, <word|false:V>)

The first argument is assumed to be an event vector for the
"quitrequested" event, and the window is killed.  If the argument V is
false, no other action is taken: but if it is a word, then false is
assigned to the variable of that name.  Thus a closure on this procedure
(see HELP *CLOSURES) can conveniently be used in the context of a
program which the user might call several times and which needs a
window.  For example,

    vars progwin = false;

    define prog();
        unless progwin do
            pwm_make_gfxwin('progwin', 100, 100) -> progwin;
            pwm_quit_window(% "progwin" %)
                    -> pwm_eventhandler(progwin, "quitrequest");
        endunless;
        ;;;
        ;;; some code operating on progwin here
        ;;;
    enddefine;

Then if the user has killed the program's window between invocations, it
will know to make another one.  (The default catcher assigned for the
"quitrequest" event is actually a closure of -pwm_quit_window- on false.)

You can assign the same procedure to catch several events (in which case
it should make sure to check the event) or to catch events on different
windows (in which case it should make sure to check the value of
-pwminputsource-).

-- Distributing mouse events in parts of windows ----------------------

Many programs that deal with, in particular, mouse input, wish to take
different actions according to where in the window the mouse button is
pressed or released.  There is a library, -pwm_itemhandler-, available to
make the task of splitting mouse input in this way easier: specifically
it takes over handling of "press" and "release" events, and distributes
them to one of a set of procedures.

The basic form of the procedure is analogous to -pwm_eventhandler-: it
takes a window and a specification of an event, and either returns the
procedure assigned to catch that event, or takes a new procedure
and assigns it to cath that event.  The specification of the event,
however, is more detailed.

    <procedure|false:H> -> pwm_itemhandler(<window-id>,
                        <word|list|false:E>,
                        <integer|list|false:B>,
                        <vector|list|false:A>);
    pwm_itemhandler(<window-id>,
                        <word|list|false:E>,
                        <integer|list|false:B>,
                        <vector|list|false:A>) -> <procedure:H>


The argument E is either an event word, as for -pwm_eventhandler-, or a
list of event words, or false: false false specifies all events.  The
argument B is either an integer, the number of a mouse button; or a list
of such integers;  or false, specifying any button.  Finally the
argument A specifies an area of the window: if it is false, it selects
the whole window; if it is a list, the list should contain four integers
representing the coordinate of the top-left corner of the area, and the
width and height (X, Y, W, H); if it is a vector, it should contain four
integers which specify the area by the coordinates of its top-left and
bottom-right corners (in the order Xleft, Ytop, Xright, Ybottom).  (Note
that since the only events handled by this library are "press" and
"release", a legitimate list argument E would have the same effect
as false: however it is possible that the library might be extended to
deal with other events (for example the "move" event) at some future
time.)

If all three of the arguments E, B and A are false, a default event is
specified.  With this exception, you cannot assign handlers for two
event specifications which "overlap": that is, that there is some event
vector that could be generated which would satisfy the conditions of
the two event specifications.  You can remove an event-specification
by giving false instead of a procedure as the handler argument H.  You
cannot assign false as the handler for the default event - if you don't
want any action to take place for events outside the specifications you
have given, assign the procedure -erase- (see HELP *ERASE) for the
default case.

When -pwm_itemhandler- is first used on a window, it assigns its own
procedure to catch the "press" and "release" events.  The procedure
which previously was assigned to catch the "press" event on that window
will be used as the initial value of the default procedure.  You should
be aware that making a subsequent call to -pwminputhandler- for the
"press" or "release" events will disable the -pwm_itemhandler- mechanism,
with consequent possibilities for confusion.

The procedure assigned to catch some specified pattern of events will be
called, as with -pwm_eventhandler-, with the event as its argument.  It
can examine the "area" portion of the event specification that triggered
it by inspecting the variable -pwmitemarea-.  Note that the value of
this variable will always be in the vector form described above (or
false).

The file HELP *PWMITEMS describes some libaries which use this facility
to provide some "input items" which may be useful in your user
interface.

-- Special facilities -------------------------------------------------

One facility which is sometimes useful is the function -pwm_reset_input-,
which instructs the PWM to reset its internal record of which window the
last input came from.  After invoking it, therefore, the next input will
be preceeded by a message describing its source.  The call is:

    pwm_reset_input()

As mentioned in the introduction, there are also a few special
facilities which to some extent bypass or pervert the event-driven,
user-in-control model of input.  These should be used sparingly.

There are some occasions where a program wishes to wait for some kind of
action by the user, without concern for the kind of event or the window
in which it occurs.  There are two functions which can be used on these
occasions:

    pwm_get_oneinput() -> <string>

    pwm_prompt_user(<integer:C>, <string:T>) -> <string>

These instruct the PWM to wait for any input event on any PWM window,
and then to return.  The function -pwm_promptuser- also displays a
message and responds to events that occur outside pwm windows.  The text
T is printed in a box C columns wide, which is removed after the input
event. The PWM does not register the window from which the input
derives.  The string returned is rarely useful: if you care about what
action the user made, you probably shouldn't be using this function.
However, in the case of a character being typed at the keyboard, the
string will consist of the character `A` followed by the character
typed; in the case of a mouse button being pressed or released, the
string will consist of the character `B` followed by the character whose
ASCII code is 32 plus the number of the button.  If something goes
wrong, the result will be the non-existent button 0 (i.e. the string 'B
' will be returned).  It would be unwise to rely on the return value, as
it's format might be changed.  The only use in the system of
-pwm_get_oneinput- is following the message "VED HERE: PRESS RETURN TO
CONTINUE"; it was found that users frequently tried typing into some
unexpected window, causing a message about the input source to be sent
to POPLOG, which kicked off the return to VED action, read the tail of
the message as garbage, and then didn't know which window the user was
working in.

There is a function available which selects a window for input. Use of
this function does not guarantee that the next input will come from that
window: the next action of the user might be to select some other
window, as they are always free to do.  POPLOG only uses this function
for -vedswapfiles- (<ESC> x) as a compatability measure.  Although it
can save the user time if you know (as with <ESC> x) that the user wants
to select a particular window, it can disturb their model of
interaction.  On some systems (such as the Sun) where a window is
selected by moving the mouse cursor into it, it can also be rather messy,
because it disturbs the correspondence between the location of the
physical mouse and the location of the mouse cursor.  The call is

    pwm_force_input(<window-id>)

Note that this always opens (and fully exposes) the specified window.

Finally, programs which insist on only accepting user input at specified
times and in specified areas can use the function -pwm_wait_inevent- when
they wish for some user input.

    pwm_wait_inevent(<window-id|list:W>, <boolean>)

The argument W is either the identifier of a user (text or graphics)
window, or a list of user window identifiers.  When this procedure is
called all input is thrown away until input occurs on the specified
window or windows.  Then if the boolean argument is true, the normal
handler as specified above is called, and -pwm_wait_inevent- returns;
otherwise the event vector is returned as the result of the call to
-pwm_wait_inevent-.  Among the problems with using this procedure is that
if the user takes any action not directed at the specified window or
windows POPLOG will never know about it: thus if the user types away
into an unspecified window, they will be ignored for no obvious reason;
if they open or close or resize a window not specified in the call to
-pwm_wait_inevent-, your program will have no way of knowing that this has
occured.  Finally, when your program finishes things might be in a
confused state, since POPLOG will probably no longer know which window
the user is typing into.  At the very least, if your program makes use
of this function it should purge the input stream (-popdevraw-: see REF
*SYSIO) and make a call to -pwm_reset_input-.  But a better solution is
to write your program in such a way that it doesn't need this facility.
For a discussion of one way you might do this, see HELP *PWMWINIO.

--- C.all/help/pwminput ------------------------------------------------
--- Copyright University of Sussex 1987. All rights reserved. ----------
