C++ - Event-Driven Programming

Introduction

In the classical approach to programming, events happen in a pre-defined sequence. In fact, classical programmers go the extent of defining programs as sets of instructions that are executed sequentially by the computer. Of course there are loop and jumps but these are still programmed sequentially. However, there are times when you want the program to respond to events on its own rather than check diligently for the event. A typical example is the incorporation of so-called "hotkeys" into a program. In the classical approach, the programmer has to constantly check for these keys, every time a key is pressed. This means a lot of extra effort for the programmer and it only gets worse as the complexity of the program increases.

An ideal situation would be one where the program responds of its own accord to such events like hotkeys. It can be done using input filters and other such innovations but if the program is object-oriented, conversion to event-driven programming is much easier.

Event-driven programming is where the program responds to events rather than follow a sequential course of instructions. Event-driven programming is especially useful in object-oriented environments and where graphical user interfaces are being constructed. Most high-level GUI APIs (OWL, MFC, Turbo Vision, etc) use event-driven programming to embed basic features into every program.

Principles of Event-driven Programming

Firstly, the computer is a sequential device and therefore cannot, on its own accord, respond to events. To simulate this auto-responding, a kernel must be constructed. This kernel must know about all objects in the program and must allow communication with these objects.

Events can be defined as any external influence on the computer. Keystrokes are events as are mouse movements and clicks. Events can also be explicitly sent from one object to another in the form of a message. These are useful for intra-process and inter-process communication. Idle events can also be generated automatically by the kernel to allow for background processing.

Lastly, a basic set of objects must be built, following strict guidelines in terms of interfacing with the kernel. Each top-level object must have the ability to respond to events.

Thus, instead of executing a sequential program, you simply execute the kernel. The kernel will then collect all events, including various forms of input, and pass it on to the relevant objects for processing.

GUIs

In a graphical user interface, the basic object is usually the window. Windows should therefore be capable of processing input. Thus they can process events. All descendants of the basic window object must also have the capability of processing events.

The kernel is normally part of the compiler's libraries (Turbo Vision) or the operating system (OWL/Microsoft Windows). This kernel provides the basis for all activity on the computer by gathering and dispatching events.

Event-driven programming lends itself to multi-tasking since the windows do not themselves have the input focus. The kernel always controls the program and can just as easily control two programs on the screen. Events can be filtered from one window, where they are not needed, to another where they are. Also, events can be buffered and processed when the kernel is idle. Since most graphical operating systems are multi-tasking, their programming APIs are invariably OO and event-driven (eg. OWL, MFC) so as to most accurately model the OS.

Windows as objects can either be self-regenerative or overlapped. MS-Windows uses the former technique where every window must be able to redraw itself at any time. Some older text-based GUIs used the latter technique of each window storing its background - the disadvantage of this is that you cannot change the window order - advantage is that its easier to program. Each window and other object must process events as fast as possible. If the event takes long to process, it must be shifted into an internal queue and processed in the background. This enables cooperative multi-tasking.

Events can be packages into objects where each type of event is identified by a code. Simple GUIs can use keystrokes to denote events.

The kernel can be run in the background or as the main process. If the kernel is part of the operating system, it is always stable and cannot be corrupted by incorrect interfacing on the part of the programmer. However, if the kernel is the main process for the program, all objects created must conform to the interface - this is essential for cooperative multi-tasking - if one object misbehaves all the others will fail to perform satisfactorily.

Sample Event-Driven GUI

This program creates a kernel in the object called Application. The basic Window object is kept in a linked list attached to the kernel. The kernel continuously searches for keystrokes and, if it finds any, passes these to the topmost window. The topmost window will try to process the keystroke, and if it fails will return this status to the kernel, which continues to traverse the linked list until it encounters success.

Window has two event-processing routines which are virtual, ProcessKey and IdleAction. The former processes keystrokes and the latter does background processing when no events are being created. Mouse events and inter-object events are not supported.

BWindow is derived from Window and enhances it by drawing a border around the window. It also shrinks the window by one block all around to exclude the border.

HorzMenu and VertMenu are basic horizontal and vertical menus that fit within a Window. They each take a list of menu items defined by a MenuData object. From these are derived the main menu and file menu of the program.

The StatusLine and MessageWindow are specialist derivation from BWindow, which display a scrolling status bar and a modal message box.

kernel and basic objects

kernel and basic objects header file

main program