This should certainly be the way user interfaces are programmed on Windows, for example:
- All possible events (mouse click, key press, expiration of a timer …) are captured by the operating system and a message is encoded for each event and fed into a message queue (message pump).
- The user interface has a (main) thread, which in turn removes the individual messages from its message queue and distributes them to routines for handling events.
- These routines are performed sequentially (one by one) by the main thread (it is associated with the GUI).
On the one hand, this serialization of the calls simplifies programming, since no parallel processes have to be taken into account. On the other hand, the event handling routines must be completed as quickly as possible. A blocking call to such a routine leads to the well-known frozen surfaces.
How is the event handling routine found when, for example, a button is clicked?
- When the graphical surface is initialized, hierarchically nested data structures are set up, whereby size and location in the form of pixel coordinates are specified for all controls.
This data is used by the operating system to redraw the interface.
Each control is associated with an object (record) of the class that belongs to the type of the control. The connection of the event handling routine belonging to the control is carried out via a function pointer to the routine, which is entered into the object.
Via the data structures of the nested graphical objects, the operating system finds the object assigned to the button (is the coordinate of the click within the coordinates of the border of the displayed object?) and calls via the object noted there function pointer the associated event handling routine.
In a broader sense, this programming paradigm also applies to multi-task systems, for example in the field of embedded software. Here, each task typically responds to events of the shape:
- Receiving a message through a FIFO (queue) where the message may have been fed by an interrupt routine or other task
- Any other synchronization mechanism makes the task runable (Semaphore, Mutex, Event Flag)
- Expiration of a timer
In such a system, the tasks are usually not uselessly busy, but any event causes a task to run and handle that event.Then there is peace again until the next event (typically always an interrupt, be it an expiring timer or any controller). These intermediate rest ingesis can be used to make the CPU fall asleep more or less deeply – saving power and lowering the temperature.