Monday, February 4, 2008

Contents


1. MinGW Starter

2. MinGW + DirectX

3. MinGW + SDL


© 2001-2008 by Mike Linkovich
www.spacejack.org

MinGW + SDL

SDL (Simple Direct Media Layer) is a very powerful, low-level, cross-platform, high-performance multimedia library. If you are unfamiliar with this library I recommend going to the SDL website and having a look around for an hour or nine. Browse the documentation and the header files, and be sure to read the license so you know what you have the rights to use and how you may use it.

Now, assuming you intend to compile SDL apps using the free MinGW compiler (MinGW?), get the latest version of the SDL from the download section on the SDL website. Get the Development Runtime for Win32, the MinGW version. SDL users tend to favour MSVC when on Windows, so some of the MinGW-specific information is a bit sparse. As-is, the development runtime package needs to be configured and installed before you can compile with it. This requires the help of a more unix-like command line shell, either Cygwin (configured to use MinGW) or MSYS from mingw.org (as described in the first section).

Instead of doing this, which will install the libraries and headers into your MinGW system, I prefer to keep SDL separate so I can easily switch between versions of SDL and MinGW just by re-naming the folder.

Furthermore, on Windows, you will most likely ship your app with the SDL DLLs, as opposed to compiling them statically to your app. In which case, you should take advantage of the MSVC-compiled DLL available from libsdl.org, which will in all likelyhood be better optimized than what MinGW will produce. Dynamically linking also gives the user the opportunity to upgrade the SDL DLLs themselves later if need be (or if they choose to share a single SDL.dll with all their apps by placing it in the Windows System folder). Statically linking will also tie your app to the LGPL which SDL was released under.

So, to create a quick-and-dirty standalone SDL SDK which you can use with MinGW, follow these steps:

  • Begin by unpacking the SDL development runtime archive to a temporary directory. You need to find the header files, the static-link libraries, and the SDL.dll file. These should be found (assuming the packaging hasn't changed since this writing) within another archive called i386-mingw32msvc.tar.gz that should have been created while extracting the first archive. Extract this into another temporary directory. From that you need the headers (.h) and libs (.a), including the dll. For development purposes, you might put those into a directory structure as follows:

    SDL/include/*.h
    /lib/*.a,*.dll


  • Then put a copy of the SDL.dll into the directory your final executable target will reside so that Windows can do the run-time linking when you start up your app. Or, you can place this dll into your Windows directory if you want share that same dll with multiple SDL apps.

  • The only trick in getting this to compile now is to add the include path (eg: -I../SDL/include), the linker path (eg: -L../SDL/lib) and then finally adding the libraries themselves in the right order. Use:

    -lmingw32 -lSDLmain -lSDL

    Also, don't forget to add the -mwindows flag, if your IDE doesn't add it automatically (in addition to whatever other libraries you want to link). If you don't put them in the right order, you'll get a linker error complaining about the missing symbol WinMain@16.

What is this libSDLmain.a file? It is built from the source file SDL_main.c, in the sdl/src/main/win32 directory (when on Win32 platform). It contains the bare framework for a Windows app, including the essential WinMain function which is a Windowed application's entry-point. If for whatever reason you are having insurmountable problems linking to libSDLmain.a, or if you have a specific need to alter the 'WinMain' or add other platform-specific calls, you can simply add this source file to your project in its place to resolve the link errors and then modify it to your heart's content. I am not recommending this approach, it is only here for your information and to give some insight into how SDL works on Windows.


MinGW and SDL_image

SDL_image is an invaluable add-on library that allows you to use a wide variety of image formats with SDL, including BMP, JPG, GIF, PNG, TGA, PCX and many more (as opposed to just BMPs with SDL alone).

If you want to get up and running quickly without re-compiling from source, simply grab the runtime development package. This time however, you can use the MSVC package. You can link directly to the SDL_image.lib file as it only requires plain vanilla C linkage (no C++ or other MinGW-specific WinMain). Just extract the archive to a location and remember to set the include and lib paths when you compile.

The documentation site for SDL_image is clear and straightforward. It also has an excellent example source to try which serves as a great little intro to both SDL and SDL_Image at the same time. Assuming your SDL/ and SDL_image/ directories are at the same level, create another folder at that same level, called viewimage. Grab the viewimage.c source from the demos directory, as well as a few image files to test. Put the files you download in the viewimage folder. Then you can use this makefile to build the test app:

make -f viewimage.makefile

Finally, copy all the .dll files (zlib.dll, jpeg.dll, libpng1.dll, SDL_image.dll and SDL.dll) to the viewimage directory so the app can find them when you run it. The app is a command-line app, so pull up a command prompt and cd to that directory. Test it by running it with the name of one of the demo images:

viewimage lena.jpg


originally posted on spacejack.org: 2001-06-06
edited: 2001-06-10
(thanks to Matt Jones for the linker tip ;) edited: 2001-08-08
edited: 2001-09-05
edited: 2002-12-31

MinGW + DirectX

I should first point out to beginner game developers that it is entirely possible to avoid using DirectX at all by using SDL (Simple DirectMedia Layer) for all your graphics, sound, input, timing, etc., and it integrates well with OpenGL for 3D graphics (it actually uses DirectX for 2D graphics, sounds, and so on) -- with the added benefit of being cross-platform. So unless you need or want to work with the DirectX API directly for some specific reason, this is a good solution that offers portability to a number of other platforms and a rabid following of both pro and amateur game developers. If you're just starting out and not sure, I would recommend looking into it before getting into DirectX. You can have a look at my MinGW + SDL page for MinGW/SDL-related setup tips.

Now, for those of you remaining, I assume you do know how to use MinGW, and I assume you are interested in getting it to compile DirectX apps. We'll need the DirectX headers and libraries for the version of DirectX we want to use. These are typically included in the DirectX SDK available from Microsoft. Whichever way you do this, you will at least need the header files, so get the SDK you want to use. The latest version can be found at www.microsoft.com/directx/. You can, at the time of this writing, still get the DirectX7 SDK from Microsoft. With DirectX8, Microsoft has combined DirectDraw into Direct3D. Some people wishing to use DirectDraw, or older interfaces may opt to use this download. (If you do want to use an older version, this version 7 SDK is a much leaner download and install.)

The Easy Way

The most straightforward way to go about building a DirectX app is simply to use Microsoft's DirectX SDK. You can link to Microsoft's .lib files to your application by specifying the .lib name explicitly to the compiler. Adding the headers location to the include path would also be a good idea. Example:

g++ main.cpp -o test.exe -I../dxsdk/include ../dxsdk/lib/dxguid.lib

Other Ways
  • There also exist MinGW-compatible libs (typically named lib*.a, where * is the name of the library). The advantage to using MinGW-compatible libraries using this naming convention is that you can put them into a location on your library search path (specified by the -L flag). The version of the DirectX static libs shipping with MinGW is probably out of date, but if they do support the functions you need then you can use those simply by adding, for example:

    -ldsound

    to your linker command.

    The implication of this library search naming convention gives us one more way to link to Microsoft's DirectX SDK libraries: simply change the names of the MS libraries to lib*.a (e.g. rename ddraw.lib to libddraw.a, and then don't forget to add the library search path with the -L option.)

  • You can also get a ready-to-go Cygwin/MinGW-compatible DirectX6 SDK from John Fortin's site (you only need the directx6_1-cyg.tar.gz file). This includes everything you need to build DirectX 6 (or earlier) apps with either Cygwin or MinGW, however keep in mind that this download is the version 6 API. If you want later versions, I suggest getting them from Peter Puck's excellent site which also contains more detail regarding DirectX and MinGW and import library issues. The reason I'm using the version 6 SDK is because I only need version 5 for what I'm working on. If there are features missing from these older versions that you require, you'll need to get a later version. Note that DirectX8 has removed the DirectDraw API; now all 2D and 3D graphics is accomplished via the Direct3D8 API. The last version of DirectX to support DirectDraw API was version 7. You can still use the latest headers to compile and build for older versions of DirectX, however the v7 & v8 headers were causing some annoying compiler warnings so I stuck with version 6. I get the feeling Microsoft doesn't like you using 3rd party compilers to build DirectX apps.

More Tips
  • When using an ad-hoc DirectX SDK, I create Microsoft-like directory for it called, eg. dx6sdk, and in that I create an include and lib directory. I leave this in a convenient spot so that I can link to the lib and include files from multiple projects.

  • Another trick I use in order to avoid a lot of hassle is to use Windows' LoadLibrary()/FreeLibrary() calls to load the DDRAW.DLL, DSOUND.DLL, etc. libraries explicitly. This minimizes your static link requirements down to just the dxguid library. (I was unsuccessful getting the #define INITGUID trick to work, which would eliminate the need to link to any DirectX static libraries. For some reason, the IID_IDirect3D2 symbol remained undefined. Perhaps there is another way around it, but as far as my experiments showed, you need to at least be able to link to libdxguid.a.)

Backwards Compatibility, Version Decisions

If you are looking to support relic Windows PCs that have never been patched since, like, 1996, you can just go with the version one interfaces of DirectDraw and DirectSound, and use the Win32 calls GetJoyPosEx()/GetKeyState() for input and Windows sockets for networking. In fact, there really is no reason not to use the old interfaces if you're not using Direct3D or the higher-level media APIs -- with a few caveats:
  • DirectSound version 1 has a bug that the streaming sound buffer read/write position to be off by 50ms or somesuch. If you are not doing any software sound filtering or realtime computation relying on accurate feedback from the API (i.e., if you just want to play/loop/stop/volume/pan stuff), DSound1 handles the basics just fine, otherwise you should look at using a later versions in which this bug was fixed.

  • DirectDraw version 1 does pretty much all you need a blitter/flipper to do, unless you're doing some strange video things. Most of the extra features later interfaces of DirectDraw provided were not implemented on all (or even hardly any) hardware, so unless you've got a specialized application/hardware, consumer hardware support for things like hardware alpha-blitting with DirectDraw never came to be. (Most features were implemented for Direct3D instead.)

  • Direct3D version 1 (DirectX3) -- this API was pretty much unusable until Direct3D2 (DirectX5) which added DrawPrimitive (OpenGL-style) calls. DirectX5 by the way was also on the Win95OSR2 release, and by that time people had really started to buy Windows machines, so I believe DX5 is quite widespread as well as being the first release to really start working like it was supposed to (this is all very scientific, you know). With the newer releases, now up to DirectX8, the interface has become much more flexible and has stripped alot of the butt-ugly setup code overhead that used to be necessary and you've got all the latest vertex shader & multi-texture features at your disposal -- at the expense of a smaller pre-installed user-base. Also keep in mind that the later the more recent the feature is, the more likely it is to be emulated in software on older hardware. Just be sure not to cut out the low-end, especially if you're not working on a AAA retail game with a multi-million dollar budget :) In any case, if you must use DirectX8 and all the latest flashy gadgets, you can always provide the DX setup utility with your game, or tell the user where to get it. DirectX 6 or DirectX 7 may be a good compromise for you as well.

    One final comment I might make about using Direct3D: this is probably the most compelling reason to use DirectX at all instead of using pure SDL/OpenGL. Direct3D (at least in my experience) tends to behave itself better on some cards (provided your app handles that cards capabilities properly) than OpenGL. In addition, a few things like swapping between fullscreen and the desktop, and synchronized page flipping are just better-handled with DirectDraw/Direct3D than OpenGL (which is partly due to the way Microsoft handles OpenGL, and partly due to some of the limits of a more hardware-abstracted 3D API). DirectDraw also provides access to the frame buffer, which is not accessible via SDL while OpenGL is running, should that be an issue. D3D also allows your app to load textures in their native format, theoretically allowing faster mixed-mode rendering. Of course, depending on your app/hardware, you may find OpenGL preferrable (it's certainly less work to port!). Many Windows games these days provide both OpenGL and Direct3D support.

  • Old Documentation can be hard to find. I have some on my resources page, however Microsoft seems to do a pretty good job of obfuscating it on their website. To constrain your usage of an interface to a certain version, use:
    #define DIRECTDRAW_VERSION 0x0500
    #define DIRECT3D_VERSION 0x0500
    ... for each DirectX component you're using, before including any DirectX headers. Note that the number refers to the version of DirectX for that API rather than the numbered version of the API. (Microsoft now synchronizes all version numbering for all DirectX interfaces.)

Roughing it

When you use static-link libraries for DirectX, they do not contain the actual DirectX functions; they only allow the linker to resolve dependencies at compile time, and Windows then searches for and loads the DLL is loaded at runtime. Keep in mind that any of the static functions that you link to, like DirectDrawCreate() need to be resolved at runtime when the dll is loaded. If the dll is missing, or is missing the function you called, your app will bail before it can start.

If, for whatever reason, you want to control this behaviour from your app, I would instead recommend using the Win32 functions LoadLibrary() and GetProcAddr() on the DLLs directly. This reduces your link requirements down to only dxguid.lib (or libdxguid.a), which does not require run-time binding. This allows you to test, for example, the presence of Direct3D2, and if it fails, resort to, say, OpenGL, or starts the DirectX installer, etc. This is a fair amount of work however. Most people can simply warn that the user "must install the latest DirectX runtimes, yadda yadda".


Final Notes

You will quickly discover that if you're attempting to use anything COM (like DirectX), you will need to add the -fvtable-thunks option to your compile commands. Using the MinIDE, you can set this from the Options->Target Switches dialog box. On the General tab, add -fvtable-thunks to the 'Other GCC options' text field. Important Gotcha: if you use the -fvtable-thunks flag to compile a static library, be sure to use that same compile option for anything you want to link to it! Otherwise the virtual function tables will get messed up, the linker won't warn you of this and all of your virtual function calls will fail badly.

originally posted on spacejack.org: 2001-06-06
edited: 2001-06-10
edited: 2001-07-30
edited: 2001-09-05
edited: 2001-09-18

MinGW Starter

This article is targeted toward C and C++ programmers, particularly Windows programmers familiar with other Windows compilers, who would like to use MinGW (Minimalist Gnu Compiler for Windows), the port of GCC, for application development on a Windows PC. I also make few related tool recommendations, keeping in mind that everyone has different tastes or needs. If you don't know what MinGW or GCC is, then you should probably visit the above links first. Most of the resources I describe are listed or linked to on the mingw.org page, but I have narrowed down the scope considerably to the tools I have found to work best for me.

What we want:
  • A modern C++ compliant compiler with solid Standard Library support.
  • A code editor and makefile tools or an IDE.
  • The ability to build console, Windowed, SDL and DirectX and OpenGL apps.
What to get:
  • Download the latest MinGW release from the MinGW home page. If you download a zip file, be sure to check that there is a root MinGW folder in the zip file before extracting; if there is not you should specify one for extraction (eg: C:\mingw). You can freely rename or move this top-level directory, provided you update your system's path environment variable (on Windows 2000, right-click My Computer --> Properties --> Advanced --> Environment Variables). For our example, we'd insert: C:\mingw\bin, preferrably at the beginning of the path. See the install instructions with MinGW for more.

    Before continuing, you may wish to experiment a little with your MinGW installation. I found Colin Peters introduction invaluable. It shows you a simple HelloWindows.exe and explains how to build various types of binaries. Highly recommended first steps. [Note: Colin's site seems to have disappeared for now, but you can find the content mirrored here.]

  • You will also want a good Text Editor/IDE (unless you're an emacs fiend, in which case you've probably made up your mind already :). An extensive list of free editors and IDEs can be found on devzoo.com. Some are definitely better than others. Of note, Dev C++ is an ambitious effort to provide a full development suite for MinGW. There is also a make utility called Jam which greatly simplifies the task of creating makefiles and managing dependencies. It is not an IDE, but it does allow you to create makefiles for large/complex projects with very simple, concise commands. This may appeal to some people better than a GUI tool. Cygwin, which provides a Unix environment on Windows can also be rigged up as an IDE/development environment in a number of ways (Unix is, after all, a developer's platform). See the Cygwin section below for more.

    My favourite editor is TextPad, a fully-functional shareware text editor for Windows, but it is very reasonably priced and is an excellent editor for C, C++, HTML, Perl, Java, etc. One of the better free text editors I've come across is the Programmers File Editor.

    If you don't want to write makefiles by hand, Rainer Schnitker has a simple IDE to work in conjunction with MinGW.

    There's also the general-purpose IDE Eclipse. I actually haven't used Eclipse with MinGW yet, but there is an article on IBM's site with more information.

  • You should also be aware of STLport. (Pure C coders can skip this, as can those who do not wish to use the STL or would rather use the default headers provided with MinGW.) STLport boasts some very efficient statistics and is ported to many different platforms/compilers, so if it's relevant, or if you just like to tinker, give it a shot. If you do, or if you plan to try compiling other 3rd party libraries or applications from source, be sure to read the following section. Some makefiles may require a "posix shell" in order to build with mingw.

  • MSYS is a lightweight posix shell for Win32, also available at the mingw.org site. This will likely allow you to use makefiles and/or run configuration scripts in order to compile projects ported from Unix (or Unix variants) and allows the posix style path separator character '/' to work with Win32 apps run from the MSYS command line. MSYS's installer will easily allow you to configure it to work with your MinGW installation. Or you can try...

CygWin
  • If you are primarily a Windows coder and are not very familiar with Unix or Linux but are interested in learning more, I would recommend installing CygWin. CygWin is kind of like a Unix emulator for your Windows machine but it is also capable of running Windows applications (like MinGW) from the command-line. CygWin is often necessary to use in conjunction with MinGW when you are attempting to build libraries from makefiles, typically programs that have been ported from *nix. make is capable of starting other executables, and *nix developers often include *nix-specific calls to programs that don't exist on Windows. Installing CygWin usually makes the required apps available; you simply run 'make' from CygWin's command line.

    In order to get MinGW to work properly with CygWin, you need to make one change to CygWin's own $PATH variable. CygWin's path is prepended your Windows path so CygWin will find its own native executables before Windows (generally, Unix and Windows programs on the path are named differently, but not always). If you installed CygWin into C:\cygwin, open the file C:\cygwin\etc\profile in a text editor. If you installed mingw into c:\mingw, then insert: /cygdrive/c/mingw/bin: at the beginning of the path string (look for the line $PATH="...). 'cygdrive' refers to the root level of your Windows system, outside of the Unix environment. Save this file after making the change. If you have any CygWin windows open, close them. From now on, CygWin will find mingw's binaries before its own. You can test this by starting up a new CygWin command-line window and typing gcc -print-libgcc-file-name. This will output c:/mingw/bin/../lib/gcc-lib/mingw32/2.95.3-6/libgcc.a (assuming you installed mingw to c:/mingw).

    Note: I believe that Cygwin now includes MinGW as an optional install component. You may wish to check to see that it includes the version of MinGW that you want before using it. If you do install Cygwin's MinGW, then take care to distinguish between the Cygwin version of MinGW, Cygwin's GCC and the Win32 standalone MinGW in your $PATH variables, by placing the one you want to use first (and/or omitting the others).

    Cygwin it is a heck of a lot more than just a tool to get GCC makefiles to compile with mingw. It is an entire Unix environment that brings with it editors like emacs and vi which have extensive programming syntax files, as well as various shells like ksh which are designed for coders. Basically, you can tailor your development environment with a myriad of tools.
Linkage Issues:
  • Another point to clear up: .a files are understood as library (actually archive) files by MinGW. This is all explained in the documentation, however it is worth noting that ld, the linker (called implicitly by g++) can link just about any type of file to build a target. However, the naming convention "lib*.a" for static libraries allows you to specify a search path for static link libraries. That is; if your library is named "libfoo.a" for example, then it can exist anywhere in the path specified by the -L flag, and should then be specified as -lfoo. If you are linking to a library named "foo.lib" however, you will need to type the full path and filename for this, and every other .lib (i.e., not .a) file.

Other Notes:
  • Exception handling is enabled by default, which will make your resulting binaries larger. You can turn off exception handling with the -fno-exceptions flag (unless of course you want to use exceptions). You can further reduce the size of your exe (but not by much) by disabling RTTI. Use -fno-rtti, however this prohibits the use of exception handling as well as run-time type identification features like dynamic_cast. There is a lot of other trickery one can do with template instantiation flags as to avoid redundant code generation, however I haven't gotten much into that yet.

  • Finally, remember to use the -s flag to strip symbols from your final exe.

Well, that wraps up my MinGW introduction. You can continue on by reading about using MinGW with SDL or MinGW with DirectX.


originally posted on spacejack.org: 2001-06-06
edited: 2001-06-07
edited: 2001-06-10
edited: 2001-06-25
edited: 2001-07-28
edited: 2001-07-31
edited: 2002-07-07
edited: 2002-11-04
edited: 2005-07-04

Welcome

I will be moving my MinGW Starter Guide articles to Blogger in the near future.

Watch for them to appear here soon...