Re: [AD] SDL2 port

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Oh, and here's the attachment I forgot.

On Sat, Jul 5, 2014 at 9:59 PM, Elias Pschernig
<elias.pschernig@xxxxxxxxxx> wrote:
> I've been working on an SDL2 port for a while (unlike fixing actual
> bugs, this is something I could do with only having 30 minutes or so
> at a time). I started with it a few years ago, my main motivation back
> then was that SDL2 already worked with emscripten and so it would have
> been a way to get Allegro to work in a web browser as well. Of course
> that's not necessary anymore as we do have a patch for that. So now I
> just see this sort of as a reference port, mostly every Allegro
> function will get to call an SDL function without having to actually
> implement anything. And it might be useful for testing - if something
> works with this port but not the "native" Allegro, we know the bug
> must be in how our implementation differs from SDL's.
>
> It's still very incomplete and probably always will be, but I figure
> it wouldn't hurt committing it. It only works on Linux, there's no
> sound (it will use Allegro's audio), no input devices other than
> keyboard/mouse, and no graphics (it will use Allegro's OpenGL driver).
>
> Also, there is one fundamental difference between how Allegro and SDL2
> events works[1]:
>
> SDL_PumpEvents documentation: "This should only be run in the thread
> that initialized the video subsystem, and for extra safety, you should
> consider only doing those things on the main thread in any case. "
>
> So there was no easy way of making the Allegro way work where we can
> receive events even when the user calls no Allegro functions at all. I
> was considering adding an SDL specific function which would call
> SDL_PumpEvents - but then figured we could just as well add a
> mechanism for Allegro to call a system driver function which does
> that. I made it get called from these functions:
>
> sdl_get_keyboard_state
> sdl_get_mouse_state
> al_is_event_queue_empty
> al_get_next_event
> al_peek_next_event
> al_drop_next_event
> al_flush_event_queue
> al_wait_for_event
> al_wait_for_event_timed
> al_wait_for_event_until
>
> So at least in a typical game everything will work without changes. If
> you have a loop without any of these functions (or wait on an event
> queue which has no timer events) the application will freeze however.
>
> My next steps will probably be implementing audio with SDL and getting
> it to run under Windows and OSX (and maybe Android and IOS).
>
> [1] http://wiki.libsdl.org/SDL_PumpEvents
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a66030d..bec512b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ option(WANT_GLES2 "Compile with GLES2 support" ON)
 if(WANT_ANDROID)
     include(${CMAKE_SOURCE_DIR}/cmake/Toolchain-android.cmake)
 endif(WANT_ANDROID)
+option(ALLEGRO_SDL "Build using the SDL backend (experimental)" OFF)
 
 # Set the project name.
 # We use C++ in a few cases.
@@ -314,6 +315,12 @@ if(ALLEGRO_RASPBERRYPI)
     set(ALLEGRO_CFG_PTHREADS_TLS 1)
 endif(ALLEGRO_RASPBERRYPI)
 
+if(ALLEGRO_SDL)
+   set(ALLEGRO_UNIX 0)
+   set(WANT_X11 off)
+   include(FindSDL2)
+endif(ALLEGRO_SDL)
+
 # Tell the compiler it can use SSE instructions on x86 architectures.
 # If compatibility with Pentium 2's and below is required then the user
 # should switch WANT_ALLOW_SSE off.
@@ -808,6 +815,12 @@ foreach(genfile ${ALLEGRO_INCLUDE_ALLEGRO_PLATFORM_FILES_GENERATED})
         )
 endforeach(genfile)
 
+if(ALLEGRO_SDL)
+    list(APPEND LIBRARY_SOURCES ${ALLEGRO_SRC_SDL_FILES})
+    list(APPEND PLATFORM_LIBS ${SDL2_LIBRARIES} pthread dl m)
+    include_directories(${SDL2_INCLUDE_DIR})
+endif(ALLEGRO_SDL)
+
 set_our_header_properties(${ALLEGRO_PUBLIC_HEADERS})
 
 if(NOT WANT_MONOLITH)
diff --git a/cmake/FileList.cmake b/cmake/FileList.cmake
index af72772..ede9c57 100644
--- a/cmake/FileList.cmake
+++ b/cmake/FileList.cmake
@@ -181,6 +181,15 @@ set(ALLEGRO_SRC_RASPBERRYPI_FILES
    src/raspberrypi/pidisplay.c
    )
 
+set(ALLEGRO_SRC_SDL_FILES
+   src/sdl/sdl_system.c
+   src/sdl/sdl_time.c
+   src/sdl/sdl_thread.c
+   src/sdl/sdl_display.c
+   src/sdl/sdl_keyboard.c
+   src/sdl/sdl_mouse.c
+   )
+
 set(ALLEGRO_INCLUDE_ALLEGRO_FILES
     include/allegro5/allegro5.h
     include/allegro5/allegro.h
diff --git a/include/allegro5/internal/aintern_system.h b/include/allegro5/internal/aintern_system.h
index c30a651..4ad698f 100644
--- a/include/allegro5/internal/aintern_system.h
+++ b/include/allegro5/internal/aintern_system.h
@@ -45,6 +45,8 @@ struct ALLEGRO_SYSTEM_INTERFACE
    void *(*open_library)(const char *filename);
    void *(*import_symbol)(void *library, const char *symbol);
    void (*close_library)(void *handle);
+   void (*heartbeat)(void);
+   void (*heartbeat_init)(void);
 };
 
 struct ALLEGRO_SYSTEM
diff --git a/include/allegro5/internal/alconfig.h b/include/allegro5/internal/alconfig.h
index 0835321..3647569 100644
--- a/include/allegro5/internal/alconfig.h
+++ b/include/allegro5/internal/alconfig.h
@@ -47,6 +47,8 @@
    #include "allegro5/platform/alraspberrypicfg.h"
 #elif defined ALLEGRO_UNIX
    #include "allegro5/platform/alucfg.h"
+#elif defined ALLEGRO_SDL
+   #include "allegro5/platform/allegro_sdl_config.h"
 #else
    #error platform not supported
 #endif
diff --git a/include/allegro5/platform/allegro_internal_sdl.h b/include/allegro5/platform/allegro_internal_sdl.h
new file mode 100644
index 0000000..6fa189e
--- /dev/null
+++ b/include/allegro5/platform/allegro_internal_sdl.h
@@ -0,0 +1,41 @@
+#include "SDL.h"
+
+#include "allegro5/internal/aintern_display.h"
+#include "allegro5/internal/aintern_keyboard.h"
+#include "allegro5/internal/aintern_mouse.h"
+#include "allegro5/internal/aintern_bitmap.h"
+#include "allegro5/internal/aintern_system.h"
+
+typedef struct
+{
+   ALLEGRO_SYSTEM system;
+   ALLEGRO_MUTEX *mutex;
+} ALLEGRO_SYSTEM_SDL;
+
+typedef struct ALLEGRO_DISPLAY_SDL
+{
+   ALLEGRO_DISPLAY display; /* This must be the first member. */
+
+   int x, y;
+   SDL_Window *window;
+   char *title;
+   SDL_Renderer *renderer;
+   SDL_GLContext context;
+} ALLEGRO_DISPLAY_SDL;
+
+typedef struct ALLEGRO_SYSTEM_INTERFACE ALLEGRO_SYSTEM_INTERFACE;
+ALLEGRO_SYSTEM_INTERFACE *_al_sdl_system_driver(void);
+ALLEGRO_DISPLAY_INTERFACE *_al_sdl_display_driver(void);
+ALLEGRO_KEYBOARD_DRIVER *_al_sdl_keyboard_driver(void);
+ALLEGRO_MOUSE_DRIVER *_al_sdl_mouse_driver(void);
+ALLEGRO_BITMAP_INTERFACE *_al_sdl_bitmap_driver(void);
+
+void _al_sdl_keyboard_event(SDL_Event *e);
+void _al_sdl_mouse_event(SDL_Event *e);
+void _al_sdl_display_event(SDL_Event *e);
+
+int _al_sdl_get_allegro_pixel_format(int sdl_format);
+int _al_sdl_get_sdl_pixel_format(int allegro_format);
+
+ALLEGRO_DISPLAY *_al_sdl_find_display(uint32_t window_id);
+
diff --git a/include/allegro5/platform/allegro_sdl_config.h b/include/allegro5/platform/allegro_sdl_config.h
new file mode 100644
index 0000000..79e98d1
--- /dev/null
+++ b/include/allegro5/platform/allegro_sdl_config.h
@@ -0,0 +1,8 @@
+#define ALLEGRO_PLATFORM_STR "SDL"
+
+#define ALLEGRO_INTERNAL_HEADER "allegro5/platform/allegro_internal_sdl.h"
+#define ALLEGRO_INTERNAL_THREAD_HEADER "allegro5/platform/allegro_sdl_thread.h"
+
+// FIXME: remove once we don't use Unix specifics anymore
+#include <fcntl.h>
+#include <unistd.h>
diff --git a/include/allegro5/platform/allegro_sdl_thread.h b/include/allegro5/platform/allegro_sdl_thread.h
new file mode 100644
index 0000000..1a4a796
--- /dev/null
+++ b/include/allegro5/platform/allegro_sdl_thread.h
@@ -0,0 +1,101 @@
+#ifndef __al_included_allegro5_allegro_sdl_thread_h
+#define __al_included_allegro5_allegro_sdl_thread_h
+
+#include "SDL.h"
+
+#ifdef __cplusplus
+   extern "C" {
+#endif
+
+
+struct _AL_MUTEX
+{
+   SDL_mutex *mutex;
+   SDL_TLSID lock_count;
+};
+
+struct _AL_THREAD
+{
+   /* private: */
+   SDL_Thread *thread;
+   bool should_stop;
+   void (*proc)(struct _AL_THREAD *self, void *arg);
+   void *arg;
+};
+
+#define _AL_MUTEX_UNINITED	       { NULL }
+#define _AL_MARK_MUTEX_UNINITED(M)     do { M.mutex = NULL; } while (0)
+
+struct _AL_COND
+{
+   SDL_cond *cond;
+};
+
+typedef struct ALLEGRO_TIMEOUT_SDL ALLEGRO_TIMEOUT_SDL;
+struct ALLEGRO_TIMEOUT_SDL
+{
+   int ms;
+};
+
+AL_INLINE(bool, _al_get_thread_should_stop, (struct _AL_THREAD *t),
+{
+    return t->should_stop;
+})
+
+
+AL_FUNC(void, _al_mutex_init, (struct _AL_MUTEX*));
+AL_FUNC(void, _al_mutex_destroy, (struct _AL_MUTEX*));
+AL_INLINE(void, _al_mutex_lock, (struct _AL_MUTEX *m),
+{
+   if (m->lock_count) {
+      int *v = (int *)SDL_TLSGet(m->lock_count);
+      (*v)++;
+      if (*v > 1)
+         return;
+   }
+   if (m->mutex)
+      SDL_LockMutex(m->mutex);
+})
+AL_INLINE(void, _al_mutex_unlock, (struct _AL_MUTEX *m),
+{
+   if (m->lock_count) {
+      int *v = (int *)SDL_TLSGet(m->lock_count);
+      (*v)--;
+      if (*v > 0)
+         return;
+   }
+   if (m->mutex)
+      SDL_UnlockMutex(m->mutex);
+})
+
+AL_INLINE(void, _al_cond_init, (struct _AL_COND *cond),
+{
+   cond->cond = SDL_CreateCond();
+})
+
+AL_INLINE(void, _al_cond_destroy, (struct _AL_COND *cond),
+{
+   SDL_DestroyCond(cond->cond);
+})
+
+AL_INLINE(void, _al_cond_wait, (struct _AL_COND *cond, struct _AL_MUTEX *mutex),
+{
+   SDL_CondWait(cond->cond, mutex->mutex);
+})
+
+AL_INLINE(void, _al_cond_broadcast, (struct _AL_COND *cond),
+{
+   SDL_CondBroadcast(cond->cond);
+})
+
+AL_INLINE(void, _al_cond_signal, (struct _AL_COND *cond),
+{
+   SDL_CondSignal(cond->cond);
+})
+
+
+#ifdef __cplusplus
+   }
+#endif
+
+#endif
diff --git a/include/allegro5/platform/alplatf.h.cmake b/include/allegro5/platform/alplatf.h.cmake
index 01775d1..24831a5 100644
--- a/include/allegro5/platform/alplatf.h.cmake
+++ b/include/allegro5/platform/alplatf.h.cmake
@@ -98,5 +98,8 @@
 /* Define if target platform is linux. */
 #cmakedefine ALLEGRO_LINUX
 
+/* Define if we are building with SDL backend. */
+#cmakedefine ALLEGRO_SDL
+
 /*---------------------------------------------------------------------------*/
 /* vi: set ft=c ts=3 sts=3 sw=3 et: */
diff --git a/src/events.c b/src/events.c
index 3bf5579..0d95a96 100644
--- a/src/events.c
+++ b/src/events.c
@@ -213,13 +213,31 @@ bool al_is_event_queue_paused(const ALLEGRO_EVENT_QUEUE *queue)
 
 
 
+static void heartbeat(void)
+{
+   ALLEGRO_SYSTEM *system = al_get_system_driver();
+   if (system->vt->heartbeat)
+      system->vt->heartbeat();
+}
+
+
+
+static bool is_event_queue_empty(ALLEGRO_EVENT_QUEUE *queue)
+{
+   return (queue->events_head == queue->events_tail);
+}
+
+
+
 /* Function: al_is_event_queue_empty
  */
 bool al_is_event_queue_empty(ALLEGRO_EVENT_QUEUE *queue)
 {
    ASSERT(queue);
 
-   return (queue->events_head == queue->events_tail);
+   heartbeat();
+
+   return is_event_queue_empty(queue);
 }
 
 
@@ -246,7 +264,7 @@ static ALLEGRO_EVENT *get_next_event_if_any(ALLEGRO_EVENT_QUEUE *queue,
 {
    ALLEGRO_EVENT *event;
 
-   if (al_is_event_queue_empty(queue)) {
+   if (is_event_queue_empty(queue)) {
       return NULL;
    }
 
@@ -267,6 +285,8 @@ bool al_get_next_event(ALLEGRO_EVENT_QUEUE *queue, ALLEGRO_EVENT *ret_event)
    ASSERT(queue);
    ASSERT(ret_event);
 
+   heartbeat();
+
    _al_mutex_lock(&queue->mutex);
 
    next_event = get_next_event_if_any(queue, true);
@@ -290,6 +310,8 @@ bool al_peek_next_event(ALLEGRO_EVENT_QUEUE *queue, ALLEGRO_EVENT *ret_event)
    ASSERT(queue);
    ASSERT(ret_event);
 
+   heartbeat();
+
    _al_mutex_lock(&queue->mutex);
 
    next_event = get_next_event_if_any(queue, false);
@@ -312,6 +334,8 @@ bool al_drop_next_event(ALLEGRO_EVENT_QUEUE *queue)
    ALLEGRO_EVENT *next_event;
    ASSERT(queue);
 
+   heartbeat();
+
    _al_mutex_lock(&queue->mutex);
 
    next_event = get_next_event_if_any(queue, true);
@@ -333,6 +357,8 @@ void al_flush_event_queue(ALLEGRO_EVENT_QUEUE *queue)
    unsigned int i;
    ASSERT(queue);
 
+   heartbeat();
+
    _al_mutex_lock(&queue->mutex);
 
    /* Decrement reference counts on all user events. */
@@ -358,9 +384,11 @@ void al_wait_for_event(ALLEGRO_EVENT_QUEUE *queue, ALLEGRO_EVENT *ret_event)
 
    ASSERT(queue);
 
+   heartbeat();
+
    _al_mutex_lock(&queue->mutex);
    {
-      while (al_is_event_queue_empty(queue)) {
+      while (is_event_queue_empty(queue)) {
          _al_cond_wait(&queue->cond, &queue->mutex);
       }
 
@@ -385,6 +413,8 @@ bool al_wait_for_event_timed(ALLEGRO_EVENT_QUEUE *queue,
    ASSERT(queue);
    ASSERT(secs >= 0);
 
+   heartbeat();
+
    if (secs < 0.0)
       al_init_timeout(&timeout, 0);
    else
@@ -402,6 +432,8 @@ bool al_wait_for_event_until(ALLEGRO_EVENT_QUEUE *queue,
 {
    ASSERT(queue);
 
+   heartbeat();
+
    return do_wait_for_event(queue, ret_event, timeout);
 }
 
@@ -421,7 +453,7 @@ static bool do_wait_for_event(ALLEGRO_EVENT_QUEUE *queue,
        * variable, which will be signaled when an event is placed into
        * the queue.
        */
-      while (al_is_event_queue_empty(queue) && (result != -1)) {
+      while (is_event_queue_empty(queue) && (result != -1)) {
          result = _al_cond_timedwait(&queue->cond, &queue->mutex, timeout);
       }
 
diff --git a/src/opengl/extensions.c b/src/opengl/extensions.c
index a16a4be..2ac5f43 100644
--- a/src/opengl/extensions.c
+++ b/src/opengl/extensions.c
@@ -340,6 +340,16 @@ static void load_extensions(ALLEGRO_OGL_EXT_API *ext)
 
    #undef AGL_API
 
+#elif defined ALLEGRO_SDL
+
+#define AGL_API(type, name, args)                                                                 \
+      ext->name = (_ALLEGRO_gl##name##_t)SDL_GL_GetProcAddress(("gl" # name)); \
+      if (ext->name) { ALLEGRO_DEBUG("gl" #name " successfully loaded\n"); }
+
+      #include "allegro5/opengl/GLext/gl_ext_api.h"
+
+   #undef AGL_API
+
 #endif
 
 }
diff --git a/src/sdl/sdl_display.c b/src/sdl/sdl_display.c
new file mode 100644
index 0000000..17ecfd6
--- /dev/null
+++ b/src/sdl/sdl_display.c
@@ -0,0 +1,256 @@
+#include "allegro5/allegro.h"
+#include "allegro5/allegro_opengl.h"
+#include "allegro5/system.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_bitmap.h"
+#include "allegro5/internal/aintern_opengl.h"
+#include "allegro5/internal/aintern_vector.h"
+#include "allegro5/internal/aintern_system.h"
+#include "allegro5/platform/allegro_internal_sdl.h"
+
+ALLEGRO_DEBUG_CHANNEL("display")
+
+int _al_win_determine_adapter(void);
+
+static ALLEGRO_DISPLAY_INTERFACE *vt;
+
+ALLEGRO_DISPLAY *_al_sdl_find_display(uint32_t window_id) {
+   unsigned int i;
+   ALLEGRO_SYSTEM *s = al_get_system_driver();
+   for (i = 0; i < _al_vector_size(&s->displays); i++) {
+      void **v = (void **)_al_vector_ref(&s->displays, i);
+      ALLEGRO_DISPLAY_SDL *d = *v;
+      if (SDL_GetWindowID(d->window) == window_id) {
+         return &d->display;
+         break;
+      }
+   }
+   return NULL;
+}
+
+void _al_sdl_display_event(SDL_Event *e)
+{
+   ALLEGRO_EVENT event;
+   event.display.timestamp = al_get_time();
+
+   ALLEGRO_DISPLAY *d = NULL;
+
+   if (e->type == SDL_WINDOWEVENT) {
+      if (e->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
+         event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_IN;
+      }
+      if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
+         event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_OUT;
+      }
+      if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
+         event.display.type = ALLEGRO_EVENT_DISPLAY_CLOSE;
+      }
+      d = _al_sdl_find_display(e->window.windowID);
+   }
+   if (e->type == SDL_QUIT) {
+      event.display.type = ALLEGRO_EVENT_DISPLAY_CLOSE;
+      /* Use the first display as event source if we have any displays. */
+      ALLEGRO_SYSTEM *s = al_get_system_driver();
+      if (_al_vector_size(&s->displays) > 0) {
+         void **v = (void **)_al_vector_ref(&s->displays, 0);
+         d = *v;
+      }
+   }
+
+   if (!d)
+      return;
+   ALLEGRO_EVENT_SOURCE *es = &d->es;
+   _al_event_source_lock(es);
+   _al_event_source_emit_event(es, &event);
+   _al_event_source_unlock(es);
+}
+
+static ALLEGRO_DISPLAY *sdl_create_display_locked(int w, int h)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = al_calloc(1, sizeof *sdl);
+   ALLEGRO_DISPLAY *d = (void *)sdl;
+   d->w = w;
+   d->h = h;
+   d->flags = al_get_new_display_flags();
+   d->flags |= ALLEGRO_OPENGL;
+   int flags = SDL_WINDOW_OPENGL;
+   if (d->flags & ALLEGRO_FULLSCREEN)
+      flags |= SDL_WINDOW_FULLSCREEN;
+   if (d->flags & ALLEGRO_FULLSCREEN_WINDOW)
+      flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+   if (d->flags & ALLEGRO_FRAMELESS)
+      flags |= SDL_WINDOW_BORDERLESS;
+   sdl->window = SDL_CreateWindow(sdl->title, sdl->x, sdl->y,
+      d->w, d->h, flags);
+   if (!sdl->window) {
+      ALLEGRO_ERROR("SDL_CreateWindow failed: %s", SDL_GetError());
+      return NULL;
+   }
+   flags =
+      SDL_RENDERER_ACCELERATED |
+      SDL_RENDERER_PRESENTVSYNC |
+      SDL_RENDERER_TARGETTEXTURE;
+   sdl->renderer = SDL_CreateRenderer(sdl->window, -1, flags);
+   sdl->context = SDL_GL_CreateContext(sdl->window);
+   ALLEGRO_DISPLAY **add;
+   ALLEGRO_SYSTEM *system = al_get_system_driver();
+   add = _al_vector_alloc_back(&system->displays);
+   *add = d;
+
+   _al_event_source_init(&d->es);
+   d->vt = vt;
+
+   d->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = true;
+
+   d->ogl_extras = al_calloc(1, sizeof *d->ogl_extras);
+   _al_ogl_manage_extensions(d);
+   _al_ogl_set_extensions(d->ogl_extras->extension_api);
+
+   _al_ogl_setup_gl(d);
+
+   return d;
+}
+
+static ALLEGRO_DISPLAY *sdl_create_display(int w, int h)
+{
+   ALLEGRO_SYSTEM_SDL *s = (void *)al_get_system_driver();
+   al_lock_mutex(s->mutex);
+   ALLEGRO_DISPLAY *d = sdl_create_display_locked(w, h);
+   al_unlock_mutex(s->mutex);
+   return d;
+}
+
+static void sdl_destroy_display_locked(ALLEGRO_DISPLAY *d)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)d;
+   ALLEGRO_SYSTEM *system = al_get_system_driver();
+   _al_vector_find_and_delete(&system->displays, &d);
+   SDL_DestroyWindow(sdl->window);
+   al_free(sdl);
+}
+
+static void sdl_destroy_display(ALLEGRO_DISPLAY *d)
+{
+   ALLEGRO_SYSTEM_SDL *s = (void *)al_get_system_driver();
+   al_lock_mutex(s->mutex);
+   sdl_destroy_display_locked(d);
+   al_unlock_mutex(s->mutex);
+}
+
+static bool sdl_set_current_display(ALLEGRO_DISPLAY *d)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)d;
+   SDL_GL_MakeCurrent(sdl->window, sdl->context);
+   return true;
+}
+
+static void sdl_unset_current_display(ALLEGRO_DISPLAY *d)
+{
+}
+
+static void sdl_flip_display(ALLEGRO_DISPLAY *d)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)d;
+   SDL_RenderPresent(sdl->renderer);
+}
+
+static void sdl_update_display_region(ALLEGRO_DISPLAY *d, int x, int y,
+   	int width, int height)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)d;
+   SDL_RenderPresent(sdl->renderer);
+}
+
+static bool sdl_is_compatible_bitmap(ALLEGRO_DISPLAY *display,
+      ALLEGRO_BITMAP *bitmap)
+{
+   return true;
+}
+
+static bool sdl_set_mouse_cursor(ALLEGRO_DISPLAY *display,
+      ALLEGRO_MOUSE_CURSOR *cursor)
+{
+   return false;
+}
+
+static bool sdl_set_system_mouse_cursor(ALLEGRO_DISPLAY *display,
+      ALLEGRO_SYSTEM_MOUSE_CURSOR cursor_id)
+{
+   return false;
+}
+
+static bool sdl_show_mouse_cursor(ALLEGRO_DISPLAY *display)
+{
+   return false;
+}
+
+static bool sdl_hide_mouse_cursor(ALLEGRO_DISPLAY *display)
+{
+   return false;
+}
+
+static void sdl_set_window_position(ALLEGRO_DISPLAY *display, int x, int y)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)display;
+   SDL_SetWindowPosition(sdl->window, x, y);
+}
+
+static void sdl_get_window_position(ALLEGRO_DISPLAY *display, int *x, int *y)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)display;
+   SDL_GetWindowPosition(sdl->window, x, y);
+}
+
+ALLEGRO_DISPLAY_INTERFACE *_al_sdl_display_driver(void)
+{
+   if (vt)
+      return vt;
+
+   vt = al_calloc(1, sizeof *vt);
+   vt->id = AL_ID('S', 'D', 'L', '2');
+   vt->create_display = sdl_create_display;
+   vt->destroy_display = sdl_destroy_display;
+   vt->set_current_display = sdl_set_current_display;
+   vt->unset_current_display = sdl_unset_current_display;
+   //vt->clear = GL
+   //vt->draw_pixel = GL
+   vt->flip_display = sdl_flip_display;
+   vt->update_display_region = sdl_update_display_region;
+   /*vt->acknowledge_resize = sdl_acknowledge_resize;
+   vt->resize_display = sdl_resize_display;
+   vt->quick_size = sdl_quick_size;
+   vt->get_orientation = sdl_get_orientation;*/
+   vt->create_bitmap = _al_ogl_create_bitmap;
+   vt->set_target_bitmap = _al_ogl_set_target_bitmap;
+   vt->get_backbuffer = _al_ogl_get_backbuffer;
+   vt->is_compatible_bitmap = sdl_is_compatible_bitmap;
+   /*vt->switch_out = sdl_switch_out;
+   vt->switch_in = sdl_switch_in;
+   vt->draw_memory_bitmap_region = sdl_draw_memory_bitmap_region;
+   vt->wait_for_vsync = sdl_wait_for_vsync;*/
+   vt->set_mouse_cursor = sdl_set_mouse_cursor;
+   vt->set_system_mouse_cursor = sdl_set_system_mouse_cursor;
+   vt->show_mouse_cursor = sdl_show_mouse_cursor;
+   vt->hide_mouse_cursor = sdl_hide_mouse_cursor;
+   /*vt->set_icons = sdl_set_icons;*/
+   vt->set_window_position = sdl_set_window_position;
+   vt->get_window_position = sdl_get_window_position;
+   /*vt->set_window_constraints = sdl_set_window_constraints;
+   vt->get_window_constraints = sdl_get_window_constraints;
+   vt->set_display_flag = sdl_set_display_flag;
+   vt->set_window_title = sdl_set_window_title;*/
+   //vt->flush_vertex_cache = GL
+   //vt->prepare_vertex_cache = GL
+   //vt->update_transformation = GL
+   //vt->set_projection = GL
+   /*vt->shutdown = sdl_shutdown;
+   vt->acknowledge_drawing_halt = sdl_acknowledge_drawing_halt;
+   vt->acknowledge_drawing_resume = sdl_acknowledge_drawing_resume;
+   vt->set_display_option = sdl_set_display_option;*/
+   //vt->clear_depth_buffer = GL
+   vt->update_render_state = _al_ogl_update_render_state;
+
+   _al_ogl_add_drawing_functions(vt);
+
+   return vt;
+}
diff --git a/src/sdl/sdl_keyboard.c b/src/sdl/sdl_keyboard.c
new file mode 100644
index 0000000..93a332a
--- /dev/null
+++ b/src/sdl/sdl_keyboard.c
@@ -0,0 +1,404 @@
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern_system.h"
+#include "allegro5/platform/allegro_internal_sdl.h"
+
+ALLEGRO_DEBUG_CHANNEL("SDL")
+
+typedef struct ALLEGRO_KEYBOARD_SDL
+{
+   ALLEGRO_KEYBOARD keyboard;
+   int table[1024];
+   int inverse[1024];
+   int unicode[1024];
+   int inverse_unicode[1024];
+   bool create_extra_char[1024];
+   ALLEGRO_DISPLAY *display;
+} ALLEGRO_KEYBOARD_SDL;
+
+static ALLEGRO_KEYBOARD_DRIVER *vt;
+static ALLEGRO_KEYBOARD_SDL *keyboard;
+
+void _al_sdl_keyboard_event(SDL_Event *e)
+{
+   if (!keyboard)
+      return;
+   if (e->type == SDL_WINDOWEVENT) {
+      if (e->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
+         keyboard->display = _al_sdl_find_display(e->window.windowID);
+      }
+      else {
+         keyboard->display = NULL;
+      }
+      return;
+   }
+   ALLEGRO_EVENT event;
+   ALLEGRO_EVENT_SOURCE *es = &keyboard->keyboard.es;
+   _al_event_source_lock(es);
+   event.keyboard.timestamp = al_get_time();
+   event.keyboard.display = NULL;
+   event.keyboard.modifiers = 0;
+   event.keyboard.repeat = false;
+
+   if (e->type == SDL_TEXTINPUT) {
+      ALLEGRO_USTR_INFO info;
+      ALLEGRO_USTR const *u = al_ref_cstr(&info, e->text.text);
+      int pos = 0;
+      while (true) {
+         int32_t c = al_ustr_get_next(u, &pos);
+         if (c <= 0)
+            break;
+         event.keyboard.type = ALLEGRO_EVENT_KEY_CHAR;
+         event.keyboard.keycode = c < 1024 ? keyboard->inverse_unicode[c] : 0;
+         event.keyboard.unichar = c;
+         event.keyboard.display = _al_sdl_find_display(e->text.windowID);
+         _al_event_source_emit_event(es, &event);
+      }
+
+   }
+   else if (e->type == SDL_KEYDOWN) {
+      event.keyboard.type = ALLEGRO_EVENT_KEY_DOWN;
+      event.keyboard.keycode = keyboard->table[e->key.keysym.scancode];
+      event.keyboard.unichar = keyboard->unicode[e->key.keysym.scancode];
+      event.keyboard.display = _al_sdl_find_display(e->key.windowID);
+      _al_event_source_emit_event(es, &event);
+
+      if (keyboard->create_extra_char[e->key.keysym.scancode]) {
+         event.keyboard.type = ALLEGRO_EVENT_KEY_CHAR;
+         _al_event_source_emit_event(es, &event);
+      }
+   }
+   else if (e->type == SDL_KEYUP) {
+      event.keyboard.type = ALLEGRO_EVENT_KEY_UP;
+      event.keyboard.keycode = keyboard->table[e->key.keysym.scancode];
+      event.keyboard.unichar = keyboard->unicode[e->key.keysym.scancode];
+      event.keyboard.display = _al_sdl_find_display(e->key.windowID);
+      _al_event_source_emit_event(es, &event);
+   }
+
+   keyboard->display = event.keyboard.display;
+
+   _al_event_source_unlock(es);
+}
+
+static void adde(int sdl_scancode, int allegro_keycode, int unicode, bool extra)
+{
+   if (sdl_scancode >= 1024) {
+      ALLEGRO_WARN("Cannot map SDL scancode %d.\n", sdl_scancode);
+      return;
+   }
+   keyboard->table[sdl_scancode] = allegro_keycode;
+   keyboard->inverse[allegro_keycode] = sdl_scancode;
+   keyboard->unicode[sdl_scancode] = unicode;
+   keyboard->inverse_unicode[unicode] = allegro_keycode;
+   keyboard->create_extra_char[sdl_scancode] = extra;
+}
+
+static void add(int sdl_scancode, int allegro_keycode, int unicode)
+{
+   adde(sdl_scancode, allegro_keycode, unicode, false);
+}
+
+#define ADD_SAME(X, c) add(SDL_SCANCODE_##X, ALLEGRO_KEY_##X, c)
+#define ADD_SAMEC(X) add(SDL_SCANCODE_##X, ALLEGRO_KEY_##X, tolower(#X[0]))
+#define ADD_SAMEE(X, c) adde(SDL_SCANCODE_##X, ALLEGRO_KEY_##X, c, true)
+
+static bool sdl_init_keyboard(void)
+{
+   keyboard = al_calloc(1, sizeof *keyboard);
+   _al_event_source_init(&keyboard->keyboard.es);
+
+   add(SDL_SCANCODE_UNKNOWN, 0, 0);
+   ADD_SAMEC(A);
+   ADD_SAMEC(B);
+   ADD_SAMEC(C);
+   ADD_SAMEC(D);
+   ADD_SAMEC(E);
+   ADD_SAMEC(F);
+   ADD_SAMEC(G);
+   ADD_SAMEC(H);
+   ADD_SAMEC(I);
+   ADD_SAMEC(J);
+   ADD_SAMEC(K);
+   ADD_SAMEC(L);
+   ADD_SAMEC(M);
+   ADD_SAMEC(N);
+   ADD_SAMEC(O);
+   ADD_SAMEC(P);
+   ADD_SAMEC(Q);
+   ADD_SAMEC(R);
+   ADD_SAMEC(S);
+   ADD_SAMEC(T);
+   ADD_SAMEC(U);
+   ADD_SAMEC(V);
+   ADD_SAMEC(W);
+   ADD_SAMEC(X);
+   ADD_SAMEC(Y);
+   ADD_SAMEC(Z);
+   ADD_SAMEC(1);
+   ADD_SAMEC(2);
+   ADD_SAMEC(3);
+   ADD_SAMEC(4);
+   ADD_SAMEC(5);
+   ADD_SAMEC(6);
+   ADD_SAMEC(7);
+   ADD_SAMEC(8);
+   ADD_SAMEC(9);
+   ADD_SAMEC(0);
+   adde(SDL_SCANCODE_RETURN, ALLEGRO_KEY_ENTER, 13, true);
+   ADD_SAMEE(ESCAPE, 27);
+   ADD_SAMEE(BACKSPACE, 8);
+   ADD_SAMEE(TAB, 9);
+   ADD_SAME(SPACE, ' ');
+   ADD_SAME(MINUS, '-');
+   ADD_SAME(EQUALS, '=');
+   add(SDL_SCANCODE_LEFTBRACKET, ALLEGRO_KEY_OPENBRACE, '[');
+   add(SDL_SCANCODE_RIGHTBRACKET, ALLEGRO_KEY_CLOSEBRACE, ']');
+   ADD_SAME(BACKSLASH, '\\');
+   add(SDL_SCANCODE_NONUSHASH, 0, '#');
+   ADD_SAME(SEMICOLON, ';');
+   add(SDL_SCANCODE_APOSTROPHE, 0, '\'');
+   add(SDL_SCANCODE_GRAVE, 0, 0);
+   ADD_SAME(COMMA, ',');
+   add(SDL_SCANCODE_PERIOD, ALLEGRO_KEY_FULLSTOP, '.');
+   ADD_SAME(SLASH, '/');
+   ADD_SAME(CAPSLOCK, 0);
+   ADD_SAMEE(F1, 0);
+   ADD_SAMEE(F2, 0);
+   ADD_SAMEE(F3, 0);
+   ADD_SAMEE(F4, 0);
+   ADD_SAMEE(F5, 0);
+   ADD_SAMEE(F6, 0);
+   ADD_SAMEE(F7, 0);
+   ADD_SAMEE(F8, 0);
+   ADD_SAMEE(F9, 0);
+   ADD_SAMEE(F10, 0);
+   ADD_SAMEE(F11, 0);
+   ADD_SAMEE(F12, 0);
+   ADD_SAME(PRINTSCREEN, 0);
+   ADD_SAME(SCROLLLOCK, 0);
+   ADD_SAME(PAUSE, 0);
+   ADD_SAMEE(INSERT, 0);
+   ADD_SAMEE(HOME, 0);
+   add(SDL_SCANCODE_PAGEUP, ALLEGRO_KEY_PGUP, 0);
+   ADD_SAMEE(DELETE, 0);
+   ADD_SAMEE(END, 0);
+   add(SDL_SCANCODE_PAGEDOWN, ALLEGRO_KEY_PGDN, 0);
+   ADD_SAMEE(RIGHT, 0);
+   ADD_SAMEE(LEFT, 0);
+   ADD_SAMEE(DOWN, 0);
+   ADD_SAMEE(UP, 0);
+   add(SDL_SCANCODE_NUMLOCKCLEAR, ALLEGRO_KEY_NUMLOCK, 0);
+   add(SDL_SCANCODE_KP_DIVIDE, ALLEGRO_KEY_PAD_SLASH, '/');
+   add(SDL_SCANCODE_KP_MULTIPLY, ALLEGRO_KEY_PAD_ASTERISK, '*');
+   add(SDL_SCANCODE_KP_MINUS, ALLEGRO_KEY_PAD_MINUS, '-');
+   add(SDL_SCANCODE_KP_PLUS, ALLEGRO_KEY_PAD_PLUS, '+');
+   add(SDL_SCANCODE_KP_ENTER, ALLEGRO_KEY_PAD_ENTER, 13);
+   add(SDL_SCANCODE_KP_1, ALLEGRO_KEY_PAD_1, '1');
+   add(SDL_SCANCODE_KP_2, ALLEGRO_KEY_PAD_2, '2');
+   add(SDL_SCANCODE_KP_3, ALLEGRO_KEY_PAD_3, '3');
+   add(SDL_SCANCODE_KP_4, ALLEGRO_KEY_PAD_4, '4');
+   add(SDL_SCANCODE_KP_5, ALLEGRO_KEY_PAD_5, '5');
+   add(SDL_SCANCODE_KP_6, ALLEGRO_KEY_PAD_6, '6');
+   add(SDL_SCANCODE_KP_7, ALLEGRO_KEY_PAD_7, '7');
+   add(SDL_SCANCODE_KP_8, ALLEGRO_KEY_PAD_8, '8');
+   add(SDL_SCANCODE_KP_9, ALLEGRO_KEY_PAD_9, '9');
+   add(SDL_SCANCODE_KP_0, ALLEGRO_KEY_PAD_0, '0');
+   add(SDL_SCANCODE_KP_PERIOD, ALLEGRO_KEY_PAD_DELETE, 0);
+   add(SDL_SCANCODE_NONUSBACKSLASH, 0, '\\');
+   add(SDL_SCANCODE_APPLICATION, 0, 0);
+   add(SDL_SCANCODE_POWER, 0, 0);
+   add(SDL_SCANCODE_KP_EQUALS, ALLEGRO_KEY_PAD_EQUALS, '=');
+   add(SDL_SCANCODE_F13, 0, 0);
+   add(SDL_SCANCODE_F14, 0, 0);
+   add(SDL_SCANCODE_F15, 0, 0);
+   add(SDL_SCANCODE_F16, 0, 0);
+   add(SDL_SCANCODE_F17, 0, 0);
+   add(SDL_SCANCODE_F18, 0, 0);
+   add(SDL_SCANCODE_F19, 0, 0);
+   add(SDL_SCANCODE_F20, 0, 0);
+   add(SDL_SCANCODE_F21, 0, 0);
+   add(SDL_SCANCODE_F22, 0, 0);
+   add(SDL_SCANCODE_F23, 0, 0);
+   add(SDL_SCANCODE_F24, 0, 0);
+   add(SDL_SCANCODE_EXECUTE, 0, 0);
+   add(SDL_SCANCODE_HELP, 0, 0);
+   ADD_SAME(MENU, 0);
+   add(SDL_SCANCODE_SELECT, 0, 0);
+   add(SDL_SCANCODE_STOP, 0, 0);
+   add(SDL_SCANCODE_AGAIN, 0, 0);
+   add(SDL_SCANCODE_UNDO, 0, 0);
+   add(SDL_SCANCODE_CUT, 0, 0);
+   add(SDL_SCANCODE_COPY, 0, 0);
+   add(SDL_SCANCODE_PASTE, 0, 0);
+   add(SDL_SCANCODE_FIND, 0, 0);
+   add(SDL_SCANCODE_MUTE, 0, 0);
+   add(SDL_SCANCODE_VOLUMEUP, 0, 0);
+   add(SDL_SCANCODE_VOLUMEDOWN, 0, 0);
+   add(SDL_SCANCODE_KP_COMMA, 0, ',');
+   add(SDL_SCANCODE_KP_EQUALSAS400, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL1, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL2, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL3, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL4, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL5, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL6, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL7, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL8, 0, 0);
+   add(SDL_SCANCODE_INTERNATIONAL9, 0, 0);
+   add(SDL_SCANCODE_LANG1, 0, 0);
+   add(SDL_SCANCODE_LANG2, 0, 0);
+   add(SDL_SCANCODE_LANG3, 0, 0);
+   add(SDL_SCANCODE_LANG4, 0, 0);
+   add(SDL_SCANCODE_LANG5, 0, 0);
+   add(SDL_SCANCODE_LANG6, 0, 0);
+   add(SDL_SCANCODE_LANG7, 0, 0);
+   add(SDL_SCANCODE_LANG8, 0, 0);
+   add(SDL_SCANCODE_LANG9, 0, 0);
+   add(SDL_SCANCODE_ALTERASE, 0, 0);
+   add(SDL_SCANCODE_SYSREQ, 0, 0);
+   add(SDL_SCANCODE_CANCEL, 0, 0);
+   add(SDL_SCANCODE_CLEAR, 0, 0);
+   add(SDL_SCANCODE_PRIOR, 0, 0);
+   add(SDL_SCANCODE_RETURN2, 0, 0);
+   add(SDL_SCANCODE_SEPARATOR, 0, 0);
+   add(SDL_SCANCODE_OUT, 0, 0);
+   add(SDL_SCANCODE_OPER, 0, 0);
+   add(SDL_SCANCODE_CLEARAGAIN, 0, 0);
+   add(SDL_SCANCODE_CRSEL, 0, 0);
+   add(SDL_SCANCODE_EXSEL, 0, 0);
+   add(SDL_SCANCODE_KP_00, 0, 0);
+   add(SDL_SCANCODE_KP_000, 0, 0);
+   add(SDL_SCANCODE_THOUSANDSSEPARATOR, 0, 0);
+   add(SDL_SCANCODE_DECIMALSEPARATOR, 0, 0);
+   add(SDL_SCANCODE_CURRENCYUNIT, 0, 0);
+   add(SDL_SCANCODE_CURRENCYSUBUNIT, 0, 0);
+   add(SDL_SCANCODE_KP_LEFTPAREN, 0, 0);
+   add(SDL_SCANCODE_KP_RIGHTPAREN, 0, 0);
+   add(SDL_SCANCODE_KP_LEFTBRACE, 0, '{');
+   add(SDL_SCANCODE_KP_RIGHTBRACE, 0, '}');
+   add(SDL_SCANCODE_KP_TAB, 0, 9);
+   add(SDL_SCANCODE_KP_BACKSPACE, 0, 8);
+   add(SDL_SCANCODE_KP_A, 0, 0);
+   add(SDL_SCANCODE_KP_B, 0, 0);
+   add(SDL_SCANCODE_KP_C, 0, 0);
+   add(SDL_SCANCODE_KP_D, 0, 0);
+   add(SDL_SCANCODE_KP_E, 0, 0);
+   add(SDL_SCANCODE_KP_F, 0, 0);
+   add(SDL_SCANCODE_KP_XOR, 0, 0);
+   add(SDL_SCANCODE_KP_POWER, 0, 0);
+   add(SDL_SCANCODE_KP_PERCENT, 0, 0);
+   add(SDL_SCANCODE_KP_LESS, 0, '<');
+   add(SDL_SCANCODE_KP_GREATER, 0, '>');
+   add(SDL_SCANCODE_KP_AMPERSAND, 0, '&');
+   add(SDL_SCANCODE_KP_DBLAMPERSAND, 0, 0);
+   add(SDL_SCANCODE_KP_VERTICALBAR, 0, '|');
+   add(SDL_SCANCODE_KP_DBLVERTICALBAR, 0, 0);
+   add(SDL_SCANCODE_KP_COLON, 0, ':');
+   add(SDL_SCANCODE_KP_HASH, 0, '#');
+   add(SDL_SCANCODE_KP_SPACE, 0, 0);
+   add(SDL_SCANCODE_KP_AT, 0, '@');
+   add(SDL_SCANCODE_KP_EXCLAM, 0, '!');
+   add(SDL_SCANCODE_KP_MEMSTORE, 0, 0);
+   add(SDL_SCANCODE_KP_MEMRECALL, 0, 0);
+   add(SDL_SCANCODE_KP_MEMCLEAR, 0, 0);
+   add(SDL_SCANCODE_KP_MEMADD, 0, 0);
+   add(SDL_SCANCODE_KP_MEMSUBTRACT, 0, 0);
+   add(SDL_SCANCODE_KP_MEMMULTIPLY, 0, 0);
+   add(SDL_SCANCODE_KP_MEMDIVIDE, 0, 0);
+   add(SDL_SCANCODE_KP_PLUSMINUS, 0, 0);
+   add(SDL_SCANCODE_KP_CLEAR, 0, 0);
+   add(SDL_SCANCODE_KP_CLEARENTRY, 0, 0);
+   add(SDL_SCANCODE_KP_BINARY, 0, 0);
+   add(SDL_SCANCODE_KP_OCTAL, 0, 0);
+   add(SDL_SCANCODE_KP_DECIMAL, 0, 0);
+   add(SDL_SCANCODE_KP_HEXADECIMAL, 0, 0);
+   ADD_SAME(LCTRL, 0);
+   ADD_SAME(LSHIFT, 0);
+   add(SDL_SCANCODE_LALT, ALLEGRO_KEY_ALT, 0);
+   add(SDL_SCANCODE_LGUI, ALLEGRO_KEY_LWIN, 0);
+   ADD_SAME(RCTRL, 0);
+   ADD_SAME(RSHIFT, 0);
+   add(SDL_SCANCODE_RALT, ALLEGRO_KEY_ALTGR, 0);
+   add(SDL_SCANCODE_RGUI, ALLEGRO_KEY_RWIN, 0);
+   add(SDL_SCANCODE_MODE, 0, 0);
+   add(SDL_SCANCODE_AUDIONEXT, 0, 0);
+   add(SDL_SCANCODE_AUDIOPREV, 0, 0);
+   add(SDL_SCANCODE_AUDIOSTOP, 0, 0);
+   add(SDL_SCANCODE_AUDIOPLAY, 0, 0);
+   add(SDL_SCANCODE_AUDIOMUTE, 0, 0);
+   add(SDL_SCANCODE_MEDIASELECT, 0, 0);
+   add(SDL_SCANCODE_WWW, 0, 0);
+   add(SDL_SCANCODE_MAIL, 0, 0);
+   add(SDL_SCANCODE_CALCULATOR, 0, 0);
+   add(SDL_SCANCODE_COMPUTER, 0, 0);
+   add(SDL_SCANCODE_AC_SEARCH, 0, 0);
+   add(SDL_SCANCODE_AC_HOME, 0, 0);
+   add(SDL_SCANCODE_AC_BACK, 0, 0);
+   add(SDL_SCANCODE_AC_FORWARD, 0, 0);
+   add(SDL_SCANCODE_AC_STOP, 0, 0);
+   add(SDL_SCANCODE_AC_REFRESH, 0, 0);
+   add(SDL_SCANCODE_AC_BOOKMARKS, 0, 0);
+   add(SDL_SCANCODE_BRIGHTNESSDOWN, 0, 0);
+   add(SDL_SCANCODE_BRIGHTNESSUP, 0, 0);
+   add(SDL_SCANCODE_DISPLAYSWITCH, 0, 0);
+   add(SDL_SCANCODE_KBDILLUMTOGGLE, 0, 0);
+   add(SDL_SCANCODE_KBDILLUMDOWN, 0, 0);
+   add(SDL_SCANCODE_KBDILLUMUP, 0, 0);
+   add(SDL_SCANCODE_EJECT, 0, 0);
+   add(SDL_SCANCODE_SLEEP, 0, 0);
+
+   return true;
+}
+
+static void sdl_exit_keyboard(void)
+{
+}
+
+static ALLEGRO_KEYBOARD *sdl_get_keyboard(void)
+{
+   return &keyboard->keyboard;
+}
+
+static bool sdl_set_keyboard_leds(int leds)
+{
+   return false;
+}
+
+static char const *sdl_keycode_to_name(int keycode)
+{
+   return SDL_GetScancodeName(keyboard->inverse[keycode]);
+}
+
+static void sdl_get_keyboard_state(ALLEGRO_KEYBOARD_STATE *ret_state)
+{
+   int i, n;
+   ALLEGRO_SYSTEM_INTERFACE *sdl = _al_sdl_system_driver();
+   sdl->heartbeat();
+   const Uint8 *s = SDL_GetKeyboardState(&n);
+   for (i = 0; i < n; i++) {
+      if (s[i])
+         _AL_KEYBOARD_STATE_SET_KEY_DOWN(*ret_state, keyboard->table[i]);
+      else
+         _AL_KEYBOARD_STATE_CLEAR_KEY_DOWN(*ret_state, keyboard->table[i]);
+   }
+   ret_state->display = keyboard->display;
+}
+
+ALLEGRO_KEYBOARD_DRIVER *_al_sdl_keyboard_driver(void)
+{
+   if (vt)
+      return vt;
+
+   vt = al_calloc(1, sizeof *vt);
+   vt->keydrv_id = AL_ID('S','D','L','2');
+   vt->keydrv_name = "SDL2 Keyboard";
+   vt->keydrv_desc = "SDL2 Keyboard";
+   vt->keydrv_ascii_name = "SDL2 Keyboard";
+   vt->init_keyboard = sdl_init_keyboard;
+   vt->exit_keyboard = sdl_exit_keyboard;
+   vt->get_keyboard = sdl_get_keyboard;
+   vt->set_keyboard_leds = sdl_set_keyboard_leds;
+   vt->keycode_to_name = sdl_keycode_to_name;
+   vt->get_keyboard_state = sdl_get_keyboard_state;
+   return vt;
+}
diff --git a/src/sdl/sdl_mouse.c b/src/sdl/sdl_mouse.c
new file mode 100644
index 0000000..5bf32db
--- /dev/null
+++ b/src/sdl/sdl_mouse.c
@@ -0,0 +1,167 @@
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern_system.h"
+#include "allegro5/platform/allegro_internal_sdl.h"
+
+ALLEGRO_DEBUG_CHANNEL("SDL")
+
+typedef struct ALLEGRO_MOUSE_SDL
+{
+   ALLEGRO_MOUSE mouse;
+   int x, y, z, w;
+   ALLEGRO_DISPLAY *display;
+   int buttons;
+} ALLEGRO_MOUSE_SDL;
+
+static ALLEGRO_MOUSE_DRIVER *vt;
+static ALLEGRO_MOUSE_SDL *mouse;
+
+void _al_sdl_mouse_event(SDL_Event *e)
+{
+   if (!mouse)
+      return;
+
+   ALLEGRO_EVENT_SOURCE *es = &mouse->mouse.es;
+   _al_event_source_lock(es);
+   ALLEGRO_EVENT event;
+   memset(&event, 0, sizeof event);
+
+   event.mouse.timestamp = al_get_time();
+
+   ALLEGRO_DISPLAY *d = NULL;
+
+   if (e->type == SDL_WINDOWEVENT) {
+      if (e->window.event == SDL_WINDOWEVENT_ENTER) {
+         event.mouse.type = ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY;
+         SDL_GetMouseState(&event.mouse.x, &event.mouse.y );
+      }
+      else {
+         event.mouse.type = ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY;
+         event.mouse.x = mouse->x;
+         event.mouse.y = mouse->y;
+      }
+      d = _al_sdl_find_display(e->window.windowID);
+      mouse->display = e->window.event == SDL_WINDOWEVENT_ENTER ? d : NULL;
+   }
+   else if (e->type == SDL_MOUSEMOTION) {
+      event.mouse.type = ALLEGRO_EVENT_MOUSE_AXES;
+      event.mouse.x = e->motion.x;
+      event.mouse.y = e->motion.y;
+      event.mouse.dx = e->motion.xrel;
+      event.mouse.dy = e->motion.yrel;
+      mouse->x = e->motion.x;
+      mouse->y = e->motion.y;
+      d = _al_sdl_find_display(e->motion.windowID);
+   }
+   else if (e->type == SDL_MOUSEWHEEL) {
+      event.mouse.type = ALLEGRO_EVENT_MOUSE_AXES;
+      mouse->z += e->wheel.y;
+      mouse->w += e->wheel.x;
+      event.mouse.z = mouse->z;
+      event.mouse.w = mouse->w;
+      event.mouse.dz = e->wheel.y;
+      event.mouse.dw = e->wheel.x;
+      d = _al_sdl_find_display(e->wheel.windowID);
+   }
+   else {
+      switch (e->button.button) {
+         case SDL_BUTTON_LEFT: event.mouse.button = 1; break;
+         case SDL_BUTTON_RIGHT: event.mouse.button = 2; break;
+         case SDL_BUTTON_MIDDLE: event.mouse.button = 3; break;
+         case SDL_BUTTON_X1: event.mouse.button = 4; break;
+         case SDL_BUTTON_X2: event.mouse.button = 5; break;
+      }
+      event.mouse.x = e->button.x;
+      event.mouse.y = e->button.y;
+      if (e->type == SDL_MOUSEBUTTONDOWN) {
+         event.mouse.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
+         mouse->buttons |= 1 << (event.mouse.button - 1);
+      }
+      if (e->type == SDL_MOUSEBUTTONUP) {
+         event.mouse.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
+         mouse->buttons &= ~(1 << (event.mouse.button - 1));
+      }
+      d = _al_sdl_find_display(e->button.windowID);
+   }
+
+   event.mouse.display = d;
+
+   _al_event_source_emit_event(es, &event);
+   _al_event_source_unlock(es);
+}
+
+static bool sdl_init_mouse(void)
+{
+   mouse = al_calloc(1, sizeof *mouse);
+   _al_event_source_init(&mouse->mouse.es);
+   return true;
+}
+
+static void sdl_exit_mouse(void)
+{
+}
+
+static ALLEGRO_MOUSE *sdl_get_mouse(void)
+{
+   return &mouse->mouse;
+}
+
+static unsigned int sdl_get_mouse_num_buttons(void)
+{
+   return 5;
+}
+
+static unsigned int sdl_get_mouse_num_axes(void)
+{
+   return 4;
+}
+
+static bool sdl_set_mouse_xy(ALLEGRO_DISPLAY *display, int x, int y)
+{
+   ALLEGRO_DISPLAY_SDL *sdl = (void *)display;
+   SDL_WarpMouseInWindow(sdl->window, x, y);
+   return true;
+}
+
+static bool sdl_set_mouse_axis(int which, int value)
+{
+   return false;
+}
+
+static void sdl_get_mouse_state(ALLEGRO_MOUSE_STATE *ret_state)
+{
+   int x, y, i;
+   ALLEGRO_SYSTEM_INTERFACE *sdl = _al_sdl_system_driver();
+   sdl->heartbeat();
+   SDL_GetMouseState(&x, &y);
+   ret_state->x = x;
+   ret_state->y = y;
+   ret_state->z = 0;
+   ret_state->w = 0;
+   for (i = 0; i < ALLEGRO_MOUSE_MAX_EXTRA_AXES; i++)
+      ret_state->more_axes[i] = 0;
+   ret_state->buttons = mouse->buttons;
+   ret_state->pressure = 0;
+   ret_state->display = mouse->display;
+}
+
+ALLEGRO_MOUSE_DRIVER *_al_sdl_mouse_driver(void)
+{
+   if (vt)
+      return vt;
+
+   vt = al_calloc(1, sizeof *vt);
+   vt->msedrv_id = AL_ID('S','D','L','2');
+   vt->msedrv_name = "SDL2 Mouse";
+   vt->msedrv_desc = "SDL2 Mouse";
+   vt->msedrv_ascii_name = "SDL2 Mouse";
+   vt->init_mouse = sdl_init_mouse;
+   vt->exit_mouse = sdl_exit_mouse;
+   vt->get_mouse = sdl_get_mouse;
+   vt->get_mouse_num_buttons = sdl_get_mouse_num_buttons;
+   vt->get_mouse_num_axes = sdl_get_mouse_num_axes;
+   vt->set_mouse_xy = sdl_set_mouse_xy;
+   vt->set_mouse_axis = sdl_set_mouse_axis;
+   vt->get_mouse_state = sdl_get_mouse_state;
+
+   return vt;
+}
diff --git a/src/sdl/sdl_system.c b/src/sdl/sdl_system.c
new file mode 100644
index 0000000..ca51f4c
--- /dev/null
+++ b/src/sdl/sdl_system.c
@@ -0,0 +1,285 @@
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern_system.h"
+#include "allegro5/platform/allegro_internal_sdl.h"
+
+ALLEGRO_DEBUG_CHANNEL("SDL")
+
+static ALLEGRO_SYSTEM_INTERFACE *vt;
+
+#define _E(x) if (type == x) return #x;
+static char const *event_name(int type)
+{
+   _E(SDL_FIRSTEVENT)
+   _E(SDL_QUIT)
+   _E(SDL_APP_TERMINATING)
+   _E(SDL_APP_LOWMEMORY)
+   _E(SDL_APP_WILLENTERBACKGROUND)
+   _E(SDL_APP_DIDENTERBACKGROUND)
+   _E(SDL_APP_WILLENTERFOREGROUND)
+   _E(SDL_APP_DIDENTERFOREGROUND)
+   _E(SDL_WINDOWEVENT)
+   _E(SDL_SYSWMEVENT)
+   _E(SDL_KEYDOWN)
+   _E(SDL_KEYUP)
+   _E(SDL_TEXTEDITING)
+   _E(SDL_TEXTINPUT)
+   _E(SDL_MOUSEMOTION)
+   _E(SDL_MOUSEBUTTONDOWN)
+   _E(SDL_MOUSEBUTTONUP)
+   _E(SDL_MOUSEWHEEL)
+   _E(SDL_JOYAXISMOTION)
+   _E(SDL_JOYBALLMOTION)
+   _E(SDL_JOYHATMOTION)
+   _E(SDL_JOYBUTTONDOWN)
+   _E(SDL_JOYBUTTONUP)
+   _E(SDL_JOYDEVICEADDED)
+   _E(SDL_JOYDEVICEREMOVED)
+   _E(SDL_CONTROLLERAXISMOTION)
+   _E(SDL_CONTROLLERBUTTONDOWN)
+   _E(SDL_CONTROLLERBUTTONUP)
+   _E(SDL_CONTROLLERDEVICEADDED)
+   _E(SDL_CONTROLLERDEVICEREMOVED)
+   _E(SDL_CONTROLLERDEVICEREMAPPED)
+   _E(SDL_FINGERDOWN)
+   _E(SDL_FINGERUP)
+   _E(SDL_FINGERMOTION)
+   _E(SDL_DOLLARGESTURE)
+   _E(SDL_DOLLARRECORD)
+   _E(SDL_MULTIGESTURE)
+   _E(SDL_CLIPBOARDUPDATE)
+   _E(SDL_DROPFILE)
+   _E(SDL_RENDER_TARGETS_RESET)
+   _E(SDL_USEREVENT)
+   return "(unknown)";
+}
+#undef _E
+
+static void sdl_heartbeat(void)
+{
+   ALLEGRO_SYSTEM_SDL *s = (void *)al_get_system_driver();
+   al_lock_mutex(s->mutex);
+   SDL_Event event;
+   while (SDL_PollEvent(&event)) {
+      //printf("event %s\n", event_name(event.type));
+      switch (event.type) {
+         case SDL_KEYDOWN:
+         case SDL_KEYUP:
+         case SDL_TEXTINPUT:
+            _al_sdl_keyboard_event(&event);
+            break;
+         case SDL_MOUSEMOTION:
+         case SDL_MOUSEBUTTONDOWN:
+         case SDL_MOUSEBUTTONUP:
+         case SDL_MOUSEWHEEL:
+            _al_sdl_mouse_event(&event);
+            break;
+         case SDL_QUIT:
+            _al_sdl_display_event(&event);
+            break;
+         case SDL_WINDOWEVENT:
+            switch (event.window.event) {
+               case SDL_WINDOWEVENT_ENTER:
+               case SDL_WINDOWEVENT_LEAVE:
+                  _al_sdl_mouse_event(&event);
+                  break;
+               case SDL_WINDOWEVENT_FOCUS_GAINED:
+               case SDL_WINDOWEVENT_FOCUS_LOST:
+                  _al_sdl_display_event(&event);
+                  _al_sdl_keyboard_event(&event);
+                  break;
+               case SDL_WINDOWEVENT_CLOSE:
+                  _al_sdl_display_event(&event);
+                  break;
+            }
+      }
+   }
+   al_unlock_mutex(s->mutex);
+}
+
+static ALLEGRO_SYSTEM *sdl_initialize(int flags)
+{
+   (void)flags;
+   ALLEGRO_SYSTEM_SDL *s = al_calloc(1, sizeof *s);
+   s->system.vt = vt;
+
+   SDL_Init(SDL_INIT_EVERYTHING);
+
+   _al_vector_init(&s->system.displays, sizeof (ALLEGRO_DISPLAY_SDL *));
+
+   return &s->system;
+}
+
+static void sdl_heartbeat_init(void)
+{
+   ALLEGRO_SYSTEM_SDL *s = (void *)al_get_system_driver();
+
+   /* This cannot be done in sdl_initialize because the threading system
+    * requires a completed ALLEGRO_SYSTEM which only exists after the
+    * function returns. This function on the other hand will get called
+    * once the system was created.
+    */
+   s->mutex = al_create_mutex();
+}
+
+static void sdl_shutdown_system(void)
+{
+   ALLEGRO_SYSTEM_SDL *s = (void *)al_get_system_driver();
+
+   al_destroy_mutex(s->mutex);
+   al_free(s);
+   SDL_Quit();
+}
+
+static ALLEGRO_PATH *sdl_get_path(int id)
+{
+   ALLEGRO_PATH *p = NULL;
+   switch (id) {
+      case ALLEGRO_TEMP_PATH:
+      case ALLEGRO_USER_DOCUMENTS_PATH:
+      case ALLEGRO_USER_DATA_PATH:
+      case ALLEGRO_USER_SETTINGS_PATH:
+         p = al_create_path_for_directory(SDL_GetPrefPath(
+            al_get_org_name(), al_get_app_name()));
+         if (id == ALLEGRO_TEMP_PATH) {
+            al_append_path_component(p, "tmp");
+         }
+         break;
+      case ALLEGRO_RESOURCES_PATH:
+      case ALLEGRO_EXENAME_PATH:
+      case ALLEGRO_USER_HOME_PATH:
+         p = al_create_path_for_directory(SDL_GetBasePath());
+         if (id == ALLEGRO_EXENAME_PATH) {
+            al_set_path_filename(p, al_get_app_name());
+         }
+         break;
+   }
+   return p;
+}
+
+static ALLEGRO_DISPLAY_INTERFACE *sdl_get_display_driver(void)
+{
+   return _al_sdl_display_driver();
+}
+
+static ALLEGRO_KEYBOARD_DRIVER *sdl_get_keyboard_driver(void)
+{
+   return _al_sdl_keyboard_driver();
+}
+
+static ALLEGRO_MOUSE_DRIVER *sdl_get_mouse_driver(void)
+{
+   return _al_sdl_mouse_driver();
+}
+
+#define ADD(allegro, sdl) if (sdl_format == \
+   SDL_PIXELFORMAT_##sdl) return ALLEGRO_PIXEL_FORMAT_##allegro;
+int _al_sdl_get_allegro_pixel_format(int sdl_format) {
+   ADD(ARGB_8888, ARGB8888)
+   ADD(RGBA_8888, RGBA8888)
+   ADD(ABGR_8888, ABGR8888)
+   return 0;
+}
+#undef ADD
+
+#define ADD(allegro, sdl) if (allegro_format == \
+   ALLEGRO_PIXEL_FORMAT_##allegro) return SDL_PIXELFORMAT_##sdl;
+int _al_sdl_get_sdl_pixel_format(int allegro_format) {
+   ADD(ANY, ABGR8888)
+   ADD(ANY_NO_ALPHA, ABGR8888)
+   ADD(ANY_WITH_ALPHA, ABGR8888)
+   ADD(ANY_32_NO_ALPHA, ABGR8888)
+   ADD(ANY_32_WITH_ALPHA, ABGR8888)
+   ADD(ARGB_8888, ARGB8888)
+   ADD(RGBA_8888, RGBA8888)
+   ADD(ABGR_8888, ABGR8888)
+   ADD(ABGR_8888_LE, ABGR8888)
+   return 0;
+}
+#undef ADD
+
+
+static int sdl_get_num_video_adapters(void)
+{
+   return SDL_GetNumVideoDisplays();
+}
+
+static bool sdl_get_monitor_info(int adapter, ALLEGRO_MONITOR_INFO *info)
+{
+   SDL_Rect rect;
+   if (SDL_GetDisplayBounds(adapter, &rect) < 0)
+      return false;
+   info->x1 = rect.x;
+   info->y1 = rect.y;
+   info->x2 = rect.x + rect.w;
+   info->y2 = rect.y + rect.h;
+   return true;
+}
+
+static int sdl_get_num_display_modes(void)
+{
+   int i = al_get_new_display_adapter();
+   if (i < 0)
+      i = 0;
+   return SDL_GetNumDisplayModes(i);
+}
+
+static ALLEGRO_DISPLAY_MODE *sdl_get_display_mode(int index, ALLEGRO_DISPLAY_MODE *mode)
+{
+   SDL_DisplayMode sdl_mode;
+   int i = al_get_new_display_adapter();
+   if (i < 0)
+      i = 0;
+   if (SDL_GetDisplayMode(i, index, &sdl_mode) < 0)
+      return NULL;
+   mode->width = sdl_mode.w;
+   mode->height = sdl_mode.h;
+   mode->format = _al_sdl_get_allegro_pixel_format(sdl_mode.format);
+   mode->refresh_rate = sdl_mode.refresh_rate;
+   return mode;
+}
+
+/* Internal function to get a reference to this driver. */
+ALLEGRO_SYSTEM_INTERFACE *_al_sdl_system_driver(void)
+{
+   if (vt)
+      return vt;
+
+   vt = al_calloc(1, sizeof *vt);
+   vt->id = AL_ID('S', 'D', 'L', '2');
+   vt->initialize = sdl_initialize;
+   vt->get_display_driver = sdl_get_display_driver;
+   vt->get_keyboard_driver = sdl_get_keyboard_driver;
+   vt->get_mouse_driver = sdl_get_mouse_driver;
+   /*
+   vt->get_touch_input_driver = sdl_get_touch_input_driver;
+   vt->get_joystick_driver = sdl_get_joystick_driver;
+   vt->get_haptic_driver = sdl_get_haptic_driver;*/
+   vt->get_num_display_modes = sdl_get_num_display_modes;
+   vt->get_display_mode = sdl_get_display_mode;
+   vt->shutdown_system = sdl_shutdown_system;
+   vt->get_num_video_adapters = sdl_get_num_video_adapters;
+   vt->get_monitor_info = sdl_get_monitor_info;
+   /*vt->create_mouse_cursor = sdl_create_mouse_cursor;
+   vt->destroy_mouse_cursor = sdl_destroy_mouse_cursor;
+   vt->get_cursor_position = sdl_get_cursor_position;
+   vt->grab_mouse = sdl_grab_mouse;
+   vt->ungrab_mouse = sdl_ungrab_mouse;*/
+   vt->get_path = sdl_get_path;
+   /*vt->inhibit_screensaver = sdl_inhibit_screensaver;
+   vt->thread_init = sdl_thread_init;
+   vt->thread_exit = sdl_thread_exit;
+   vt->open_library = sdl_open_library;
+   vt->import_symbol = sdl_import_symbol;
+   vt->close_library = sdl_close_library;*/
+   vt->heartbeat = sdl_heartbeat;
+   vt->heartbeat_init = sdl_heartbeat_init;
+
+   return vt;
+}
+
+void _al_register_system_interfaces(void)
+{
+   ALLEGRO_SYSTEM_INTERFACE **add;
+   add = _al_vector_alloc_back(&_al_system_interfaces);
+   *add = _al_sdl_system_driver();
+}
diff --git a/src/sdl/sdl_thread.c b/src/sdl/sdl_thread.c
new file mode 100644
index 0000000..1d90071
--- /dev/null
+++ b/src/sdl/sdl_thread.c
@@ -0,0 +1,89 @@
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_thread.h"
+#include "allegro5/platform/allegro_internal_sdl.h"
+
+static int thread_trampoline(void* data)
+{
+   _AL_THREAD *thread = data;
+   (*thread->proc)(thread, thread->arg);
+   return 0;
+}
+
+void _al_thread_create(_AL_THREAD *thread, void (*proc)(_AL_THREAD*, void*),
+   void *arg)
+{
+   ASSERT(thread);
+   ASSERT(proc);
+   thread->should_stop = false;
+   thread->proc = proc;
+   thread->arg = arg;
+   thread->thread = SDL_CreateThread(thread_trampoline, "allegro", thread);
+}
+
+void _al_thread_set_should_stop(_AL_THREAD *thread)
+{
+   ASSERT(thread);
+   thread->should_stop = true;
+}
+
+void _al_thread_join(_AL_THREAD *thread)
+{
+   ASSERT(thread);
+   _al_thread_set_should_stop(thread);
+   int r;
+   SDL_WaitThread(thread->thread, &r);
+}
+
+void _al_thread_detach(_AL_THREAD *thread)
+{
+   ASSERT(thread);
+   SDL_DetachThread(thread->thread);
+}
+
+/* mutexes */
+
+void _al_mutex_init(_AL_MUTEX *mutex)
+{
+   ASSERT(mutex);
+    
+   mutex->mutex = SDL_CreateMutex();
+   mutex->lock_count = 0;
+}
+
+static void free_tls(void *v)
+{
+   al_free(v);
+}
+
+void _al_mutex_init_recursive(_AL_MUTEX *mutex)
+{
+   ASSERT(mutex);
+    
+   mutex->mutex = SDL_CreateMutex();
+   mutex->lock_count = SDL_TLSCreate();
+   int *v = al_calloc(1, sizeof *v);
+   SDL_TLSSet(mutex->lock_count, v, free_tls);
+}
+
+void _al_mutex_destroy(_AL_MUTEX *mutex)
+{
+   ASSERT(mutex);
+
+   if (mutex->mutex) {
+      SDL_DestroyMutex(mutex->mutex);
+      mutex->mutex = NULL;
+   }
+}
+
+/* condition variables */
+/* most of the condition variable implementation is actually inline */
+
+int _al_cond_timedwait(_AL_COND *cond, _AL_MUTEX *mutex,
+   const ALLEGRO_TIMEOUT *timeout)
+{
+   ALLEGRO_TIMEOUT_SDL *timeout_sdl = (void *)timeout;
+   int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, timeout_sdl->ms);
+
+   return (r == SDL_MUTEX_TIMEDOUT) ? -1 : 0;
+}
diff --git a/src/sdl/sdl_time.c b/src/sdl/sdl_time.c
new file mode 100644
index 0000000..cdfc5f3
--- /dev/null
+++ b/src/sdl/sdl_time.c
@@ -0,0 +1,35 @@
+#include "SDL.h"
+
+#include "allegro5/altime.h"
+#include "allegro5/platform/allegro_sdl_thread.h"
+#include "allegro5/debug.h"
+
+ALLEGRO_DEBUG_CHANNEL("SDL")
+
+/* Function: al_get_time
+ */
+double al_get_time(void)
+{
+   return 1.0 * SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
+}
+
+
+
+/* Function: al_rest
+ */
+void al_rest(double seconds)
+{
+   SDL_Delay(seconds * 1000);
+}
+
+
+
+/* Function: al_init_timeout
+ */
+void al_init_timeout(ALLEGRO_TIMEOUT *timeout, double seconds)
+{
+   ALLEGRO_TIMEOUT_SDL *timeout_sdl = (void *)timeout;
+   timeout_sdl->ms = seconds * 1000;
+}
+
+/* vim: set sts=3 sw=3 et */
diff --git a/src/system.c b/src/system.c
index ce47a88..66f9f9b 100644
--- a/src/system.c
+++ b/src/system.c
@@ -104,6 +104,8 @@ static ALLEGRO_PATH *early_get_exename_path(void)
    return _al_unix_get_path(ALLEGRO_EXENAME_PATH);
 #elif defined(ALLEGRO_ANDROID)
    return _al_android_get_path(ALLEGRO_EXENAME_PATH);
+#elif defined(ALLEGRO_SDL)
+   return al_create_path_for_directory(SDL_GetBasePath());
 #else
    #error early_get_exename_path not implemented
 #endif
@@ -271,6 +273,9 @@ bool al_install_system(int version, int (*atexit_ptr)(void (*)(void)))
 
    _al_init_timers();
 
+   if (active_sysdrv->vt->heartbeat_init)
+      active_sysdrv->vt->heartbeat_init();
+
    if (atexit_ptr && atexit_virgin) {
       atexit_ptr(al_uninstall_system);
       atexit_virgin = false;


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/