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

Leave a Reply

Your email address will not be published. Required fields are marked *