Please ensure you have the latest version (1.4) of the RMREvent example program before reading through this tutorial. If you use an older version, some of the text may not make sense.
We're looking today at the way RMREvent handles system, pen and keyboard
events without breaking sweat. Most probably, your very first OPL program used
GET inside a loop:
WHILE 1
k%=GET
IF k%=48 THEN
etc. etc.
ENDWH
In other words, a single keyboard character is read in each time through the loop and then acted upon. The problem comes when needing to handle such complexities as a user dragging the pen across the screen or a PsiWin backup asking the program to close down. The classic solution to all this is the 'event loop' and it looks remarkably similar to the simple loop above, as we shall see.
The important part of the main procedure is this:
DO
Run:
UNTIL FOREVER
Before we 'drill down' to examine the Run: procedure in more detail,
note the DO...UNTIL loop. we haven't seen this concept before in this series, but
the principle is almost identical to WHILE...ENDWH and I hope it's obvious after a
moment's study. The UNTIL FOREVER is particularly elegant, and making your source
code read as much as possible like plain english is always a goal at the back
of the programmer's mind. Note that FOREVER is just a constant set to zero,
it's not some magic new OPL keyword! 8-)
Now to explore what Run: actually does.
Reducing it to its general form of:
PROC Run:
DO
GETEVENT32 A&()
IF A&(1)=first option
etc. etc.
ELSE
etc. etc.
ENDIF
UNTIL FOREVER
RETURN
ENDP
we can see that
the idea is again to carry on looping forever. Well, at least until an error of
some kind, in which case RMR have seen to it that the error is nicely reported,
control returned to the main loop and that things carry on happily thereafter.
Again, this is another good programming principle: your program should
never crash. Sure, errors can happen (printers being off-line, files
being unavailable, whatever), but your program should always 'handle' things
and survive if at all possible.
Inside the loop, the first command is GETEVENT32 A&() and it's vital to get to
grips with this as its operation is the core of the event loop.
GETEVENT32 does just about what it says. It waits for an event (a
keystroke, system message, pen tap etc) and returns information about the event
in the integer array A&() that you've set up specially. The manual says that
the array must have at least 16 elements, but RMR have chosen to give it 20
(just in case). See the OPL manual for exact details on the content of each
array element, but the most important is A&(1), which contains the 'event
code' for that particular event. It's also important to realise that keypresses
and pen taps usually generate more than one event each. For example, pressing a
key involves the 'Key down' event, a key event code corresponding to the key
pressed and the 'Key up' event. To better understand the way event codes are
generated by EPOC, have a look at the OPL/32 "Demo" application in the ROM (you
can easily get a copy by choosing the "Create standard files" command from
within an OPL/32 document). The "Events" menu option inside the Demo
application is an excellent way to get a feel for what your program needs to be
able to trap. Try tapping and dragging the pen on different parts of the
screen, pressing different keys etc., while you watch the reporting of each
event that gets generated. Press the Esc key to quit when you're done.
Back in RMREvent, you can see that the event loop in Run: involves
grabbing the event itself, checking whether it's something really major (like
needing to close down or being switched to the background) and then passing the
code to a procedure called Action_Pen_or_Keypress: for detailed handling. Don't worry for now
about the block of lines which detect whether the user had Shift or Control
pressed down during the event; suffice it to say that variables Shift% and
Control% get set to the appropriate logical state, to put what happens in
Action_Pen_or_Keypress: into the proper perspective.
It may seem confusing to you for RMR to use this series of procedures within procedures, but it's really just an extension of the things we've been learning so far, as each procedure does a specific job at the right level. If all of this logic were combined into a single stream of commands in the top level procedure (quite possible), the code would be a lot less elegant. Different authors will always find different degrees of granularity acceptable to them in the great 'performance versus readability' trade-off.
On to Action_Pen_or_Keypress: itself. This is basically one large IF...ELSE...ENDIF sequence -
testing Key& for various values and then performing the appropriate actions.
RMREvent is, of course, only an example program and so little work actually
gets done. In a real-world program, this would be the place in which you call
the major routines of your application. Cast your eye down the list of tests on
Key& and try to get a feel for what's happening.
Let me pick out a few points of note:
ELSEIF Key&=Pointer_Used
IF TBarOffer%:(A&(3),A&(4),A&(6),A&(7))
RETURN
ELSEIF A&(4)=1
Pointer_Event:(A&(3),A&(6),A&(7))
ENDIF Essentially, the code says: "Offer the pen event to the standard
toolbar. If it 'accepts' it, then return and carry on. If it doesn't accept it,
check if the pen event was 'pen up' and if so call Pointer_Event:, giving it the
graphics window name and x and y coordinates where the pen-up event occurred."
We'll look at Pointer_Event: in more detail at a later date.
Key&=Key&-32. Don't try to understand it for now, it's just
the start of a whole series of arithmetic juggling tricks that RMREvent uses to
keep track of the key event codes returned by the program. There's no simple
way to implement this sort of thing and every author I know does this bit
differently. The main thing to remember is that once you've found a system
which works (as here), just use it, plugging things in where necessary, and
don't worry about keeping how it works in your brain.
IF Key&<=300 might seem a little strange, if only because it's
not obvious where the number 300 comes from. In fact it could be 255 or 286 or
any other similar number, it's simply a way of dividing between events which
return larger numbers (arrow key presses, command icons etc) and 'normal'
(alphabetic) key events. Whew! That's quite enough to stretch the old brain cells for now. More next week!
Go to next lesson | Programming index