Event-driven programming is a flexible way to allow your programs to respond to many different inputs or events.
Traditionally, programs operated in a sequential fashion: they did some processing and displayed it, waited for a user response, did some more processing, and so on. Your first OPL program was probably like this:
PROC main: LOCAL name$( 20 ) PRINT "What is your name?" INPUT name$ PRINT "That's a nice name, "; n$; "!" GET ENDP
It prints a prompt, wait for you to enter your name, displays a message with your name in it, then waits for you to press a key to quit the program.
This style is easy to understand and program. The problem is when your programs need to become more sophisticated and must deal with more than just you entering information on the keyboard.
Perhaps, like me, you watch a bit of TV and you've filled up your Time application with reminders of shows to watch. You need some more alarms. What to do? In OPL you can access the built-in alarm system, and schedule as many alarms as you like. The Agenda does this sort of thing already. What happens is Agenda keeps track of the next alarm and only tells the system about that one. When it goes off, Agenda tells the system about the next alarm, and so on. Agenda can do all this at the same time as letting you type in new entries, search for appointments or print to-do lists.
It can do this by waiting for events to occur:
Many other types of events are possible, including, but not limited to:
Event-driven programs improve on sequential programs by having a central event handler and dispatcher that waits for an event (any event) to occur, and then processes that event by calling that event handler.
Separation of the event detection and the event handling is an important technique for keeping your program simple and flexible.
It is quite common for applications to have a menu. Each menu item has a hotkey that can be used to do things without going through the menu. Event-driven programs will commonly write a single keyboard event handler that identifies when hotkeys are pressed and called the appropriate function. To handle the situation when the menus are used, they will often translate the menu selection into a keyboard event and let the keyboard handler do all the work! The complete menu handler is often a simple procedure consisting of just a few lines of code!
Hopefully I've sold you on the idea of event-driven programming. The next step is: how does it work on the Psion?
The key concept to event-driven programming is that your program doesn't just stop and wait for a specific event, like the above example program did, waiting for you to type on your name.
You might have looked through the Series 3a Programming Manual and found the
KEY command. Initially, the
KEY command looks quite
useful. It lets you check for a keypress without stopping and waiting for it.
That means your application can be busy doing its' thing, and can check the
keyboard every now and again to see if you've pressed something.
This technique works, but it suffers from a major drawback that particularly impacts on battery-powered handheld computers like your Psion: your program never stops. What this means is your your computer will always be busy, either doing some work, or spinning 'round and 'round, checking if you've pressed a key in the millisecond since it last checked. All this work would quickly run down your batteries. It doesn't seem to bother the built-in programs. How to they do it?
The difference is due to the two techniques used to implement event-driven programs:
Polling is the
KEY command: it just polls the keyboard
to see if there has been a keypress. The
TESTEVENT command is the
same. This technique of programming is not at all suitable for the Psion, so it
won't be discussed any further.
Stop and wait is when your computer stops and waits for something to happen.
This allows it to change to a special low-power idle state that is nice to your
batteries. The command to use for this technique is
Some typical code using
GETEVENT might look like (see the
Programming Manual for a full explanation):
PROC mainloop: GLOBAL done% LOCAL a%( 6 ), keycode%, keymod%, keyrep%, syscmd$( 255 ) done% = 0 DO GETEVENT a%() IF ( a%( 1 ) AND $400 ) = 0 REM it's a keypress keycode% = a%( 1 ) keymod% = a%( 2 ) AND $00FF keyrep% = a%( 2 ) / 256 hdlKey:( keycode%, keymod%, keyrep% ) ELSEIF a%( 1 ) = $401 REM program has come to the foregound ELSEIF a%( 1 ) = $402 REM program has gone to the background ELSEIF a%( 1 ) = $403 REM machine has switched on ELSEIF a%( 1 ) = $404 REM system command syscmd$ = GETCMD$ hdlCmd:( LEFT$( syscmd$, 1 ), MID$( syscmd$, 2, 255 ) ) ELSEIF a%( 1 ) = $405 REM date has changed ELSE REM some unknown event (should never happen) ENDIF UNTIL done% ENDP REM Handle keypresses PROC hdlKey:( keycode%, keymod%, keyrep% ) IF keycode% = 27 done% = 1 :REM allow main loop to exit ELSE REM do something with the key PRINT REPT$( CHR$( keycode% ), keyrep% ); ENDIF ENDP REM Handle system command events PROC hdlCmd:( syscmd$, sysfile$ ) IF syscmd$ = "X" done% = 1 :REM allow main loop to exit ENDIF REM there are other "O" open and "C" create commands REM - see the Programming Manual ENDP
This allows your program to handle keyboard events, plus quite a few others. This is sufficient for many applications. However, it does not let you do things like schedule alarms or use the serial port. To do these sorts of things as well requires the use of asynchronous I/O. It is covered in the next tutorial called, funnily enough, Asynchronous I/O.