Tag Archives: events

Text Input in SDL3


For text input there is a special way and a special event in place in SDL3.

The SDL_EVENT_TEXT_INPUT Event and PAnsiChar?

The SDL_EVENT_TEXT_INPUT event has the follwing structure:

TSDL_TextInputEvent = record
      type_: TSDL_EventType;       {*< SDL_EVENT_TEXT_INPUT  }
      reserved: cuint32;
      timestamp: cuint64;          {*< In nanoseconds, populated using SDL_GetTicksNS()  }
      windowID: TSDL_WindowID;     {*< The window with keyboard focus, if any  }
      text: PAnsiChar;             {*< The input text, UTF-8 encoded  }
    end;

You can see that apart from the first four fields, which all events have in common, it has just one further field, called text. It is of the PAnsiChar type. If you are familar with Pascal and its String type for text handling, you may wonder, why in this record the text field uses this unusual type.

SDL3 is written in C/C++, and in these languages pointers to null-terminated character “strings” are used to store and handle text. For better compatibility and easier translation of SDL3 (and other C/C++ projects) the Pascal compiler provides a similar type, PAnsiChar.

In principle it is no big deal to use PAnsiChar instead of String, even though Pascal programmers prefer the much more convenient String variables. In short: Strings in Pascal are always safe to use, whereas C’s strings may cause memory leaks if handled wrongly.

The best practice is to convert a C string as soon as possible into a Pascal string, especially if string manipulations follow.

The Text Field does not contain what is considered a Text!

It may surprise you to read, that this text field does not contain a whole text but just a single (or a bunch of) character(s), which represent a word.

In Western languages (especially English) with Latin characters you always only submit one character at a time by each key stroke and compose words and sentences this way. But even in some Western languages you sometimes must combine a Latin character with an accent (which means two key strokes, but just one character) to get special characters. In many non-Western languages (think of e. g. Asian languages) words and sentences are constructed by highly individual signs. To construct these signs you need many key strokes before they are submitted to the text field which then generates the correct sign or sometimes several signs. (I would be glad to give a more detailed explanation here, please feel free to contact me to improve this paragraph.)

How is Text Input done in SDL3?

This is the code:

program SDL_TextInput;

uses
  SDL3;

var
  sdlWindow1: PSDL_Window;
  sdlEvent: TSDL_Event;
  RunLoop: Boolean = True;
  Text1: AnsiString = '';

begin

  // initilization of event subsystem
  if not SDL_Init(SDL_INIT_VIDEO) then
    Halt;

  // create a window (necessary to receive events)
  sdlWindow1 := SDL_CreateWindow('SDL3 Text Input', 500, 500, 0);

  while RunLoop do
  begin
    while SDL_PollEvent(@sdlEvent) do
    begin
      case sdlEvent.type_ of

        // quit program
        SDL_EVENT_QUIT: RunLoop := False;

        SDL_EVENT_KEY_DOWN:
          case sdlEvent.key.key of

            // switch text input mode on/off by ENTER/RETURN key
            SDLK_KP_ENTER, SDLK_RETURN:
              begin
                Write('Text Input Mode is ' );
                if SDL_TextInputActive(sdlWindow1) then
                  begin
                    SDL_StopTextInput(sdlWindow1);
                    WriteLn('OFF');
                  end else
                  begin
                    SDL_StartTextInput(sdlWindow1);
                    WriteLn('ON');
                  end;
              end;

            // remove last character
            SDLK_BACKSPACE:
                begin
                  Delete(Text1, Length(Text1), 1);
                  WriteLn( '                --> ' + Text1 );
                end;

          end;

        SDL_EVENT_TEXT_INPUT:
          begin
            Write( 'Text Input: "', sdlEvent.text.text, '"' );
            Text1 := Text1 + StrPas(sdlEvent.text.text);
            WriteLn( ' --> ' + Text1 );
          end;

      end;
    end;
    SDL_Delay(20);
  end;

  // clear memory
  SDL_DestroyWindow(sdlWindow1);

  // quit SDL3
  SDL_Quit;

end.                

The result looks like this:

As you see, in this example we have a blank window and just use the text output to your command line. In the Lazarus IDE you can get the raw output window by Ctrl + Alt + O.

program SDL_TextInput;

uses
  SDL3;

var
  sdlWindow1: PSDL_Window;
  sdlEvent: TSDL_Event;
  RunLoop: Boolean = True;
  Text1: AnsiString = '';

begin

  // initilization of event subsystem
  if not SDL_Init(SDL_INIT_VIDEO) then
    Halt;

  // create a window (necessary to receive events)
  sdlWindow1 := SDL_CreateWindow('SDL3 Text Input', 500, 500, 0);

  while RunLoop do
  begin
    while SDL_PollEvent(@sdlEvent) do
    begin
      case sdlEvent.type_ of 

Most of this first code bit is known to you from previous chapters. We have no renderer variable because we do not need to render something.

Instead we have “Text1” variable of the Pascal AnsiString type. In contrast to the classical ShortString, it has no length limit. If you just use String, it depends on your compiler settings if they are treated as AnsiString or ShortString. We want to be clear about this here!

We create a window “sdlWindow1” without a renderer. You may ask yourself, why do we need a window if we do not render something to it. You are right, in principle we wouldn’t need one. We still need it, because the events are connected to a window, without a window we have no event detection.

As known from previous chapter about event handling, we enter two while loops. The first one for the application to run and the second one to gather all the events each cycle.

        // quit program
        SDL_EVENT_QUIT: RunLoop := False;

        SDL_EVENT_KEY_DOWN:
          case sdlEvent.key.key of

            // switch text input mode on/off by ENTER/RETURN key
            SDLK_KP_ENTER, SDLK_RETURN:
              begin
                Write('Text Input Mode is ' );
                if SDL_TextInputActive(sdlWindow1) then
                  begin
                    SDL_StopTextInput(sdlWindow1);
                    WriteLn('OFF');
                  end else
                  begin
                    SDL_StartTextInput(sdlWindow1);
                    WriteLn('ON');
                  end;
              end;

            // remove last character
            SDLK_BACKSPACE:
                begin
                  Delete(Text1, Length(Text1), 1);
                  WriteLn( '                --> ' + Text1 );
                end;

          end;

        SDL_EVENT_TEXT_INPUT:
          begin
            Write( 'Text Input: "', sdlEvent.text.text, '"' );
            Text1 := Text1 + StrPas(sdlEvent.text.text);
            WriteLn( ' --> ' + Text1 );
          end;

We in the inner “event loop”, we have three events, two of whom are new, SDL_EVENT_QUIT and SDL_EVENT_TEXT_INPUT.

The SDL_EVENT_QUIT

SDL_EVENT_QUIT is triggered when the application gets the signal to quit, e. g. by clicking on the “X” of a typical application window. We didn’t catch this event in the last chapter, you may have noticed that clicking the “X” didn’t do anything.

SDL_EVENT_TEXT_INPUT Functions

Before getting to the SDL_EVENT_TEXT_INPUT, let’s look at the SDL_EVENT_KEY_DOWN part. If you press the enter or return key, the function SDL_TextInputActive(window) checks, if the text input mechanism is started or not. Obviously, its return value is boolean.

If the text input mechanism is started already, it is stopped by the function SDL_StopTextInput(window). If it is not started, it is started by the function SDL_StartTextInput(window). Only if t he mechanism is started, SDL_EVENT_TEXT_INPUT events are created and can be caught.

This is accompanied by a text which is generated reading “Text Input Mode is ON” or “… OFF” accordingly.

Let proceed to the SDL_EVENT_TEXT_INPUT part: If the the text input mechanism has been started by SDL_StartTextInput(window), we get SDL_EVENT_TEXT_INPUT events as soon as keys are pressed. The new character is stored in:

sdlEvent.text.text

The Pascal function Write() prints out PAnsiChar strings (or rather characters in this case) natively and will show us directly what character has been created.

In line

Text1 := Text1 + StrPas(sdlEvent.text.text);

we concatenate the new character to the “Text1” string. Note how the PAnsiChar character is converted into a native Pascal ShortString by Pascal function StrPas(PAnsiChar string) beforehand. The concatenation will convert it to an AnsiString result in “Text1”.

The resulting text in “Text1” is printed out by the Pascal WriteLn statement.

It is remarkable how easy it is to get even complex characters added to the text without any hassle. Try doing this with just SDL_EVENT_KEY_DOWN events and to make accents possible alone would take a lot of effort.

Removing a Character from the Text

The text input mechanism does not provide a way to remove a character natively. Also other features for convenience, like moving a cursor into the text and inserting a character in between are not supported natively.

As “Text1” is a convenient Pascal string now, we can use Pascal’s string routines to implement all the features we need with little effort. You see, if you press the backspace key with key code SDLK_BACKSPACE, we use Pascal’s Delete(string, index, number of characters) and Length(string) functions to pick the last character of the string an delete it.

The resulting text is printed out.

      end;
    end;
    SDL_Delay(20);
  end;

  // clear memory
  SDL_DestroyWindow(sdlWindow1);

  // quit SDL3
  SDL_Quit;

end.                 

The application’s loop speed is reduced again by SDL_Delay().

If the application loop is left, the window and the SDL3 system is destroyed and shut down accordingly.

Great! You learned about the text input now.

Previous Chapter | Next Chapter

[Title image created with https://deepimg.ai; modified; public domain]

Event Handling in SDL3


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]

Keyboard State and Key States

Last updated on February 17th, 2024

In the next chapter we will see how so called events are processed to handle input of any kind (keyboard, mouse, joystick,…) Before that I’d like to introduce a simple, yet powerful way to handle keyboard input via the keyboard state.

Lets have a look at the most important function here. The keyboard state represents the state (pressed = 1 or unpressed = 0) of all the keyboard keys, hence the key states. By

function SDL_GetKeyboardState(numkeys: PInt): PUInt8

we have easy access to this array. This is a highly efficient and fast way of gathering keyboard information without the need to queue any events.

Advantages of using the keyboard state

It is very fast and efficient compared to processing keyboard events. It can (and should) be combined freely with the classical event processing of key-events or other events. You have no hassle with case-statements to care for all the keys you like to consider but rather instant access to all keys on the keyboard. The keyboard state can easily propagated through your program as it is a simple and plain pointer. – Running speed is a crucial property for many games so you should consider using keyboard states if your game relies heavily on keyboard input.

Disadvantages of using the keyboard state

The keyboard states does not consider key modifiers like the shift-key. So you know a key has been pressed but you cannot distinguish between a letter and a capital letter for example. There are no equivalent input states for other devices like mouse, joystick, joypad, and so on. Last but not least, you still need the event processing in the program loop because otherwise the state does not get updated (see below for more information on that). This also means, if the key state of a certain key changes from unpressed to pressed and back to unpressed before the event loop has been processed, the pressed key remains unnoticed.

Keyboard state demo.
The red rectangle can be moved by the WASD keys by reading out their key states.

The following code example will draw a red rectangle which can be moved by the WASD keys. Therefore we read out their key states on every cycle of the program loop.

To get the keyboard state, we define a unsigned 8 bit pointer variable sdlKeyboardState in the var clause. It points to the array of key states.

After setting up a SDL2 window and and preparing a SDL2 rectangle, we jump right into allocating the keyboard state pointer to the sdlKeyboardState variable by the function SDL_GetKeyboardState. The argument should be nil. It is sufficient to this once in your program code as you see in the second line:

In the program event loop we need to update the event queue by procedure SDL_PumpEvents (or SDL_PollEvent or SDL_WaitEvent; both call SDL_PumpEvent implicitly). Only calling one of these procedures in the program loop updates the keyboard state.

We now can check for the of any key in the array by sdlKeyboardState[SDL_SCANCODE_…] using its
scancode as a handle (e.g. SDL_SCANCODE_ESCAPE for the escape key) and react as desired, e.g. exit the program loop or change the x/y coordinates of the rectangle. The scancode represents the position of the related key state in the array. A detailed description of scancodes in the next chapter. A list of all the scancodes shows all possible scancodes.

previous Chapter | next Chapter


A Custom Mouse Cursor

Last updated on November 20th, 2021

Any good game has a custom mouse cursor. You may think it would be a good idea to have a SDL2 surface or SDL2 texture and render it as any other sprite right at the mouse position to simulate a mouse cursor. DO NOT do this! The mouse cursor is handled separatly from the other rendering to have it smooth and working in critical situations. 

The following code shows how to set up a custom mouse cursor with SDL2 the correct way.

program SDL_MouseCursor;

uses SDL2, SDL2_image;

var
  sdlWindow1: PSDL_Window;
  sdlRenderer: PSDL_Renderer;
  sdlSurface1: PSDL_Surface;
  sdlMouseCursor: PSDL_Cursor;
  sdlEvent: TSDL_Event;
  ExitLoop: Boolean = False;

begin

  //initilization of video subsystem
  if SDL_Init(SDL_INIT_VIDEO) < 0 then Halt;

  SDL_CreateWindowAndRenderer(500, 500, SDL_WINDOW_SHOWN, @sdlWindow1, @sdlRenderer);
  if (sdlWindow1 = nil) or (sdlRenderer = nil) then Halt;

  sdlSurface1 := IMG_Load('Cursor.png' );
  if sdlSurface1 = nil then Halt;

  // create and set new mouse cursor
  sdlMouseCursor := SDL_CreateColorCursor(sdlSurface1, 8, 8);
  if sdlMouseCursor = nil then Halt;

  SDL_SetCursor(sdlMouseCursor);

  while ExitLoop = False do
  begin

    // exit loop if mouse button pressed
    while SDL_PollEvent(@sdlEvent) = 1 do
      if sdlEvent.type_ = SDL_MOUSEBUTTONDOWN then
        ExitLoop := True;

    SDL_SetRenderDrawColor(sdlRenderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(sdlRenderer);

    SDL_RenderPresent(sdlRenderer);
    SDL_Delay( 20 );
  end;

  SDL_FreeCursor(sdlMouseCursor);

  SDL_FreeSurface(sdlSurface1);
  SDL_DestroyRenderer(sdlRenderer);
  SDL_DestroyWindow (sdlWindow1);

  //shutting down video subsystem
  SDL_Quit;

end.

To have a custom mouse cursor we need a variable of type PSDL_Cursor. We call it “sdlMouseCursor” here.

The result looks like this:

Custom Mouse Cursor in SDL2
Creative Commons License This image by https://www.freepascal-meets-sdl.net is licensed under a Creative Commons Attribution 4.0 International License.
The blue cross with the yellow outline is the mouse cursor on the grey canvas/window. 
  sdlSurface1 := IMG_Load('Cursor.png' );
  if sdlSurface1 = nil then Halt;

  // create and set new mouse cursor
  sdlMouseCursor := SDL_CreateColorCursor(sdlSurface1, 8, 8);
  if sdlMouseCursor = nil then Halt;

  SDL_SetCursor(sdlMouseCursor);  

This is the interesting part of the code with regard to creating a custom mouse cursor. The cursor’s image is defined by a SDL surface. We create the SDL surface as known from a previous chapter from a png image file to “sdlSurface1” here.

The custom mouse cursor is created by the following function, which returns nil on error.

SDL_CreateColorCursor(surface: PSDL_Surface; hot_x: SInt32; hot_y: SInt32): PSDL_Cursor

It needs the surface to use as cursor image and two coordinates (hot_x/hot_y) as arguments. They determine where the actual hitting point for this cursor is. Since the example cursor image is of dimensions 16×16 px and represents a cross, the “hot” (hitting) coordiates are (8/8), hence the cross’ center is used for hitting a button or something. In contrast you may imagine a typical arrow shaped mouse cursor, where the hitting point has to be adjusted to be right on the tip of the arrow in the arrow’s image.

If the cursor creation has been successful, it is necessary to set it to be the actual cursor. You may have created many different cursors, so tell SDL which one to use by the following procedure.

SDL_SetCursor(cursor: PSDL_Cursor)

The remaining part of the code is just rendering a 500 by 500 pixels window with a grey (128, 128, 128) background that is updated as long as no mouse button has been pressed.

Finally do not forget to free the mouse cursor by SDL_FreeCursor(mouse cursor) as shown.

← previous Chapter | next Chapter →

Event handling Pt. 2, Mouse handling

Last updated on February 17th, 2024

This is part 2 of the chapter about event handling. Mouse and window handling is treated here.

Let’s start with the full example code:

program Chapter8_SDL2;

uses SDL2;

var
sdlWindow1: PSDL_Window;
sdlEvent: PSDL_Event;
exitloop: boolean = false;
text1: string = '';

begin

  //initilization of video subsystem
  if SDL_Init( SDL_INIT_VIDEO ) < 0 then HALT;

  sdlWindow1 := SDL_CreateWindow( 'Window1', 50, 50, 500, 500, SDL_WINDOW_SHOWN );
  if sdlWindow1 = nil then HALT;

  new( sdlEvent );

  while exitloop = false do
  begin
    while SDL_PollEvent( sdlEvent ) = 1 do
    begin
      write( 'Event detected: ' );
      case sdlEvent^.type_ of

        //keyboard events
        SDL_KEYDOWN: begin
                       writeln( 'Key pressed:');
                       writeln( '  Key code: ', sdlEvent^.key.keysym.sym );
                       writeln( '  Key name: "', SDL_GetKeyName( sdlEvent^.key.keysym.sym ), '"' );
                       writeln( '  Scancode: ', sdlEvent^.key.keysym.scancode );
                       writeln( '  Scancode name: "', SDL_GetScancodeName( sdlEvent^.key.keysym.scancode ), '"' );
                       writeln( '  Key modifiers: ', sdlEvent^.key.keysym._mod );
                       case sdlEvent^.key.keysym.sym of
                         27: exitloop := true;  // exit on pressing ESC key

                         //switching text input mode on/off
                         SDLK_F1: begin
                                    if SDL_IsTextInputActive = SDL_True then SDL_StopTextInput
                                    else SDL_StartTextInput;
                                    writeln(' Text Input Mode switched' );
                                  end;
                       end;
                     end;
        SDL_KEYUP: writeln( 'Key released ' );
        SDL_TEXTINPUT: begin
                         writeln( 'Text input: "', sdlEvent^.text.text, '"' );
                         text1 := text1 + sdlEvent^.text.text;
                         writeln( 'Full string: ' + text1 );
                       end;

        //mouse events
        SDL_MOUSEMOTION: begin
                           writeln( 'X: ', sdlEvent^.motion.x, '   Y: ', sdlEvent^.motion.y,
                                    '   dX: ', sdlEvent^.motion.xrel, '   dY: ', sdlEvent^.motion.yrel );
                         end;
        SDL_MOUSEBUTTONDOWN: writeln( 'Mouse button pressed: Button index: ', sdlEvent^.button.button );
        SDL_MOUSEBUTTONUP: writeln( 'Mouse button released' );
        SDL_MOUSEWHEEL: begin
                          write( 'Mouse wheel scrolled: ' );
                          if sdlEvent^.wheel.y > 0 then writeln( 'Scroll forward, Y: ', sdlEvent^.wheel.y )
                          else writeln( 'Scroll backward, Y: ', sdlEvent^.wheel.y );
                        end;

        //window events
        SDL_WINDOWEVENT: begin
                           write( 'Window event: ' );
                           case sdlEvent^.window.event of
                             SDL_WINDOWEVENT_SHOWN: writeln( 'Window shown' );
                             SDL_WINDOWEVENT_MOVED: writeln( 'Window moved' );
                             SDL_WINDOWEVENT_MINIMIZED: writeln( 'Window minimized' );
                             SDL_WINDOWEVENT_MAXIMIZED: writeln( 'Window maximized' );
                             SDL_WINDOWEVENT_ENTER: writeln( 'Window got mouse focus' );
                             SDL_WINDOWEVENT_LEAVE: writeln( 'Window lost mouse focus' );
                           end;
                         end;
      end;
    end;
    SDL_Delay( 20 );
  end;

  dispose( sdlEvent );
  SDL_DestroyWindow ( sdlWindow1 );

  //shutting down video subsystem
  SDL_Quit;

end.

Mouse handling in SDL 2.0

If you got the basic concept of event handling, you will find that mouse handling and keyboard handling have a lot in common.

        //mouse events
        SDL_MOUSEMOTION: begin
                           writeln( 'X: ', sdlEvent^.motion.x, '   Y: ', sdlEvent^.motion.y,
                                    '   dX: ', sdlEvent^.motion.xrel, '   dY: ', sdlEvent^.motion.yrel );
                         end;

For mouse motions, mouse buttons and the mouse wheel there are three different mouse event structures: SDL_MouseMotionEvent, SDL_MouseButtonEvent and SDL_MouseWheelEvent.

Mouse motion handling in SDL 2.0

If you are moving the mouse, the SDL_MOUSEMOTION event is triggered. The record structure of the SDL_MouseMotionEvent is shown below:

TSDL_MouseMotionEvent = record
    type_: UInt32;       // SDL_MOUSEMOTION
    timestamp: UInt32;
    windowID: UInt32;    // The window with mouse focus, if any
    which: UInt32;       // The mouse instance id, or SDL_TOUCH_MOUSEID
    state: UInt8;        // The current button state 
    padding1: UInt8;
    padding2: UInt8;
    padding3: UInt8;
    x: SInt32;           // X coordinate, relative to window
    y: SInt32;           // Y coordinate, relative to window
    xrel: SInt32;        // The relative motion in the X direction 
    yrel: SInt32;        // The relative motion in the Y direction
  end;

Again there are the type_, the timestamp and the windowID fields. Nothing new here. The field which contains the mouse id. This is important if you have more than one mouse device attached to your computer. Think for example of a laptop with a touchpad area to move the mouse cursor and at the same time there is an usb mouse attached to the laptop. To distinguish between the two, you may retrieve their id’s.

The state field is known from the SDL_KeyBoardEvent structure. It may be a difference if you have a mouse button pressed and move the mouse or if you don’t have a button pressed. The most famous example is if you want to select a bunch of files on your desktop or in a folder. By the way, the state field encodes a number which is different depending on which buttons you pressed actually. This works similar to the key modifiers, if you keep two mousebuttons pressed while moving, the state is the sum of each individual mouse button state value. As an example for my mouse: No mouse button 0, left mouse button 1, right mouse button 4, middle mouse button 2, thumb button 8. If I keep pressed left and right mouse button 5 (sum 1 + 4).

The x and y fields contain the coordinate of the mouse cursor in pixels. These coordinates are relative to the window of the SDL 2.0 application. Keep in mind, the coordinates (0/0) correspond to the left upper corner. Positive x values are counted from left to right and positive y values are counted from top to bottom.

The fields xrel and yrel are used to determine how fast the mouse has been moved from one point to another. Let’s assume you move the mouse surcor from left to right in your application’s window. The first time you do it slowly, xrel might be 1, means, you just moved pixel from left to right between two mouse motion events. If you move fast, xrel might be 50, meaning that this time you moved by 50 pixels between two mouse motion events. Especially for game programming this can be a extremely important information. E.g., think of first person shooter, if the movement speed of the first person view would be independent of the actual movement of the mouse, this game wouldn’t make much sense.

To access these fields the event’s motion field has to be read out. In the example code the (x/y) coordinates and the relative positions xrel and yrel are read out by

sdlEvent^.motion.x

sdlEvent^.motion.y

sdlEvent^.motion.xrel

sdlEvent^.motion.yrel

and simply printed out to the screen. Let’s go for the next chunk of code.

        SDL_MOUSEBUTTONDOWN: writeln( 'Mouse button pressed: Button index: ', sdlEvent^.button.button );
        SDL_MOUSEBUTTONUP: writeln( 'Mouse button released' );
        SDL_MOUSEWHEEL: begin
                          write( 'Mouse wheel scrolled: ' );
                          if sdlEvent^.wheel.y > 0 then writeln( 'Scroll forward, Y: ', sdlEvent^.wheel.y )
                          else writeln( 'Scroll backward, Y: ', sdlEvent^.wheel.y );
                        end;

Pressing a mouse button in SDL 2.0

As for the SDL_KeyBoardEvent, you would want to know if a mouse button and which one is pressed or released. If a mouse button is pressed, a SDL_MOUSEBUTTONDOWN event is triggered. On releasing a mouse button a SDL_MOUSEBUTTONUP event is triggered. It has SDL_MouseButtonEvent structure. Let’s have a look into the structure:

TSDL_MouseButtonEvent = record
    type_: UInt32;       // SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP 
    timestamp: UInt32;
    windowID: UInt32;    // The window with mouse focus, if any
    which: UInt32;       // The mouse instance id, or SDL_TOUCH_MOUSEID 
    button: UInt8;       // The mouse button index
    state: UInt8;        // SDL_PRESSED or SDL_RELEASED
    padding1: UInt8;
    padding2: UInt8;
    x: SInt32;           // X coordinate, relative to window
    y: SInt32;           // Y coordinate, relative to window 
  end;

If you compare this structure to the structure of the SDL_MouseMotionEvent, you will find, only the two field xrel and yrel are gone and a new field, a crucial one to be clear, is new, which is button of 8 bit unsigned integer type.

I won’t discuss all the fields again we discussed for the SDL_MouseMotionEvent structure. Attention, if you compare the state field of the SDL_MouseButtonEvent, it works another way. It allows just for two values, SDL_PRESSED or SDL_RELEASED, as known from SDL_KeyBoardEvent. As a reminder: For the SDL_MouseMotionEvent, it represented that full state of all the buttons being pressed while mouse motion.

The button field allows to recognize which button has triggered the SDL_MouseButtonEvent. Each button of the mouse has its own index. As an example for my mouse they are as follows: Left mouse button 1, right mouse button 3, middle mouse button 2, thumb mouse button 4. Do not confuse these index numbers with the mouse button state values of the SDL_MouseMotionEvent structure. There can only be one button which triggered this event! Combinations as for the motion event are not possible. In the example code this value is just printed to the screen using

sdlEvent^.button.button.

You see, to access the fields of this record you need to address the event’s button field. This mustn’t be confused with SDL_MouseButtonEvent’s button field discussed above.

The x and y fields contain the (x/y) coordinates when the mouse button (whose index is stored in field button) has been pressed (or released). This is crucial to know. What would an application be worth if you could recognized that a certain button has been pressed but you don’t know where exactly? Not shown in the code but you could access these fields by

sdlEvent^.button.x

sdlEvent^.button.y.

The mouse wheel in SDL 2.0

If the mouse wheel is used a SDL_MOUSEWHEEL event is triggered. Let’s look into the corresponding SDL_MouseWheelEvent structure.

TSDL_MouseWheelEvent = record
    type_: UInt32;        // SDL_MOUSEWHEEL
    timestamp: UInt32;
    windowID: UInt32;    // The window with mouse focus, if any
    which: UInt32;       // The mouse instance id, or SDL_TOUCH_MOUSEID
    x: SInt32;           // The amount scrolled horizontally 
    y: SInt32;           // The amount scrolled vertically
  end;

New fields here are the x and y field which do not correspond to the mouse cursor position this time. Instead of that they refer to the direction of the mouse wheel being scrolled. If you scroll the mouse wheel upwards or forward it will return 1, and if you scroll it backwards it will return -1. If you have a mouse wheel which can be scrolled horizontally, it will be similar. By the way scrolling a mouse wheel can be considered as pressing a button very quickly for that direction.

In the code, if a SDL_MOUSEWHEEL event is triggered, the y value is checked to be positive or negative. This decides if an upward or downward scrolling has happened and the corresponding value (1 or -1) will be returned and printed out. Anyway, could you guess what happens if anyone would use a mouse wheel that is scrolling horizontally? – The same block will be executed since a SDL_MOUSEWHEEL event is triggered. Instead of y being 1 or -1, it will be 0 but the x value will be 1 or -1. Nevertheless, the else-block will be executed since y is not greater than 0. So for the example program it will print out wrongly that an backward scroll has happened. Anyway, you get the idea.

Window handling in SDL 2.0

Modern applications are always run in windows. The famous operation system “Windows” by Microsoft even derived it’s name from this. The first task for most of SDL 2.0 applications is the creation of a window. The example code creates a window of width 500 pixels and height 500 pixels. It may be important to know if the user interacts with the application window. Whenever this happens a SDL_WINDOWEVENT is triggered.

        //window events
        SDL_WINDOWEVENT: begin
                           write( 'Window event: ' );
                           case sdlEvent^.window.event of
                             SDL_WINDOWEVENT_SHOWN: writeln( 'Window shown' );
                             SDL_WINDOWEVENT_MOVED: writeln( 'Window moved' );
                             SDL_WINDOWEVENT_MINIMIZED: writeln( 'Window minimized' );
                             SDL_WINDOWEVENT_MAXIMIZED: writeln( 'Window maximized' );
                             SDL_WINDOWEVENT_ENTER: writeln( 'Window got mouse focus' );
                             SDL_WINDOWEVENT_LEAVE: writeln( 'Window lost mouse focus' );
                           end;

If the an event of type SDL_WINDOWEVENT is triggered, the text message “Window event: ” is printed out.

To access the window event fields, you need to access the event’s window field. The field event contains the window event’s type information, hence what window event has been triggered.

sdlEvent^.window.event

In contrast to the keyboard and the mouse event we discussed before, the different event types are not distinguished by the type_ field but by an additional field event.

In the example code six different window event types are checked: SDL_WINDOWEVENT_SHOWN, SDL_WINDOWEVENT_MOVED, SDL_WINDOWEVENT_MINIMIZED, SDL_WINDOWEVENT_MAXIMIZED, SDL_WINDOWEVENT_ENTER and SDL_WINDOWEVENT_LEAVE. From the texts printed out you can guess when they get triggered. I think no further explanation is needed here.

By the way, there are more window event types which are shown a little bit later. Sometimes if one of these is triggered, only the text that a window event has been triggered is shown but without any further details since the example code doesn’t covers further treatment. Feel free to extent the code yourself.

Let’s have a look at the event structure of SDL_WindowEvent.

TSDL_WindowEvent = record
    type_: UInt32;       // SDL_WINDOWEVENT
    timestamp: UInt32;
    windowID: UInt32;    // The associated window
    event: UInt8;        // SDL_WindowEventID
    padding1: UInt8;
    padding2: UInt8;
    padding3: UInt8;
    data1: SInt32;       // event dependent data
    data2: SInt32;       // event dependent data 
  end;

The fields type_, timestamp and windowID are known and have the same meaning as discussed before.

The field event stores an identifier (SDL_WindowEventID) to distinguish between different window events. Here they are listed and in brackets you find the window related action which has triggered them:

  1. SDL_WINDOWEVENT_SHOWN (window has been shown)
  2. SDL_WINDOWEVENT_HIDDEN (window has been hidden)
  3. SDL_WINDOWEVENT_EXPOSED (window has been exposed and should be redrawn)
  4. SDL_WINDOWEVENT_MOVED (window has been moved to data1, data2)
  5. SDL_WINDOWEVENT_RESIZED (window has been resized to data1xdata2; this is event is always preceded by SDL_WINDOWEVENT_SIZE_CHANGED)
  6. SDL_WINDOWEVENT_SIZE_CHANGED (window size has changed, either as a result of an API call or through the system or user changing the window size; this event is followed by SDL_WINDOWEVENT_RESIZED if the size was changed by an external event, i.e. the user or the window manager)
  7. SDL_WINDOWEVENT_MINIMIZED (window has been minimized)
  8. SDL_WINDOWEVENT_MAXIMIZED (window has been maximized)
  9. SDL_WINDOWEVENT_RESTORED (window has been restored to normal size and position)
  10. SDL_WINDOWEVENT_ENTER (window has gained mouse focus)
  11. SDL_WINDOWEVENT_LEAVE (window has lost mouse focus)
  12. SDL_WINDOWEVENT_FOCUS_GAINED (window has gained keyboard focus)
  13. SDL_WINDOWEVENT_FOCUS_LOST (window has lost keyboard focus)
  14. SDL_WINDOWEVENT_CLOSE (the window manager requests that the window be closed)

This list is based upon information found at the SDL 2.0 wiki.

If you read through the list carefully you will notice the mention of data1 and data2 which rather explains their occurance in the event structure :-)! They need to be read out for SDL_WINDOWEVENT_MOVED and SDL_WINDOWEVENT_RESIZED to get the new window position or dimensions.

At the moment I’m not sure why for window events the distinction between the individual window events (e.g. SDL_WINDOWEVENT_MOVED, SDL_WINDOWEVENT_RESIZED, and so on) is not done by the type_ field as for keyboard, mouse and other events, but rather by the additional event field.

                         end;
      end;
    end;
    SDL_Delay( 20 );
  end;

  dispose( sdlEvent );
  SDL_DestroyWindow ( sdlWindow1 );

  //shutting down video subsystem
  SDL_Quit;

end.

Not much to learn in the final part. The loop is delayed by 20 milliseconds for better recognizability of the text output.

If the loop is left, the event pointer gets free’d, the SDL 2.0 window gets destroyed and SDL 2.0 quit. That’s it :-)!

Touchscreen events, Joystick events and many more!

This chapter covered keyboard, mouse and window events in some detail. Keep in mind, SDL 2.0 has much more to show! – There are many more events you can use for application development. They basically cover any modern type of interaction you could wish for. This includes touchscreen events (important for smartphone development), joystick events (game console development), and even a dropfile event (drag and drop files) and more.

previous Chapter | next Chapter

Event handling Pt. 1, Keyboard handling

Last updated on October 27th, 2025

What’s an event and event handling in programming?

Event handling is a major concept in game and application programming. It allows for the user to interact with your program. Whenever you move your mouse cursor, press or release a key on the keyboard or use a touch screen, all these interactions are recognized as so-called events.

Events in SDL 2.0, SDL_Event

In SDL 2.0 whenever an event occurs, for instance a key gets pressed on the keyboard, all the information related to that specific event is stored in a SDL_Event record. Depending on the event type (e.g. mouse motion, key pressed on a keyboard, maximizing the application window) there are very different fields which can be accessed. For a mouse motion you can read out the x and y position of the cursor whereas 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.

Event types available in SDL 2.0

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_MOUSEMOTION. The full list according to the official SDL 2.0 documentation is:

Event typesEvent structureSDL_Event field
Event type Event structure SDL_Event field
SDL_AUDIODEVICEADDED
SDL_AUDIODEVICEREMOVED
SDL_AudioDeviceEvent adevice
SDL_CONTROLLERAXISMOTIONSDL_ControllerAxisEventcaxis
SDL_CONTROLLERBUTTONDOWN
SDL_CONTROLLERBUTTONUP
SDL_ControllerButtonEventcbutton
SDL_CONTROLLERDEVICEADDED
SDL_CONTROLLERDEVICEREMOVED
SDL_CONTROLLERDEVICEREMAPPED
SDL_ControllerDeviceEventcdevice
SDL_DOLLARGESTURE
SDL_DOLLARRECORD
SDL_DollarGestureEventdgesture
SDL_DROPFILE SDL_DropEvent drop
SDL_FINGERMOTION
SDL_FINGERDOWN
SDL_FINGERUP
SDL_TouchFingerEvent tfinger
SDL_KEYDOWN
SDL_KEYUP
SDL_KeyboardEvent key
SDL_JOYAXISMOTIONSDL_JoyAxisEventjaxis
SDL_JOYBALLMOTIONSDL_JoyBallEventjball
SDL_JOYHATMOTIONSDL_JoyHatEventjhat
SDL_JOYBUTTONDOWN
SDL_JOYBUTTONUP
SDL_JoyButtonEventjbutton
SDL_JOYDEVICEADDED
SDL_JOYDEVICEREMOVED
SDL_JoyDeviceEventjdevice
SDL_MOUSEMOTIONSDL_MouseMotionEventmotion
SDL_MOUSEBUTTONDOWN
SDL_MOUSEBUTTONUP
SDL_MouseButtonEventbutton
SDL_MOUSEWHEELSDL_MouseWheelEventwheel
SDL_MULTIGESTURESDL_MultiGestureEventmgesture
SDL_QUITSDL_QuitEventquit
SDL_SYSWMEVENTSDL_SysWMEventsyswm
SDL_TEXTEDITINGSDL_TextEditingEventedit
SDL_TEXTINPUTSDL_TextInputEventtext
SDL_USEREVENTSDL_UserEventuser
SDL_WINDOWEVENTSDL_WindowEventwindow
Other eventsSDL_CommonEventcommon

Source: SDL 2.0 Documentation: SDL_Event

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. The most important events are covered by this tutorial in detail anyway. You will be able to work with the remaining events once you got the concept.

In contrast to SDL 1.2 there are some event types gone and many new types available in SDL 2.0 which are useful to use new forms of interaction between the user and the application (e.g. touch screen technology).

What is the difference between event type, event structure and the event field?

In the table above you’ll notice the first column covers the event type. It determines which type of event occured, e.g. a key is pressed down on the keyboard (SDL_KEYDOWN).

The event structure in the second column is the record structure which is dependend upon the event type. As discussed before, a pressed key will need a record structure which stores the key identifier rather than x,y-coordinated (which would in turn be necessary for a mouse motion). Each event type has a certain apropriate (event) record structure to hold the event information.

Many event types can share the same event structure. The event types SDL_KEYDOWN and SDL_KEYUP which are generated by pressing or releasing a key share the same event structure SDL_KeyboardEvent since the information are the same, e.g. the key identifier.

The third column shows the SDL_Event field name to access the event specific fields. In case of an event type SDL_KEYDOWN the event structure is SDL_KeyboardEvent. The specific information, e.g. the key identifier, is accessible via the field key in the SDL_Event record.

This may sound confusing. Later on the relation is discussed in more detail. Let’s right proceed into the code.

program Chapter8_SDL2;

uses SDL2;

var
sdlWindow1: PSDL_Window;
sdlEvent: PSDL_Event;
exitloop: boolean = false;
text1: string = '';

begin

  //initilization of video subsystem
  if SDL_Init( SDL_INIT_VIDEO ) < 0 then HALT;

  sdlWindow1 := SDL_CreateWindow( 'Window1', 50, 50, 500, 500, SDL_WINDOW_SHOWN );
  if sdlWindow1 = nil then HALT;

  new( sdlEvent );

  while exitloop = false do
  begin
    while SDL_PollEvent( sdlEvent ) = 1 do
    begin
      write( 'Event detected: ' );
      case sdlEvent^.type_ of

        //keyboard events
        SDL_KEYDOWN: begin
                       writeln( 'Key pressed:');
                       writeln( '  Key code: ', sdlEvent^.key.keysym.sym );
                       writeln( '  Key name: "', SDL_GetKeyName( sdlEvent^.key.keysym.sym ), '"' );
                       writeln( '  Scancode: ', sdlEvent^.key.keysym.scancode );
                       writeln( '  Scancode name: "', SDL_GetScancodeName( sdlEvent^.key.keysym.scancode ), '"' );
                       writeln( '  Key modifiers: ', sdlEvent^.key.keysym._mod );
                       case sdlEvent^.key.keysym.sym of
                         SDLK_ESCAPE: exitloop := true;  // exit on pressing ESC key

                         //switching text input mode on/off
                         SDLK_F1: begin
                                    if SDL_IsTextInputActive = SDL_True then SDL_StopTextInput
                                    else SDL_StartTextInput;
                                    writeln(' Text Input Mode switched' );
                                  end;
                       end;
                     end;
        SDL_KEYUP: writeln( 'Key released ' );
        SDL_TEXTINPUT: begin
                         writeln( 'Text input: "', sdlEvent^.text.text, '"' );
                         text1 := text1 + sdlEvent^.text.text;
                         writeln( 'Full string: ' + text1 );
                       end;

        //mouse events
        SDL_MOUSEMOTION: begin
                           writeln( 'X: ', sdlEvent^.motion.x, '   Y: ', sdlEvent^.motion.y,
                                    '   dX: ', sdlEvent^.motion.xrel, '   dY: ', sdlEvent^.motion.yrel );
                         end;
        SDL_MOUSEBUTTONDOWN: writeln( 'Mouse button pressed: Button index: ', sdlEvent^.button.button );
        SDL_MOUSEBUTTONUP: writeln( 'Mouse button released' );
        SDL_MOUSEWHEEL: begin
                          write( 'Mouse wheel scrolled: ' );
                          if sdlEvent^.wheel.y > 0 then writeln( 'Scroll forward, Y: ', sdlEvent^.wheel.y )
                          else writeln( 'Scroll backward, Y: ', sdlEvent^.wheel.y );
                        end;

        //window events
        SDL_WINDOWEVENT: begin
                           write( 'Window event: ' );
                           case sdlEvent^.window.event of
                             SDL_WINDOWEVENT_SHOWN: writeln( 'Window shown' );
                             SDL_WINDOWEVENT_MOVED: writeln( 'Window moved' );
                             SDL_WINDOWEVENT_MINIMIZED: writeln( 'Window minimized' );
                             SDL_WINDOWEVENT_MAXIMIZED: writeln( 'Window maximized' );
                             SDL_WINDOWEVENT_ENTER: writeln( 'Window got mouse focus' );
                             SDL_WINDOWEVENT_LEAVE: writeln( 'Window lost mouse focus' );
                           end;
                         end;
      end;
    end;
    SDL_Delay( 20 );
  end;
  
  dispose( sdlEvent );
  SDL_DestroyWindow ( sdlWindow1 );

  //shutting down video subsystem
  SDL_Quit;

end.

The result will not be seen in the actually SDL 2.0 window but in the command line window (which usually is showing up along with the SDL 2.0 window on Windows environments).

Chapter 8 - result

The initial lines of code are:

program Chapter8_SDL2;

uses SDL2;

var
sdlWindow1: PSDL_Window;
sdlEvent: PSDL_Event;
exitloop: boolean = false;
text1: string = '';

begin

  //initilization of video subsystem
  if SDL_Init( SDL_INIT_VIDEO ) < 0 then HALT;

  sdlWindow1 := SDL_CreateWindow( 'Window1', 50, 50, 500, 500, SDL_WINDOW_SHOWN );
  if sdlWindow1 = nil then HALT;

  new( sdlEvent );

The program is called “Chapter8_SDL2”. Since event handling is a basic feature of SDL 2.0, no further units except for SDL2 itself necessary.

We need three variables. “sdlWindow1” is necessary to create a window as known from previous chapters. This time we won’t draw anything to it but use it to recognize events (e.g. mouse clicks into the window).

The SDL_Event variable “sdlEvent” stores the events generated by the user of the application. It is of pointer type PSDL_Event. Then there is a simple Pascal boolean variable “exitloop” which is set to false since we don’t want to leave the program loop initially. Also we have a “text1” string variable we will need to demonstrate text input later.

Nothing new for the following lines, SDL 2.0 is initilised and the SDL 2.0 window is created.

Since “sdlEvent” is a pointer type variable we need to allocate some memory. This is done by the new command, as known.

  while exitloop = false do
  begin
    while SDL_PollEvent( sdlEvent ) = 1 do
    begin
      write( 'Event detected: ' );

First a while-do loop is started which will run as long as the variable “exitloop” is false. If it is changed to true the loop will be exited. (This is triggered by pressing ESC, later this is discussed in detail.)

Within the outer loop the first thing is to poll for an event, which is done by function SDL_PollEvent.

SDL_PollEvent(event: PSDL_Event): SInt32

This function returns integer value 1 if one or more events are in the queue. The event data is allocated to the SDL_Event variable and deleted from the queue. If there are no events waiting, it returns 0 and the event variable is filled with nil instead of specific event data.

If there is an event waiting, its information are fed to “sdlEvent” and 1 is returned. The inner while loop is running until all events in the queue are treated. This will print out a text saying “Event detected” and, more important, check for the event type!

Don’t use an if-clause (instead of the inner while loop) to check for the events because that means we only check once a event each every cycle of the outer loop. By combining two while loops and check for all the events in the inner loop we can treat every event occured for this program loop cycle.

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

        //keyboard events
        SDL_KEYDOWN: begin
                       writeln( 'Key pressed:');
                       writeln( '  Key code: ', sdlEvent^.key.keysym.sym );
                       writeln( '  Key name: "', SDL_GetKeyName( sdlEvent^.key.keysym.sym ), '"' );
                       writeln( '  Scancode: ', sdlEvent^.key.keysym.scancode );
                       writeln( '  Scancode name: "', SDL_GetScancodeName( sdlEvent^.key.keysym.scancode ), '"' );
                       writeln( '  Key modifiers: ', sdlEvent^.key.keysym._mod );

If _type is a SDL_KEYDOWN event a begin-end block is entered. There are several writeln output lines. Let’s discuss them one after another.

In the first line the SDL (virtual) key code is returned, which is represented by an integer value. For special keys (e.g. F-keys, Insert, …) these values often range beyond the scope of SmallInt (-32768 to 32767) or Word (0 to 65535) variables, which you should keep in mind if you intend to return these values to variables. The SDL virtual key code can be accessed by

sdlEvent^.key.keysym.sym.

Let’s try to break this down a little bit. The event is stored in sdlEvent which is of pointer type so to access the content we need sdlEvent^. In the keyboard event there is a field keysym which itself is a record. The SDL virtual key code is stored in the field sym of the keysym record. Don’t worry if this sounds kind of confusing, in the next part we treat these two records (SDL_KeyboardEvent record and keysym record) in detail.

Most of the key codes have a name, e.g. “Escape” for the escape key. In the next line of the code, the function SDL_GetKeyName is used to get the name of the key whose key code we found:

function SDL_GetKeyName(key: TSDL_ScanCode): PAnsiChar

The other way round it is also possible by this function:

function SDL_GetKeyFromName(const name: PAnsiChar): TSDL_KeyCode

This is not shown in the code though.

The scancode of a key is another representation. The details about the difference between key codes and scancodes are discussed a little bit later. Anyway, the scancode is stored in the scancode field of the keysym record. So it can be accessed by:

sdlEvent^.key.keysym.scancode

and its name can be read out by

function SDL_GetScancodeName(scancode: TSDL_ScanCode): PAnsiChar.

Also here you can do it the other way round by

function SDL_GetScancodeFromName(const name: PAnsiChar): TSDL_ScanCode.

For completion and your information, it is possible to get the key code from the scancode and vice versa. The functions to use are:

function SDL_GetKeyFromScancode(scancode: TSDL_ScanCode): TSDL_KeyCode

and

function SDL_GetScancodeFromKey(key: TSDL_KeyCode): TSDL_ScanCode.

You see, there is a strong relation between the two but they are not the same. Lets have a short example of what happens if you press a key:

Keycodes and Scancodes
Keycodes and Scancodes

The tutorial code returns these key- and scancodes for the “Q”-key, the escape key and the return key. As you can see the codes do not only differ between different keys but also for the same key, e.g. the key code for the escape key is 27 but the scanscode is 41. Anyway, the key names seems to be consistent. Later we will see an example where even the names differ.

The last line returns the code value of key modifiers. Key modifiers are keys which literally modify them. E.g., if you press a letter key while holding the shift key, usually you modify the letter to be the capital letter. Typical key modifiers are shift, ctrl and alt. The field which stores the key modifier value is called _mod. Later we will discuss this field more in detail.

                       case sdlEvent^.key.keysym.sym of
                         SDLK_ESCAPE: exitloop := true;  // exit on pressing ESC key

                         //switching text input mode on/off
                         SDLK_F1: begin
                                    if SDL_IsTextInputActive = SDL_True then SDL_StopTextInput
                                    else SDL_StartTextInput;
                                    writeln(' Text Input Mode switched' );
                                  end;
                       end;
                     end;

We check for the value of the key code by a case-statement with sdlEvent^.key.keysym.sym. If we know the key code of a certain key, we may check for this key and react accordingly. First, we check if the event’s key code is SDLK_ESCAPE since this is the key code of the escape key (ESC). If so, the variable exitloop is set to true to stop the outer while loop and exiting the program.

Every key code is represented by the key code constant (e.g. SDLK_ESCAPE), a decimal value (27 here) and a hexadecimal value ($001B here).  You may check the key codes in the following (official) SDL 2.0 key code lookup table:

https://wiki.libsdl.org/SDLKeycodeLookup or SDL 2.0 Key code lookup table (Backup hosted here)

In the fifth row the escape key key code is found.

In this table you find additionally to the decimal value, the hexadecimal value. You may try to use $001B instead of 27. This will do the trick either :-)! Also there is a character representation shown if possible.

If the the F1-key is pressed we would like to turn on or off the text input mode. This time we do not use a decimal key code to recognize the key but rather a constant representation (SDLK_F1). You could guess you have the choice and could use either the decimal value, the constant representation (what about SDLK_ESCAPE in the case before?) or the hexadecimal representation. – SDLK_ESCAPE even exists(!) but guess what, that doesn’t work. For some special keys this works like shown for the F1-key, for most of the other keys it doesn’t work (you will find that most keys are stored as string constants so the compiler will complain that they are of wrong type). So for most keys you have to look up the decimal representation and do as shown.

In the same way we check if the F1 key got pressed by checking for SDLK_F1. If F1 got pressed it is checked if the so called Text input mode is active by

function SDL_IsTextInputActive: TSDL_Bool.

If it is active, it gets deactivated by

procedure SDL_StopTextInput

and if it is not active, it gets activated by

procedure SDL_StartTextInput.

Finally a short text is returned which states that the Text input mode has been changed. More about the input mode later.

Last but not least, if _type is a SDL_KEYUP event, we know a key has been released, hence it physically moves up on the keyboard. We just print out “Key released”, we don’t care what exactly key is released here.

The keyboard events SDL_KEYDOWN and SDL_KEYUP

Let’s have a look at the structure of the keyboard event record and discuss the fields from top to bottom:

TSDL_KeyboardEvent = record
    type_: UInt32;        // SDL_KEYDOWN or SDL_KEYUP
    timestamp: UInt32;
    windowID: UInt32;     // The window with keyboard focus, if any
    state: UInt8;         // SDL_PRESSED or SDL_RELEASED 
    _repeat: UInt8;       // Non-zero if this is a key repeat
    padding2: UInt8;
    padding3: UInt8;
    keysym: TSDL_KeySym;  // The key that was pressed or released
  end;

type_ is an unsigned 32 bit integer (hence UInt32) value which determines what exact type of event you have. As usual the integer values are represented by constants, here SDL_KEYDOWN (if pressed down) and SDL_KEYUP (if key has been released). They share the same overall event record structure (SDL_KeyboardEvent). As a sidenote, all the SDL 2.0 events have a type_ field for obvious reason.

The next field timestamp obviously contains a timestamp of integer type which is used internally by SDL 2.0 to resolute the sequence in which all the events were triggered. All SDL 2.0 events have the timestamp field.

The windowID field is necessary to distinguish between events raised from different SDL 2.0 windows if there is more than one. Let’s assume your program has two SDL 2.0 windows, window1 and window2. These two windows have certain constant id’s allocated by SDL 2.0. Now, if you have the focus on window1 (meaning the active window is window1) and press a key, the event’s windowID contains the specific id for window1. If the active SDL 2.0 window is window2 and you press a key, the specific id of window2 will be present in windowID. This way you can easily distinguish for which window the program should react to the keyboard event, e.g. a pressed key. Anyway, the capability to handle more than one window has been introduced by SDL 2.0, so you can imagine that for many programs and if you do not have more than one window in your program, you may ignore this field. The windowID field isn’t present (and necessary) for all the events SDL 2.0 provides.

The 8 bit integer state field may be read out to get the state of the key (pressed or release) which are encoded by SDL_PRESSED and SDL_RELEASED. You may be a little bit confused what the difference between the pair SDL_PRESSED and SDL_RELEASED and the pair SDL_KEYDOWN and SDL_KEYUP is. Essentially they have the same meaning for a key of a keyboard. Formally, the difference is that SDL_KEYDOWN and SDL_KEYUP are two different event types whereas SDL_PRESSED and SDL_RELEASED are two different key states. In the case of a pressed key on a keyboard the state is kind of redundant because if you get a SDL_KEYDOWN event you already know that a key was pressed and to read out the state (which will be SDL_PRESSED) is unnecessary. Anyway, the state field seems to be introduced for completeness, since for other event types (e.g. mouse events) there is a huge difference if you move the mouse and have mouse buttons pressed or released.

_repeat let’s you know if the corresponding key 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”. 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 a value different from 0 (most likely 1) and otherwise it will be 0. 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. In SDL 1.2 I described here how to do it simply by using function called SDL_EnableKeyRepeat, this function is obsolete and does not exist in SDL 2.0 anymore!

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 you have a spaceship which should move left on pressing the “a”-key. Instead of changing it’s coordinates only once when the key down event is triggered, you start a switch (e.g. MoveSpaceshipLeft := true). The trigger is treated independently of the events treatment somewhere in the main game loop. As soon as the key up event is triggered for the “a”-key, the switch is turned off (e.g. MoveSpaceshipLeft := false).

I don’t know about meaning of the fields padding2 and padding3. Maybe they are kind of place holders for future developments or used internally.

Last but not least a very important field. The field keysym of SDL_KeySym type contains several information about the identity of the pressed key. In most cases we need to know which exact key has been pressed. Let’s have look into the SDL_KeySym record:

TSDL_Keysym = record
    scancode: TSDL_ScanCode;      // SDL physical key code 
    sym: TSDL_KeyCode;            // SDL virtual key code
    _mod: UInt16;                 // current key modifiers
    unicode: UInt32;              // (deprecated) 
  end;

As you can see the first two fields contain keycode representations for identitifcation of the key pressed or released. Even though both fields seem to have again special records, namely SDL_ScanCode and SDL_KeyCode, they actually consist of only one field each, DWord (integer type) for SDL_ScanCode and SInt32 (integer type) for SDL_KeyCode.

You may have a look into the SDL 2.0 scancode lookup table: https://wiki.libsdl.org/SDLScancodeLookup or SDL 2.0 Scancode lookup table (Backup)

The difference between scancode and key code

The difference is that the scancode refers to a specific physical location of a key on the keyboard. The scancode 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 I press the “Z” key on the German keyboard, the returned scancode will represent the “Y” key since the position of the key (independent of the layout) is equal to the position of the “Y” key on an US keyboard. Scancodes 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 scancode for the “Z”-key on a German keyboard will return that the “Y”-key has been pressed since the key has the location of the “Y”-key of an US keyboard. But the key code will not return it is the “Y”-key but it will correctly return that the “Z”-key has been pressed. The red marked output in the following image illustrates the result if the”Z” key is pressed on a German keyboard.

Difference Key code and Scancode

You may say, then I should always use keycodes to read out text input, right? – Wrong :-). In fact since SDL 2.0 it depends strongly on what you actually want to do. If you want to get a text or a single character from the user, you should neither use scancodes nor use key codes (anymore). (In former SDL 1.2 key codes or the unicode representation were indeed the preferable choice.) Treatment of real text input is not done this way anymore. We will discuss this case later. Anyway, this quote from the SDL 1.2 to SDL 2.0 migration guide sums it up excellently:

Use SDL_KEYDOWN to treat the keyboard like a 101-button joystick now. Text input comes from somewhere else.

Think of the famous T-shaped WASD key arrangement (arrangement of the four keys “W”, “A”, “S” and “D”) in the US layout, even if you keyboard without any latin letters, you may want to use these four keys 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.

Again, keep in mind:

Never use key codes or scancodes to read out text input in SDL 2.0 (anymore)

The remaining _mod field is a 16 bit unsigned integer (corresponds to Pascal’s Word) and represents key modifiers (ctrl, alt, shift, num lock, caps lock, …). If one or more key modifiers are pressed the _mod value has a unique number for each key or the key combination. For example, the left shift key has the decimal value 1, the right shift key has the value 2, the left control (ctrl) key has the value 64, the right ctrl key has the value 128. If the left shift and ctrl key are pressed at the same time the _mod vlue will be 65 (1 + 64). Let’s assume you want to have your application to be quit by the user pressing the ctrl key and “Q”. So you read out the key code for “Q” and check if _mod is 64 or 128. Since there doesn’t seem to exist a table for key modifiers, here the most important ones:

Modifier keyUInt16 value
Left shift 1
Right shift 2
Left ctrl 64
Right ctrl 128
Left alt 256
Right alt 576 (64 + 512?)
Caps lock 8192
Num lock 4096

The unicode field is deprecated and will not be discussed here. By the way, also procedure SDL_EnableUnicode is gone, which turned on the unicode mode.

Text input in SDL 2.0

Let’s have a look in the next part of the code and learn about the correct text input in SDL 2.0.

        SDL_TEXTINPUT: begin
                         writeln( 'Text input: "', sdlEvent^.text.text, '"' );
                         text1 := text1 + sdlEvent^.text.text;
                         writeln( 'Full string: ' + text1 );
                       end;

With SDL 2.0 there is a new event type named SDL_TEXTINPUT. This one has been explicitly introduce to SDL 2.0 to make text input more flexible and easy.

Let’s have a look into the record structure of SDL_TextInputEvent.

TSDL_TextInputEvent = record
    type_: UInt32;                                          // SDL_TEXTINPUT 
    timestamp: UInt32;
    windowID: UInt32;                                       // The window with keyboard focus, if any
    text: array[0..SDL_TEXTINPUTEVENT_TEXT_SIZE] of Char;   // The input text 
  end;

In contrast to the event structure SDL_KeyBoardEvent where two event types (SDL_KEYDOWN and KEYUP) were available, at the moment for the event structure SDL_TextInputEvent only one event type, SDL_TEXTINPUT, is possible.

The timestamp and windowID field have been discussed earlier in great detail. They contain some general information about when this event was triggered and which application window had the focus when the event was triggered. You may scroll up to get more information about this.

Unique about this event structure is the field text which is an array of char elements. This array contains from 0 to SDL_TEXTINPUTEVENT_TEXT_SIZE char elements. SDL_TEXTINPUTEVENT_TEXT_SIZE has a size of 32 by default. Attention here, this doesn’t mean you can only have 32 characters in texts or something like this! It means that in the moment this event is triggered not more than 32 characters are submittable to the event. Keep in mind though, if you use a Western language with Latin characters you always only submit one chracter at a time by each key stroke.

So why there are 32 possible characters anyway then? – To understand this, it would be necessary to go deeper into the construction of non-Western languages and how words and sentences are constructed. The massive amount and complexity of the way words and sentences are constructed makes it so that these are constructed beforehand before they are submitted to the text field of SDL_TextInputEvent. These constructs are not simply created by just a single key stroke which could be read out. For these constructs there are 32 chars reserved. (I would be glad to give a more detailed explanation here, please feel free to contact me to improve this paragraph.)

Let’s go back to the code. If a SDL_TEXTINPUT event has been found, its record (SDL_TextInputEvent) can be accessed by text. Its text input information is stored in the text field which we have seen recently. That is why we can use

sdlEvent^.text.text

to access the text input information. In the first line of the code we simply print out the content of the text field. The string variable “text1” is used to store the found character and add to the characters which have been found before. This string is also printed out.

Note how special characters (e.g. currency symbols, French accent symbols, the German “ß” symbol, …) and capital letters are recognized correctly. Try this with key codes or scancodes (it is a pain).

Note also that function keys ( F1, Backspace, …) are not recognized as characters. It is your responsibility for them to work the desired way :-).

previous Chapter | next Chapter

Chapter 6: Event handling (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

Whenever the user of your program is doing something related to it, for instance if the user is moving the mouse, pressing/releasing a button on the keyboard or pressing/releasing the fire key on the joystick, then you speak of events. Further events are resizing a window or switching between several applications. For games though the events described first are much more important. SDL provides a quite easy way to notice and react to such events, which is called event handling.

Each event has a special structure, for example a pressed key is an event or a moved mouse is an event. SDL differs altogether sixteen such events (check the table below). Every event stores different information depending on its kind. For example the key board event stores the information which key was pressed. The mouse motion event stores the information to which position the mouse got moved. If you get a mouse motion event you can’t read out key information, and from a key pressed event you can’t read out its mouse coordinates, and so on. Therefore you have the following general structure to the event data: event.eventstructure.data. The following list will give you an overview of all possible eventstructures in SDL:

eventstructure Description
SDL_NOEVENT Whenever the user is not raising an actual event.
SDL_ACTIVEEVENT Notices if application is active or inactive (for example when you switch to another windowed application and your applications gets into the background or gets minimized).
SDL_KEYDOWN Both have the same record structure (tSDL_KEYBOARDEVENT) which stores the triggering key with state SDL_PRESSED or SDL_RELEASED
SDL_KEYUP
SDL_MOUSEMOTION Notices the movement of the mouse cursor and stores it position and the relative movement from former origin.
SDL_MOUSEBUTTONDOWN Both have the same record structure (tSDL_MOUSEBUTTONEVENT) which stores the triggering mouse and its key with state SDL_PRESSED or SDL_RELEASED. Futhermore the position is stored.
SDL_MOUSEBUTTONUP
SDL_JOYAXISMOTION Notices the usage of the stick of a joystick.
SDL_JOYBALLMOTION Notices the usage of a joyball and the relative movement from its origin.
SDL_JOYHATMOTION Notices the triggering joystick and its hat. Furthermore one of the nine positions is stored (1=up, 2=right upper corner, 3=right and so on; 0=center).
SDL_JOYBUTTONDOWN Both have the same record structure (tSDL_JOYBUTTONEVENT) which stores the triggering joystick and its key with state SDL_PRESSED or SDL_RELEASED.
SDL_JOYBUTTONUP
SDL_VIDEORESIZE Notices when apllication window gets resized and stores the new height and width.
SDL_QUITEV Notices when user quits the application (by clicking application’s X-button at right upper corner)
SDL_USEREVENT Unedfined event which can be definied by user.
SDL_SYSWMEVENT Notices system window manager events.

The event handling subsystem is automatically initialized along with the video subsystem. Since all the events are related to the program’s window there is no sense of intilizing it individually from the video subsystem. Here is the code of the program all at once.

PROGRAM chap6_1;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    write('pending event: ');
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN: BEGIN
                     write('Key pressed ');
                     writeln('(SDLKey=',test_event^.key.keysym.sym,')');

                     {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;
                   END;
      SDL_KEYUP: writeln('Key released');
      SDL_MOUSEMOTION: writeln('Mouse motion');
      SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
      SDL_MOUSEBUTTONUP: writeln ('Mouse button up');
      SDL_JOYAXISMOTION: writeln ('Joystick axis motion');
      SDL_JOYBALLMOTION: writeln('Joystickïs trackball motion');
      SDL_JOYHATMOTION: writeln('Joystickïs hat position changed');
      SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
      SDL_JOYBUTTONUP: writeln('Joystick button released');
      SDL_QUITEV: writeln('User-requested quit');
    END;
  END
  ELSE writeln('no pending events');
  SDL_DELAY(150);
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Okay, let’s right start with the first part.

PROGRAM chap6_1;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

The first part to be discussed doesn’t contain a lot of new things. The program is called chap6_1, it uses the SDL unit and the screen variable is defined. New is the event variable test_event which is of pointer type pSDL_EVENT. We will create a second variable of boolean type just to control the while loop. Then SDL is initialized and the program’s window set with the width and height of 200 pixels. Finally the event variable gets allocated.

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    write('pending event: ');

We want to check if any event occured and if so, we want to know which kind of event happened. So we make a while loop which will run until loopstop gets true. This will be if the user presses the application’s X (right upper corner) or pressing ESC key.

The command SDL_POLLEVENT(parameter), or more precise SDL_POLLEVENT(event: pSDL_EVENT): INTEGER, checks if there are pending events and if so it will take the oldest and save it to parameter which is an event record of pSDL_EVENT type. For example if the user presses (then releases) left mouse button, then presses (then releases) space button on keyboard and finally moves the mouse there are altogether five events: 1. left mouse button pressed, 2. left mouse button released, 3. space key pressed, 4. space key released, 5. mouse moved. If you poll for events now you will get the first event (left mouse button pressed) and saved it’s properties to the event variable we specified as parameter. The next poll will save the properties of next event (left mouse button released) to event variable and so on. SDL_POLLEVENT(parameter) will return 1 if it has found pending event or 0 if there isn’t any pending event.

So, if SDL_POLLEVENT has an event, then the program writes “pending event:” and after that will add the type of event found. Notice that therefore write instead of writeln has been used here. The actual event is determined as follows:

CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN: BEGIN
                     write('Key pressed ');
                     writeln('(SDLKey=',test_event^.key.keysym.sym,')');

                     {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;
                   END;
      SDL_KEYUP: writeln('Key released');
      SDL_MOUSEMOTION: writeln('Mouse motion');
      SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
      SDL_MOUSEBUTTONUP: writeln ('Mouse button up');
      SDL_JOYAXISMOTION: writeln ('Joystick axis motion');
      SDL_JOYBALLMOTION: writeln('Joystickïs trackball motion');
      SDL_JOYHATMOTION: writeln('Joystickïs hat position changed');
      SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
      SDL_JOYBUTTONUP: writeln('Joystick button released');
      SDL_QUITEV: writeln('User-requested quit');
    END;
END

Fortunately you don’t have to check manually for every event which is made by the user. In general by event^.eventtype you can easily check which type of event you got. The event is stored in test_event and the type is checked by type_ so the expression is test_event^.type_. Whatever event is stored in test_event, the corresponding string expression is then added to the previous “pending event:” string. For the example described before the returned event types would be SDL_MOUSEBUTTONDOWN, 2. SDL_MOUSEBUTTONUP, 3. SDL_KEYDOWN, 4. SDL_KEYUP, 5. SDL_MOUSEMOTION. It is senseful to check for the event type by using the CASE command.

You may have noticed that in case of SDL_KEYDOWN not just a string gets added. Actually in this case also the corresponding SDLKey code is determined and given. Furthermore if the pressed key is the escape key (Esc) the program should stop. An keyboard event record contains a field called keysym. Keysym is a record of SDL_KEYSYM. It contains four fields. Scancode which is a hardware dependent scancode and should be avoided if you want to make hardware independent programs. It is usually an INTEGER variable. Next is sym which stores the SDLKEY. These SDL keys are independent and it is strongly recommended to use them! In the example we want break up if the escape key gets pressed. Its SDL key code is 27. If you want to know what SDL keys are defined look up in the table page. The variable modifier stores modifier keys (like shift, ctrl,…) pressed and stores SDLMod. SDL modifier keys can be found at table page as well. The fourth variable is unicode which may be used to read out unicode characters which is enabled by function SDL_ENABLEUNICODE(enable: INTEGER): INTEGER. 1 enables and 0 disables the unicode translation, however by default it is turned off.

To read out or compare the key the user pressed we must use the expression test_event^.key.keysym.sym. test_event^ is the event, the event structure is defined as an key event by key and the data we want to know is the key pressed contained in keysym.sym. The key code is printed to th screen but if it corresponds to the code for the ESC key, it sets loopstop to TRUE meaning the the while loop will be stopped and finally stop the program.

  ELSE writeln('no pending events');
  SDL_DELAY(150);
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

In the case SDL_POLLEVENT finds no event all the checking for event is skipped and just “no pending events” is written. To slow down the program the known SDL_DELAY is used.

Finally the event and the screen are disposed and SDL as well as the program are quit.

Now I will present a command without showing its appliance at any code since it is easy to understand and very important. Imagine you open any text editor and just hold pressed any letter on the keyboard. What will happen? First the letter will be written to the text editor once and after a short break the letter will be written in a loop (without break). – You may want to change this behaviour, especially for action games and such. The command SDL_ENABLEKEYREPEAT(DELAY, INTERVAL), or more precise SDL_ENABLEKEYREPEAT(delay: INTEGER; interval: INTEGER): INTEGER, is used for changing this behaviour. If delay is set to 0 the key repetition is completly disabled and a held pressed key will just trigger ONCE. If you enable the the delay by setting it to 1 and setting an interval in ms the pressed key will trigger anytime the set interval has passed. E.g. SDL_ENABLEKEYREPEAT(1,1000) will trigger the key any second if pressed. Note: When using this command the specific text edior behaviour is removed and the first triggering will happen immediatly. To get back to the common behaviour use SDL_DEFAULT_REPEAT_DELAY and SDL_DEFAULT_REPEAT_INTERVAL.

Now we will discuss mouse handling. Since it is rather similar to keyboard handling I will keep it short. Here we go:

PROGRAM chap6_2;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

PROCEDURE mouse_check;
BEGIN
  writeln('X: ',test_event^.motion.x,' Y: ',test_event^.motion.y,
        ' dX: ',test_event^.motion.xrel,' dY: ',test_event^.motion.yrel,
        ' button state: ',test_event^.motion.state);
  writeln('button state (pressed/released): ',test_event^.button.state,
        ' button index: ',test_event^.button.button);
END;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

SDL_EVENTSTATE(SDL_ACTIVEEVENT,SDL_DISABLE);

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN:   {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;

      SDL_MOUSEMOTION: mouse_check;
      SDL_MOUSEBUTTONDOWN: mouse_check;
      SDL_MOUSEBUTTONUP: mouse_check;
    END;
  END;
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Anyhow this code looks complicated, it is not.

PROGRAM chap6_2;

USES SDL;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

PROCEDURE mouse_check;
BEGIN
  writeln('X: ',test_event^.motion.x,' Y: ',test_event^.motion.y,
        ' dX: ',test_event^.motion.xrel,' dY: ',test_event^.motion.yrel,
        ' button state: ',test_event^.motion.state);
  writeln('button state (pressed/released): ',test_event^.button.state,
        ' button index: ',test_event^.button.button);
END;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);

screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

This is exactly the same code as for the first part of this chapter, however a PROCEDURE mouse_check is created which will be used several times later. You can read out the position of the mouse (relative to your program window!) by test_event^.motion.x and test_event^.motion.y. The relative movement which means the difference from the actual position compared to last polled position you can get by test_event^.motion.xrel and test_event^.motion.yrel. Furthermore it is possbile to return the pressed buttons by test_event^.motion.state. All this data belongs to tSDL_MOUSEMOTIONEVENT record. All these information are just printed out by the mouse_check PROCEDURE later.

SDL_EVENTSTATE(SDL_ACTIVEEVENT,SDL_DISABLE);

Function SDL_EVENTSTATE(type_: UInt8; state: INTEGER): UInt8 allows you to enable/disable certain event structures. For example in the code SDL_ACTIVEEVENT got disabled by SDL_EVENTSTATE(SDL_ACTIVEEVENT, SDL_DISABLE). That is why you never will receive a note about an active or inactive application even though the status may change when you run the program. The same result you get by using SDL_IGNORE instead of SDL_DISABLE. You can reenable the event structure by SDL_ENABLE.

WHILE loopstop=FALSE DO
BEGIN
  IF SDL_POLLEVENT(test_event)=1 THEN
  BEGIN
    CASE test_event^.type_ OF
      SDL_ACTIVEEVENT: writeln('Application is/is not active');
      SDL_KEYDOWN:   {SDLKey 27 = ESCAPE}
                     IF test_event^.key.keysym.sym=27 THEN loopstop:=TRUE;

      SDL_MOUSEMOTION: mouse_check;
      SDL_MOUSEBUTTONDOWN: mouse_check;
      SDL_MOUSEBUTTONUP: mouse_check;
    END;
  END;
END;


DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

This part now looks also very similar to the code of the first part of this chapter. Since we are only treating mouse handling here all the other possible events are not treated in the CASE block. The SDL_ACTIVEEVENT event is just introduced here to demonstrate that it is succesfully disabled by SDL_EVENTSTATE as discussed right before.

The SDL_KEYDOWN event is introduced to make sure the user can exit the program by pressing the ESC key.

Similar to the handling of keyboard events the mouse key events are handled. It also provides the possibility to return the pressed button by test_event^.button.button. It has the very same meaning as test_event^.motion.state! Don’t confuse it with test_event^.button.state which here means to check if a button is pressed (SDL_PRESSED) or released (SDL_RELEASED). This data belongs to the tSDL_MOUSEBUTTONEVENT record. Whatever the user is doing with the mouse, moving or clicking, the mouse_ckeck PROCEDURE already discussed is entered and the corresponding data written to the screen.

By the way, you should check what happens if you combine several mouse buttons or use the mouse wheel. Some button combinations lead to the same button index as the usage of the mouse wheel and so on.

With this chapter you got introduced into a major concept of game programming.

This file contains the source code: chap6_1.pas (right click and “save as”)
This file is the executable: chap6_1.exe (right click and “save as”)
This file contains the source code: chap6_2.pas (right click and “save as”)
This file is the executable: chap6_2.exe (right click and “save as”)

The final result should look and behave like this: While being with the mouse onto the SDL application window any event from the keyboard is recognized and it is said what exactly happened (button down, up, …)

Result of JEDI-SDL Chapter 6, keyboard handling

The final result should look and behave like this: While being with the mouse onto the SDL application window any event from the mouse is recognized and its (x/y) position, the difference between last recognized position and new position (dX and dY) and if a button is pressed or released and the corresponding button’s index is shown.

Result of JEDI-SDL Chapter 6, mouse handling

Chapter 6: Event handling (SDL4FP)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

If the user is moving the mouse, pressing/releasing a button on the keyboard or pressing/releasing the fire key on the joystick then you speak of events. Further events are resizing a window or switching between several applications. For games though the events described first are much more important. SDL provides a quite easy way to notice and react to such events, which is called event handling.

First of all using event handling means to understand the concept of event handling. Events have a special structure you should know about. For example an pressed key is an event or a moved mouse is an event. SDL differs altogether fourteen such events. Every event stores different information depending on its kind. For example the key board event stores the information which key was pressed. The mouse motion event stores the information to which position the mouse got moved. If you got a mouse motion event you can’t read out key information and so on. Therefore you have the following general structure to the event data: event.eventstructure.data.

To make event handling possible you have to include the unit SDL_EVENTS. The event handling subsystem is automatically initialized along with the video subsystem.

We have to create an event variable which is of pointer type pSDL_EVENT. We will create a second variable of boolean type just to control the while loop.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

We want to check the program if any event occured and if so, we want to know which kind of event happened. So we make a while loop which will run until loopstop gets true.

The command SDL_POLLEVENT(parameter) checks if there are pending events and if so it will take the oldest and save it to parameter which is an event record of pSDL_EVENT type. For example if the user presses (then releases) left mouse button, then presses (then releases) space button on keyboard and finally moves the mouse there are altogether five events: 1. left mouse button pressed, 2. left mouse button released, 3. space key pressed, 4. space key released, 5. mouse moved. If you poll for events now you will get the first event (left mouse button pressed) and saved it’s properties to the event variable we specified as parameter. The next poll will save the properties of next event (left mouse button released) to event variable and so on. SDL_POLLEVENT(parameter) will return 1 if it has found pending event or 0 if there wasn’t any pending event.

Fortunately you don’t have to notice any event which is made by the user ;). By event^.eventtype you can easily check which type of event you got. In the case described before it would be 1. SDL_MOUSEBUTTONDOWN, 2. SDL_MOUSEBUTTONUP, 3. SDL_KEYDOWN, 4. SDL_KEYUP, 5. SDL_MOUSEMOTION. It should be senseful to check for the event kind by using the case command.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
IF SDL_POLLEVENT(test_event)=1 THEN
BEGIN
write('pending event: ');

CASE test_event^.eventtype OF
SDL_EVENTACTIVE: writeln('Application is/is not active');
SDL_KEYDOWN: writeln('Key pressed ');
SDL_KEYUP: writeln('Key released');
SDL_MOUSEMOTION: writeln('Mouse motion');
SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
SDL_MOUSEBUTTONUP: writeln('Mouse button up');
SDL_JOYAXISMOTION: writeln('Joystick axis motion');
SDL_JOYBALLMOTION: writeln('Joystick's trackball motion');
SDL_JOYHATMOTION: writeln('Joystick's hat position changed');
SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
SDL_JOYBUTTONUP: writeln('Joystick button released');
SDL_EVENTQUIT: writeln('User-requested quit');
END;

END ELSE writeln('no pending events');
DELAY(150);
END;
DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Actually you shouldn’t try this source as it is written there because it will end up in an endless loop. The DELAY command is used because otherwise you won’t notice any pending events because they are polled too fast and it seems as if there would never be any pending events (just remove DELAY to confirm).

We now want to try the program to read out which key the user pressed on the keyboard. If the key is the escape-key (Esc) the program should stop. We already check the type of the event we polled. So if the event is a keyboard event the further checking should proceed. In our case especially if a key gets pressed. An keyboard event record contains a field called keysym. Keysym is a record of SDL_KEYSYM. SDL_KEYSYM is defined in SDL_KEYBOARD unit. It contains four fields. Scancode which is hardware dependent scancode should be avoided if you want to make hardware independent programs. It is usual integer variable. Next is sym which stores SDLKEY. These SDL keys are independent and it is strongly recommended to use them! In the example we want break up if escape key gets pressed. Its SDL key code is 27. If you want to know what SDL keys are defined look up in the table page. The variable modifier stores modifier keys (like shift, ctrl,…) pressed and stores SDLMod. SDL modifier keys can be found at table page as well. The fourth variable is unicode which may be used to read out unicode characters but to use this you have to enable it what is not described in this tutorial.

To read out or compare the key the user pressed we must use the expression test_event^.key.keysym.sym. Do you remember the general structure of an event (event.eventstructure.data)? – test_event^ is the event, the event structure is defined as an key event by key and the data we want to know is the key pressed contained in keysym.sym.

PROGRAM chap6_1;
USES crt, SDL, SDL_VIDEO, SDL_EVENTS;

VAR
screen:pSDL_SURFACE;
loopstop:boolean=FALSE;
test_event:pSDL_EVENT;

BEGIN
CLRSCR;
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,8,SDL_SWSURFACE);
IF screen=NIL THEN HALT;

NEW(test_event);

WHILE loopstop=FALSE DO
BEGIN
IF SDL_POLLEVENT(test_event)=1 THEN
BEGIN
write('pending event: ');

CASE test_event^.eventtype OF
SDL_EVENTACTIVE: writeln('Application is/is not active');
SDL_KEYDOWN:

BEGIN
write('Key pressed ');
writeln('(SDLKey=',test_event^.key.keysym.sym,')');
IF test_event^.key.keysym.sym=27 THEN loopstop:=true;
END;

SDL_KEYUP: writeln('Key released');
SDL_MOUSEMOTION: writeln('Mouse motion');
SDL_MOUSEBUTTONDOWN: writeln('Mouse button down');
SDL_MOUSEBUTTONUP: writeln('Mouse button up');
SDL_JOYAXISMOTION: writeln('Joystick axis motion');
SDL_JOYBALLMOTION: writeln('Joystick's trackball motion');
SDL_JOYHATMOTION: writeln('Joystick's hat position changed');
SDL_JOYBUTTONDOWN: writeln('Joystick button pressed');
SDL_JOYBUTTONUP: writeln('Joystick button released');
SDL_EVENTQUIT: writeln('User-requested quit');
END;

END ELSE writeln('no pending events');
DELAY(150);
END;
DISPOSE(test_event);
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Actually this is easy, isn’t it?

This file contains the source code: chap6_1.pas (right click and “save as”)
This file is the executable: chap6_1.exe (right click and “save as”)

ATTENTION: This is just the first part. The second part will describe mouse handling, which is much easier.