Skip to content
George Kudrayvtsev edited this page Oct 23, 2013 · 3 revisions

Handling events is essential to having a game window that's not.. well.. frozen and unresponsive. Zenderer has a simple API in-place for taking care of system events such as keyboard presses, window events, and mouse state. The system is an object-oriented wrapper over GLFW's event handling API. The style itself takes slightly after Pygame's style of events, with separate structures based on the type of event generated.

Note: All of the code below assumes using namespace zen; using namespace evt; for brevity's sake.

Event Types

You can see the identifiers for the various events supported by Zenderer in the code here; they are also outlined below.

Event Structure

The structure used to trigger event sequences throughout Zenderer contains several components, each pertaining to different EventType values. You can access keyboard event data through event_t::key, mouse data through event_t::mouse, and the type data through event_t::type. For any given event, the relevant structure is guaranteed to contain valid data. For example, a KEY_DOWN event ensures that the event_t::key structure has valid information about the keyboard state.

Keyboard Events

The keyboard event structure (key_t) contains information about events generated by pressing keys, letting them go, and holding them down. They are recognized by the various KEY_* enumeration values.

  • KEY_DOWN: This event is triggered when key on the keyboard is initially pressed.
  • KEY_UP: This event is triggered when a previously pressed key is released.
  • KEY_HOLD: This event is triggered after a KEY_DOWN event if the key is held for a certain period of time. It is consistently triggered until the key is released.
  • KEY_PRINTABLE: This event is triggered simultaneously with KEY_DOWN if the pressed key has an ASCII printable representation.

The keyboard event structure holds several data fields: information about various keyboard modifier keys (such as Shift, Alt, etc.), an identifier for the key that was pressed, a ASCII printable representation of the key, if possible, and the system-dependant scan code for the key.

If the character is not printable, event_t::key::symbol is the NULL terminator (\0). A unique identifier for the key pressed can be access through event_t::key::key, and a full list of the possible keys is in the Key enumeration

Assuming a previously polled event structure, you could check for user attempts to quit the program though the keyboard like so:

// -snip-
// Event polling
// -snip-

// Assume "Evt" is of type "evt::event_t" and has been polled.
if (Evt.type    == EventType::KEY_DOWN &&
    Evt.key.key == Key::ESCAPE)
{
    Quit();
}

Mouse Events

The mouse event structure (mouse_t) contains information about events generated by pressing buttons on the mouse, letting them go, and moving the mouse. There is support for up to 8 different mouse buttons. The first three are identified by LEFT, MIDDLE, and RIGHT, respectively, but the remainder are identified by BUTTONi where i is in the range [4, 8]. They are recognized by the various MOUSE_* enumeration values.

  • MOUSE_DOWN: This event is triggered when a mouse button is initially pressed.
  • MOUSE_UP: This event is triggered when a previously pressed button is released.
  • MOUSE_MOTION: This event is triggered when the mouse is moved from its previous position.

The mouse event structure holds several data fields: information about various keyboard modifier keys (such as Shift, Alt, etc.), an identifier for the button that was pressed, the current position of the mouse, and whether or not the relevant button (if any) is pressed down or not.

Note: The mouse_t::down and mouse_t::button specifiers are not set to some "valid" value with MOUSE_MOTION events. Rather, they are set to their defaults; namely, the button specifier is MouseButton::UNKNOWN, and the mouse-down state is false.

Assuming a previously polled event structure, you could output current mouse position like so:

// -snip-
// Event boilerplate as above.
// -snip-

if (Evt.type == EventType::MOUSE_MOTION)
{
    std::cout << Evt.mouse.position << std::endl;
}

Mouse Helper Functions

Sometimes it's not feasible to wait for events to trigger for the mouse, so it's possible to directly retrieve the state of the mouse. GetMousePosition() returns the mouse position for the current window. Additionally, GetMouseState(MouseButton) gives the state of a button at that moment in time, returning true if the button is down, and false if not.

Window Events

Currently, the only window event that causes a trigger is the WINDOW_CLOSE event, fired when the user clicks the 'X' on the window, or whatever other mechanism your supported OS uses for closing applications.

Note: If a WINDOW_CLOSE event was triggered, this does NOT mean that the window will be closed. This is a note that says that the user wants the close the window, it's up to the client application to handle the event appropriately.

In example, a "quit-handling" loop such as this will not work as expected:

while (Window.IsOpen())
{
    // -snip-
    // Event boilerplate as above.
    // -snip-
    
    if (Evt.type == EventType::WINDOW_CLOSE) break;
}

You must actively do something with the event, such as call gfx::zWindow::Destroy().

Polling Events

There is a global singleton event state manager, known as zEventHandler, which is accessible through zEventHandler::GetInstance(). Every frame, you should call PollEvents() on this instance to pump all of the latest events into the queue. After this, call PopEvent(event_t&) repeatedly until there are no more events in the queue, handling them as would be appropriate for your application. The event queue actually performs as a stack, giving the latest events first (LIFO).

In code, the process would look something like this:

// Event structure.
event_t e;

// Shortcut to global instance.
zEventHandler& Evts = zEventHandler::GetInstance();
Evts.PollEvents();

while (Evts.PopEvent(e))
{
    // Handle events somehow.
}

Architectural Notes

The various event callbacks are registered by gfx::zWindow when one is created. Theoretically, multiple windows should registered and trigger separate callbacks, but this has most assuredly not been tested. Future implementations may add a window identifier to event_t instances.