Using SDL2: Opening a Window
Tags: c, cpp, sdl, window
Published: 2018-10-29 21:04:30

In this part, we'll get a window up and running. First, create a new C++ file and call it whatever you want. It doesn't matter, but I'll call mine `main.cpp`.

Copy and paste the following code:

```cpp
#include <SDL2/SDL.h>
#include <iostream>

int main()
{
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "Failed to initialize the SDL2 library\n";
        return -1;
    }

    SDL_Window *window = SDL_CreateWindow("SDL2 Window",
                                          SDL_WINDOWPOS_CENTERED,
                                          SDL_WINDOWPOS_CENTERED,
                                          680, 480,
                                          0);

    if(!window)
    {
        std::cout << "Failed to create window\n";
        return -1;
    }

    SDL_Surface *window_surface = SDL_GetWindowSurface(window);

    if(!window_surface)
    {
        std::cout << "Failed to get the surface from the window\n";
        return -1;
    }
    
    SDL_UpdateWindowSurface(window);

    SDL_Delay(5000);
}
```

If you have your IDE like Visual Studio correctly configured then press the "run" button. If you use the terminal like me, you can use g++ or clang++.

```g++ -o sdl2-program main.cpp `sdl2-config --cflags --libs```
```clang++ -o sdl2-program main.cpp `sdl2-config --cflags --libs```

Compile and run it. You will see a black window pop up that is unresponsive and will close after 5000 milliseconds or 5 seconds.

![](https://thepracticaldev.s3.amazonaws.com/i/06oc79wbr8hhopf6072t.png)

Now let's walk through the code to see what's happening.

`if(SDL_Init(SDL_INIT_VIDEO) < 0)`

SDL2 needs to be initialized first before we can use any functions that it provides. SDL2 is separated into sections called subsystems. Right now, we only want video support. If you want more than one subsystem initialized, OR the different flags together:

`SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)`

`SDL_Init()` returns 0 on success and a negative number on failure. We check to see if this function fails. If so, print to the console letting the user know.

```cpp
{
        std::cout << "Failed to initialize the SDL2 library\n";
        std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
        return -1;
}
```

`SDL_GetError()` gets the error message that a failed SDL function sets.

 Next, we create the actual window.

```cpp
SDL_Window *window = SDL_CreateWindow("SDL2 Window",
                                          SDL_WINDOWPOS_CENTERED,
                                          SDL_WINDOWPOS_CENTERED,
                                          680, 480,
                                          0);
```

`SDL_CreateWindow()` takes several arguments:

1. The title of the window
2. the x position of the window
3. the y position of the window
4. width of the window
5. height of the window

It returns a pointer to an `SDL_Window` on success or `NULL` on failure.

We check to see if the window failed and if so, let the user know:

```cpp
if(!window)
{
    std::cout << "Failed to create window\n";
    std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
    return -1;
}
```

Now we get the window's surface. An `SDL_Surface` is a `struct` that contains a collection of pixels for software blitting. We'll get more into what blitting is later when we get an image on the screen, but suffice it to say, blitting is the process of copying an image from one surface to another.

```cpp
SDL_Surface *window_surface = SDL_GetWindowSurface(window);

if(!window_surface)
{
    std::cout << "Failed to get the surface from the window\n";
    std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
    return -1;
}
```

You know the drill by now.

Finally, we update the window's surface by calling `SDL_UpdateWindowSurface()`. The function takes a pointer to an `SDL_Window` and returns 0 on success and a negative number on failure. Here we won't check if something goes wrong.

We then call the `SDL_Delay()` function. If we didn't have it we wouldn't see the window because it would be created and quickly close. We pause for 5 seconds to see if everything works.

**Okay, Noah, this is dandy and all but this isn't very functional. How do we keep the window open until the user closes it?**

Good question. Before I can answer that question I'll have to explain what an *event* is.

An event is something that allows us to know if the user did something. Did the user move their mouse, typed on the keyword, moved the joystick on a controller, etc.

The way events get to us is in the form of a **queue**. A queue is a data structure that follows the First In, First Out or FIFO principle. Think of a line of people. The first person who enters the line is the first to exit the line. The same with a queue of events. The event that is first triggered is the first to be processed.

Events in SDL2 are stored in a union called `SDL_Event`. If you don't know what a union is it's like a `struct` except only one member is stored with data at a time.

We can **poll** for events with `SDL_PollEvent()`. Polling means to retrieve an event from the queue. `SDL_PollEvent()` takes a pointer to an `SDL_Event` and returns 1 if there is a pending event and 0 if no events are in the queue.

We can use a `while` loop to continuously check if an event needs to be processed.

```cpp
int main()
{
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "Failed to initialize the SDL2 library\n";
        std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
        return -1;
    }

    SDL_Window *window = SDL_CreateWindow("SDL2 Window",
                                          SDL_WINDOWPOS_CENTERED,
                                          SDL_WINDOWPOS_CENTERED,
                                          680, 480,
                                          0);

    if(!window)
    {
        std::cout << "Failed to create window\n";
        std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
        return -1;
    }

    SDL_Surface *window_surface = SDL_GetWindowSurface(window);

    if(!window_surface)
    {
        std::cout << "Failed to get the surface from the window\n";
        std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
        return -1;
    }
    
    SDL_Event e;
    while(SDL_PollEvent(&e) > 0)
    {
        SDL_UpdateWindowSurface(window);
    }
}
```

Compile and run.

And now it quickly pops up and vanishes.

**Hey! I thought we would have a window stay up on screen! You lied to us!**

Woah! Before you start pelting me with tomatoes, I need you to remember something: `SDL_PollEvent()` returns 0 if _no events are in the queue_. A 0 in a `while` loop condition means to stop executing. This just means no events need to be processed. There is an easy fix for this: surround the event processing `while` loop with another while loop:

```cpp
bool keep_window_open = true;
while(keep_window_open)
{
    SDL_Event e;
    while(SDL_PollEvent(&e) > 0)
    {
        SDL_UpdateWindowSurface(window);
    }
}
```

Compile and run.

**Okay, great. Now I can't close the window. How do we fix this?**

The reason why you can't close the window is that we haven't processed the window close event.

**Why doesn't SDL2 does this by default?**

Well, think of this scenario: Let's say you've created a game and you want it set up where if the user clicks the X button then a dialog will prompt them to save any progress they made before they exit the game. If SDL2 didn't give us this ability to process the window close event, then you couldn't implement such a feature.

I'll be using a `switch` instead of a chain of `if`/`if else` statements because it looks nicer especially when we start processing more events.

```cpp
bool keep_window_open = true;
while(keep_window_open)
{
    SDL_Event e;
    while(SDL_PollEvent(&e) > 0)
    {
        switch(e.type)
        {
            case SDL_QUIT:
                keep_window_open = false;
                break;
        }

        SDL_UpdateWindowSurface(window);
    }
}
```

In an `SDL_Event` union we have a `type` member. Its purpose is to store the type of event it currently holds.

Compile and run.

Now everything works as expected.

Before we end this part, let's get an image onto the screen. You need a bitmap image for this to work. Bitmap images have the extension `.bmp`. Download one or create one and then we can get started.

```cpp
SDL_Surface *image = SDL_LoadBMP("image.bmp");

if(!image)
{
    std::cout << "Failed to load image\n";
    std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
    return -1;
}

bool keep_window_open = true;
while(keep_window_open)
{
    SDL_Event e;
    while(SDL_PollEvent(&e) > 0)
    {
        switch(e.type)
        {
            case SDL_QUIT:
                keep_window_open = false;
                break;
        }

        SDL_BlitSurface(image, NULL, window_surface, NULL);
        SDL_UpdateWindowSurface(window);
    }
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/amkaenduil98xs6abdir.png)

We load a bitmap image using `SDL_LoadBMP()` and check to see if it fails and if successful, blit it onto the screen using `SDL_BlitSurface()`. This function takes four arguments:

1. The source surface from which we want to copy the pixels from
2. A pointer to an `SDL_Rect` for the source surface
3. The destination surface to where we want to copy the pixels to
4. A pointer to an `SDL_Rect` for the destination surface

What is an `SDL_Rect`? It's a `struct` holding the position and dimensions of a rectangle.

**Why do we need this exactly?**

If we only wanted to copy a part of the image, we can use an `SDL_Rect` and position it over the source surface and extract the portion we want. If we want to position and scale the source surface we can use an `SDL_Rect` to overlay on top of the destination surface to where we want the surface to be copied to.

For now, pass in NULL for both the source and destination `SDL_Rect`s because we want the whole image and we want to stretch the image to fit the entire window.

I made my bitmap in GIMP and made it with the same dimensions as the window and made the background black so that it seems like that there is text on the screen. If you have an image with different dimensions it might be blurry/squished.

SDL2 uses something called **double buffering**. We have two buffers available to us. The first one is called the back buffer and is our canvas: we can draw anything we want on it but until we call `SDL_UpdateWindowSurface()` it won't be displayed to the world. The front buffer is what is currently shown. When you call `SDL_UpdateWindowSurface()` it takes the back and front buffers and switches their places. This makes the front the back and the back the front.

Earlier I promised I would get more into what blitting was. Blitting is a term that means **bit-boundary block transfer**.

**Well, gee, Noah. That's really helpful.**

I know, but once you know what it is then it will hopefully make more sense.

Bit-boundary block transfer or blitting is the procedure of copying blocks of memory typically pixels from one source to another.

When we loaded the bitmap and displayed it onto the window, we were blitting the image's surface onto the window's surface. Remember that a surface is a collection of pixels. We were effectively copying the pixels from the image and pasting it onto the window.

Before we wrap up, I have one more thing to share. While these tutorials will hopefully be helpful, it is important to have a good documentation to refer.

The official one is over at the [SDL2 Wiki](https://wiki.libsdl.org/APIByCategory).

### What's next for us?

Next, we'll learn how to use `SDL_Rect`s because they are important in SDL2 development and we will use them to move an image across the window.

All the source code for this series can be viewed and downloaded at Github:

https://github.com/Noah11012/sdl2-tutorial-code