A collection of ISO C binding interfaces to Simple DirectMedia Layer 2.0 (SDL 2.0), for 2D and 3D game programming in Fortran. SDL versions tested against:
Library | Version |
---|---|
SDL2 | 2.28.2 |
SDL2_image | 2.6.3 |
SDL2_mixer | 2.6.3 |
SDL2_ttf | 2.20.2 |
For bindings to SDL2_gfx, see fortran-sdl2_gfx.
If not present already, install SDL 2.0 with development headers (and optionally: SDL_image 2.0, SDL_mixer 2.0, and/or SDL_ttf 2.0). On FreeBSD, run:
# pkg install devel/sdl20 graphics/sdl2_image audio/sdl2_mixer graphics/sdl2_ttf
Either use GNU/BSD make, xmake to build fortran-sdl2, or the Fortran Package Manager (fpm).
Run make sdl2
to compile the static library libsdl2.a
:
$ git clone --depth 1 https://github.com/interkosmos/fortran-sdl2
$ cd fortran-sdl2/
$ make sdl2
On Microsoft Windows, you have to set LIBGL
and LIBGLU
:
$ make all LIBGL=-lopengl32 LIBGLU=-lglu32
On macOS, replace -lGL -lGLU
with -framework OpenGL
. You can override the
default compiler (gfortran
) by passing the FC
argument, for example:
$ make all FC=/opt/intel/bin/ifort
Link your Fortran project with libsdl2.a
and (optionally) libsdl2_*.a
, or
simply with libfortran-sdl2.a
:
Library | Compilation | Linking |
---|---|---|
SDL2 | make sdl2 |
libsdl2.a -lSDL2 |
SDL2_image | make sdl2_image |
libsdl2.a libsdl2_image.a -lSDL2 -lSDL2_image |
SDL2_mixer | make sdl2_mixer |
libsdl2.a libsdl2_mixer.a -lSDL2 -lSDL2_mixer |
SDL2_ttf | make sdl2_ttf |
libsdl2.a libsdl2_ttf.a -lSDL2 -lSDL2_ttf |
all | make all |
libfortran-sdl2.a -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf |
Build all static libraries with:
$ xmake
Build a particular library with:
$ xmake build <name>
The default output directory is build/
.
The following example shows how to fill a rectangle, using the hardware renderer.
! example.f90
program main
use, intrinsic :: iso_c_binding, only: c_associated, c_null_char, c_ptr
use, intrinsic :: iso_fortran_env, only: stdout => output_unit, stderr => error_unit
use :: sdl2
implicit none
integer, parameter :: SCREEN_WIDTH = 640
integer, parameter :: SCREEN_HEIGHT = 480
type(c_ptr) :: window
type(c_ptr) :: renderer
type(sdl_event) :: event
type(sdl_rect) :: rect
integer :: rc
logical :: is_running
! Initialise SDL.
if (sdl_init(SDL_INIT_VIDEO) < 0) then
write (stderr, '("SDL Error: ", a)') sdl_get_error()
stop
end if
! Create the SDL window.
window = sdl_create_window('Fortran SDL 2.0' // c_null_char, &
SDL_WINDOWPOS_UNDEFINED, &
SDL_WINDOWPOS_UNDEFINED, &
SCREEN_WIDTH, &
SCREEN_HEIGHT, &
SDL_WINDOW_SHOWN)
if (.not. c_associated(window)) then
write (stderr, '("SDL Error: ", a)') sdl_get_error()
stop
end if
! Create the renderer.
renderer = sdl_create_renderer(window, -1, 0)
! Set position and size of the rectangle.
rect = sdl_rect(50, 50, 250, 250)
! Event loop.
is_running = .true.
do while (is_running)
! Catch events.
do while (sdl_poll_event(event) > 0)
select case (event%type)
case (SDL_QUITEVENT)
is_running = .false.
end select
end do
! Fill screen black.
rc = sdl_set_render_draw_color(renderer, &
uint8(0), &
uint8(0), &
uint8(0), &
uint8(SDL_ALPHA_OPAQUE))
rc = sdl_render_clear(renderer)
! Fill the rectangle.
rc = sdl_set_render_draw_color(renderer, &
uint8(127), &
uint8(255), &
uint8(0), &
uint8(SDL_ALPHA_OPAQUE))
rc = sdl_render_fill_rect(renderer, rect)
! Render to screen and wait 20 ms.
call sdl_render_present(renderer)
call sdl_delay(20)
end do
! Quit gracefully.
call sdl_destroy_renderer(renderer)
call sdl_destroy_window(window)
call sdl_quit()
end program main
To compile the source code with GNU Fortran, run:
$ gfortran `sdl2-config --cflags` -o example example.f90 libsdl2.a `sdl2-config --libs`
Some demo applications can be found in examples/
:
- alpha makes one color of an image transparent (software renderer).
- cyclic implements a 2D cyclic cellular automaton (hardware renderer).
- draw draws some shapes (hardware renderer).
- dvd loads a PNG file with SDL_image and lets it bounce on the screen (hardware renderer).
- events polls SDL events (software renderer).
- fire renders the DOOM fire effect (hardware renderer).
- forest implements a cellular automaton, based on the forest fire model (hardware renderer).
- gl renders a triangle with OpenGL 1.3.
- gl3d rotates textured cubes with OpenGL 1.3.
- glsphere rotates the camera around GLU spheres.
- image loads and displays an image (software renderer).
- info prints debug information to console (software renderer).
- log prints log messages with
SDL_Log()
(software renderer). - logo lets you fly through a field of Fortran logos (hardware renderer).
- msgbox shows a simple message box (software renderer).
- opera plays an OGG file with SDL_mixer (software renderer).
- pixel copies an SDL_Surface to an SDL_Texture pixelwise (hardware renderer).
- scaling displays a scaled image (software renderer).
- text outputs text with SDL_ttf (hardware renderer).
- vertex shows geometry renderer of SDL 2.0.18.
- voxel renders a voxel space with direct pixel manipulation. Use arrow keys and Q, A for camera movement (hardware renderer).
- window opens a window and fills rectangles (software renderer).
Compile all examples with:
$ make examples
If you prefer xmake, build and run an example with:
$ xmake build <name>
$ xmake run <name>
To compile all examples, simply run:
$ xmake build examples
All Fortran interface names are written in snake case. For instance,
SDL_CreateWindow()
can be accessed by calling Fortran interface
sdl_create_window()
. The same is valid for derived types and their
components. Enums and constants have kept their original names.
A c_null_char
must be appended to all strings passed to the interfaces,
except sdl_set_hint()
and sdl_log*()
, which are wrappers that terminate the
arguments for convenience.
SDL 2.0 stores RGB colour values as Uint8
. As Fortran does not feature unsigned
types, the intrinsic procedure transfer()
has to be used to transfer bit
patterns directly. For example:
type(sdl_color) :: color
color = sdl_color(r = transfer([255, 1], 1_c_int8_t)
g = transfer([127, 1], 1_c_int8_t)
b = transfer([ 0, 1], 1_c_int8_t)
a = transfer([SDL_ALPHA_OPAQUE, 1], 1_c_int8_t))
The Fortran bindings provide a utility function uint8()
that simplifies the
conversion:
color = sdl_color(r = uint8(255), &
g = uint8(127), &
b = uint8(0), &
a = uint8(SDL_ALPHA_OPAQUE))
C pointers in derived types like sdl_surface
must be converted to Fortran
types manually by calling the intrinsic procedure c_f_pointer()
. For instance,
to assign the sdl_pixel_format
pointer in sdl_surface
:
type(sdl_pixel_format), pointer :: pixel_format
type(sdl_surface), pointer :: surface
! Convert C pointer to Fortran pointer.
call c_f_pointer(surface%format, pixel_format)
The C struct SDL_Surface
stores RGB pixel values as Uint8
. Use transfer()
and ichar()
to convert Uint8
to Fortran signed integer. For example:
integer, parameter :: X = 10
integer, parameter :: Y = 20
integer(kind=int8) :: r, g, b
integer(kind=c_int32_t) :: pixel
integer(kind=c_int8_t), pointer :: pixels(:)
type(sdl_pixel_format), pointer :: pixel_format
type(sdl_surface), pointer :: surface
! Load BMP file into SDL_Surface.
surface => sdl_load_bmp('image.bmp' // c_null_char)
! Get SDL_PixelFormat.
call c_f_pointer(surface%format, pixel_format)
! Get Fortran array of pixel pointers.
call c_f_pointer(surface%pixels, pixels, shape=[surface%pitch * surface%h])
! Get single pixel of coordinates X and Y. Convert to Fortran integer.
pixel = ichar(transfer(pixels((Y - 1) * surface%pitch + X), 'a'), kind=c_int32_t)
! Get RGB values of pixel.
call sdl_get_rgb(pixel, pixel_format, r, g, b)
Only a single string message can be passed to sdl_log()
and sdl_log_*()
routines, as Fortran does not support ellipsis arguments. For example:
call sdl_log_debug(SDL_LOG_CATEGORY_TEST, 'debug message')
You may have to write your log message to a string first:
character(len=32) :: msg
integer :: rc
write (msg, '(a, i0)') 'Error: ', rc
call sdl_log(msg)
Log messages will be trimmed and null-terminated by the wrapper routines.
The SDL event SDL_QUIT has
been renamed to SDL_QUITEVENT
in Fortran to avoid conflict with interface
sdl_quit()
.
Generate the source code documentation with
FORD. Add FORD with pip
, for example:
$ python3 -m venv virtual-environment/
$ source virtual-environment/bin/activate
$ python3 -m pip install ford
Or, instead, just install the package in your user directory:
$ python3 -m pip install --user ford
Then, run:
$ ford project.md -d ./src
Open doc/index.html
in a web browser.
See coverage for the current status.
Thanks go to angelog0.
ISC