6.2 User Interface Tool-Kit
6.2.2 Event Distribution
WIN supports multiple applications. Since Napier88 provides a single thread of control only one application can execute at a time and the others are suspended until control is transferred. This transfer of control is event-driven: an application is active only so long as input events are directed to it. Each application is modelled as a procedure that accepts a single input event, performs some action and then returns control to a central event routing system (ERS). At any time the ERS may have several such applications registered with it. When an input event occurs the ERS determines to which application to route it and calls the appropriate application procedure with the event as parameter. As there is only one thread of control the ERS is suspended while the application performs its processing. When the processing terminates the ERS resumes polling for input events. This control structure is illustrated in Figure 6.2:
which application? input event detection applications event routing system (ERS)
Figure 6.2: Event distribution in WIN
The successful operation of this system depends on adherence to a convention that application procedures will return control as soon as possible. In particular it is assumed that applications will not poll for input events themselves but will rely on the ERS to detect and route events.
The ERS deals with fairly low-level input events that describe mouse and keyboard activity. On each event detection/routing cycle the ERS determines whether any keyboard characters have been typed since the last event was routed. If so, all the new characters are concatenated into a single string and routed as an event. Otherwise a mouse event is routed. The representation of the mouse event contains information about the current position of the cursor and whether or not the mouse buttons are currently pressed. Mouse events are routed even if the state of the mouse has not changed since the last event, thus when the system is quiescent with no user input occurring a continuous stream of identical mouse events is routed.
The ERS also generates two kinds of ‘pseudo-event’ which are used to signal to applications when the event distribution path changes. Each time a keyboard or mouse event is routed the ERS first checks whether it is being routed to the same application that received the previous event. If not, before routing the event, the ERS routes a ‘deselect’ event to the application that received the previous event and then a ‘select’ event to the new application. The main purpose of this is to let an application know when it is about to become inactive. For example a light-button might be programmed by an application that highlights and de- highlights an area of the screen depending on whether a mouse button is pressed when the cursor is in the area. The use of deselect events allows the application to de-highlight the light-button if the cursor moves out of the area while the mouse button is pressed down. Without deselect events the application would not be called once the cursor was moved away.
Events are represented by the following Napier88 type:
type Event is variant( chars : string;
mouse : Mouse;
where
type Mouse is structure( x, y : int ; buttons : *bool )
The state of the mouse buttons is represented by the vector of booleans buttons, the it h element of which is true if button i is pressed and false otherwise. Higher level mouse events such as double clicks and button state transitions are not represented in the ERS but can be detected by computation within applications if required. Keyboard input, however, is treated at a higher level in that events represent characters rather than states of the entire keyboard. Whether or not this approach gives a more suitable level of abstraction for the application programmer, it is forced by the underlying IO facilities of Napier88.
The routing of events is performed by notifiers. The main notifier keeps track of the applications registered with the ERS and the criteria that determine how events are routed between them. When each application is registered the programmer supplies a filter procedure that, when invoked, takes an input event as its parameter and returns a boolean. A value of true indicates that the application accepts the event for processing while false indicates that the event is ignored by the application. The notifier maintains an ordered list of applications and their associated filter procedures. To route an event the notifier scans the list, calling each filter procedure in turn with the new event as parameter, until one of them returns true. The event is then passed as a parameter to the corresponding application and deemed to have been consumed. If none of the filter procedures return true the event is discarded.
Notifiers can be composed in hierarchies. This is achieved by registering one notifier as an application with another notifier. Thus a top-level notifier might route events between different user applications while sub-notifiers are used to route them to different components within an application.
Notifiers route events given to them; another component is needed to poll for user input, construct events from it, and pass them to the top-level notifier. This role is performed by the event monitor, the only system that actively polls the input devices. Figure 6.3 shows the interactions of notifiers, applications and the event monitor:
. event monitor filter application order of testing repeated calls notifier notifier notifier
Figure 6.3: Event monitor and a notifier hierarchy
Notifiers can be re-configured dynamically, that is, (filter, application) pairs can be removed and new ones added at any point in the list.