Events
This chapter is not like the previous ones, in the sense that it is more about pure concepts than following an example. In fact, this chapter will not include a demo, although things said here will be applied in the next one.
We will get into detail about how EWS handles user input (and some other
stuff that may not come from the user). Almost every action done by EWS
is triggered by EVENT
, which are some small objects usually
associated to user input or external events.
You may have noted that, except some setup, all of the application work
is made during the do_event_loop
call. This routine works
calling repeatedly to the do_event_loop_iteration
procedure
defined on your DISPLAY
. This routine gets an event (it may
block or not to get one depending on its wait
argument),
puts it on an internal queue, "processes" it (we will see soon what this
means), adds a new event to the queue if available, and repeats until no
events are available. After that, the display is updated.
New external events may be inserted to the queue by the
poll_event
routine of the DISPLAY
.
wait_event
is similar but ensures that a new event will be
added to the queue. These methods are defined in SDL_DISPLAY
to get the kind of input that SDL has access to, i.e.:
- Keyboard action (key presses and releases)
- Mouse movement
- Mouse buttons (press and release)
- Timer
You can also add events artificially using the
add_event
routine (you have seen this one before, being used
to add a quit event). next_event
removes the first event from
the queue. event
and event_available
can be used
to query the queue.
Note that the above algorithm will produce usually a redraw for each event, except in the following cases:
- Events are coming too fast to be processed
- Processing or redrawing takes long and new events start accumulating
before being get by
poll_event
- Events are synthesized by the processing stage
Event processing consists in passing the event to the
handle_event
procedure of the appropiate windows. The display
passes the event to the root window, and trusts the window to pass it on.
This chain of calls to handle_event
is called "event propagation".
Not all events propagate the same way. Events usually move from the root
to the branches of the window tree, but may be selective about which
branches to take. Each EVENT
has a method propagate
which takes a WINDOW
as argument, and selects the proper child(s)
to propagate itself calling handle_event
of the child(s) It may
be important not just which childs it select but also the propagation order.
So, the usual propagation chain works like this:
- The display polls or wait for an event
e
, and it gets into the queue - Some time later, the event reaches the top of the queue
- The display takes it from the top and calls
root.handle_event (e)
- The root window calls
e.propagate (Current)
- The event chooses a child
c
to propagate, and callsc.handle_event (Current)
- c calls
e.propagate (Current)
- ...
This chain is not a sequence, it can grow and shrink for a while during the processing. For example, step 6 may finish and go back to step 5, where the event might choose another child. The chain will stop growing when the event decides not to get passed to any child (possibly because the window has no children), or when a window decides not to propagate the event.
The interesting part of all of this, is that windows will usually want to
do something else with the event besides tossing it around. So to add
behaviour to a window you have to redefine its handle_event
to do something useful besides (or instead of!) propagation. We will see an
example in the next chapter.
It is possible to have almost any propagation policy defining a new
heir of EVENT
with some strange implementation of
propagate
. Note that it could even be something that does not
traverse the code from root to branch. However, there are a few generally
useful propagation policies, and all of them are implemented in EWS as
deferred classes:
EVENT_BROADCAST
: This events propagate to every visible child, in depth first order. Note that windows which are not visible (seehide
andshow
operations inWINDOW
) must be skipped by all event propagation schemes (see precondition ofhandle_event
). Timer and quit events have this policy.EVENT_POSITIONAL
: This event has a coordinate pair associated. It is propagated to children that are covering the position of the event. The receiving window will always receive the event with local coordinates. Windows which appear to be on the front will receive this event first. Mouse moves and clicks have this policy.EVENT_FOCUSED
: This event propagates lineally from the root to the window having focus. Keyboard events have this policy
All events have a flag called handled
that is initially
set to False
, but can be enable using the routine
set_handled
. Once enabled, it can not be disabled again.
This flag is used to indicate that the event is "consumed" and its
processing should stop. Most events will stop propagating after having
this flag set. Most windows will stop asking the event to propagate after
seeing it labeled as handled. In fact, the default implementation
for handle_event
is
if not event.handled then event.propagate (Current) end
And a usual redefinition of handle_event
will be
if event is interesting then do something event.set_handled elseif event should be filtered then event.set_handled end Precursor (event)
The precursor call can be at the top or at the bottom depending if you want the window to have priority on event handling over its children or not.
Most of the time you will be defining a few kind of new windows, and very rarely a new kind of event. New events can be useful when you have a new special kind of input, or as a very general intercommunication mechanism between parts of your application where direct procedure call is difficultor impractical. Most of the time, you will be working with the following predefined events:
EVENT_FOCUS
: sent to a window when it gets or loses focus; synthesized byWINDOW
. It has the same policy as keyboard events.EVENT_KEY
: generated when a key is pressed or released.EVENT_MOUSE_BUTTON
: generated when a mouse button is pressed or released over a window.EVENT_MOUSE_MOVE
: generated when the mouse moves over a window.EVENT_MOUSE_NOTIFY
: generated when the mouse enters or exits a window; synthesized byEVENT_MOUSE_MOVE
EVENT_QUIT
: generated when the close button of the window frame (if using windowed mode) is clickedEVENT_REMOVE
: This event is quite artificial. It is generated by theremove
action ofWINDOW
, which instead of removing the window inmediatly queues one of these events to force the removal to happen while no other event is being processed. This event does not propagate, and only the root window will see it. This is done in this way to avoid some problems that would happen if a window were removed while still propagating events.
EVENT_QUIT
has a little special handling by EWS besides
propagation. After the event has been processed, it stops the looping of
do_event_loop
if it has not been handled. It is usually
synthesized to force a quit. You can set it as handled if you want to
block external (or internal) requests for quit, and it will be ignored
by the event loop. EVENT_MOUSE_MOVE
has also some minor special
handling to update the mouse pointer (So, you can move the mouse pointer
sending synthetic mouse move events).
At this time, you are encouraged to read the short forms of
WINDOW
and DISPLAY
; at this time all methods should
be clear. Now that you have mastered EWS basics, the next step is moving to
widget programming
Previous | Table of contents | Next |