aboutsummaryrefslogtreecommitdiffstats

ESDL2

SDL2 Erlang NIF.

This project tries to implement SDL2 and some of its extensions in one convenient NIF. The supported versions and features are listed below.

  • SDL >= 2.0.5

  • SDL_image 2.0.3

  • SDL_ttf 2.0.14

The implementation is cut into sections corresponding to the public headers.

Fully implemented

  • SDL.h

  • SDL_blendmode.h

  • SDL_clipboard.h

  • SDL_cpuinfo.h

  • SDL_filesystem.h

  • SDL_keyboard.h

  • SDL_keycode.h

  • SDL_mouse.h

  • SDL_platform.h

  • SDL_power.h

  • SDL_rect.h

  • SDL_scancode.h

Partially implemented

  • SDL_events.h: The following events and functions are missing:

    • SDL_SYSWMEVENT

    • SDL_TEXTEDITING

    • SDL_TEXTINPUT

    • SDL_JOYAXISMOTION

    • SDL_JOYBALLMOTION

    • SDL_JOYHATMOTION

    • SDL_JOYBUTTONDOWN

    • SDL_JOYBUTTONUP

    • SDL_JOYDEVICEADDED

    • SDL_JOYDEVICEREMOVED

    • SDL_CONTROLLERAXISMOTION

    • SDL_CONTROLLERBUTTONDOWN

    • SDL_CONTROLLERBUTTONUP

    • SDL_CONTROLLERDEVICEADDED

    • SDL_CONTROLLERDEVICEREMOVED

    • SDL_CONTROLLERDEVICEREMAPPED

    • SDL_FINGERDOWN

    • SDL_FINGERUP

    • SDL_FINGERMOTION

    • SDL_DOLLARGESTURE

    • SDL_DOLLARRECORD

    • SDL_MULTIGESTURE

    • SDL_DROPFILE

    • SDL_DROPTEXT

    • SDL_DROPBEGIN

    • SDL_DROPCOMPLETE

    • SDL_AUDIODEVICEADDED

    • SDL_AUDIODEVICEREMOVED

    • SDL_PeepEvents with action argument SDL_ADDEVENT

    • SDL_PushEvents

    • SDL_SetEventFilter

    • SDL_GetEventFilter

    • SDL_AddEventWatch

    • SDL_DelEventWatch

    • SDL_FilterEvents

    • SDL_EventState and SDL_GetEventState

    • SDL_RegisterEvents

  • SDL_hints.h: We only have a proof of concept callback system.

  • SDL_pixels.h: Only the pixel format enum and conversion function is defined.

  • SDL_render.h: The following elements are missing:

    • SDL_TextureAccess enum

    • SDL_TextureModulate enum

    • SDL_CreateWindowAndRenderer is currently located in sdl_window. Move it?

    • SDL_CreateSoftwareRenderer (renderer)

    • SDL_CreateTexture (texture)

    • SDL_QueryTexture (texture)

    • SDL_UpdateTexture (texture)

    • SDL_UpdateYUVTexture (texture)

    • SDL_LockTexture (texture)

    • SDL_UnlockTexture (texture)

    • SDL_SetRenderTarget (renderer)

    • SDL_GetRenderTarget (renderer)

    • SDL_RenderIsClipEnabled (renderer)

    • SDL_RenderReadPixels (renderer)

  • SDL_stdinc.h: Erlang does not come with the functions copysign and scalbn.

  • SDL_surface.h: Only surface creation (via IMG_Load) and destruction is implemented. Might be better to move IMG_* functions in their own space.

    • Everything in this header can be implemented as simple functions (as opposed to cast/call).

    • We’ll need to add functions to retrieve the pixel format and possibly pitch (though format+dimensions is enough to get it).

    • We’ll need to add extra functions for direct access to the pixels.

  • SDL_ttf.h:

    • A function to obtain the SDL_ttf version could be useful

    • TTF_Linked_Version (if it makes sense, depends on how the library is loaded)

    • TTF_OpenFontRW (unclear if we need it)

    • TTF_OpenFontIndexRW (unclear if we need it)

  • SDL_version.h: SDL_GetRevisionNumber must be implemented. The macros may also be useful.

  • SDL_video.h: The following elements are missing:

    • SDL_WINDOWPOS_* values for different displays

    • SDL_GetWindowSurface (window)

    • SDL_UpdateWindowSurface (window)

    • SDL_UpdateWindowSurfaceRects (window)

To be implemented

  • SDL_audio.h

  • SDL_error.h (for completion)

  • SDL_gamecontroller.h

  • SDL_gesture.h

  • SDL_haptic.h

  • SDL_joystick.h

  • SDL_messagebox.h

  • SDL_rwops.h (unclear if we need it)

  • SDL_shape.h

  • SDL_system.h

  • SDL_syswm.h

  • SDL_timer.h (unclear if we need it)

  • SDL_touch.h

Other SDL extensions need to be investigated and implemented. We definitely want at least some of SDL_image and SDL_mixer. We probably do not need SDL_net or SDL_rtf.

OpenGL and Vulkan

For OpenGL we need to figure out whether we can call the functions from wxErlang. If we can, great! If not, find an automated way to provide access to OpenGL.

The following functions are related to OpenGL and Vulkan and still need to be implemented:

  • SDL_render.h:

    • SDL_GL_BindTexture

    • SDL_GL_UnbindTexture

  • SDL_video.h:

    • SDL_GL_LoadLibrary (unclear if we need it)

    • SDL_GL_GetProcAddress (unclear if we need it)

    • SDL_GL_UnloadLibrary (unclear if we need it)

    • SDL_GL_ExtensionSupported

    • SDL_GL_ResetAttributes

    • SDL_GL_SetAttribute

    • SDL_GL_GetAttribute

    • SDL_GL_MakeCurrent

    • SDL_GL_GetCurrentWindow

    • SDL_GL_GetCurrentContext

    • SDL_GL_GetDrawableSize

    • SDL_GL_SetSwapInterval

    • SDL_GL_GetSwapInterval

  • SDL_vulkan.h

To be removed

  • SDL_SetMainReady which has no public interface, only the NIF function.

Don’t implement

These don’t make a lot of sense for Erlang.

  • SDL_assert.h

  • SDL_atomic.h

  • SDL_bits.h

  • SDL_endian.h

  • SDL_events.h: the functions SDL_WaitEvent and SDL_WaitEventTimeout are blocking.

  • SDL_loadso.h

  • SDL_log.h

  • SDL_main.h

  • SDL_mutex.h

  • SDL_quit.h (only necessary when using SDL_Main?)

  • SDL_stdinc.h: only a few functions are implemented, others are not interesting.

  • SDL_thread.h

  • SDL_ttf.h: the rendering functions for Text and UNICODE are not interesting because they internally call the UTF8 functions so we may as well provide utf8-encoded binaries directly. The TTF_ByteSwappedUNICODE also falls in this category and is not provided.

  • SDL_video.h: the functions SDL_CreateWindowFrom, SDL_SetWindowData and SDL_GetWindowData take external data as argument.

Nothing to implement

These are either private headers, duplicated OpenGL/Vulkan headers or simply deprecated.

  • SDL_config.h

  • SDL_config_android.h

  • SDL_config_iphoneos.h

  • SDL_config_macosx.h

  • SDL_config_minimal.h

  • SDL_config_pandora.h

  • SDL_config_psp.h

  • SDL_config_windows.h

  • SDL_config_winrt.h

  • SDL_config_wiz.h

  • SDL_copying.h

  • SDL_egl.h

  • SDL_name.h

  • SDL_opengl.h

  • SDL_opengl_glext.h

  • SDL_opengles.h

  • SDL_opengles2.h

  • SDL_opengles2_gl2.h

  • SDL_opengles2_gl2ext.h

  • SDL_opengles2_gl2platform.h

  • SDL_opengles2_khrplatform.h

  • SDL_revision.h

  • SDL_test.h

  • SDL_test_assert.h

  • SDL_test_common.h

  • SDL_test_compare.h

  • SDL_test_crc32.h

  • SDL_test_font.h

  • SDL_test_fuzzer.h

  • SDL_test_harness.h

  • SDL_test_images.h

  • SDL_test_log.h

  • SDL_test_md5.h

  • SDL_test_memory.h

  • SDL_test_random.h

  • SDL_types.h

  • begin_code.h

  • close_code.h

Thoughts on callbacks

SDL2 has a number of callback interfaces. While we probably do not want to implement some of them (like the OS-specific callbacks for Windows and iOS) we do need others.

Callbacks that have no return value are easy to implement. The idea is to have an Erlang process that waits for messages containing the callback MFA to execute. The following callbacks have no return value and no other caveat:

  • SDL_AudioCallback

  • SDL_iOSSetAnimationCallback (iOS)

  • SDL_SetWindowsMessageHook (Windows)

The callback functions for hints do not have a return value either, but they have an extra caveat: there can be more than one per hint. SDL2 identifies these callbacks with the tuple (callback, userdata) and we need to give SDL2 this same tuple in order to remove the callback.

The best way to handle this is probably to do it mostly via Erlang where a process would take care of the callbacks and would enable/disable the SDL2 callbacks when required. The userdata would in this case always be NULL since all the handling would be done from the Erlang side.

The alternative would be to create a resource per callback that the user would have to keep around and that’s not very convenient.

The functions in question are:

  • SDL_AddHintCallback

  • SDL_DelHintCallback

Other callbacks have a return value but otherwise work in a slightly different manner from each other. The callback can be invoked in a similar manner to others, by sending a message to the Erlang code. The difficulty comes in returning the result to the NIF code. The solution for doing that will vary depending on the callback in question.

There can be one window hit test callback per window. This means we can use the window’s user data for storing the result and then signal the NIF to read from it using the NIF mutex/cond mechanism. Both of those can also be stored in the window’s user data.

This means the window must be sent to Erlang and passed back to the NIF when giving the result back, which should be trivial. The callback data can stay empty since we store everything in the window data.

The function in question is:

  • SDL_SetWindowHitTest

The final set of callbacks is timers. When you add a timer it returns a TimerID and you can use it to remove the timer. In addition, the callback can decide to change the timer interval or to stop the timer. Unlike for windows there is no way to attach information to a TimerID so a separate solution will be necessary. Since there can be any number of timers and they can fire off at any time then some kind of queue will be necessary to store return values.

Even though we don’t know the TimerID when setting up the timer, we should be able to keep it around in the same data structure used for the callback extra parameter. There is however the concern of memory allocation: we will probably need to hook into all functions that can remove timers to make sure we free the memory we allocated too.

They’re the hardest callback functions to implement, but thankfully they’re also some of the least interesting considering Erlang already comes with many ways to deal with timers.

Even if we do implement them, their scope may be reduced so that we always return the same interval as a return value and therefore don’t allow changing the interval or stopping the timer from inside a timer callback.

The functions in question are:

  • SDL_AddTimer

  • SDL_RemoveTimer

Other than hints, it should be possible to have a common mechanism for all callbacks. The following messages may be sent from the NIF:

  • {callback, M, F, Args} for void

  • {callback, M, F, Args, ResF, ResExtraArg} for others

The esdl2:ResF(Result, ResExtraArg) function would be called in the second case after the callback returns. The NIF function can then decide what the appropriate behavior is for sending the result back to the SDL2 callback.

The main concern when dealing with SDL2 callbacks is the memory allocations since SDL2 will not free the memory we allocate. Solutions should be extra careful not to introduce leaks and try to avoid allocating memory entirely for callbacks. When not possible then the memory must be allocated and freed in the course of running the Erlang callback and not be kept any longer.