Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411279 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 04:33:44 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsSDL2 from scratch and SDL2 migration
Pages: [1]
Print
Author Topic: SDL2 from scratch and SDL2 migration  (Read 9632 times)
soryy708
Level 3
***


View Profile WWW
« on: October 31, 2013, 12:51:07 PM »

Hi.
For those of you who don't know what SDL is, go to www.libsdl.org

Introduction
So SDL2 is out. It's awesome, I know.
Unfortunately, there's still not much documentation out there on the internet. This is some of what I gathered here and there.
There's an official migration guide, but I find it lacking. Link: http://wiki.libsdl.org/MigrationGuide

The main
In general, you want your main to do the least amount of work possible. Have it delegate it to other functions (or objects if you're using OOP). The general structure should be something like this:
Code:
int main(int argc, char* argv[])
{
    /* Initialization */

    /* Main loop: */
    bool running = true;
    while(running)
    {
        /* Render */
        /* Get input and poll events */
        if(/* Got quit event */)
        {
            running = false;
        }
        /* Run logic */
    }

    /* Deinitialization */

    return 0;
}
In SDL2, it would look like this:
Code:
int main(int argc, char* argv[])
{
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        /* Handle problem */
        fprintf(stderr, "%s\n", SDL_GetError());
    }
    SDL_Window* window = SDL_CreateWindow("Window caption", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 852, 480, 0);
    if(window == NULL)
    {
        /* Handle problem */
        fprintf(stderr, "%s\n", SDL_GetError());
        SDL_Quit();
    }
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
    if(renderer == NULL)
    {
        /* Handle problem */
        fprintf(stderr, "%s\n", SDL_GetError());
        SDL_Quit();
    }

    bool running = true;
    while(running)
    {
        SDL_Rect where;

        /* Clear the buffer of color, setting it to black */
        SDL_RenderClear(renderer);
        /* Do some rendering to the screen surface. For example: */
        where.x = player.position_x; where.y = player.position_y;
        where.w = player.width; where.h = player.height;
        SDL_RenderCopy(renderer, player.texture, NULL, NULL);
        /* Draw the buffer into the window */
        SDL_RenderPresent(renderer);
        
        /* Handle input and events: */
        SDL_Event sdl_event;
        while(SDL_PollEvent(&sdl_event) > 0)
        {
            switch(sdl_event.type)
            {
            case(SDL_QUIT):
                running = false;
                break;
            }
        }        

        /* Run your logic. For example: */
        player.update();
    }

    SDL_Quit();

    return 0;
}
As opposed to SDL1.2:
Code:
int main(int argc, char* argv[])
{
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        /* Handle problem */
        fprintf(stderr, "%s\n", SDL_GetError());
    }
    SDL_Surface* screen = SDL_SetVideoMode(640, 480, 0, 0);
    if(screen == NULL)
    {
        /* Handle problem */
        fprintf(stderr, "%s\n", SDL_GetError());
        SDL_Quit();
    }

    bool running = true;
    while(running)
    {
        SDL_Rect where;

        /* Clear the buffer of color, setting it to black */
        SDL_FillRect(screen, NULL, SDL_MapRGB(window->format, 0, 0, 0));
        /* Do some rendering to the screen surface. For example: */
        where.x = player.position_x; where.y = player.position_y;
        SDL_BlitSurface(player.graphic, NULL, screen, &where);
        /* Draw the buffer into the window */
        SDL_Flip(screen);
        
        /* Handle input and events: */
        SDL_Event sdl_event;
        while(SDL_PollEvent(&sdl_event) > 0)
        {
            switch(sdl_event.type)
            {
            case(SDL_QUIT):
                running = false;
                break;
            }
        }        

        /* Run your logic. For example: */
        player.update();
    }

    SDL_Quit();

    return 0;
}

The pragmatic main
In the previous chapter, I've written something like fprintf(stderr, "%s\n", SDL_GetError()); wherever some error happened. What this does is simply ask SDL for a string describing what happened (using SDL_GetError() and print it into stderr (which is redirected by SDL to be a log file named stderr.txt). I find it inconvenient.
One very convenient thing that SDL2 provides you with is the ability to display message boxes in a cross-platform manner. The way you do this is with SDL_ShowSimpleMessageBox()
The way I handle this is with exceptions.
Code:
int main(int argc, char* argv[])
{
    SDL_Window* window = NULL;

    try
    {
        if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        {
            throw(std::exception(SDL_GetError()));
        }
        window = SDL_CreateWindow("Window caption", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 852, 480, 0);
        if(window == NULL)
        {
            throw(std::exception(SDL_GetError()));
        }
        SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
        if(renderer == NULL)
        {
            throw(std::exception(SDL_GetError()));
        }

        /* The main loop... */

        SDL_Quit();
    }
    catch(std::exception& e)
    {
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unhandled exception", e.what(), window);
    }
    catch(std::exception* e)
    {
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unhandled exception", e->what(), window);
    }
    catch(...)
    {
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unhandled exception", "Unknown exception type", window);
    }

    return 0;
}
The next thing that I want to change is the C-style handling of things. Yuck!
Let's use RAII!
The benefit of using RAII is that the language's scoping rules handle the resource's lifetime. While writing this, I actually forgot to call SDL_Quit() on some return paths. RAII makes this implicit.
Code:
class SDLHandle
{
private:
    static int ref_count;
    SDL_Window* window;
    SDL_Renderer* renderer;

public:
    SDL_Window* getWindow() const { return window; }
    SDL_Renderer* getRenderer() const { return renderer; }

    SDLHandle(const char* caption)
    {
        if(ref_count == 0)
        {
            if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
            {
                const char* err = SDL_GetError();
                SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Critical: Unable to initialize SDL", err, NULL);
                throw(std::exception(err));
            }
        }
        ++ref_count;

        try
        {
            window = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 852, 480, 0);
            if(window == NULL)
            {
                throw(std::exception(SDL_GetError()));
            }
            renderer = SDL_CreateRenderer(window, -1, 0);
            if(renderer == NULL)
            {
                throw(std::exception(SDL_GetError()));
            }
        }
        catch(std::exception& e)
        {
            const char* err = SDL_GetError();
            SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to initialize SDL", err, window);
            throw(std::exception(err));
        }
    }

    ~SDLHandle()
    {
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);

        --ref_count;
        if(ref_count == 0)
        {
            SDL_Quit();
        }
    }
};
int SDLHandle::ref_count = 0;

int main(int argc, char* argv[])
{
    SDLHandle sdl("Window caption");
    
    bool running = true;
    while(running)
    {
        // Business as usual...
    }

    return 0;
}
And by the way, handling multiple windows just got easier.

More on rendering
Rendering can be done in two ways: Either you render with a specific API (like OpenGL), or you render with SDL's rendering API.
To render with OpenGL, simply don't create an SDL_Renderer. Instead, you need to SDL_GL_CreateContext() Then use OpenGL as usual. Since you don't have an SDL_Renderer, you can't use SDL_RenderPresent() to draw your buffer. Instead, use SDL_GL_SwapWindow()
But this is not a tutorial on OpenGL. More on rendering with SDL:
Rendering with SDL does not mean that you're rendering in software. In fact, SDL2's new API is very friendly towards hardware accelerated APIs (OpenGL guys will know). What SDL actually does behind the scenes is look at which platform you're running the application on, and select the API which is estimated to give you the best performance and all of the features you ask when initializing the renderer.
Naturally, it also gives you some convenience functions that you wouldn't have if you'd have used the API directly. One such feature is SDL_RenderSetLogicalSize(). Simply put, this thing handles letterboxing and scaling of the display for you. You might want to do a SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); before calling this to improve your chances of getting a nice quality on a scaled resolution.

Window management and input
If you're using OpenGL, then this is what you came to SDL for. Nevertheless, users that use only SDL's rendering API need to know this just as well.
Window Management
SDL2 provides you with a lot more window management features than what SDL1.2 had in store. Most are done when you're calling SDL_CreateWindow(). Here you can set the caption, the initial position, the dimensions (resolution), and many other flags. Notable ones are SDL_WINDOW_FULLSCREEN, SDL_WINDOW_OPENGL and SDL_WINDOW_RESIZABLE.
You also have plenty of setters to modify these on the fly.
This really makes SDL1.2 look puny and insignificant, because it only had a couple of flags (SDL_FULLSCREEN, SDL_OPENGL, SDL_RESIZABLE, SDL_SWSURFACE, SDL_HWSURFACE, SDL_HWACCEL), caption setting had to be done separately (preferably before window creation) using SDL_WM_SetCaption(), and window positions had to be done with nasty environment-variable hacks. Oh, and you could have only one window at a time.
Here are some more awesome SDL2 window-management related functions: SDL_ShowMessageBox(), SDL_ShowSimpleMessageBox()
Input
Nothing much changed in this department between SDL1.2 and SDL2. There are is some minor refactoring that you can see on the official migration guide.
Input management in SDL is tied to event management. This makes sense; after all, the OS does it that way.
The process is simple: Something generates events (like the OS), SDL creates an SDL_Event and enqueues it in a queue, your application dequeues it and handles it.
You have three functions to manipulate the queue. You can dequeue from it with SDL_PollEvent(), peek in it with SDL_PeepEvents(), and you can enqueue into it with SDL_PushEvent().
During one main loop iteration, multiple events may be generated by the OS. This is why you want to do the polling in a loop.
Code:
SDL_Event sdl_event;
while(SDL_PollEvent(&sdl_event) > 0) /* While there are more than 0 events in the queue */
{
    /* Handle events */
}
SDL_Event is a rather big structure that you might want to read a lot about. You can get all sorts of useful information about the event in question from it.
The most useful piece of information that you're going to get is the event's type. Get it like sdl_event.type. Notable ones are: SDL_QUIT, SDL_KEYDOWN, SDL_KEYUP, SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP
Here's an example:
Code:
int  cursor_x   = 0;
int  cursor_y   = 0;
bool move_up    = false;
bool move_down  = false;
bool move_left  = false;
bool move_right = false;
bool shoot      = false;

SDL_Event sdl_event;
while(SDL_PollEvent(&sdl_event) > 0)
{
    switch(sdl_event.type)
    {
    case(SDL_QUIT):
        running = false;
        break;
    case(SDL_KEYDOWN):
        switch(sdl_event.key.keysym.sym)
        {
        case(SDLK_w):
        case(SDLK_UP):
            move_up = true;
            break;
        case(SDLK_a):
        case(SDLK_LEFT):
            move_left = true;
            break;
        case(SDLK_s):
        case(SDLK_DOWN):
            move_down = true;
            break
        case(SDLK_d):
        case(SDLK_RIGHT):
            move_right = true;
            break;
        }
    }
    case(SDL_KEYUP):
        switch(sdl_event.key.keysym.sym)
        {
        case(SDLK_w):
        case(SDLK_UP):
            move_up = false;
            break;
        case(SDLK_a):
        case(SDLK_LEFT):
            move_left = false;
            break;
        case(SDLK_s):
        case(SDLK_DOWN):
            move_down = false;
            break
        case(SDLK_d):
        case(SDLK_RIGHT):
            move_right = false;
            break;
        }
    case(SDL_MOUSEMOTION):
        cursor_x = sdl_event.motion.x;
        cursor_y = sdl_event.motion.y;
        break;
    case(SDL_MOUSEBUTTONDOWN):
        if(sdl_event.button.button == SDL_BUTTON_LEFT)
        {
            shoot = true;
        }
        break;
    case(SDL_MOUSEBUTTONUP):
        if(sdl_event.button.button == SDL_BUTTON_LEFT)
        {
            shoot = false;
        }
        break;
    }
}
« Last Edit: October 31, 2013, 02:37:06 PM by soryy708 » Logged
SIGVADER
Level 0
**


卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐


View Profile
« Reply #1 on: October 31, 2013, 12:59:08 PM »

yuck why would you cahnge teh video mode just use the full natiev screen and a frame buffer or something i hate it when i gotta alt tab outa a game and the screen does a backflp
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic