fixed compiler warning due to missing function prototype
[rocksndiamonds.git] / src / libgame / sdl.c
index 893f765351bf8c45a9a58e0cf3ddde11d61de68e..2c8e116acf4025ab75f06e323806050b746d9d04 100644 (file)
 #include "misc.h"
 #include "setup.h"
 
-#define ENABLE_UNUSED_CODE     0       /* currently unused functions */
+#define ENABLE_UNUSED_CODE     0       // currently unused functions
 
+#define DEBUG_JOYSTICKS                0
 
-/* ========================================================================= */
-/* video functions                                                           */
-/* ========================================================================= */
 
-/* SDL internal variables */
-#if defined(TARGET_SDL2)
+// ============================================================================
+// video functions
+// ============================================================================
+
+// SDL internal variables
 static SDL_Window *sdl_window = NULL;
 static SDL_Renderer *sdl_renderer = NULL;
 static SDL_Texture *sdl_texture_stream = NULL;
 static SDL_Texture *sdl_texture_target = NULL;
 static boolean fullscreen_enabled = FALSE;
-#endif
-
 static boolean limit_screen_updates = FALSE;
 
 
-/* functions from SGE library */
+// functions from SGE library
 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
 
+#if defined(USE_TOUCH_INPUT_OVERLAY)
+// functions to draw overlay graphics for touch device input
+static void DrawTouchInputOverlay(void);
+#endif
+
 void SDLLimitScreenUpdates(boolean enable)
 {
   limit_screen_updates = enable;
@@ -55,12 +59,16 @@ static void FinalizeScreen(int draw_target)
   // copy global animations to render target buffer, if defined (above border)
   if (gfx.draw_global_anim_function != NULL)
     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
+
+  // copy tile selection cursor to render target buffer, if defined (above all)
+  if (gfx.draw_tile_cursor_function != NULL)
+    gfx.draw_tile_cursor_function(draw_target);
 }
 
 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
 {
   static unsigned int update_screen_delay = 0;
-  unsigned int update_screen_delay_value = 50;         /* (milliseconds) */
+  unsigned int update_screen_delay_value = 50;         // (milliseconds)
   SDL_Surface *screen = backbuffer->surface;
 
   if (limit_screen_updates &&
@@ -103,7 +111,6 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
     rect = NULL;
   }
 
-#if defined(TARGET_SDL2)
   SDL_Texture *sdl_texture = sdl_texture_stream;
 
   // deactivate use of target texture if render targets are not supported
@@ -129,6 +136,56 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
   }
 
+  int xoff = video.screen_xoffset;
+  int yoff = video.screen_yoffset;
+  SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
+  SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
+  SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
+
+  if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
+      video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
+    dst_rect2 = &dst_rect_screen;
+  else
+    dst_rect1 = &dst_rect_screen;
+
+#if defined(HAS_SCREEN_KEYBOARD)
+  if (video.shifted_up || video.shifted_up_delay)
+  {
+    int time_current = SDL_GetTicks();
+    int pos = video.shifted_up_pos;
+    int pos_last = video.shifted_up_pos_last;
+
+    if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
+                        time_current))
+    {
+      int delay = time_current - video.shifted_up_delay;
+      int delay_value = video.shifted_up_delay_value;
+
+      pos = pos_last + (pos - pos_last) * delay / delay_value;
+    }
+    else
+    {
+      video.shifted_up_pos_last = pos;
+      video.shifted_up_delay = 0;
+    }
+
+    SDL_Rect src_rect_up = { 0,    pos,  video.width, video.height - pos };
+    SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
+
+    if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
+       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
+    {
+      src_rect2 = &src_rect_up;
+      dst_rect2 = &dst_rect_up;
+    }
+    else
+    {
+      src_rect1 = &src_rect_up;
+      dst_rect1 = &dst_rect_up;
+    }
+  }
+#endif
+
   // clear render target buffer
   SDL_RenderClear(sdl_renderer);
 
@@ -139,7 +196,7 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
 
   // copy backbuffer texture to render target buffer
   if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
-    SDL_RenderCopy(sdl_renderer, sdl_texture_stream, NULL, NULL);
+    SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
 
   if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
     FinalizeScreen(DRAW_TO_SCREEN);
@@ -149,23 +206,20 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
   {
     SDL_SetRenderTarget(sdl_renderer, NULL);
-    SDL_RenderCopy(sdl_renderer, sdl_texture_target, NULL, NULL);
+    SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
   }
+
+#if defined(USE_TOUCH_INPUT_OVERLAY)
+  // draw overlay graphics for touch device input, if needed
+  DrawTouchInputOverlay();
 #endif
 
   // global synchronization point of the game to align video frame delay
   if (with_frame_delay)
     WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
 
-#if defined(TARGET_SDL2)
- // show render target buffer on screen
+  // show render target buffer on screen
   SDL_RenderPresent(sdl_renderer);
-#else  // TARGET_SDL
-  if (rect)
-    SDL_UpdateRects(screen, 1, rect);
-  else
-    SDL_UpdateRect(screen, 0, 0, 0, 0);
-#endif
 }
 
 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
@@ -178,10 +232,24 @@ static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
   UpdateScreenExt(rect, FALSE);
 }
 
+void Delay_WithScreenUpdates(unsigned int delay)
+{
+  unsigned int time_current = SDL_GetTicks();
+  unsigned int time_delayed = time_current + delay;
+
+  while (time_current < time_delayed)
+  {
+    // updating the screen contains waiting for frame delay (non-busy)
+    UpdateScreen_WithFrameDelay(NULL);
+
+    time_current = SDL_GetTicks();
+  }
+}
+
 static void SDLSetWindowIcon(char *basename)
 {
-  /* (setting the window icon on Mac OS X would replace the high-quality
-     dock icon with the currently smaller (and uglier) icon from file) */
+  // (setting the window icon on Mac OS X would replace the high-quality
+  // dock icon with the currently smaller (and uglier) icon from file)
 
 #if !defined(PLATFORM_MACOSX)
   char *filename = getCustomImageFilename(basename);
@@ -201,20 +269,14 @@ static void SDLSetWindowIcon(char *basename)
     return;
   }
 
-  /* set transparent color */
+  // set transparent color
   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
                  SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
 
-#if defined(TARGET_SDL2)
   SDL_SetWindowIcon(sdl_window, surface);
-#else
-  SDL_WM_SetIcon(surface, NULL);
-#endif
 #endif
 }
 
-#if defined(TARGET_SDL2)
-
 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
                                   SDL_PixelFormat *format2)
 {
@@ -308,74 +370,11 @@ boolean SDLSetNativeSurface(SDL_Surface **surface)
   return TRUE;
 }
 
-#else
-
-static Pixel SDLGetColorKey(SDL_Surface *surface)
-{
-  if ((surface->flags & SDL_SRCCOLORKEY) == 0)
-    return -1;
-
-  return surface->format->colorkey;
-}
-
-static boolean SDLHasColorKey(SDL_Surface *surface)
-{
-  return (SDLGetColorKey(surface) != -1);
-}
-
-static boolean SDLHasAlpha(SDL_Surface *surface)
-{
-  return ((surface->flags & SDL_SRCALPHA) != 0);
-}
-
-static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
-{
-  SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
-}
-
-SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
+static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
 {
-  SDL_Surface *new_surface;
-
-  if (surface == NULL)
+  if (program.headless)
     return NULL;
 
-  if (video.initialized)
-    new_surface = SDL_DisplayFormat(surface);
-  else
-    new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
-
-  if (new_surface == NULL)
-    Error(ERR_EXIT, "%s() failed: %s",
-         (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
-         SDL_GetError());
-
-  return new_surface;
-}
-
-boolean SDLSetNativeSurface(SDL_Surface **surface)
-{
-  SDL_Surface *new_surface;
-
-  if (surface == NULL ||
-      *surface == NULL ||
-      !video.initialized)
-    return FALSE;
-
-  new_surface = SDLGetNativeSurface(*surface);
-
-  SDL_FreeSurface(*surface);
-
-  *surface = new_surface;
-
-  return TRUE;
-}
-
-#endif
-
-#if defined(TARGET_SDL2)
-static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
-{
   SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
 
   if (texture == NULL)
@@ -384,11 +383,9 @@ static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
 
   return texture;
 }
-#endif
 
 void SDLCreateBitmapTextures(Bitmap *bitmap)
 {
-#if defined(TARGET_SDL2)
   if (bitmap == NULL)
     return;
 
@@ -399,12 +396,10 @@ void SDLCreateBitmapTextures(Bitmap *bitmap)
 
   bitmap->texture        = SDLCreateTextureFromSurface(bitmap->surface);
   bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
-#endif
 }
 
 void SDLFreeBitmapTextures(Bitmap *bitmap)
 {
-#if defined(TARGET_SDL2)
   if (bitmap == NULL)
     return;
 
@@ -415,60 +410,48 @@ void SDLFreeBitmapTextures(Bitmap *bitmap)
 
   bitmap->texture = NULL;
   bitmap->texture_masked = NULL;
-#endif
 }
 
 void SDLInitVideoDisplay(void)
 {
-#if !defined(TARGET_SDL2)
-  if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
-    SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
-
-  SDL_putenv("SDL_VIDEO_CENTERED=1");
-#endif
-
-  /* initialize SDL video */
+  // initialize SDL video
   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
 
-  /* set default SDL depth */
-#if !defined(TARGET_SDL2)
-  video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
-#else
+  // set default SDL depth
   video.default_depth = 32;    // (how to determine video depth in SDL2?)
-#endif
+  //
+  // Code used with SDL 1.2:
+  // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
 }
 
-void SDLInitVideoBuffer(boolean fullscreen)
+static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
 {
+  if (program.headless)
+    return;
+
   video.window_scaling_percent = setup.window_scaling_percent;
   video.window_scaling_quality = setup.window_scaling_quality;
 
   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
 
-#if defined(TARGET_SDL2)
   // SDL 2.0: support for (desktop) fullscreen mode available
   video.fullscreen_available = TRUE;
-#else
-  // SDL 1.2: no support for fullscreen mode in R'n'D anymore
-  video.fullscreen_available = FALSE;
-#endif
 
-  /* open SDL video output device (window or fullscreen mode) */
+  // open SDL video output device (window or fullscreen mode)
   if (!SDLSetVideoMode(fullscreen))
     Error(ERR_EXIT, "setting video mode failed");
 
-  /* !!! SDL2 can only set the window icon if the window already exists !!! */
-  /* set window icon */
+  // !!! SDL2 can only set the window icon if the window already exists !!!
+  // set window icon
   SDLSetWindowIcon(program.icon_filename);
 
-  /* set window and icon title */
-#if defined(TARGET_SDL2)
-  SDL_SetWindowTitle(sdl_window, program.window_title);
-#else
-  SDL_WM_SetCaption(program.window_title, program.window_title);
-#endif
+  // set window and icon title
+  SDLSetWindowTitle();
+}
 
+static void SDLInitVideoBuffer_DrawBuffer(void)
+{
   /* SDL cannot directly draw to the visible video framebuffer like X11,
      but always uses a backbuffer, which is then blitted to the visible
      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
@@ -482,23 +465,27 @@ void SDLInitVideoBuffer(boolean fullscreen)
      buffer 'window' at the same size as the SDL backbuffer. Although it
      should never be drawn to directly, it would do no harm nevertheless. */
 
-  /* create additional (symbolic) buffer for double-buffering */
+  // create additional (symbolic) buffer for double-buffering
   ReCreateBitmap(&window, video.width, video.height);
+
+  // create dummy drawing buffer for headless mode, if needed
+  if (program.headless)
+    ReCreateBitmap(&backbuffer, video.width, video.height);
+}
+
+void SDLInitVideoBuffer(boolean fullscreen)
+{
+  SDLInitVideoBuffer_VideoBuffer(fullscreen);
+  SDLInitVideoBuffer_DrawBuffer();
 }
 
 static boolean SDLCreateScreen(boolean fullscreen)
 {
   SDL_Surface *new_surface = NULL;
 
-#if defined(TARGET_SDL2)
   int surface_flags_window     = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
-#else
-  int surface_flags_window     = SURFACE_FLAGS;
-  int surface_flags_fullscreen = SURFACE_FLAGS;        // (no fullscreen in SDL 1.2)
-#endif
 
-#if defined(TARGET_SDL2)
 #if 1
   int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
 #else
@@ -508,26 +495,27 @@ static boolean SDLCreateScreen(boolean fullscreen)
      it will try to use accelerated graphics and apparently fails miserably) */
   int renderer_flags = SDL_RENDERER_SOFTWARE;
 #endif
-#endif
+
+  SDLSetScreenSizeAndOffsets(video.width, video.height);
 
   int width  = video.width;
   int height = video.height;
+  int screen_width  = video.screen_width;
+  int screen_height = video.screen_height;
   int surface_flags = (fullscreen ? surface_flags_fullscreen :
                       surface_flags_window);
 
   // default window size is unscaled
-  video.window_width  = video.width;
-  video.window_height = video.height;
-
-#if defined(TARGET_SDL2)
+  video.window_width  = screen_width;
+  video.window_height = screen_height;
 
   // store if initial screen mode is fullscreen mode when changing screen size
   video.fullscreen_initial = fullscreen;
 
   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
 
-  video.window_width  = window_scaling_factor * width;
-  video.window_height = window_scaling_factor * height;
+  video.window_width  = window_scaling_factor * screen_width;
+  video.window_height = window_scaling_factor * screen_height;
 
   if (sdl_texture_stream)
   {
@@ -571,10 +559,12 @@ static boolean SDLCreateScreen(boolean fullscreen)
 
     if (sdl_renderer != NULL)
     {
-      SDL_RenderSetLogicalSize(sdl_renderer, width, height);
+      SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
 
+      SDLSetScreenVsyncMode(setup.vsync_mode);
+
       sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
                                             SDL_PIXELFORMAT_ARGB8888,
                                             SDL_TEXTUREACCESS_STREAMING,
@@ -609,42 +599,9 @@ static boolean SDLCreateScreen(boolean fullscreen)
     Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
   }
 
-#else  // TARGET_SDL
-
-  if (gfx.final_screen_bitmap == NULL)
-    gfx.final_screen_bitmap = CreateBitmapStruct();
-
-  gfx.final_screen_bitmap->width = width;
-  gfx.final_screen_bitmap->height = height;
-
-  gfx.final_screen_bitmap->surface =
-    SDL_SetVideoMode(width, height, video.depth, surface_flags);
-
-  if (gfx.final_screen_bitmap->surface != NULL)
-  {
-    new_surface =
-      SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
-
-    if (new_surface == NULL)
-      Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
-
-#if 0
-    new_surface = gfx.final_screen_bitmap->surface;
-    gfx.final_screen_bitmap = NULL;
-#endif
-
-  }
-  else
-  {
-    Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
-  }
-#endif
-
-#if defined(TARGET_SDL2)
   // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
   if (new_surface != NULL)
     fullscreen_enabled = fullscreen;
-#endif
 
   if (backbuffer == NULL)
     backbuffer = CreateBitmapStruct();
@@ -668,12 +625,12 @@ boolean SDLSetVideoMode(boolean fullscreen)
 
   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
   {
-    /* switch display to fullscreen mode, if available */
+    // switch display to fullscreen mode, if available
     success = SDLCreateScreen(TRUE);
 
     if (!success)
     {
-      /* switching display to fullscreen mode failed -- do not try it again */
+      // switching display to fullscreen mode failed -- do not try it again
       video.fullscreen_available = FALSE;
     }
     else
@@ -684,12 +641,12 @@ boolean SDLSetVideoMode(boolean fullscreen)
 
   if ((!fullscreen && video.fullscreen_enabled) || !success)
   {
-    /* switch display to window mode */
+    // switch display to window mode
     success = SDLCreateScreen(FALSE);
 
     if (!success)
     {
-      /* switching display to window mode failed -- should not happen */
+      // switching display to window mode failed -- should not happen
     }
     else
     {
@@ -701,9 +658,7 @@ boolean SDLSetVideoMode(boolean fullscreen)
     }
   }
 
-#if defined(TARGET_SDL2)
   SDLRedrawWindow();                   // map window
-#endif
 
 #ifdef DEBUG
 #if defined(PLATFORM_WIN32)
@@ -717,20 +672,13 @@ boolean SDLSetVideoMode(boolean fullscreen)
     boolean wminfo_success = FALSE;
 
     SDL_VERSION(&wminfo.version);
-#if defined(TARGET_SDL2)
+
     if (sdl_window)
       wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
-#else
-    wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
-#endif
 
     if (wminfo_success)
     {
-#if defined(TARGET_SDL2)
       hwnd = wminfo.info.win.window;
-#else
-      hwnd = wminfo.window;
-#endif
 
       DragAcceptFiles(hwnd, TRUE);
     }
@@ -741,24 +689,22 @@ boolean SDLSetVideoMode(boolean fullscreen)
   return success;
 }
 
-void SDLSetWindowTitle()
+void SDLSetWindowTitle(void)
 {
-#if defined(TARGET_SDL2)
+  if (sdl_window == NULL)
+    return;
+
   SDL_SetWindowTitle(sdl_window, program.window_title);
-#else
-  SDL_WM_SetCaption(program.window_title, program.window_title);
-#endif
 }
 
-#if defined(TARGET_SDL2)
 void SDLSetWindowScaling(int window_scaling_percent)
 {
   if (sdl_window == NULL)
     return;
 
   float window_scaling_factor = (float)window_scaling_percent / 100;
-  int new_window_width  = (int)(window_scaling_factor * video.width);
-  int new_window_height = (int)(window_scaling_factor * video.height);
+  int new_window_width  = (int)(window_scaling_factor * video.screen_width);
+  int new_window_height = (int)(window_scaling_factor * video.screen_height);
 
   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
 
@@ -830,11 +776,69 @@ void SDLSetWindowFullscreen(boolean fullscreen)
     video.fullscreen_initial = FALSE;
   }
 }
+
+void SDLSetDisplaySize(void)
+{
+  SDL_Rect display_bounds;
+
+  SDL_GetDisplayBounds(0, &display_bounds);
+
+  video.display_width  = display_bounds.w;
+  video.display_height = display_bounds.h;
+
+#if 0
+  Error(ERR_DEBUG, "SDL real screen size: %d x %d",
+       video.display_width, video.display_height);
 #endif
+}
+
+void SDLSetScreenSizeAndOffsets(int width, int height)
+{
+  // set default video screen size and offsets
+  video.screen_width = width;
+  video.screen_height = height;
+  video.screen_xoffset = 0;
+  video.screen_yoffset = 0;
+
+#if defined(USE_COMPLETE_DISPLAY)
+  float ratio_video   = (float) width / height;
+  float ratio_display = (float) video.display_width / video.display_height;
+
+  if (ratio_video != ratio_display)
+  {
+    // adjust drawable screen size to cover the whole device display
+
+    if (ratio_video < ratio_display)
+      video.screen_width  *= ratio_display / ratio_video;
+    else
+      video.screen_height *= ratio_video / ratio_display;
+
+    video.screen_xoffset = (video.screen_width  - width)  / 2;
+    video.screen_yoffset = (video.screen_height - height) / 2;
+
+#if 0
+    Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
+         width, height,
+         video.screen_width, video.screen_height,
+         ratio_video, ratio_display);
+#endif
+  }
+#endif
+}
+
+void SDLSetScreenSizeForRenderer(int width, int height)
+{
+  SDL_RenderSetLogicalSize(sdl_renderer, width, height);
+}
+
+void SDLSetScreenProperties(void)
+{
+  SDLSetScreenSizeAndOffsets(video.width, video.height);
+  SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
+}
 
 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
 {
-#if defined(TARGET_SDL2)
   video.screen_rendering_mode =
     (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
      SPECIAL_RENDERING_BITMAP :
@@ -842,12 +846,22 @@ void SDLSetScreenRenderingMode(char *screen_rendering_mode)
      SPECIAL_RENDERING_TARGET:
      strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
      SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
-#else
-  video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
-#endif
 }
 
-void SDLRedrawWindow()
+void SDLSetScreenVsyncMode(char *vsync_mode)
+{
+  int interval =
+    (strEqual(vsync_mode, STR_VSYNC_MODE_NORMAL)   ? VSYNC_MODE_NORMAL :
+     strEqual(vsync_mode, STR_VSYNC_MODE_ADAPTIVE) ? VSYNC_MODE_ADAPTIVE :
+     VSYNC_MODE_OFF);
+  int result = SDL_GL_SetSwapInterval(interval);
+
+  // if adaptive vsync requested, but not supported, retry with normal vsync
+  if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
+    SDL_GL_SetSwapInterval(VSYNC_MODE_NORMAL);
+}
+
+void SDLRedrawWindow(void)
 {
   UpdateScreen_WithoutFrameDelay(NULL);
 }
@@ -855,6 +869,9 @@ void SDLRedrawWindow()
 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
                            int depth)
 {
+  if (program.headless)
+    return;
+
   SDL_Surface *surface =
     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
 
@@ -876,7 +893,6 @@ void SDLFreeBitmapPointers(Bitmap *bitmap)
   bitmap->surface = NULL;
   bitmap->surface_masked = NULL;
 
-#if defined(TARGET_SDL2)
   if (bitmap->texture)
     SDL_DestroyTexture(bitmap->texture);
   if (bitmap->texture_masked)
@@ -884,7 +900,6 @@ void SDLFreeBitmapPointers(Bitmap *bitmap)
 
   bitmap->texture = NULL;
   bitmap->texture_masked = NULL;
-#endif
 }
 
 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
@@ -918,7 +933,6 @@ void SDLBlitTexture(Bitmap *bitmap,
                    int src_x, int src_y, int width, int height,
                    int dst_x, int dst_y, int mask_mode)
 {
-#if defined(TARGET_SDL2)
   SDL_Texture *texture;
   SDL_Rect src_rect;
   SDL_Rect dst_rect;
@@ -940,7 +954,6 @@ void SDLBlitTexture(Bitmap *bitmap,
   dst_rect.h = height;
 
   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
-#endif
 }
 
 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
@@ -1005,8 +1018,8 @@ void SDLFadeRectangle(int x, int y, int width, int height,
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
-  dst_rect.w = width;          /* (ignored) */
-  dst_rect.h = height;         /* (ignored) */
+  dst_rect.w = width;          // (ignored)
+  dst_rect.h = height;         // (ignored)
 
   dst_rect2 = dst_rect;
 
@@ -1014,7 +1027,7 @@ void SDLFadeRectangle(int x, int y, int width, int height,
   if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
     SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
 
-  /* copy source and target surfaces to temporary surfaces for fading */
+  // copy source and target surfaces to temporary surfaces for fading
   if (fade_mode & FADE_TYPE_TRANSFORM)
   {
     // (source and target fading buffer already prepared)
@@ -1024,7 +1037,7 @@ void SDLFadeRectangle(int x, int y, int width, int height,
     // (target fading buffer already prepared)
     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
   }
-  else         /* FADE_TYPE_FADE_OUT */
+  else         // FADE_TYPE_FADE_OUT
   {
     // (source fading buffer already prepared)
     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
@@ -1044,17 +1057,14 @@ void SDLFadeRectangle(int x, int y, int width, int height,
     int i;
 
     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
-#if defined(TARGET_SDL2)
-    SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
-#else
-    SDL_SetAlpha(surface_target, 0, 0);                /* disable alpha blending */
-#endif
+
+    SDLSetAlpha(surface_target, FALSE, 0);     // disable alpha blending
 
     ypos[0] = -GetSimpleRandom(16);
 
     for (i = 1 ; i < melt_columns; i++)
     {
-      int r = GetSimpleRandom(3) - 1;  /* randomly choose from { -1, 0, -1 } */
+      int r = GetSimpleRandom(3) - 1;  // randomly choose from { -1, 0, -1 }
 
       ypos[i] = ypos[i - 1] + r;
 
@@ -1096,7 +1106,7 @@ void SDLFadeRectangle(int x, int y, int width, int height,
          if (ypos[i] + dy >= height)
            dy = height - ypos[i];
 
-         /* copy part of (appearing) target surface to upper area */
+         // copy part of (appearing) target surface to upper area
          src_rect.x = src_x + i * melt_pixels;
          // src_rect.y = src_y + ypos[i];
          src_rect.y = src_y;
@@ -1114,7 +1124,7 @@ void SDLFadeRectangle(int x, int y, int width, int height,
 
          ypos[i] += dy;
 
-         /* copy part of (disappearing) source surface to lower area */
+         // copy part of (disappearing) source surface to lower area
          src_rect.x = src_x + i * melt_pixels;
          src_rect.y = src_y;
          src_rect.w = melt_pixels;
@@ -1161,11 +1171,8 @@ void SDLFadeRectangle(int x, int y, int width, int height,
     int xx_size = width / 2;
 
     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
-#if defined(TARGET_SDL2)
-    SDL_SetSurfaceBlendMode(surface_source, SDL_BLENDMODE_NONE);
-#else
-    SDL_SetAlpha(surface_source, 0, 0);                /* disable alpha blending */
-#endif
+
+    SDLSetAlpha(surface_source, FALSE, 0);     // disable alpha blending
 
     for (xx = 0; xx < xx_size;)
     {
@@ -1182,7 +1189,7 @@ void SDLFadeRectangle(int x, int y, int width, int height,
       dst_rect.x = dst_x;
       dst_rect.y = dst_y;
 
-      /* draw new (target) image to screen buffer */
+      // draw new (target) image to screen buffer
       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
 
       if (xx_final < xx_size)
@@ -1190,14 +1197,14 @@ void SDLFadeRectangle(int x, int y, int width, int height,
        src_rect.w = xx_size - xx_final;
        src_rect.h = height;
 
-       /* draw old (source) image to screen buffer (left side) */
+       // draw old (source) image to screen buffer (left side)
 
        src_rect.x = src_x + xx_final;
        dst_rect.x = dst_x;
 
        SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
 
-       /* draw old (source) image to screen buffer (right side) */
+       // draw old (source) image to screen buffer (right side)
 
        src_rect.x = src_x + xx_size;
        dst_rect.x = dst_x + xx_size + xx_final;
@@ -1208,11 +1215,11 @@ void SDLFadeRectangle(int x, int y, int width, int height,
       if (draw_border_function != NULL)
        draw_border_function();
 
-      /* only update the region of the screen that is affected from fading */
+      // only update the region of the screen that is affected from fading
       UpdateScreen_WithFrameDelay(&dst_rect2);
     }
   }
-  else         /* fading in, fading out or cross-fading */
+  else         // fading in, fading out or cross-fading
   {
     float alpha;
     int alpha_final;
@@ -1224,41 +1231,23 @@ void SDLFadeRectangle(int x, int y, int width, int height,
       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
       alpha_final = MIN(MAX(0, alpha), 255);
 
-      /* draw existing (source) image to screen buffer */
+      // draw existing (source) image to screen buffer
       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
 
-      /* draw new (target) image to screen buffer using alpha blending */
-#if defined(TARGET_SDL2)
-      SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
-      SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
-#else
-      SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
-#endif
+      // draw new (target) image to screen buffer using alpha blending
+      SDLSetAlpha(surface_target, TRUE, alpha_final);
       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
 
       if (draw_border_function != NULL)
        draw_border_function();
 
-      /* only update the region of the screen that is affected from fading */
+      // only update the region of the screen that is affected from fading
       UpdateScreen_WithFrameDelay(&dst_rect);
     }
   }
 
   if (post_delay > 0)
-  {
-    unsigned int time_post_delay;
-
-    time_current = SDL_GetTicks();
-    time_post_delay = time_current + post_delay;
-
-    while (time_current < time_post_delay)
-    {
-      // updating the screen contains waiting for frame delay (non-busy)
-      UpdateScreen_WithFrameDelay(NULL);
-
-      time_current = SDL_GetTicks();
-    }
-  }
+    Delay_WithScreenUpdates(post_delay);
 
   // restore function for drawing global masked border
   gfx.draw_global_border_function = draw_global_border_function;
@@ -1330,21 +1319,21 @@ Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
 
   switch (surface->format->BytesPerPixel)
   {
-    case 1:            /* assuming 8-bpp */
+    case 1:            // assuming 8-bpp
     {
       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
     }
     break;
 
-    case 2:            /* probably 15-bpp or 16-bpp */
+    case 2:            // probably 15-bpp or 16-bpp
     {
       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
     }
     break;
 
-  case 3:              /* slow 24-bpp mode; usually not used */
+  case 3:              // slow 24-bpp mode; usually not used
     {
-      /* does this work? */
+      // does this work?
       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
       Uint32 color = 0;
       int shift;
@@ -1360,7 +1349,7 @@ Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
     }
     break;
 
-  case 4:              /* probably 32-bpp */
+  case 4:              // probably 32-bpp
     {
       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
     }
@@ -1371,13 +1360,13 @@ Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
 }
 
 
-/* ========================================================================= */
-/* The following functions were taken from the SGE library                   */
-/* (SDL Graphics Extension Library) by Anders Lindström                      */
-/* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
-/* ========================================================================= */
+// ============================================================================
+// The following functions were taken from the SGE library
+// (SDL Graphics Extension Library) by Anders Lindström
+// http://www.etek.chalmers.se/~e8cal1/sge/index.html
+// ============================================================================
 
-void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
   {
@@ -1385,25 +1374,25 @@ void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
     {
       case 1:
       {
-       /* Assuming 8-bpp */
+       // Assuming 8-bpp
        *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
       }
       break;
 
       case 2:
       {
-       /* Probably 15-bpp or 16-bpp */
+       // Probably 15-bpp or 16-bpp
        *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
       }
       break;
 
       case 3:
       {
-       /* Slow 24-bpp mode, usually not used */
+       // Slow 24-bpp mode, usually not used
        Uint8 *pix;
        int shift;
 
-       /* Gack - slow, but endian correct */
+       // Gack - slow, but endian correct
        pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
        shift = surface->format->Rshift;
        *(pix+shift/8) = color>>shift;
@@ -1416,7 +1405,7 @@ void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 
       case 4:
       {
-       /* Probably 32-bpp */
+       // Probably 32-bpp
        *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
       }
       break;
@@ -1424,28 +1413,29 @@ void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
   }
 }
 
-void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
-                 Uint8 R, Uint8 G, Uint8 B)
+#if 0
+static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
+                        Uint8 R, Uint8 G, Uint8 B)
 {
   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
 }
 
-void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
 }
 
-void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
 }
 
-void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   Uint8 *pix;
   int shift;
 
-  /* Gack - slow, but endian correct */
+  // Gack - slow, but endian correct
   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
   shift = surface->format->Rshift;
   *(pix+shift/8) = color>>shift;
@@ -1455,12 +1445,12 @@ void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
   *(pix+shift/8) = color>>shift;
 }
 
-void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
 }
 
-void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
+static void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
 {
   switch (dest->format->BytesPerPixel)
   {
@@ -1481,8 +1471,9 @@ void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
       break;
   }
 }
+#endif
 
-void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
+static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
 {
   if (SDL_MUSTLOCK(surface))
   {
@@ -1500,13 +1491,14 @@ void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
   }
 }
 
-void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
-                 Uint8 r, Uint8 g, Uint8 b)
+#if 0
+static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
+                           Uint8 r, Uint8 g, Uint8 b)
 {
   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
 }
 
-Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
+static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
 {
   if (y >= 0 && y <= dest->h - 1)
   {
@@ -1533,7 +1525,8 @@ Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
   return -1;
 }
 
-void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
+static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
+                         Uint32 color)
 {
   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
   {
@@ -1541,25 +1534,25 @@ void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
     {
       case 1:
       {
-       /* Assuming 8-bpp */
+       // Assuming 8-bpp
        *((Uint8 *)surface->pixels + ypitch + x) = color;
       }
       break;
 
       case 2:
       {
-       /* Probably 15-bpp or 16-bpp */
+       // Probably 15-bpp or 16-bpp
        *((Uint16 *)surface->pixels + ypitch + x) = color;
       }
       break;
 
       case 3:
       {
-       /* Slow 24-bpp mode, usually not used */
+       // Slow 24-bpp mode, usually not used
        Uint8 *pix;
        int shift;
 
-       /* Gack - slow, but endian correct */
+       // Gack - slow, but endian correct
        pix = (Uint8 *)surface->pixels + ypitch + x*3;
        shift = surface->format->Rshift;
        *(pix+shift/8) = color>>shift;
@@ -1572,7 +1565,7 @@ void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
 
       case 4:
       {
-       /* Probably 32-bpp */
+       // Probably 32-bpp
        *((Uint32 *)surface->pixels + ypitch + x) = color;
       }
       break;
@@ -1580,8 +1573,8 @@ void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
   }
 }
 
-void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
-              Uint32 Color)
+static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
+                     Uint32 Color)
 {
   SDL_Rect l;
 
@@ -1600,7 +1593,7 @@ void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
     x2 = tmp;
   }
 
-  /* Do the clipping */
+  // Do the clipping
   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
     return;
   if (x1 < 0)
@@ -1621,13 +1614,14 @@ void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
   }
 }
 
-void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
-                 Uint8 R, Uint8 G, Uint8 B)
+static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
+                        Uint8 R, Uint8 G, Uint8 B)
 {
   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
 }
 
-void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
+static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
+                  Uint32 Color)
 {
   SDL_Rect l;
 
@@ -1638,7 +1632,7 @@ void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
     x2 = tmp;
   }
 
-  /* Do the clipping */
+  // Do the clipping
   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
     return;
   if (x1 < 0)
@@ -1654,8 +1648,8 @@ void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
   SDL_FillRect(Surface, &l, Color);
 }
 
-void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
-              Uint32 Color)
+static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
+                     Uint32 Color)
 {
   SDL_Rect l;
 
@@ -1674,7 +1668,7 @@ void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
     y2 = tmp;
   }
 
-  /* Do the clipping */
+  // Do the clipping
   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
     return;
   if (y1 < 0)
@@ -1695,13 +1689,14 @@ void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
   }
 }
 
-void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
-                 Uint8 R, Uint8 G, Uint8 B)
+static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
+                        Uint8 R, Uint8 G, Uint8 B)
 {
   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
 }
 
-void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
+static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
+                  Uint32 Color)
 {
   SDL_Rect l;
 
@@ -1712,7 +1707,7 @@ void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
     y2 = tmp;
   }
 
-  /* Do the clipping */
+  // Do the clipping
   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
     return;
   if (y1 < 0)
@@ -1727,11 +1722,12 @@ void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
 
   SDL_FillRect(Surface, &l, Color);
 }
+#endif
 
-void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
-               Sint16 x2, Sint16 y2, Uint32 Color,
-               void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
-                             Uint32 Color))
+static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
+                      Sint16 x2, Sint16 y2, Uint32 Color,
+                      void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
+                                    Uint32 Color))
 {
   Sint16 dx, dy, sdx, sdy, x, y, px, py;
 
@@ -1783,14 +1779,16 @@ void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
   }
 }
 
-void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
-                  Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
-                  void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
-                                Uint32 Color))
+#if 0
+static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
+                         Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
+                         void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
+                                       Uint32 Color))
 {
   sge_DoLine(Surface, X1, Y1, X2, Y2,
             SDL_MapRGB(Surface->format, R, G, B), Callback);
 }
+#endif
 
 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
              Uint32 Color)
@@ -1801,21 +1799,23 @@ void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
       return;
    }
 
-   /* Draw the line */
+   // Draw the line
    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
 
-   /* unlock the display */
+   // unlock the display
    if (SDL_MUSTLOCK(Surface))
    {
       SDL_UnlockSurface(Surface);
    }
 }
 
-void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
-                Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
+#if 0
+static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
+                       Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
 {
   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
 }
+#endif
 
 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
 {
@@ -1823,11 +1823,9 @@ void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
 }
 
 
-/*
-  -----------------------------------------------------------------------------
-  quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
-  -----------------------------------------------------------------------------
-*/
+// ----------------------------------------------------------------------------
+// quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
+// ----------------------------------------------------------------------------
 
 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
                   int width, int height, Uint32 color)
@@ -1864,19 +1862,17 @@ void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
 }
 
 
-/* ========================================================================= */
-/* The following functions were taken from the SDL_gfx library version 2.0.3 */
-/* (Rotozoomer) by Andreas Schiffler                                         */
-/* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
-/* ========================================================================= */
-
-/*
-  -----------------------------------------------------------------------------
-  32 bit zoomer
+// ============================================================================
+// The following functions were taken from the SDL_gfx library version 2.0.3
+// (Rotozoomer) by Andreas Schiffler
+// http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
+// ============================================================================
 
-  zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
-  -----------------------------------------------------------------------------
-*/
+// ----------------------------------------------------------------------------
+// 32 bit zoomer
+//
+// zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
+// ----------------------------------------------------------------------------
 
 typedef struct
 {
@@ -1886,13 +1882,13 @@ typedef struct
   Uint8 a;
 } tColorRGBA;
 
-int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
+static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
 {
   int x, y;
   tColorRGBA *sp, *csp, *dp;
   int dgap;
 
-  /* pointer setup */
+  // pointer setup
   sp = csp = (tColorRGBA *) src->pixels;
   dp = (tColorRGBA *) dst->pixels;
   dgap = dst->pitch - dst->w * 4;
@@ -1911,60 +1907,60 @@ int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
       tColorRGBA *sp11 = &sp1[1];
       tColorRGBA new;
 
-      /* create new color pixel from all four source color pixels */
+      // create new color pixel from all four source color pixels
       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
 
-      /* draw */
+      // draw
       *dp = new;
 
-      /* advance source pointers */
+      // advance source pointers
       sp += 2;
 
-      /* advance destination pointer */
+      // advance destination pointer
       dp++;
     }
 
-    /* advance source pointer */
+    // advance source pointer
     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
 
-    /* advance destination pointers */
+    // advance destination pointers
     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
   }
 
   return 0;
 }
 
-int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
+static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 {
   int x, y, *sax, *say, *csax, *csay;
   float sx, sy;
   tColorRGBA *sp, *csp, *csp0, *dp;
   int dgap;
 
-  /* use specialized zoom function when scaling down to exactly half size */
+  // use specialized zoom function when scaling down to exactly half size
   if (src->w == 2 * dst->w &&
       src->h == 2 * dst->h)
     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
 
-  /* variable setup */
+  // variable setup
   sx = (float) src->w / (float) dst->w;
   sy = (float) src->h / (float) dst->h;
 
-  /* allocate memory for row increments */
+  // allocate memory for row increments
   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
 
-  /* precalculate row increments */
+  // precalculate row increments
   for (x = 0; x <= dst->w; x++)
     *csax++ = (int)(sx * x);
 
   for (y = 0; y <= dst->h; y++)
     *csay++ = (int)(sy * y);
 
-  /* pointer setup */
+  // pointer setup
   sp = csp = csp0 = (tColorRGBA *) src->pixels;
   dp = (tColorRGBA *) dst->pixels;
   dgap = dst->pitch - dst->w * 4;
@@ -1977,22 +1973,22 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 
     for (x = 0; x < dst->w; x++)
     {
-      /* draw */
+      // draw
       *dp = *sp;
 
-      /* advance source pointers */
+      // advance source pointers
       csax++;
       sp = csp + *csax;
 
-      /* advance destination pointer */
+      // advance destination pointer
       dp++;
     }
 
-    /* advance source pointer */
+    // advance source pointer
     csay++;
     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
 
-    /* advance destination pointers */
+    // advance destination pointers
     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
   }
 
@@ -2002,29 +1998,27 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
   return 0;
 }
 
-/*
-  -----------------------------------------------------------------------------
-  8 bit zoomer
-
-  zoomes 8 bit palette/Y 'src' surface to 'dst' surface
-  -----------------------------------------------------------------------------
-*/
+// ----------------------------------------------------------------------------
+// 8 bit zoomer
+//
+// zoomes 8 bit palette/Y 'src' surface to 'dst' surface
+// ----------------------------------------------------------------------------
 
-int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
+static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
 {
   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
   Uint8 *sp, *dp, *csp;
   int dgap;
 
-  /* variable setup */
+  // variable setup
   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
 
-  /* allocate memory for row increments */
+  // allocate memory for row increments
   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
 
-  /* precalculate row increments */
+  // precalculate row increments
   csx = 0;
   csax = sax;
   for (x = 0; x < dst->w; x++)
@@ -2061,12 +2055,12 @@ int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
     csay++;
   }
 
-  /* pointer setup */
+  // pointer setup
   sp = csp = (Uint8 *) src->pixels;
   dp = (Uint8 *) dst->pixels;
   dgap = dst->pitch - dst->w;
 
-  /* draw */
+  // draw
   csay = say;
   for (y = 0; y < dst->h; y++)
   {
@@ -2074,22 +2068,22 @@ int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
     sp = csp;
     for (x = 0; x < dst->w; x++)
     {
-      /* draw */
+      // draw
       *dp = *sp;
 
-      /* advance source pointers */
+      // advance source pointers
       sp += (*csax);
       csax++;
 
-      /* advance destination pointer */
+      // advance destination pointer
       dp++;
     }
 
-    /* advance source pointer (for row) */
+    // advance source pointer (for row)
     csp += ((*csay) * src->pitch);
     csay++;
 
-    /* advance destination pointers */
+    // advance destination pointers
     dp += dgap;
   }
 
@@ -2099,18 +2093,16 @@ int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
   return 0;
 }
 
-/*
-  -----------------------------------------------------------------------------
-  zoomSurface()
-
-  Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
-  'zoomx' and 'zoomy' are scaling factors for width and height.
-  If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
-  into a 32bit RGBA format on the fly.
-  -----------------------------------------------------------------------------
-*/
+// ----------------------------------------------------------------------------
+// zoomSurface()
+//
+// Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+// 'zoomx' and 'zoomy' are scaling factors for width and height.
+// If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
+// into a 32bit RGBA format on the fly.
+// ----------------------------------------------------------------------------
 
-SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
+static SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
 {
   SDL_Surface *zoom_src = NULL;
   SDL_Surface *zoom_dst = NULL;
@@ -2121,147 +2113,178 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   if (src == NULL)
     return NULL;
 
-  /* determine if source surface is 32 bit or 8 bit */
+  // determine if source surface is 32 bit or 8 bit
   is_32bit = (src->format->BitsPerPixel == 32);
 
   if (is_32bit || src->format->BitsPerPixel == 8)
   {
-    /* use source surface 'as is' */
+    // use source surface 'as is'
     zoom_src = src;
   }
   else
   {
-    /* new source surface is 32 bit with a defined RGB ordering */
+    // new source surface is 32 bit with a defined RGB ordering
     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
-                                   0x000000ff, 0x0000ff00, 0x00ff0000, 0);
+                                   0x000000ff, 0x0000ff00, 0x00ff0000,
+                                   (src->format->Amask ? 0xff000000 : 0));
     SDL_BlitSurface(src, NULL, zoom_src, NULL);
     is_32bit = TRUE;
     is_converted = TRUE;
   }
 
-  /* allocate surface to completely contain the zoomed surface */
+  // allocate surface to completely contain the zoomed surface
   if (is_32bit)
   {
-    /* target surface is 32 bit with source RGBA/ABGR ordering */
+    // target surface is 32 bit with source RGBA/ABGR ordering
     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
                                    zoom_src->format->Rmask,
                                    zoom_src->format->Gmask,
-                                   zoom_src->format->Bmask, 0);
+                                   zoom_src->format->Bmask,
+                                   zoom_src->format->Amask);
   }
   else
   {
-    /* target surface is 8 bit */
+    // target surface is 8 bit
     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
                                    0, 0, 0, 0);
   }
 
-  /* lock source surface */
+  // lock source surface
   SDL_LockSurface(zoom_src);
 
-  /* check which kind of surface we have */
+  // check which kind of surface we have
   if (is_32bit)
   {
-    /* call the 32 bit transformation routine to do the zooming */
+    // call the 32 bit transformation routine to do the zooming
     zoomSurfaceRGBA(zoom_src, zoom_dst);
   }
   else
   {
-    /* copy palette */
+    // copy palette
     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
       zoom_dst->format->palette->colors[i] =
        zoom_src->format->palette->colors[i];
     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
 
-    /* call the 8 bit transformation routine to do the zooming */
+    // call the 8 bit transformation routine to do the zooming
     zoomSurfaceY(zoom_src, zoom_dst);
   }
 
-  /* unlock source surface */
+  // unlock source surface
   SDL_UnlockSurface(zoom_src);
 
-  /* free temporary surface */
+  // free temporary surface
   if (is_converted)
     SDL_FreeSurface(zoom_src);
 
-  /* return destination surface */
+  // return destination surface
   return zoom_dst;
 }
 
+static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
+{
+  SDL_Surface *new_surface;
+
+  if (surface == NULL)
+    return NULL;
+
+  if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
+    Error(ERR_EXIT, "SDLGetNativeSurface() failed");
+
+  // remove alpha channel from native non-transparent surface, if defined
+  SDLSetAlpha(new_surface, FALSE, 0);
+
+  // remove transparent color from native non-transparent surface, if defined
+  SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
+
+  return new_surface;
+}
+
 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
 {
   Bitmap *dst_bitmap = CreateBitmapStruct();
-  SDL_Surface **dst_surface = &dst_bitmap->surface;
+  SDL_Surface *src_surface = src_bitmap->surface_masked;
+  SDL_Surface *dst_surface;
 
-  dst_width  = MAX(1, dst_width);      /* prevent zero bitmap width */
-  dst_height = MAX(1, dst_height);     /* prevent zero bitmap height */
+  dst_width  = MAX(1, dst_width);      // prevent zero bitmap width
+  dst_height = MAX(1, dst_height);     // prevent zero bitmap height
 
   dst_bitmap->width  = dst_width;
   dst_bitmap->height = dst_height;
 
-  /* create zoomed temporary surface from source surface */
-  *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
+  // create zoomed temporary surface from source surface
+  dst_surface = zoomSurface(src_surface, dst_width, dst_height);
+
+  // create native format destination surface from zoomed temporary surface
+  SDLSetNativeSurface(&dst_surface);
+
+  // set color key for zoomed surface from source surface, if defined
+  if (SDLHasColorKey(src_surface))
+    SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
+                   SDLGetColorKey(src_surface));
 
-  /* create native format destination surface from zoomed temporary surface */
-  SDLSetNativeSurface(dst_surface);
+  // create native non-transparent surface for opaque blitting
+  dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
+
+  // set native transparent surface for masked blitting
+  dst_bitmap->surface_masked = dst_surface;
 
   return dst_bitmap;
 }
 
 
-/* ========================================================================= */
-/* load image to bitmap                                                      */
-/* ========================================================================= */
+// ============================================================================
+// load image to bitmap
+// ============================================================================
 
 Bitmap *SDLLoadImage(char *filename)
 {
   Bitmap *new_bitmap = CreateBitmapStruct();
   SDL_Surface *sdl_image_tmp;
 
+  if (program.headless)
+  {
+    // prevent sanity check warnings at later stage
+    new_bitmap->width = new_bitmap->height = 1;
+
+    return new_bitmap;
+  }
+
   print_timestamp_init("SDLLoadImage");
 
   print_timestamp_time(getBaseNamePtr(filename));
 
-  /* load image to temporary surface */
+  // load image to temporary surface
   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
-  {
-    SetError("IMG_Load(): %s", SDL_GetError());
-
-    return NULL;
-  }
+    Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
 
   print_timestamp_time("IMG_Load");
 
   UPDATE_BUSY_STATE();
 
-  /* create native non-transparent surface for current image */
-  if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
-  {
-    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
-
-    return NULL;
-  }
+  // create native non-transparent surface for current image
+  if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
+    Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
 
-  print_timestamp_time("SDL_DisplayFormat (opaque)");
+  print_timestamp_time("SDLGetNativeSurface (opaque)");
 
   UPDATE_BUSY_STATE();
 
-  /* create native transparent surface for current image */
-  if (sdl_image_tmp->format->Amask == 0)
+  // set black pixel to transparent if no alpha channel / transparent color
+  if (!SDLHasAlpha(sdl_image_tmp) &&
+      !SDLHasColorKey(sdl_image_tmp))
     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
                    SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
 
+  // create native transparent surface for current image
   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
-  {
-    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
-
-    return NULL;
-  }
+    Error(ERR_EXIT, "SDLGetNativeSurface() failed");
 
-  print_timestamp_time("SDL_DisplayFormat (masked)");
+  print_timestamp_time("SDLGetNativeSurface (masked)");
 
   UPDATE_BUSY_STATE();
 
-  /* free temporary surface */
+  // free temporary surface
   SDL_FreeSurface(sdl_image_tmp);
 
   new_bitmap->width = new_bitmap->surface->w;
@@ -2273,9 +2296,9 @@ Bitmap *SDLLoadImage(char *filename)
 }
 
 
-/* ------------------------------------------------------------------------- */
-/* custom cursor fuctions                                                    */
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
+// custom cursor fuctions
+// ----------------------------------------------------------------------------
 
 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
 {
@@ -2291,18 +2314,18 @@ void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
   static SDL_Cursor *cursor_default = NULL;
   static SDL_Cursor *cursor_current = NULL;
 
-  /* if invoked for the first time, store the SDL default cursor */
+  // if invoked for the first time, store the SDL default cursor
   if (cursor_default == NULL)
     cursor_default = SDL_GetCursor();
 
-  /* only create new cursor if cursor info (custom only) has changed */
+  // only create new cursor if cursor info (custom only) has changed
   if (cursor_info != NULL && cursor_info != last_cursor_info)
   {
     cursor_current = create_cursor(cursor_info);
     last_cursor_info = cursor_info;
   }
 
-  /* only set new cursor if cursor info (custom or NULL) has changed */
+  // only set new cursor if cursor info (custom or NULL) has changed
   if (cursor_info != last_cursor_info2)
     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
 
@@ -2310,16 +2333,14 @@ void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
 }
 
 
-/* ========================================================================= */
-/* audio functions                                                           */
-/* ========================================================================= */
+// ============================================================================
+// audio functions
+// ============================================================================
 
 void SDLOpenAudio(void)
 {
-#if !defined(TARGET_SDL2)
-  if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
-    SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
-#endif
+  if (program.headless)
+    return;
 
   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
   {
@@ -2340,7 +2361,7 @@ void SDLOpenAudio(void)
   audio.loops_available = TRUE;
   audio.sound_enabled = TRUE;
 
-  /* set number of available mixer channels */
+  // set number of available mixer channels
   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
   audio.music_channel = MUSIC_CHANNEL;
   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
@@ -2358,11 +2379,11 @@ void SDLCloseAudio(void)
 }
 
 
-/* ========================================================================= */
-/* event functions                                                           */
-/* ========================================================================= */
+// ============================================================================
+// event functions
+// ============================================================================
 
-void SDLNextEvent(Event *event)
+void SDLWaitEvent(Event *event)
 {
   SDL_WaitEvent(event);
 }
@@ -2376,17 +2397,9 @@ void SDLHandleWindowManagerEvent(Event *event)
   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
 
-#if defined(TARGET_SDL2)
   if (syswmmsg->msg.win.msg == WM_DROPFILES)
-#else
-  if (syswmmsg->msg == WM_DROPFILES)
-#endif
   {
-#if defined(TARGET_SDL2)
     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
-#else
-    HDROP hdrop = (HDROP)syswmmsg->wParam;
-#endif
     int i, num_files;
 
     printf("::: SDL_SYSWMEVENT:\n");
@@ -2403,72 +2416,231 @@ void SDLHandleWindowManagerEvent(Event *event)
       printf("::: - '%s'\n", buffer);
     }
 
-#if defined(TARGET_SDL2)
     DragFinish((HDROP)syswmmsg->msg.win.wParam);
-#else
-    DragFinish((HDROP)syswmmsg->wParam);
-#endif
   }
 #endif
 #endif
 }
 
 
-/* ========================================================================= */
-/* joystick functions                                                        */
-/* ========================================================================= */
+// ============================================================================
+// joystick functions
+// ============================================================================
 
-static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
-static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+static void *sdl_joystick[MAX_PLAYERS];                // game controller or joystick
+static int sdl_js_axis_raw[MAX_PLAYERS][2];
+static int sdl_js_axis[MAX_PLAYERS][2];
+static int sdl_js_button[MAX_PLAYERS][2];
+static boolean sdl_is_controller[MAX_PLAYERS];
 
-static boolean SDLOpenJoystick(int nr)
+void SDLClearJoystickState(void)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  int i, j;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    for (j = 0; j < 2; j++)
+    {
+      sdl_js_axis_raw[i][j] = -1;
+      sdl_js_axis[i][j] = 0;
+      sdl_js_button[i][j] = 0;
+    }
+  }
+}
+
+boolean SDLOpenJoystick(int nr)
+{
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return FALSE;
 
-  return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
+  sdl_is_controller[nr] = SDL_IsGameController(nr);
+
+#if DEBUG_JOYSTICKS
+  Error(ERR_DEBUG, "opening joystick %d (%s)",
+       nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
+#endif
+
+  if (sdl_is_controller[nr])
+    sdl_joystick[nr] = SDL_GameControllerOpen(nr);
+  else
+    sdl_joystick[nr] = SDL_JoystickOpen(nr);
+
+  return (sdl_joystick[nr] != NULL);
 }
 
-static void SDLCloseJoystick(int nr)
+void SDLCloseJoystick(int nr)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return;
 
-  SDL_JoystickClose(sdl_joystick[nr]);
+#if DEBUG_JOYSTICKS
+  Error(ERR_DEBUG, "closing joystick %d", nr);
+#endif
+
+  if (sdl_is_controller[nr])
+    SDL_GameControllerClose(sdl_joystick[nr]);
+  else
+    SDL_JoystickClose(sdl_joystick[nr]);
 
   sdl_joystick[nr] = NULL;
 }
 
-static boolean SDLCheckJoystickOpened(int nr)
+boolean SDLCheckJoystickOpened(int nr)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return FALSE;
 
-#if defined(TARGET_SDL2)
   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
-#else
-  return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
-#endif
+}
+
+static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
+{
+  int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
+                axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
+                axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
+                axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
+
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return;
+
+  if (axis_id == -1)
+    return;
+
+  // prevent (slightly jittering, but centered) axis A from resetting axis B
+  if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
+      axis_id_raw != sdl_js_axis_raw[nr][axis_id])
+    return;
+
+  sdl_js_axis[nr][axis_id] = axis_value;
+  sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
+}
+
+static void setJoystickButton(int nr, int button_id_raw, int button_state)
+{
+  int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_X ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
+                  button_id_raw == SDL_CONTROLLER_BUTTON_B ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
+                  button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
+                  -1);
+
+  if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
+    sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
+  else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
+    sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
+  else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
+    sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
+  else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
+    sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
+
+  if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
+      button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
+      button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
+      button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
+    sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
+
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return;
+
+  if (button_id == -1)
+    return;
+
+  sdl_js_button[nr][button_id] = button_state;
 }
 
 void HandleJoystickEvent(Event *event)
 {
   switch(event->type)
   {
+    case SDL_CONTROLLERDEVICEADDED:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
+           event->cdevice.which);
+#endif
+      InitJoysticks();
+      break;
+
+    case SDL_CONTROLLERDEVICEREMOVED:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
+           event->cdevice.which);
+#endif
+      InitJoysticks();
+      break;
+
+    case SDL_CONTROLLERAXISMOTION:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
+           event->caxis.which, event->caxis.axis, event->caxis.value);
+#endif
+      setJoystickAxis(event->caxis.which,
+                     event->caxis.axis,
+                     event->caxis.value);
+      break;
+
+    case SDL_CONTROLLERBUTTONDOWN:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
+           event->cbutton.which, event->cbutton.button);
+#endif
+      setJoystickButton(event->cbutton.which,
+                       event->cbutton.button,
+                       TRUE);
+      break;
+
+    case SDL_CONTROLLERBUTTONUP:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
+           event->cbutton.which, event->cbutton.button);
+#endif
+      setJoystickButton(event->cbutton.which,
+                       event->cbutton.button,
+                       FALSE);
+      break;
+
     case SDL_JOYAXISMOTION:
-      if (event->jaxis.axis < 2)
-       sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
+      if (sdl_is_controller[event->jaxis.which])
+       break;
+
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
+           event->jaxis.which, event->jaxis.axis, event->jaxis.value);
+#endif
+      if (event->jaxis.axis < 4)
+       setJoystickAxis(event->jaxis.which,
+                       event->jaxis.axis,
+                       event->jaxis.value);
       break;
 
     case SDL_JOYBUTTONDOWN:
-      if (event->jbutton.button < 2)
-       sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
+      if (sdl_is_controller[event->jaxis.which])
+       break;
+
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
+           event->jbutton.which, event->jbutton.button);
+#endif
+      if (event->jbutton.button < 4)
+       setJoystickButton(event->jbutton.which,
+                         event->jbutton.button,
+                         TRUE);
       break;
 
     case SDL_JOYBUTTONUP:
-      if (event->jbutton.button < 2)
-       sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
+      if (sdl_is_controller[event->jaxis.which])
+       break;
+
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
+           event->jbutton.which, event->jbutton.button);
+#endif
+      if (event->jbutton.button < 4)
+       setJoystickButton(event->jbutton.which,
+                         event->jbutton.button,
+                         FALSE);
       break;
 
     default:
@@ -2476,26 +2648,80 @@ void HandleJoystickEvent(Event *event)
   }
 }
 
-void SDLInitJoysticks()
+void SDLInitJoysticks(void)
 {
   static boolean sdl_joystick_subsystem_initialized = FALSE;
   boolean print_warning = !sdl_joystick_subsystem_initialized;
+  char *mappings_file_base = getPath2(options.conf_directory,
+                                     GAMECONTROLLER_BASENAME);
+  char *mappings_file_user = getPath2(getUserGameDataDir(),
+                                     GAMECONTROLLER_BASENAME);
+  int num_mappings;
   int i;
 
   if (!sdl_joystick_subsystem_initialized)
   {
     sdl_joystick_subsystem_initialized = TRUE;
 
-    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
+    SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
+
+    if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
     {
       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
       return;
     }
+
+    num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
+
+    // the included game controller base mappings should always be found
+    if (num_mappings == -1)
+      Error(ERR_WARN, "no game controller base mappings found");
+#if DEBUG_JOYSTICKS
+    else
+      Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
+#endif
+
+    num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
+
+#if DEBUG_JOYSTICKS
+    // the personal game controller user mappings may or may not be found
+    if (num_mappings == -1)
+      Error(ERR_WARN, "no game controller user mappings found");
+    else
+      Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
+
+    Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
+#endif
+
+    checked_free(mappings_file_base);
+    checked_free(mappings_file_user);
+
+#if DEBUG_JOYSTICKS
+    for (i = 0; i < SDL_NumJoysticks(); i++)
+    {
+      const char *name, *type;
+
+      if (SDL_IsGameController(i))
+      {
+       name = SDL_GameControllerNameForIndex(i);
+       type = "game controller";
+      }
+      else
+      {
+       name = SDL_JoystickNameForIndex(i);
+       type = "joystick";
+      }
+
+      Error(ERR_INFO, "- joystick %d (%s): '%s'",
+           i, type, (name ? name : "(Unknown)"));
+    }
+#endif
   }
 
+  // assign joysticks from configured to connected joystick for all players
   for (i = 0; i < MAX_PLAYERS; i++)
   {
-    /* get configured joystick for this player */
+    // get configured joystick for this player
     char *device_name = setup.input[i].joy.device_name;
     int joystick_nr = getJoystickNrFromDeviceName(device_name);
 
@@ -2507,29 +2733,24 @@ void SDLInitJoysticks()
       joystick_nr = -1;
     }
 
-    /* misuse joystick file descriptor variable to store joystick number */
-    joystick.fd[i] = joystick_nr;
-
-    if (joystick_nr == -1)
-      continue;
-
-    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
-    if (SDLCheckJoystickOpened(joystick_nr))
-      SDLCloseJoystick(joystick_nr);
-
-    if (!setup.input[i].use_joystick)
-      continue;
-
-    if (!SDLOpenJoystick(joystick_nr))
-    {
-      if (print_warning)
-       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
+    // store configured joystick number for each player
+    joystick.nr[i] = joystick_nr;
+  }
 
-      continue;
-    }
+  // now open all connected joysticks (regardless if configured or not)
+  for (i = 0; i < SDL_NumJoysticks(); i++)
+  {
+    // this allows subsequent calls to 'InitJoysticks' for re-initialization
+    if (SDLCheckJoystickOpened(i))
+      SDLCloseJoystick(i);
 
-    joystick.status = JOYSTICK_ACTIVATED;
+    if (SDLOpenJoystick(i))
+      joystick.status = JOYSTICK_ACTIVATED;
+    else if (print_warning)
+      Error(ERR_WARN, "cannot open joystick %d", i);
   }
+
+  SDLClearJoystickState();
 }
 
 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
@@ -2549,3 +2770,285 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
 
   return TRUE;
 }
+
+
+// ============================================================================
+// touch input overlay functions
+// ============================================================================
+
+#if defined(USE_TOUCH_INPUT_OVERLAY)
+static void DrawTouchInputOverlay_ShowGrid(int alpha)
+{
+  SDL_Rect rect;
+  int grid_xsize = overlay.grid_xsize;
+  int grid_ysize = overlay.grid_ysize;
+  int x, y;
+
+  SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
+  SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
+
+  for (x = 0; x < grid_xsize; x++)
+  {
+    rect.x = (x + 0) * video.screen_width / grid_xsize;
+    rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
+
+    for (y = 0; y < grid_ysize; y++)
+    {
+      rect.y = (y + 0) * video.screen_height / grid_ysize;
+      rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
+
+      if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
+       SDL_RenderDrawRect(sdl_renderer, &rect);
+    }
+  }
+
+  SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
+}
+
+static void RenderFillRectangle(int x, int y, int width, int height)
+{
+  SDL_Rect rect = { x, y, width, height };
+
+  SDL_RenderFillRect(sdl_renderer, &rect);
+}
+
+static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
+{
+  static int alpha_direction = 0;
+  static int alpha_highlight = 0;
+  int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
+  int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
+  SDL_Rect rect;
+  int grid_xsize = overlay.grid_xsize;
+  int grid_ysize = overlay.grid_ysize;
+  int x, y;
+
+  if (alpha == alpha_max)
+  {
+    if (alpha_direction < 0)
+    {
+      alpha_highlight = MAX(0, alpha_highlight - alpha_step);
+
+      if (alpha_highlight == 0)
+       alpha_direction = 1;
+    }
+    else
+    {
+      alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
+
+      if (alpha_highlight == alpha_max)
+       alpha_direction = -1;
+    }
+  }
+  else
+  {
+    alpha_direction = 1;
+    alpha_highlight = alpha;
+  }
+
+  SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
+
+  for (x = 0; x < grid_xsize; x++)
+  {
+    for (y = 0; y < grid_ysize; y++)
+    {
+      int grid_button = overlay.grid_button[x][y];
+      int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
+      int alpha_draw = alpha;
+      int outline_border = MV_NONE;
+      int border_size = 2;
+      boolean draw_outlined = setup.touch.draw_outlined;
+      boolean draw_pressed = setup.touch.draw_pressed;
+
+      if (grid_button == CHAR_GRID_BUTTON_NONE)
+       continue;
+
+      if (grid_button == overlay.grid_button_highlight)
+       alpha_draw = alpha_highlight;
+
+      if (draw_pressed && overlay.grid_button_action & grid_button_action)
+      {
+       if (draw_outlined)
+         draw_outlined = FALSE;
+       else
+         alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
+      }
+
+      SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
+
+      rect.x = (x + 0) * video.screen_width  / grid_xsize;
+      rect.y = (y + 0) * video.screen_height / grid_ysize;
+      rect.w = (x + 1) * video.screen_width  / grid_xsize - rect.x;
+      rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
+
+      if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
+      {
+       rect.x += border_size;
+       rect.w -= border_size;
+
+       outline_border |= MV_LEFT;
+      }
+
+      if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
+      {
+       rect.w -= border_size;
+
+       outline_border |= MV_RIGHT;
+      }
+
+      if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
+      {
+       rect.y += border_size;
+       rect.h -= border_size;
+
+       outline_border |= MV_UP;
+      }
+
+      if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
+      {
+       rect.h -= border_size;
+
+       outline_border |= MV_DOWN;
+      }
+
+      if (draw_outlined)
+      {
+       int rect_x = rect.x +
+         (outline_border & MV_LEFT  ? border_size : 0);
+       int rect_w = rect.w -
+         (outline_border & MV_LEFT  ? border_size : 0) -
+         (outline_border & MV_RIGHT ? border_size : 0);
+
+       if (outline_border & MV_LEFT)
+         RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
+
+       if (outline_border & MV_RIGHT)
+         RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
+                             border_size, rect.h);
+
+       if (outline_border & MV_UP)
+         RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
+
+       if (outline_border & MV_DOWN)
+         RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
+                             rect_w, border_size);
+      }
+      else
+      {
+       SDL_RenderFillRect(sdl_renderer, &rect);
+      }
+    }
+  }
+
+  SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
+}
+
+static void DrawTouchInputOverlay(void)
+{
+  static SDL_Texture *texture = NULL;
+  static boolean initialized = FALSE;
+  static boolean deactivated = TRUE;
+  static boolean show_grid = FALSE;
+  static int width = 0, height = 0;
+  static int alpha_last = -1;
+  static int alpha = 0;
+  int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
+  int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
+  boolean active = (overlay.enabled && overlay.active);
+
+  if (!active && deactivated)
+    return;
+
+  if (active)
+  {
+    if (alpha < alpha_max)
+      alpha = MIN(alpha + alpha_step, alpha_max);
+
+    deactivated = FALSE;
+  }
+  else
+  {
+    alpha = MAX(0, alpha - alpha_step);
+
+    if (alpha == 0)
+      deactivated = TRUE;
+  }
+
+  if (overlay.show_grid)
+    show_grid = TRUE;
+  else if (deactivated)
+    show_grid = FALSE;
+
+  if (show_grid)
+    DrawTouchInputOverlay_ShowGrid(alpha);
+
+  DrawTouchInputOverlay_ShowGridButtons(alpha);
+
+  return;
+
+
+  // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
+
+  if (!initialized)
+  {
+    char *basename = "overlay/VirtualButtons.png";
+    char *filename = getCustomImageFilename(basename);
+
+    if (filename == NULL)
+      Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
+
+    SDL_Surface *surface;
+
+    if ((surface = IMG_Load(filename)) == NULL)
+      Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
+
+    width  = surface->w;
+    height = surface->h;
+
+    // set black pixel to transparent if no alpha channel / transparent color
+    if (!SDLHasAlpha(surface) &&
+       !SDLHasColorKey(surface))
+      SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
+                     SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
+
+    if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
+      Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
+
+    SDL_FreeSurface(surface);
+
+    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+
+    initialized = TRUE;
+  }
+
+  if (alpha != alpha_last)
+    SDL_SetTextureAlphaMod(texture, alpha);
+
+  alpha_last = alpha;
+
+  float ratio_overlay = (float) width / height;
+  float ratio_screen = (float) video.screen_width / video.screen_height;
+  int width_scaled, height_scaled;
+  int xpos, ypos;
+
+  if (ratio_overlay > ratio_screen)
+  {
+    width_scaled = video.screen_width;
+    height_scaled = video.screen_height * ratio_screen / ratio_overlay;
+    xpos = 0;
+    ypos = video.screen_height - height_scaled;
+  }
+  else
+  {
+    width_scaled = video.screen_width * ratio_overlay / ratio_screen;
+    height_scaled = video.screen_height;
+    xpos = (video.screen_width - width_scaled) / 2;
+    ypos = 0;
+  }
+
+  SDL_Rect src_rect = { 0, 0, width, height };
+  SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
+
+  SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
+}
+#endif