Example: Basic window management and game loop - OutOfTheVoid/ProjectRed GitHub Wiki

Using SDLX, we can easily open a window and run our game loop.

Example  01

We'll start off by initializing the SDLX library. Note that we pass the address of the local variable Status in order to receive a status code from SDLX. ( Requires SDLX/Lib.h )

uint32_t Status;
SDLX::Lib :: Init ( & Status, SDLX::Lib :: kSDLFlag_Video );

Here, we initialize the library and pass it the flag kSDLFlag_Video so that SDL knows we want to use the windowing system. Normally, this should succeed, but if you want to fail gracefully in the case that it does not, you can check that Status was not set to anything other than kStatus_Success.

if ( SDLX :: kStatus_Success )
    return - 1;

Now that SDLX is initialized, we can go ahead and create a window! (Simple, huh?) This is pretty simple, all you have to do is call SDLX::Window :: CreateWindow (), which takes the parameters uint32_t * Status, the familiar status parameter, const std :: string & Name, the name of the window which will be shown on the title bar, uint32_t Width, the width (in pixels) of the window, and uint32_t Height, the height in pixels of the window.

(requires SDLX/Window.h)

SDLX::Window * MyWindow = SDLX::Window :: CreateWindow ( & Status, "My First Window", 800, 600 );

As always, we should check that Status was set to kStatus_Success, which from this point forward, I will assume you know to do. In all cases, the pointer to the window object will be valid if and only if Status indicates success. Interestingly, since at this point, we know that the SDLX library was properly initialized, we should deinitialize the library if we choose to return from our main function. We do this by calling SDLX :: DeInit ().

if ( Status != SDLX::Window :: kStatus_Success )
{
    // Do this wherever we potentially exit the application.
    SDLX::Lib :: DeInit ();
    
    return - 2;
}

Alright, that's a valid window pointer! Lets make sure it stays valid. In SDLX, the Window class uses reference counting to decide when it should deallocate itself. We can reference and later deference our Window object using the SDLX::Window :: Own () and SDLX::Window :: Disown () calls defined in SDLX::RefCounted (form which Window is derived). (Note, in SDLX, refcounting starts at zero, so you must always initially reference a new object before you use it.)

MyWindow -> Own ();
// MyWindow -> Disown ();
// MyWindow = NULL; // It's always a good idea to null a pointer you're no longer in ownership of.

Now that we've claimed ownership of the window, we can enter our event loop! This is a convenience of SDL2 and hence SDLX, so all we have to do to enter the loop is to call SDLX::Lib :: EventLoop () with a Status parameter. Once we call this, we relinquish control to SDLX, which will only return from the event loop when (simply put) there are no more windows or event sources.

SDLX::Lib :: EventLoop ( & Status );

If you were to run your application now, you might notice that you can't close it traditionally. You'll likely have to force quit it. The reason for this is that while SDLX is registering that your os is telling the window to close, your application isn't doing anything with that information. How unfortunate!

We can solve this by adding an event hook to the game loop. An event hook is simply a callback that will be executed when a certain event happens. The method for adding window-related event hooks is with the method SDLX::Window :: AddEventHook (), which accepts three required parameters: uint32_t SDLWindowEventID, which tells the window which event you're trying to hook into (SDLX::Window :: kWindowEventID_Closed in this case), void ( * EventCallback ) ( SDL_WindowEvent * Event, Window * Origin, void * Data ), a function pointer to our OnWindowClosed callback ( which i'll talk about in a moment ), and void * Data, which is a pointer to a user data object of our choice ( in this case we'll use NULL ) to be used as the second parameter to our callback, which i'll call WindowClosedEvent.

This happens before we enter the event loop, as if we put it afterward, we'd never reach it!

MyWindow -> AddEventHook ( SDLX::Window :: kWindowEventID_Closed, & WindowCloseEvent, NULL );

Now let's write that callback! As per the function pointer, the function should return void, taking in an SDL_WindowEvent * for the verbatim SDL2 event, Window * Origin, a pointer to the window that caused the event, and a void * Data for the user data that I mentioned earlier.

void WindowCloseEvent ( SDL_WindowEvent * Event, SDLX :: Window * Origin, void * Data )
{
    // Do stuff here
}

You could test this if you want by putting a printf in the callback and seeing that it prints to the console every time you try to close the window, but i'll just jump right to actually closing it. We do this by calling SDLX::Window :: Close () on our passed window argument, which requires a status parameter.

void WindowCloseEvent ( SDL_WindowEvent * Event, SDLX :: Window * Origin, void * Data )
{
    uint32_t Status;
    Origin -> Close ( & Status );
    
    // Just to suppress unused argument warnings, we'll cast these to void.
    (void) Event;
    (void) Data;
}

Alright! With all that together, we should have an application that can close! Hooray! All that's really left to do is to clean up our application after the event loop exits. (The event loop will return once our window is closed, since we no longer have any windows open.)

// Always a good idea to NULL a pointer we no longer own.
MyWindow -> Disown ();
MyWindow = NULL;
// Cleans up any resources that SDL2/SDLX might be using
SDLX::Lib :: DeInit ();

Here's a link to the complete application: Example 01 Code

Written by Liam Taylor / OutOfTheVoid