Difference between revisions of "Porting SDL applications to OS/2"
m |
m |
||
(4 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | + | == Requirements == | |
+ | To port [[SDL]] applications to OS/2, you'll need the latest SDL binaries and header files from netlabs.org. Currently the [[OpenWatcom]] compiler is '''the''' compiler for the OS/2 port of SDL, but it should also work with [[GCC]]. | ||
− | + | == Using the OpenWatcom compiler == | |
− | + | There are some requirements and tricks with using the OpenWatcom compiler to port SDL applications. You should note the followings things. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | There are some requirements and tricks with using the OpenWatcom compiler | + | |
− | to port SDL applications. You should note the followings things. | + | |
==== Use the -ei switch for the OpenWatcom C compiler ==== | ==== Use the -ei switch for the OpenWatcom C compiler ==== | ||
− | + | You have to use the '''-ei''' switch for the OpenWatcom C compiler to have the size of enums equal to the size of ints. This is a requirement of SDL itself. | |
− | You have to use the | + | |
− | have the size of enums equal to the size of ints. This is a requirement of | + | |
− | SDL itself. | + | |
==== Use the -5s switch for the OpenWatcom C compiler ==== | ==== Use the -5s switch for the OpenWatcom C compiler ==== | ||
+ | Starting with the build of 2005.03.30. of SDL/2, the calling convention has been changed to be stack-based (switch <i>-5s</i>) instead of the previous register-based one (switch ''-5r'' or simply ''-5''). | ||
− | + | Although the register-based version was faster (in theory), the calling convention has been changed to be compatible with GCC, so people using GCC can use SDL.DLL without problems, even where SDL calls into the GCC code (e.g. audio mixer callback), because now both of them use the very same convention. | |
− | changed to be | + | |
− | + | ||
− | + | So, don't forget to use the ''-5s'' switch for the OpenWatcom C compiler if you use that to compile your SDL application! | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | So, don't forget to use the | + | |
− | that to compile your SDL application! | + | |
==== Wrapping the SDL_Quit() function ==== | ==== Wrapping the SDL_Quit() function ==== | ||
− | + | The OS/2 version of SDL is compiled into a DLL file. This DLL file has all the SDL functions exported by name. In order to be able to use this DLL from most of the available compilers, these exports are using the ''_Syscall'' calling convention. This leads to problems with some SDL apps, where they assume that the calling convention of the API is the same as the calling convention of their runtime library. For example, the following line is very usual in SDL applications: | |
− | The OS/2 version of SDL is compiled into a DLL file. This DLL file has all | + | |
− | the SDL functions exported by name. In order to be able to use this DLL from | + | |
− | most of the available compilers, these exports are using the | + | |
− | calling convention. This leads to problems with some SDL apps, where they assume | + | |
− | that the calling convention of the API is the same as the calling convention of | + | |
− | their runtime library. For example, the following line is very usual in SDL | + | |
− | applications: | + | |
− | + | ||
... | ... | ||
atexit(SDL_Quit); | atexit(SDL_Quit); | ||
... | ... | ||
− | + | This should be worked around by creating a wrapper function for SDL_Quit, and using that one, this way: | |
− | This should be worked around by creating a wrapper function for SDL_Quit, and | + | |
− | using that one, this way: | + | |
− | + | ||
#ifdef __WATCOMC__ | #ifdef __WATCOMC__ | ||
void SDL_Quit_Wrapper() | void SDL_Quit_Wrapper() | ||
Line 61: | Line 34: | ||
#endif; | #endif; | ||
− | + | == Using the GNU C Compiler == | |
− | + | It was reported that some (or maybe all) of the GNU C compilers (GCC) available for OS/2 can not handle the SDL.LIB file included in the SDL DevPack. The problem is that the SDL.LIB file was created directly from the SDL.DLL file, using the IMPLIB.EXE tool from the OS/2 Developer's Toolkit, and the GCC does not recognize it to be a valid library file. | |
− | It was reported that some (or maybe all) of the GNU C compilers | + | |
− | (GCC) available for OS/2 can not handle the SDL.LIB file included in | + | |
− | the SDL DevPack. The problem is that the SDL.LIB file was created | + | |
− | directly from the SDL.DLL file, using the IMPLIB.EXE tool from the | + | |
− | OS/2 Developer's Toolkit, and the GCC does not recognize it to be a | + | |
− | valid library file. | + | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
+ | There is a workaround, which is reported to work in this case. One has to create a GCC-compatible library files (.a .lib), using the EMXIMP tool, and link against that file: | ||
emximp -o sdl.imp sdl.lib | emximp -o sdl.imp sdl.lib | ||
mv sdl.lib sdl.lib.owc | mv sdl.lib sdl.lib.owc | ||
emximp -o sdl.a sdl.imp | emximp -o sdl.a sdl.imp | ||
emximp -o sdl.lib sdl.imp | emximp -o sdl.lib sdl.imp | ||
− | |||
Don't forget to use the correct EMXIMP tool distributed with the libc that you are using. | Don't forget to use the correct EMXIMP tool distributed with the libc that you are using. | ||
− | If you plan on using SDL's multithreading capabilities (e.g. if you want | + | If you plan on using SDL's multithreading capabilities (e.g. if you want to include "SDL_thread.h") with the older EMX libc, be sure that enable multithreading with -Zmt on your GCC command line, otherwise you'll get an error because _beginthread() and _endthread() will not be defined. This is not needed with kLIBC as all the libraries are multithreaded but is harmless if used (silently ignored). |
− | to include "SDL_thread.h") with the older EMX libc, be sure that enable multithreading with -Zmt on your GCC | + | |
− | and _endthread() will not be defined. This is not needed with kLIBC as all the libraries are multithreaded but is harmless if used (silently ignored) | + | |
− | + | ||
− | + | ||
+ | == General tips and tricks == | ||
==== Configure scripts and SDL ==== | ==== Configure scripts and SDL ==== | ||
− | + | For configure scripts to find and use SDL you need to have a sdl-config script in your path and/or a pkg-config file in your %PKG_CONFIG_PATH%, e.g. set PKG_CONFIG_PATH=i:/usr/local/lib/pkgconfig;%PKG_CONFIG_PATH% | |
− | For configure scripts to find and use SDL you need to have a sdl-config script in your path and/or a pkg-config file in your %PKG_CONFIG_PATH%, | + | |
For example this is the sdl-config I use (note static linking does not work.) Adjust prefix to reflect your setup. | For example this is the sdl-config I use (note static linking does not work.) Adjust prefix to reflect your setup. | ||
− | |||
#!/bin/sh | #!/bin/sh | ||
prefix=/usr/local | prefix=/usr/local | ||
Line 157: | Line 115: | ||
And this is an example pkg-config file, sdl.pc. Adjust prefix to reflect your install. | And this is an example pkg-config file, sdl.pc. Adjust prefix to reflect your install. | ||
− | |||
# sdl pkg-config source file | # sdl pkg-config source file | ||
Line 166: | Line 123: | ||
Name: sdl | Name: sdl | ||
− | Description: Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. | + | Description: Simple DirectMedia Layer is a cross-platform multimedia library designed to provide |
+ | low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and | ||
+ | 2D video framebuffer. | ||
Version: 1.2.13 | Version: 1.2.13 | ||
Requires: | Requires: | ||
Line 175: | Line 134: | ||
==== Configure can't link SDL_Init ==== | ==== Configure can't link SDL_Init ==== | ||
− | |||
Some configure scripts fail to detect SDL as the configure script creates a program linked to SDL then fail with an error related to not finding _SDL_Init (look in config.log) | Some configure scripts fail to detect SDL as the configure script creates a program linked to SDL then fail with an error related to not finding _SDL_Init (look in config.log) | ||
One workaround is to create the import libs this way | One workaround is to create the import libs this way | ||
− | |||
emximp -o SDL12.def SDL12.dll | emximp -o SDL12.def SDL12.dll | ||
− | + | Then edit the def adding "_SDL_Init"="SDL_Init" just below the SDL_Init line. The run | |
− | Then edit the def adding | + | |
− | + | ||
emximp -o SDL12.a SDL12.def | emximp -o SDL12.a SDL12.def | ||
emximp -o SDL12.lib SDL12.def | emximp -o SDL12.lib SDL12.def | ||
− | |||
To create your import libs. Adjust names as needed. | To create your import libs. Adjust names as needed. | ||
==== Convert your program to a PM program ==== | ==== Convert your program to a PM program ==== | ||
− | |||
After compiling running emxbind.exe -e -p execname.exe will convert your program from windowed to PM. A window will open then vanish as your program becomes PM. Stdout can be captured with foo > out.log and Stderr (error messages) can be captured with foo 2> error.log. | After compiling running emxbind.exe -e -p execname.exe will convert your program from windowed to PM. A window will open then vanish as your program becomes PM. Stdout can be captured with foo > out.log and Stderr (error messages) can be captured with foo 2> error.log. | ||
==== Use a DEF file at linking time ==== | ==== Use a DEF file at linking time ==== | ||
− | |||
If during the linking stage you supply the linker (either LD or link386) with a DEF file the program will become a PM app while still leaving the console open and displaying messages. | If during the linking stage you supply the linker (either LD or link386) with a DEF file the program will become a PM app while still leaving the console open and displaying messages. | ||
A simple DEF file can just have the line | A simple DEF file can just have the line | ||
− | |||
NAME foo WINDOWCOMPAT | NAME foo WINDOWCOMPAT | ||
− | + | Where foo will be the window title. Of course replace foo with the program name. The EMX docs has more info on what can go into a DEF file e.g. description | |
− | Where foo will be the window title. Of course replace foo with the program name. | + | |
− | The EMX docs has more info on what can go into a DEF file | + | |
==== Morph your program to a PM program at runtime ==== | ==== Morph your program to a PM program at runtime ==== | ||
− | + | Most SDL applications assume that they can write to STDOUT, and the user will see it. In OS/2, you have to compile your application to a PM application in order to be able to have a PM Window, but the STDOUT will go to NULL in this case. The solution is to compile your code to a VIO application, and morph the application to a PM one dynamically, at runtime, before calling SDL_Init(). | |
− | Most SDL applications assume that they can write to STDOUT, and the user will | + | |
− | see it. In OS/2, you have to compile your application to a PM application in | + | |
− | order to be able to have a PM Window, but the STDOUT will go to NULL in this | + | |
− | case. The solution is to compile your code to a VIO application, and morph the | + | |
− | application to a PM one dynamically, at runtime, before calling SDL_Init(). | + | |
Once you have your text output back, you might use it for showing debug messages. | Once you have your text output back, you might use it for showing debug messages. | ||
− | If you do so, it's a good idea to make the STDOUT and STDERR unbuffered, so | + | If you do so, it's a good idea to make the STDOUT and STDERR unbuffered, so the debug messages will be shown right at the time when the printf() is called, so every message will be visible even in case of a crash. |
− | the debug messages will be shown right at the time when the printf() is called, | + | |
− | so every message will be visible even in case of a crash. | + | |
− | + | ||
− | + | ||
− | + | ||
+ | Here is an example code on how to morph your VIO application to a PM one dynamically, at runtime, and how to set STDOUT and STDERR unbuffered: | ||
#define INCL_DOS | #define INCL_DOS | ||
#include <os2.h> | #include <os2.h> |
Latest revision as of 11:13, 5 November 2019
Contents
Requirements
To port SDL applications to OS/2, you'll need the latest SDL binaries and header files from netlabs.org. Currently the OpenWatcom compiler is the compiler for the OS/2 port of SDL, but it should also work with GCC.
Using the OpenWatcom compiler
There are some requirements and tricks with using the OpenWatcom compiler to port SDL applications. You should note the followings things.
Use the -ei switch for the OpenWatcom C compiler
You have to use the -ei switch for the OpenWatcom C compiler to have the size of enums equal to the size of ints. This is a requirement of SDL itself.
Use the -5s switch for the OpenWatcom C compiler
Starting with the build of 2005.03.30. of SDL/2, the calling convention has been changed to be stack-based (switch -5s) instead of the previous register-based one (switch -5r or simply -5).
Although the register-based version was faster (in theory), the calling convention has been changed to be compatible with GCC, so people using GCC can use SDL.DLL without problems, even where SDL calls into the GCC code (e.g. audio mixer callback), because now both of them use the very same convention.
So, don't forget to use the -5s switch for the OpenWatcom C compiler if you use that to compile your SDL application!
Wrapping the SDL_Quit() function
The OS/2 version of SDL is compiled into a DLL file. This DLL file has all the SDL functions exported by name. In order to be able to use this DLL from most of the available compilers, these exports are using the _Syscall calling convention. This leads to problems with some SDL apps, where they assume that the calling convention of the API is the same as the calling convention of their runtime library. For example, the following line is very usual in SDL applications:
... atexit(SDL_Quit); ...
This should be worked around by creating a wrapper function for SDL_Quit, and using that one, this way:
#ifdef __WATCOMC__ void SDL_Quit_Wrapper() { SDL_Quit(); } #endif ... #ifdef __WATCOMC__ atexit(SDL_Quit_Wrapper); #else atexit(SDL_Quit); #endif;
Using the GNU C Compiler
It was reported that some (or maybe all) of the GNU C compilers (GCC) available for OS/2 can not handle the SDL.LIB file included in the SDL DevPack. The problem is that the SDL.LIB file was created directly from the SDL.DLL file, using the IMPLIB.EXE tool from the OS/2 Developer's Toolkit, and the GCC does not recognize it to be a valid library file.
There is a workaround, which is reported to work in this case. One has to create a GCC-compatible library files (.a .lib), using the EMXIMP tool, and link against that file:
emximp -o sdl.imp sdl.lib mv sdl.lib sdl.lib.owc emximp -o sdl.a sdl.imp emximp -o sdl.lib sdl.imp
Don't forget to use the correct EMXIMP tool distributed with the libc that you are using.
If you plan on using SDL's multithreading capabilities (e.g. if you want to include "SDL_thread.h") with the older EMX libc, be sure that enable multithreading with -Zmt on your GCC command line, otherwise you'll get an error because _beginthread() and _endthread() will not be defined. This is not needed with kLIBC as all the libraries are multithreaded but is harmless if used (silently ignored).
General tips and tricks
Configure scripts and SDL
For configure scripts to find and use SDL you need to have a sdl-config script in your path and/or a pkg-config file in your %PKG_CONFIG_PATH%, e.g. set PKG_CONFIG_PATH=i:/usr/local/lib/pkgconfig;%PKG_CONFIG_PATH%
For example this is the sdl-config I use (note static linking does not work.) Adjust prefix to reflect your setup.
#!/bin/sh prefix=/usr/local exec_prefix=${prefix} exec_prefix_set=no usage="\ Usage: sdl-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--cflags] [--libs] [--static-libs]" if test $# -eq 0; then echo "${usage}" 1>&2 exit 1 fi while test $# -gt 0; do case "$1" in -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) optarg= ;; esac case $1 in --prefix=*) prefix=$optarg if test $exec_prefix_set = no ; then exec_prefix=$optarg fi ;; --prefix) echo $prefix ;; --exec-prefix=*) exec_prefix=$optarg exec_prefix_set=yes ;; --exec-prefix) echo $exec_prefix ;; --version) echo 1.2.7 ;; --cflags) # echo -Zomf echo -I/usr/local/include/SDL # The portable way of including SDL is #include "SDL.h" #if test ${prefix}/include != /usr/include ; then # # Handle oddities in Win32 path handling (assumes prefix) # prefix=`echo ${prefix} | sed 's,^//\([A-Z]\),\1:,'` # # includes=-I${prefix}/include #fi #echo $includes -I/usr/local/include/SDL -Dmain=SDL_main ;; --libs) echo $libdirs -L/usr/local/lib -lSDL ;; --static-libs) # --libs|--static-libs) libdirs="-L${exec_prefix}/lib " echo $libdirs -lmingw32 -lSDLmain -lSDL -mwindows -luser32 -lgdi32 -lwinmm -ldxguid ;; *) echo "${usage}" 1>&2 exit 1 ;; esac shift done
And this is an example pkg-config file, sdl.pc. Adjust prefix to reflect your install.
# sdl pkg-config source file prefix=/usr/local exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include Name: sdl Description: Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. Version: 1.2.13 Requires: Conflicts: Libs: -L${libdir} -lSDL Libs.private: -lSDL -lm Cflags: -I${includedir}/SDL -D_GNU_SOURCE=1
Configure can't link SDL_Init
Some configure scripts fail to detect SDL as the configure script creates a program linked to SDL then fail with an error related to not finding _SDL_Init (look in config.log)
One workaround is to create the import libs this way
emximp -o SDL12.def SDL12.dll
Then edit the def adding "_SDL_Init"="SDL_Init" just below the SDL_Init line. The run
emximp -o SDL12.a SDL12.def emximp -o SDL12.lib SDL12.def
To create your import libs. Adjust names as needed.
Convert your program to a PM program
After compiling running emxbind.exe -e -p execname.exe will convert your program from windowed to PM. A window will open then vanish as your program becomes PM. Stdout can be captured with foo > out.log and Stderr (error messages) can be captured with foo 2> error.log.
Use a DEF file at linking time
If during the linking stage you supply the linker (either LD or link386) with a DEF file the program will become a PM app while still leaving the console open and displaying messages. A simple DEF file can just have the line
NAME foo WINDOWCOMPAT
Where foo will be the window title. Of course replace foo with the program name. The EMX docs has more info on what can go into a DEF file e.g. description
Morph your program to a PM program at runtime
Most SDL applications assume that they can write to STDOUT, and the user will see it. In OS/2, you have to compile your application to a PM application in order to be able to have a PM Window, but the STDOUT will go to NULL in this case. The solution is to compile your code to a VIO application, and morph the application to a PM one dynamically, at runtime, before calling SDL_Init().
Once you have your text output back, you might use it for showing debug messages. If you do so, it's a good idea to make the STDOUT and STDERR unbuffered, so the debug messages will be shown right at the time when the printf() is called, so every message will be visible even in case of a crash.
Here is an example code on how to morph your VIO application to a PM one dynamically, at runtime, and how to set STDOUT and STDERR unbuffered:
#define INCL_DOS #include <os2.h> #include <stdio.h> ... void MorphToPM() { PPIB pib; PTIB tib; DosGetInfoBlocks(&tib, &pib); // Change flag from VIO to PM: if (pib->pib_ultype==2) pib->pib_ultype = 3; } ... int main(int argc, char *argv[]) { MorphToPM(); // Morph the VIO application to a PM one to be able to use Win* functions // Make stdout and stderr unbuffered setbuf( stdout, NULL ); setbuf( stderr, NULL ); /* Initialize SDL */ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); exit(1); } ...