What are Events in Programming?
Event handling is a major concept in game and application programming. It allows for the user to interact dynamically with your program. Whenever you move your mouse cursor, press or release a key on the keyboard, use a touch screen or maximize/minimize the application window, all these interactions (and many, many more) are recognized as so-called events.
Events in SDL3
Whenever an event occurs in SDL3, for instance a key gets pressed on the keyboard, all the information related to that specific event is stored in a TSDL_Event record. Depending on the event type (e.g. mouse motion, key pressed on a keyboard, maximizing the application window) there is a different event field available in the record.
For example for a mouse motion you can read out the x and y position of the cursor (in the motion field). In contrast there is no sense in having x and y values for a pressed key on the keyboard, but to know which specific key has been pressed on the keyboard (in the key field).
Event Types, Event Fields and Event Structures
There are many more types of events than a mouse motion and a key being pressed on a keyboard. Think of using a joystick, using a touchscreen, minimizing/maximizing the application window, and so forth. There are plenty of different events that could occur.
All these event types have certain names, e.g. the event type which indicates a mouse motion is called SDL_EVENT_MOUSE_MOTION. All names of event types start with SDL_EVENT_, then often follows the device it is related to, e.g. MOUSE, KEYBOARD, GAMEPAD, JOYSTICK, PEN, CAMERA, and finally the actual action, e. g. MOTION, BUTTON_DOWN, ADDED, … .
A few selected examples are described below:
- SDL_EVENT_MOUSE_MOTION
- created by a mouse motion
- field name: motion
- provides x- and y-coordinates of mouse position
- provides relative distance to last x- and y-coordinate
- provides state of the pressed buttons (during motion)
- SDL_EVENT_MOUSE_BUTTON_DOWN / SDL_EVENT_MOUSE_BUTTON_UP
- created by mouse button press / release
- field name: button
- provides the button which has been pressed / released
- provides click rate (single click, double click, …)
- provides x- and y-coordinates of mouse position
- SDL_EVENT_KEY_DOWN, SDL_EVENT_KEY_UP
- created by a keyboard button press / release
- field name: key
- provides key code, scan code and possible key modifiers for the pressed / released button
- SDL_EVENT_WINDOW_RESIZED, SDL_EVENT_WINDOW_MOVED
- created when the application window is resized / moved
- field name: window
- SDL_EVENT_QUIT
- created when the application window is quit (e. g. click on “X”)
- field name: quit
The full list according to the official SDL3 documentation is linked here.
This list is overwhelmingly long but don’t worry, as soon as you get the concept behind events you will easily understand which of these event types will play a role for the applications you like to develop.
Relationship between Event Type, Event Field and Event Structure
The event type determines the available event field, which determines the event structure.
Let’s assume you press a key. This will create an event of type SDL_EVENT_KEY_DOWN. This event provides an event field key which allows access to an event (record) structure.
TSDL_KeyboardEvent = record
type_: TSDL_EventType; {*< SDL_EVENT_KEY_DOWN or SDL_EVENT_KEY_UP }
reserved: cuint32;
timestamp: cuint64; {*< In nanoseconds, populated using SDL_GetTicksNS() }
windowID: TSDL_WindowID; {*< The window with keyboard focus, if any }
...
end;
The first four fields of the event record structure is the same for all events: their event type, a reserved field, a timestamp when the event was triggered and the window id, hence, in which application window has the event been triggered.
Then follow event specific fields, which are dependent of the specific event as described in the list above. For a keyboard event these may be key codes, for a mouse motion event this may be the mouse position.
Some event types can share the same event field and structure. For example, the event types SDL_EVENT_KEY_DOWN and SDL_EVENT_KEY_UP which are generated by pressing or releasing a key share the same event structure since the information are the same.
Keyboard Event and Mouse Event in Action
Let’s dive into the example code.
program SDL3_EventHandling;
uses SDL3;
var
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlEvent: TSDL_Event;
sdlPoint: TSDL_FPoint;
RunLoop: Boolean = True;
begin
// initilization of video subsystem
if not SDL_Init(SDL_INIT_VIDEO) then
Halt;
// create window and renderer
if not SDL_CreateWindowAndRenderer('SDL3 Events', 500, 500, 0, @sdlWindow1, @sdlRenderer) then
Halt;
// render and show cleared window with grey background color
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
// set point coordinates at center of window
sdlPoint.x := 255;
sdlPoint.y := 255;
// run the (game) loop until it should stop (= false); triggered by escape or q key
while RunLoop do
begin
// run the event loop until all events in queue have been treated
while SDL_PollEvent(@sdlEvent) do
begin
case sdlEvent.type_ of
SDL_EVENT_KEY_DOWN:
begin
case sdlEvent.key.key of
SDLK_W: sdlPoint.y := sdlPoint.y-1;
SDLK_A: sdlPoint.x := sdlPoint.x-1;
SDLK_S: sdlPoint.y := sdlPoint.y+1;
SDLK_D: sdlPoint.x := sdlPoint.x+1;
SDLK_SPACE:
begin
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
end;
SDLK_ESCAPE, SDLK_Q: RunLoop := False;
end;
// set yellow drawing color
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 0, SDL_ALPHA_OPAQUE);
end;
SDL_EVENT_MOUSE_MOTION:
begin
sdlPoint.x := sdlEvent.motion.x;
sdlPoint.y := sdlEvent.motion.y;
// set red drawing color
SDL_SetRenderDrawColor(sdlRenderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
end;
end;
end;
// render the point
SDL_RenderPoint(sdlRenderer, sdlPoint.x, sdlPoint.y);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(20);
end;
// clear memory
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow (sdlWindow1);
// quitting SDL3
SDL_Quit;
end.
The result depends on your mouse movement in the application window (red dots) and/or the pressed W-, A-, S- or D- keys on the keyboard (yellow dots).

The initial lines of code are:
program SDL3_EventHandling;
uses SDL3;
var
sdlWindow1: PSDL_Window;
sdlRenderer: PSDL_Renderer;
sdlEvent: TSDL_Event;
sdlPoint: TSDL_FPoint;
RunLoop: Boolean = True;
begin
// initilization of video subsystem
if not SDL_Init(SDL_INIT_VIDEO) then
Halt;
// create window and renderer
if not SDL_CreateWindowAndRenderer('SDL3 Events', 500, 500, 0, @sdlWindow1, @sdlRenderer) then
Halt;
We initialize a SDL3 application with a 500 by 500 pixels window as known.
The TSDL_Event variable “sdlEvent” stores the events generated by the user of the application. We use it later to read out the event information of each individual event that gets created.
We also need a variable “sdlPoint” of known type TSDL_FPoint to describe a point with float point coordinates.
Finally there is a simple Pascal boolean variable “RunLoop”, which is initilized to true.
// render and show cleared window with grey background color
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
// set point coordinates at center of window
sdlPoint.x := 255;
sdlPoint.y := 255;
Also known from previous chapters, we set up a grey drawing color (RGB: 50, 50, 50) for the background and render it.
Also we set the initial coordinates of the point which is at the center of the window.
// run the (game) loop until it should stop (= false); triggered by escape or q key
while RunLoop do
begin
// run the event loop until all events in queue have been treated
while SDL_PollEvent(@sdlEvent) do
begin
We now enter two while loops. The first, outer while loop keeps the application running as long as the “RunLoop” variable is true. The loop is exited as soon as “RunLoop” equals false.
For every application cycle (outer while loop), we enter a second, inner while loop. It runs as long as the SDL3 function SDL_PollEvent(pointer of TSDL_Event) equals to true. SDL_PollEvent() equals to true as long as there are still events in the event queue.
Every time the SDL_PollEvent() is called, it feeds the event type and the specific event information to the event of the argument and deletes the event from the event queue. In our case, the information is fed to “sdlEvent”. To be precise, the argument does not need the event itself but rather its pointer, so we use the @ operator to get its pointer. That is why we call SDL_PollEvent() by
SDL_PollEvent(@sdlEvent)
as the while loop condition.
The type_ field
Every event provides the type_ field. The event type can be read out from the field type_, hence we check for the type in sdlEvent.type_ by a case-statement.
case sdlEvent.type_ of
SDL_EVENT_KEY_DOWN:
begin
case sdlEvent.key.key of
SDLK_W: sdlPoint.y := sdlPoint.y-1;
SDLK_A: sdlPoint.x := sdlPoint.x-1;
SDLK_S: sdlPoint.y := sdlPoint.y+1;
SDLK_D: sdlPoint.x := sdlPoint.x+1;
SDLK_SPACE:
begin
SDL_SetRenderDrawColor(sdlRenderer, 50, 50, 50, SDL_ALPHA_OPAQUE);
SDL_RenderClear(sdlRenderer);
end;
SDLK_ESCAPE, SDLK_Q: RunLoop := False;
end;
// set yellow drawing color
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 0, SDL_ALPHA_OPAQUE);
end;
In our example program we distinguish and treat only two event types by a case statement, namely SDL_EVENT_KEY_DOWN and SDL_EVENT_MOUSE_MOTION. Every other possible event type is ignored by our program.
The SDL_EVENT_KEY_DOWN Event
If _type is a SDL_EVENT_KEY_DOWN event, a begin-end block is entered.
We know from this event type, that the event field which has all the event’s information is called key. The event structure you have there is as follows:
SDL_KeyboardEvent = record
type_: TSDL_EventType; {*< SDL_EVENT_KEY_DOWN or SDL_EVENT_KEY_UP }
reserved: cuint32;
timestamp: cuint64; {*< In nanoseconds, populated using SDL_GetTicksNS() }
windowID: TSDL_WindowID; {*< The window with keyboard focus, if any }
which: TSDL_KeyboardID; {*< The keyboard instance id, or 0 if unknown or virtual }
scancode: TSDL_Scancode; {*< SDL physical key code }
key: TSDL_Keycode; {*< SDL virtual key code }
mod_: TSDL_Keymod; {*< current key modifiers }
raw: cuint16; {*< The platform dependent scancode for this event }
down: cbool; {*< true if the key is pressed }
repeat_: cbool; {*< true if this is a key repeat }
end;
Additionally to the already seen first four fields, we have
- the keyboard id,
- the scan code,
- the key code,
- pressed key modifiers (shift, ctrl, …),
- a raw scan code,
- if the key is pressed (if not, it means it got released),
- and if the event is triggered by the repeat mechanism, which kicks in if you keep a key pressed for a longer time.
We are interested in the key code of the pressed key, so we read out the information from the event structure’s key field, hence the information is found in
sdlEvent.key.key
Basically a key code is a constant which corresponds to a certain, unique hexadecimal number. For example the constant for the W-key is SDLK_W, its hexadecimal number is 77 (decimal number 119). All key codes’ names start with SDLK_.
With the case statement we look for specific key codes that may have been pressed. The key code names are self-explanatory. We check for the W-, A-, S-, D-. Space-, Escape- and Q-key by the key codes SDLK_W, SDLK_A, SDLK_S, SDLK_D, SDLK_SPACE, SDLK_ESCAPE and SDLK_Q. A list of all SDL3 key codes and their hexadecimal numbers is found in this link.
Depending on the pressed key, we change the position of the point (W: up, A: left, S: down, D: right), clear the window with the background color (space) or set the “RunLoop” variable to false, which effectively stops the program.
At the end of this event’s block the drawing color is changed to yellow.
The SDL_EVENT_MOUSE_MOTION Event
The second case of the case statement is the mouse motion event.
SDL_EVENT_MOUSE_MOTION:
begin
sdlPoint.x := sdlEvent.motion.x;
sdlPoint.y := sdlEvent.motion.y;
// set red drawing color
SDL_SetRenderDrawColor(sdlRenderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
end;
end;
The event field for a mouse motion is called motion. It’s record structure is shown below:
SDL_MouseMotionEvent = record
type_: TSDL_EventType; {*< SDL_EVENT_MOUSE_MOTION }
reserved: cuint32;
timestamp: cuint64; {*< In nanoseconds, populated using SDL_GetTicksNS() }
windowID: TSDL_WindowID; {*< The window with mouse focus, if any }
which: TSDL_MouseID; {*< The mouse instance id or SDL_TOUCH_MOUSEID }
state: TSDL_MouseButtonFlags; {*< The current button state }
x: cfloat; {*< X coordinate, relative to window }
y: cfloat; {*< Y coordinate, relative to window }
xrel: cfloat; {*< The relative motion in the X direction }
yrel: cfloat; {*< The relative motion in the Y direction }
end;
If an SDL_EVENT_MOUSE_MOTION event is found, we change the x- and y-values according to the x- and y-values (mouse coordinates) provided by the event.
Finally we change the drawing color to red.
end;
end;
// render the point
SDL_RenderPoint(sdlRenderer, sdlPoint.x, sdlPoint.y);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(20);
end;
The first “end;” closes the case statement for the event types, the second “end;” closes the inner while loop which checks for pending events in the event queue. After that we are back in the outer application-“RunLoop”. Every cycle we render the point once, update the rendered window and delay for 20 milliseconds.
The delay is necessary to prevent the CPU from running as fast as it can, which is kind of short-circuiting the program. You should never do this for your CPU’s sake.
// clear memory
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow (sdlWindow1);
// quitting SDL3
SDL_Quit;
end.
Nothing new here, the renderer and window get destroyed and SDL3 is quit.
Congratulations! Event handling in SDL3 is now known to you.
I will will elaborate on some specifics here, which may or may not be interesting to you. Please skip them if you are not interested right now.
The Repeat-Delay Problem (Keyboard)
The _repeat field let’s you know if the corresponding key on a keyboard is in repeat mode. For most operation systems the repeat mode kicks in after a short delay when you keep a key pressed.
You may try out to open up a simple text editor. If you press any key that has a letter (e.g. “a”-key), in the text editor you will see an “a”. And if you keep the key pressed after a short delay the repeat mode kicks in and rapidly several further a’s are coming up. If you are in repeat mode for a certain key, repeat_ has true boolean value, otherwise false.
Especially for games, you may want to turn off the initial delay if you keep a key pressed and let the constant repeat mode kick in without delay. Let’s assume you have a spaceship which should move left on pressing the “a”-key. – You do not want to have this happening with a delay.
A simple solution to the “repeat-delay”-problem: Instead of looking for the actual event being repeatedly triggered by an key event, use a switch which gets turned on if the key down event is occuring and which is turned off if the key up event is occuring.
Example: Let’s assume the spaceship moving left on pressing the “a”-key again. Instead of using the repeat_ field at all, you make a switch (e.g. MoveSpaceshipLeft := True) if the “a”-key is pressed. As soon as the SDL_EVENT_KEY_UP event is triggered for the “a”-key, the switch is turned off (e.g. MoveSpaceshipLeft := False).
The Difference between Key code and Scan code
You may have noticed that we used key codes in the example code, but there is another field in the keyboard event structure, which reads scan code. Scan codes also represent keys on the keyboard. So, why wouldn’t we use these instead?
The scan code refers to a specific physical location of a key on the keyboard. The scan code is referenced to the typical US keyboard layout (QWERTY layout). The term “QWERTY” just refers to the the first six letters from left to right in the first row with letters on a typical US keyboard. For example: The German keyboard layout (QWERTZ layout) is similar to the US one (for most of the letter keys), though the “Y”-key and the “Z”-key have exactly opposite positions (hence QWERTZ for the German layout in contrast to QWERTY for the US layout). If you press the “Z”-key on a German keyboard, the returned scan code will represent the letter “Y”, since the position of the key (independent of the layout) is equal to the position of the “Y” key on an US keyboard. Scan codes are layout-independent.
The key code refers to the virtual representation of the key according to the keyboard layout. Here you consider the layout, hence key codes are layout-dependent. As discussed before the scan code for the “Z”-key on a German keyboard will return a letter “Y”, since the key has the location of the “Y”-key of an US keyboard. In contrast the key code will correctly return that the “Z”-key has been pressed.
Think of the famous WASD key arrangement (arrangement of the four keys “W”, “A”, “S” and “D”) in the US layout, even if you have a keyboard without any latin letters, you may want to use the four keys which are physically at the WASD location to move a game character forward (“W”), left (“A”), backward (“S”) or right (“D”). The labeling of the keys doesn’t matter in that case and the keys are not used to input some text. That is when you use scan codes.
Text Input is done in SDL3 in another way!
The shown way of handling keyboard events could be used to create kind of a text input mechanism. DO NOT do it this way! SDL3 provides a special and much more convenient event for that.
This modified quote from the SDL 1.2 to SDL 2.0 migration guide sums it up excellently:
Use SDL_EVENT_KEY_DOWN to treat the keyboard like a 101-button joystick now. Text input comes from somewhere else.
Key Modifiers: Shift, Ctrl, Alt, …
The _mod field is a 16 bit unsigned integer (corresponds to Pascal’s Word) and represents key modifiers (ctrl, alt, shift, num lock, caps lock, …), which may be pressed while pressing another key. Their names always start with SDL_KMOD_. For example, the left shift key has the name SDL_KMOD_LSHIFT. The full list of possible key modifiers is not very long and found in the link.
If a SDL_EVENT_KEY_DOWN event is caught, you can check the _mod field too see if a key modifiers was pressed at the same time.
Let’s assume you want to have your application to be quit by the user pressing the left or the right ctrl key and “Q”. So you read out the key code for “Q” and check if _mod equals to SDL_KMOD_CTRL.
Previous Chapter | Next Chapter
[Title image created with https://deepimg.ai; public domain]