Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use SDL2 in my programs correctly?

Tags:

c++

c

sdl

sdl-2

I want to make a game using SDL2, but I'm unable to compile and/or run my code, please help!

SDL2 is notoriously hard to set up, and it's often the first library aspiring game developers try to use.

This post is intended as a canonical duplicate for common problems with setting up SDL2.

like image 383
HolyBlackCat Avatar asked Oct 16 '20 22:10

HolyBlackCat


People also ask

Where do I put SDL2?

Step 2: Installing SDL2 Go to the SDL2 download page and download the latest development library for Windows using MinGW. After extracting the contents using for example 7-Zip, copy the folder "x86_64-w64-mingw32", to where you want to store the library. Note that this is still the 64-bit version of the library.

How do I enable SDL?

Select the Configuration Properties > C/C++ > General property page. Set the SDL checks property by using the property drop-down control. Choose OK or Apply to save your changes.

How do I add SDL2 to Visual Studio?

First, set up an empty project. Then, download SDL. For Visual Studio, download the "VC" development library. Extract the files and move the folder into your project.


Video Answer


1 Answers

This answer deals with MinGW / GCC, and not Visual Studio.

This answer deals primarily with Windows. Things are easier on other operating systems, see the bottom of the answer for a summary.


Common errors

The common errors are:

  • SDL.h: No such file or directory (when compiling)
  • undefined reference to various functions (when linking)
  • Mysterious .dll-related errors (when running your program).

This list is sorted from bad to good. If you change something and get a different error, use this list to tell if you made things better or worse.


The preamble

0. Don't follow bad advice.

Some resources will suggest you to do #define SDL_MAIN_HANDLED or #undef main. Don't blindly follow that advice, it's not how SDL2 is intended to be used.

If you do everything correcty, it will never be necessary. Learn the intended approach first. Then you can research what exactly that does, and make an educated decision.

1. Figure out how to compile directly from the console, you can start using an IDE and/or build system later. If you're using an IDE, I suggest to first make sure you're able to compile your program directly from the console, to rule out any IDE configuration problems. After you figure that out, you can use the same compiler options in your IDE.

The same applies to build systems, such as CMake.

2. Download the right SDL2 files. Make sure you have the right files. You need the archive called SDL2-devel-2.0.x-mingw.tar.gz from here.

Extract it to any directory, preferably somewhere near your source code. Extracting into the compiler installation directory is often considered a bad practice (and so is copying them to C:\Windows, which is a horrible idea).

3. Know the difference between compiler flags and linker flags. A "flag" is an option you specify in the command line when building your program. When you use a single command, e.g. g++ foo.cpp -o foo.exe, all your flags are added to the same place (to this single command).

But when you build your program in two steps, e.g.:

  • g++ foo.cpp -c -o foo.o (compiling)
  • g++ foo.o -o foo.exe (linking)

you have to know which of the two commands to add a flag to. Those are "compiler flags" and "linker flags" respectively.

Most IDEs will require you to specify compiler and linker flags separately, so even if you use a single command now, it's good to know which flag goes where.

Unless specified otherwise, the order of the flags doesn't matter.


SDL.h: No such file or directory

Or any similar error related to including SDL.h or SDL2/SDL.h.

You need to tell your compiler where to look for SDL.h. It's in the SDL files you've downloaded (see preamble).

Add -Ipath to your compiler flags, where path is the directory where SDL.h is located.

Example: -IC:/Users/HolyBlackCat/Downloads/SDL2-2.0.12/x86_64-w64-mingw32/include/SDL2. Relative paths work too, e.g. -ISDL2-2.0.12/x86_64-w64-mingw32/include/SDL2.

Note that the path will be different depending on how you write the #include:

  • If you do #include <SDL.h>, then the path should end with .../include/SDL2 (like above). This is the recommended way.
  • If you do #include <SDL2/SDL.h>, then the path should end with .../include.

undefined reference to various functions

The error message will mention various SDL_... functions, and/or WinMain. If it mentions SDL_main, consult the section "undefined reference to SDL_main only" below.

You need to add following linker flags: -lmingw32 -lSDL2main -lSDL2 -Lpath, where path is the directory where libSDL2.a and other .a files (which you've downloaded) are located. The order of the -l... flags matters. They must appear AFTER any .c/.cpp/.o files.

Example: -LC:/Users/HolyBlackCat/Desktop/SDL2-2.0.12/x86_64-w64-mingw32/lib. Relative paths work too, e.g. -LSDL2-2.0.12/x86_64-w64-mingw32/lib.

When you use -l???, the linker will look for a file called lib???.dll.a or lib???.a (and some other variants), which is why we need to pass the location of those files. libmingw32.a (corresponding to -lmingw32) is shipped with your compiler, so it already knows where to find it.

I added all those flags and nothing changed:

You probably use the wrong SDL .a files. The archive you downloaded contains two sets of files: i686-w64-mingw32 (32-bit) and x86_64-w64-mingw32 (64-bit). You must use the files matching your compiler, which can also be either 32-bit or 64-bit.

Print (8*sizeof(void*)) to see if your compiler is 32-bit or 64-bit.

Even if you think you use the right files, try the other ones to be sure.

Some MinGW versions can be switched between 32-bit and 64-bit modes using -m32 and -m64 flags (add them to both compiler and linker flags).

I get undefined reference to a specific function:

undefined reference to WinMain only

There are several possibilities, all of which were covered in the previous section:

  • You forgot -lmingw32 and/or -lSDL2main linker flags.
    You must use following linker flags, in this exact order, after any .c/.cpp/.o files: -lmingw32 -lSDL2main -lSDL2
  • The libSDL2main.a file you use doesn't match your compiler (32-bit file with a 64-bit compiler, or vice versa).

Try to avoid #define SDL_MAIN_HANDLED or #undef main when solving this issue, see preamble for explanation.

undefined reference to SDL_main only

You need to have a main function. Your main function must look like int main(int, char **). NOT int main() and NOT void main(). This is a quirk of SDL2, related to it doing #define main SDL_main.

Adding any parameter names is allowed, e.g. int main(int argc, char **argv). Also the second parameter can be written as char *[] or with a name: char *argv[]. No other changes are allowed.

If your project has multiple source files, make sure to include SDL.h in the file that defines the main function, even if it doesn't otherwise use SDL directly.

Try to avoid #define SDL_MAIN_HANDLED or #undef main when solving this issue, see preamble for explanation.


.dll-related errors

The .exe you made needs some .dlls to run.

If it doesn't find them, it will tell you what files it needs. It can also find wrong versions of the DLLs (left over from other programs you have installed), which often causes rather cryptic errors.

Your program will look for the DLLs in different locations, but the most reliable solution is to put them in the same directory as the .exe. This directory is searched first, so other versions of the DLLs you might have elsewhere won't interfere.

You need to figure out which DLLs your program needs and put them in the directory where your .exe is located. Except for the system DLLs, which you don't need to copy.

There are two types of errors you can get:

  • Some DLL is missing.

    • SDL2.dll is missing. — It's in the SDL files you've downloaded.

      Be aware that there are two different SDL2.dlls: a 32-bit one (in the i686-w64-mingw32 directory), and a 64-bit one (in x86_64-w64-mingw32). Get the right one, if necessary try both.

    • Some other .dll is missing. — It's shipped with your compiler. Look in the directory where your gcc.exe is located.

      If you copy one dll and it asks for more, that's normal. Having to repeat this ~3 times is normal.

  • A cryptic .dll-related error: "procedure entry point not found in ...", etc — Your program found a wrong version of a DLL` somewhere in your system.

    You need to copy all non-system .dlls it needs to the directory where your .exe is located.

    First, copy SDL2.dll (see the section "SDL2.dll is missing" above for where to find it).

    Then, copy following .dlls from your compiler directory (the directory where gcc.exe is located):

    • libgcc???.dll (the name can vary depending on your MinGW version, but will always start with libgcc)
    • libstdc++-6.dll (for C++ only, skip if you're writing in C)
    • libwinpthread-1.dll (the name can vary depending on your MinGW version, but will always mention thread; some versions don't seem to use this at all)

    For some MinGW distributions, those may not be the right .dlls. If you don't see those or copying them doesn't help, continue reading.

That should fix all your errors. If you want to know more or it didn't help:

More information about the .dlls

It's possible to make an .exe that doesn't depend on any (non-system) .dlls by using the -static linker flag, this is called "static linking". This is rarely done, and you shouldn't need to do this if you did the above steps correctly. This requires different linker flags than usual (around 20 of them), see sdl2.pc file shipped with SDL for the exact flags (they are in the Libs.private section).

In the previous section I tried to guess what DLLs your program depends on. How did I know?

There are tools that show the list of DLLs your program uses, like ntldd or Dependency Walker.

You'll have to go through their output and determine if each .dll is a system one or not. If a .dll is shipped with your compiler or belongs to a library you use (SDL2.dll), then it's not a system one, and needs to be copied to the location of the .exe. Any remaining .dlls are system ones, ignore them.

For ntldd algorithm is:

  • Copy all DLLs from your compiler's bin directory to the location of the .exe. Also copy the DLLs for the libraries you use (SDL2.dll).
  • Run ntldd -R my_program.exe.
  • Any DLL that doesn't appear in the list should be removed from the current directory.
  • Everything else is used by your program, and should be shipped with it when you distribute it to others.

Sometimes copying DLLs is not necessary: DLLs can be found if their locations are listed in PATH, or if they are in C:\Windows (or some subdirectories). But this is less reliable: multiple incompatible versions of the DLLs can be in PATH directories, or even in C:\Windows\....


A saner alternative?

There is MSYS2.

It has a package manager that lets you download prebuilt libraries, and, as a bonus, a fresh version of the compiler.

Install SDL2 from its package manager. Use a tool called pkg-config (also from the package manager) to automatically determine all necessary flags (pkg-config --cflags SDL2 for compiler flags, pkg-config --libs SDL2 for linker flags).

This is the same experience as you would have on Linux (maybe except for some DLL management hassle).


Bonus - Other problems

  • Q: My program always opens a console window when I run it, how do I hide it?

    • A: Add -mwindows to the linker flags.
  • Q: I get error 'SDL_VideoMode' wasn't declared in this scope.

    • A: SDL_VideoMode is from SDL1.2, it's not a part of the newer SDL2. Your code was written for the outdated version of SDL. Find a better tutorial that deals specifically with SDL2.
  • Q: My program has the default file icon, but I want a custom one.

    • A: Your icon must be in the .ico format. If your graphics editor doesn't support it, make a series of .pngs of common sizes (e.g. 16x16, 32x32, 48x48, 64x64), then convert them to a single .ico using ImageMagick: magick *.png result.ico (or with convert instead of magick).

      Create a file with the .rc extension (say, icon.rc), with following contents MyIconName ICON "icon.ico" (where MyIconName is an arbitrary name, and "icon.ico" is the path to the icon). Convert the file to an .o using windres -O res -i icon.rc -o icon.o (the windres program is shipped with your compiler). Specify the resulting .o file when linking, e.g. g++ foo.cpp icon.o -o foo.exe.

      Recent versions of SDL2 have a nice property of using the same icon as the window icon, so you don't have to use SDL_SetWindowIcon.

like image 64
HolyBlackCat Avatar answered Oct 10 '22 20:10

HolyBlackCat