Logo Events

Programming inside a browser environment is different from the traditional programming style. In the late seventies when Logo was invented, people used punch cards and TTYs to enter data, and the computer responded by printing on endless paper. Even the first screens kept this traditional approach.

With the arrival of Windows and other graphical operating systems, things became much more complicated. There was a multitude of input devices, including mice, keyboards, and even fingers, and programs were not expected to run in a loop, waiting for any input, because they wasted valuable CPU cycles this way.

This program is an example of the traditional way:

The programs checks the mouse position, moves the turtle to where the mouse is, and checks the left mouse button. If it is pressed, the program exits; otherwise, it loops and starts over.

It is easy to see that the program must be running all the time so it does not miss any mouse movements. Therefore, it would block its assigned CPU until it is finished. Not a good idea nowadays.

Modern programming works differently. After a program has initialized, it leans back and waits for any input events to happen. Only if any input becomes available, the program wakes up, processes the input and goes to sleep again. No CPU cycles are wasted, and the CPU is available for other jobs.

This is called event-driven programming. As event-driven programming, the above program would look like this:

The EVENTS property list contains event handlers as properties; each property name is a special event, and the property value is the runlist that Logo executed when that event arrives. The first line defines the event “MOUSE.MOVED; So, if the mouse is moved, the turtle's position is set to whatever happens to be the content of :EVENTDATA. For the “MOUSE.MOVED event, this happens to be the mouse's position.

The second line defines what happens if the user clicks a mouse button. Here, the REMPROP command removes the “MOUSE.MOVED event handler; this causes the updates to the turtle's position to stop.

And what does Logo do in the meantime?

Nothing.

You could even run a Logo program at the same time, and the event handlers would be executed, briefly pausing the execution while the event handlers are run.

There is one exception, however. You can write programs that do not actually run a main program. Instead, the program solely waits for events to happen, and executes all logic when an event arrives. Phil Lewis' Mousegraph program is such an example. Most of the program logic is inside the MOUSE.MOVED handler, which is perfectly OK, because it does not run any main program.

While Logo runs an event handler, it ignores the same event if it is created during the execution of the event handler. This makes sense, because it prevents, for example, mouse moved events from piling up too much, On the other hand, a KEY.PRESSED handler may lose keys if an event handler takes too long to run.

As you can see, event handling can be tricky if you do not follow the rules.

Be very careful with event handlers! All Logo programs share the same environment. An event handler should not change any global names so other Logo programs can continue running. And do not execute any loops in the event handler, because that would prevent your main Logo program from running.

Changing global variables can have grave consequences. Imagine a Logo program writing to :STANDARD.OUTPUT to create a file. At the same time, a Logo event handler creates another file and changes :STANDARD.OUTPUT to write to that file, and all of a sudden, all Logo programs write to the new file at the same time!

To get rid of all events, enter the command ERPL “EVENTS. This command erases all properties from the EVENTS property list. If you cannot enter the command in the Listener because Logo prints all the time, for example, enter it into the Editor and click Run.

Event Data

Events come with event data. A mouse movement comes with the X and Y coordinates of the mouse pointer; a keyboard event receives the character typed; resize events carry the new size of the window etc.

The Logo environment stores this event data under the global name :EVENTDATA before executing the runlist that is associated with an event name. The runlist can in turn access the event data, and process that data.

Again: the format of the data depends on the event.

When the browser finds that Logo has its own handler for an event, it will not handle the event itself. Imagine that you wanted to define a runlist that executes a Logo command if you click the right mouse button. You would simply define this runlist, store it under the name “MOUSE.CLICKED, and have the runlist check the :EVENTDATA variable which button actually has been clicked.

Fine.

Unfortunately, the browser usually has a different opinion about how to handle a right mouse click. It would display a context menu. This would be really undesirable if you already handled the right-click for yourself.

Therefore, the browser forgets about the event if Logo handles it already.

Programming Style

You may want to write a procedure to handle an event; this makes your code more readable. Look at the example above:

PPROP "EVENTS "MOUSE.MOVED [SETXY FIRST :EVENTDATA]

When you look at your code in a few years from now, you would have to look up the MOUSE.MOVED event to figure out what data the :EVENTDATA variable contains, so why not write a procedure with well-defined inputs?

Because the value of :EVENTDATA for the MOUSE.MOVED event is a two element list consisting of the mouse position and the state of the mouse’s left button, you can use procedures to make your code more readable. For example:

TO HANDLE.MOUSE.MOVED :position :button
    ; we do not need the :button input here
    SETXY :position
END

PPROP "EVENTS "MOUSE.MOVED [HANDLE.MOUSE.MOVED FIRST :EVENTDATA LAST :EVENTDATA]

The APPLY command comes in handy here. This command has two inputs, the name of the function to run and a list with the function inputs. In the case above, you could simple apply the :EVENTDATA list to HANDLE.MOSE.MOVEDs inputs:

PPROP "EVENTS "MOUSE.MOVED [APPLY "HANDLE.MOUSE.MOVED :EVENTDATA]

APPLY would kindly bind the first list item of :EVENTDATA to the :position input, and the second item to the :button input.

If you want to more than one mouse button, read the property BUTTON of the GRAPHICS object.

Important: Your code begins to process events as soon as you have defined your event handler! If you, for example, define the MOUSE.MOVED event, Logo calls your code as soon as you move your mouse, even if your program is not ready to run yet. Therefore, please think twice about when to define event handler in your program flow. Your program must be ready to process these events as soon as the handlers are defined. Therefore, the EVENTS object is not part of your saved workspace. Logo cannot know when exactly to define a saved event handler, so it is better to keep the EVENTS object out of the saved workspace.

Event Names

The operating system that monitors input devices knows about hundreds, if not thousands of different events. There are many more events than just input events. A sheet of paper was not resizable unless you used scissors, but operating system windows are. So, there are events that happen when a window is moved, resized, closed, and more.

The browser environment processes these events automatically without Logo having to know. Logo is, of course, interested in keyboard and mouse events, and a few more events. The browser hands these events over to Logo, and the Logo environment forwards the events to the Logo program.

The Logo environment needs to know where to send, say, a mouse event. And this is why event names are predefined. If the OS sends a mouse movement event, the browser forwards it to the Logo environment, and Logo knows that it needs to look for a runlist stored together with the event name “MOUSE.MOVED.

Operating system events include

  • Keyboard input.
  • Mouse movements
  • Mouse button clicks
  • Menu commands

Common Events

Now that you know everything about event-driven programming, here is the list of possible events that you can define along with the format of the :EVENTDATA. Not that another global variable, :EVENTSOURCE, contains the name of the device that triggered the event, which is either the name of a control widget or the name of a robot device.

Event Name EVENTDATA Description
KEY.PRESSED Key code Sent when the user presses any key. The data is the character that the user pressed. For keys that are not associated with characters, the data is a word describing the key, like ENTER, HOME, or LEFT. Note that when you tell the browser to ignore the event, the Listener will not accept any keys anymore.
KEY.PRESSED.x Key code Sent when the user presses the key “x”, where “x” can be the name of any known key. The data is the character that the user pressed. For example, the event name KEY.PRESSED.A would run if the “A” keys was pressed, or KEY.PRESSED.HOME for the HOME key.
MENU.XXXXX Sent when the user clicks a user-defined menu item (see APPENDMENIITEM). The “XXXXX” is the name of the menu item. For example, if you define a menu item “TEST”, give it the name “TESTITEM” and append it to the Window menu, the command would be APPENDMENUITEM “WINDOW “TEST “TESTITEM, and the corresponding event name would be “MENU.TESTITEM”.
MOUSE.MOVED [[x y] buttons] Sent when the mouse moves over the Graphics panel. The data is a two-element list. The first element is a two-element list of the X and Y coordinates of the mouse position. The second element is TRUE if the left mouse button is down and FALSE if not. If you want to check more than one mouse button, read the GRAPHICS object's BUTTON property.
MOUSE.CLICKED [[x y] buttons] Sent when a mouse button is clicked in the Graphics panel. The data is the same as for the MOUSE.MOVED event.
TOUCHED Array of finger data Sent on touch-enabled devices when one or more fingers touch the surface of the Graphics panel. The data is a list of lists, where each list element stands for a finger. Each “finger list” has three elements. The first element is a two-element list of the X and Y coordinates. The next element is again a two-element list of the touch area's width and height; a finger is never so small that it touches only a single pixel like a mouse does. Finally, the third element indicates the amount of pressure that the finger applied; this is a value between 0 and 1. For touch screens without pressure sensitivity (most are not touch sensitive), the value is 1.
TOUCH.MOVED Array of finger data Sent on touch-enabled devices when one or more fingers that are currently touching the Graphics panel move. The event data is the same as for the TOUCHED event.
GRAPHICS.RESIZED [width height] Sent when the size of the Graphics panel changes. The data is a two-element list with the new width and height.
BLUETOOTH.CONNECTED [type name] When a Bluetooth device connects to Logo, Logo receives this event. The data is a two-element list with the device type being the first element, and the device name the second element, like, for example, [BLUEBOT BETTY].
BLUETOOTH.DISCONNECTED [type name] Sent when a Bluetooth device disconnects. This can either happen when Logo closes the connection, or the connection is lost becasue the device has been turned off or is out of range.

Control Events

Controls like push buttons are input devices; at least, they should input whatever the user selected to the program. Controls do not have event handlers that you can define with an "EVENTS property. Since there are so many different controls with different behaviors, it would be difficult to define event handlers for all occasions.

Instead, controls have a RUN property. When a control is clicked, altered, changed, and wants to inform the program, it executes any runlist that is stored under the RUN property. The runlist in turn can check the control's current value and react accordingly.

By the way: Also turtles have their own RUN property that a turtle attempts to execute whenever it is clicked! Try this:

PPROP 0 "RUN [PLAY "~HOME/SOUNDS/COW]

Now, you may believe that your turtle actually is a cow whenever you click it!

For these runlists, Logo sets both :EVENTDATA and :EVENTSOURCE to the name of the widget.

Robot Events

Sophisticated devices like robots often issue a lot of events. Typical events are the value of a proximity sensor, whether a motor has started or stopped, and more. Also, robots carry a lot of properties that reflect the status of its sensors.

Currently, only InO-Bot and the Root bot have their own events. See the page about advanced floor robots for details.