fixed re-initialization of textures after change of renderer
[rocksndiamonds.git] / src / libgame / sdl.c
index e478e8aaddb27df22630345f5385705f00492bdd..d431aacf29c96c34d73b2a4db489d075608a5640 100644 (file)
@@ -1,30 +1,36 @@
-/***********************************************************
-* Artsoft Retro-Game Library                               *
-*----------------------------------------------------------*
-* (c) 1994-2002 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* sdl.c                                                    *
-***********************************************************/
+// ============================================================================
+// Artsoft Retro-Game Library
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// sdl.c
+// ============================================================================
 
 #include "system.h"
 #include "sound.h"
 #include "joystick.h"
 #include "misc.h"
 
 #include "system.h"
 #include "sound.h"
 #include "joystick.h"
 #include "misc.h"
+#include "setup.h"
 
 
+#define ENABLE_UNUSED_CODE     0       /* currently unused functions */
 
 
-#if defined(TARGET_SDL)
 
 /* ========================================================================= */
 /* video functions                                                           */
 /* ========================================================================= */
 
 
 /* ========================================================================= */
 /* video functions                                                           */
 /* ========================================================================= */
 
-/* functions from SGE library */
-void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
+/* SDL internal variables */
+#if defined(TARGET_SDL2)
+static SDL_Window *sdl_window = NULL;
+static SDL_Renderer *sdl_renderer = NULL;
+static SDL_Texture *sdl_texture = NULL;
+static boolean fullscreen_enabled = FALSE;
+
+#define USE_RENDERER   TRUE
+#endif
 
 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
 static int fullscreen_width;
 
 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
 static int fullscreen_width;
@@ -33,13 +39,131 @@ static int fullscreen_xoffset;
 static int fullscreen_yoffset;
 static int video_xoffset;
 static int video_yoffset;
 static int fullscreen_yoffset;
 static int video_xoffset;
 static int video_yoffset;
+static boolean limit_screen_updates = FALSE;
+
+
+/* functions from SGE library */
+void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
+
+void SDLLimitScreenUpdates(boolean enable)
+{
+  limit_screen_updates = enable;
+}
+
+static void UpdateScreen(SDL_Rect *rect)
+{
+  static unsigned int update_screen_delay = 0;
+  unsigned int update_screen_delay_value = 20;         /* (milliseconds) */
+  SDL_Surface *screen = backbuffer->surface;
+
+  if (limit_screen_updates &&
+      !DelayReached(&update_screen_delay, update_screen_delay_value))
+    return;
+
+  LimitScreenUpdates(FALSE);
+
+#if 0
+  {
+    static int LastFrameCounter = 0;
+    boolean changed = (FrameCounter != LastFrameCounter);
+
+    printf("::: FrameCounter == %d [%s]\n", FrameCounter,
+          (changed ? "-" : "SAME FRAME UPDATED"));
+
+    LastFrameCounter = FrameCounter;
+
+    /*
+    if (FrameCounter % 2)
+      return;
+    */
+  }
+#endif
+
+#if USE_FINAL_SCREEN_BITMAP
+  if (gfx.final_screen_bitmap != NULL) // may not be initialized yet
+  {
+    // !!! TEST !!!
+    // draw global animations using bitmaps instead of using textures
+    // to prevent texture scaling artefacts (this is potentially slower)
+
+    BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
+              gfx.win_xsize, gfx.win_ysize, 0, 0);
+
+    // copy global animations to render target buffer, if defined
+    if (gfx.draw_global_anim_function != NULL)
+      gfx.draw_global_anim_function();
+
+    screen = gfx.final_screen_bitmap->surface;
+
+    // force full window redraw
+    rect = NULL;
+  }
+#endif
+
+#if defined(TARGET_SDL2)
+#if USE_RENDERER
+  if (rect)
+  {
+    int bytes_x = screen->pitch / video.width;
+    int bytes_y = screen->pitch;
+
+    if (video.fullscreen_enabled)
+      bytes_x = screen->pitch / fullscreen_width;
+
+    SDL_UpdateTexture(sdl_texture, rect,
+                     screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
+                     screen->pitch);
+  }
+  else
+  {
+    SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
+  }
+
+  // clear render target buffer
+  SDL_RenderClear(sdl_renderer);
+
+  // copy backbuffer to render target buffer
+  SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
+
+#if !USE_FINAL_SCREEN_BITMAP
+  // copy global animations to render target buffer, if defined
+  if (gfx.draw_global_anim_function != NULL)
+    gfx.draw_global_anim_function();
+#endif
+
+  // show render target buffer on screen
+  SDL_RenderPresent(sdl_renderer);
+
+#else
+
+  if (rect)
+    SDL_UpdateWindowSurfaceRects(sdl_window, rect, 1);
+  else
+    SDL_UpdateWindowSurface(sdl_window);
+#endif
+
+#else  // TARGET_SDL
+  if (rect)
+    SDL_UpdateRects(screen, 1, rect);
+  else
+    SDL_UpdateRect(screen, 0, 0, 0, 0);
+#endif
+}
 
 
-static void setFullscreenParameters()
+static void setFullscreenParameters(char *fullscreen_mode_string)
 {
 {
+#if defined(TARGET_SDL2)
+  fullscreen_width = video.width;
+  fullscreen_height = video.height;
+  fullscreen_xoffset = 0;
+  fullscreen_yoffset = 0;  
+
+#else
+
   struct ScreenModeInfo *fullscreen_mode;
   int i;
 
   struct ScreenModeInfo *fullscreen_mode;
   int i;
 
-  fullscreen_mode = get_screen_mode_from_string(setup.fullscreen_mode);
+  fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
 
   if (fullscreen_mode == NULL)
     return;
 
   if (fullscreen_mode == NULL)
     return;
@@ -58,23 +182,214 @@ static void setFullscreenParameters()
       break;
     }
   }
       break;
     }
   }
+#endif
+}
+
+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) */
+
+#if !defined(PLATFORM_MACOSX)
+  char *filename = getCustomImageFilename(basename);
+  SDL_Surface *surface;
+
+  if (filename == NULL)
+  {
+    Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
+
+    return;
+  }
+
+  if ((surface = IMG_Load(filename)) == NULL)
+  {
+    Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
+
+    return;
+  }
+
+  /* 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)
+{
+  return (format1->format       == format2->format &&
+         format1->BitsPerPixel  == format2->BitsPerPixel &&
+         format1->BytesPerPixel == format2->BytesPerPixel &&
+         format1->Rmask         == format2->Rmask &&
+         format1->Gmask         == format2->Gmask &&
+         format1->Bmask         == format2->Bmask &&
+         format1->Amask         == format2->Amask);
+}
+
+boolean SDLSetNativeSurface(SDL_Surface **surface)
+{
+  SDL_Surface *new_surface;
+
+  if (surface == NULL ||
+      *surface == NULL ||
+      backbuffer == NULL ||
+      backbuffer->surface == NULL)
+    return FALSE;
+
+  // if pixel format already optimized for destination surface, do nothing
+  if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
+    return FALSE;
+
+  new_surface = SDL_ConvertSurface(*surface, backbuffer->surface->format, 0);
+
+  if (new_surface == NULL)
+    Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
+
+  SDL_FreeSurface(*surface);
+
+  *surface = new_surface;
+
+  return TRUE;
+}
+
+SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
+{
+  SDL_Surface *new_surface;
+
+  if (surface == NULL)
+    return NULL;
+
+  if (backbuffer && backbuffer->surface)
+    new_surface = SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
+  else
+    new_surface = SDL_ConvertSurface(surface, surface->format, 0);
+
+  if (new_surface == NULL)
+    Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
+
+  return new_surface;
+}
+
+#else
+
+boolean SDLSetNativeSurface(SDL_Surface **surface)
+{
+  SDL_Surface *new_surface;
+
+  if (surface == NULL ||
+      *surface == NULL ||
+      !video.initialized)
+    return FALSE;
+
+  new_surface = SDL_DisplayFormat(*surface);
+
+  if (new_surface == NULL)
+    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+
+  SDL_FreeSurface(*surface);
+
+  *surface = new_surface;
+
+  return TRUE;
+}
+
+SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
+{
+  SDL_Surface *new_surface;
+
+  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;
+}
+
+#endif
+
+#if defined(TARGET_SDL2)
+static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
+{
+  SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
+
+  if (texture == NULL)
+    Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
+         SDL_GetError());
+
+  return texture;
+}
+#endif
+
+void SDLCreateBitmapTextures(Bitmap *bitmap)
+{
+#if defined(TARGET_SDL2)
+  if (bitmap == NULL)
+    return;
+
+  if (bitmap->texture)
+    SDL_DestroyTexture(bitmap->texture);
+  if (bitmap->texture_masked)
+    SDL_DestroyTexture(bitmap->texture_masked);
+
+  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;
+
+  if (bitmap->texture)
+    SDL_DestroyTexture(bitmap->texture);
+  if (bitmap->texture_masked)
+    SDL_DestroyTexture(bitmap->texture_masked);
+
+  bitmap->texture = NULL;
+  bitmap->texture_masked = NULL;
+#endif
 }
 
 void SDLInitVideoDisplay(void)
 {
 }
 
 void SDLInitVideoDisplay(void)
 {
-  putenv("SDL_VIDEO_CENTERED=1");
+#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 */
   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
 
   /* set default SDL depth */
 
   /* 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;
   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
+#else
+  video.default_depth = 32;    // (how to determine video depth in SDL2?)
+#endif
 }
 
 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
                        boolean fullscreen)
 {
 }
 
 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
                        boolean fullscreen)
 {
+#if !defined(TARGET_SDL2)
   static int screen_xy[][2] =
   {
     {  640, 480 },
   static int screen_xy[][2] =
   {
     {  640, 480 },
@@ -82,7 +397,9 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     { 1024, 768 },
     {   -1,  -1 }
   };
     { 1024, 768 },
     {   -1,  -1 }
   };
-  SDL_Rect **modes;
+#endif
+  SDL_Rect **modes = NULL;
+  boolean hardware_fullscreen_available = TRUE;
   int i, j;
 
   /* default: normal game window size */
   int i, j;
 
   /* default: normal game window size */
@@ -91,6 +408,8 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
   fullscreen_xoffset = 0;
   fullscreen_yoffset = 0;
 
   fullscreen_xoffset = 0;
   fullscreen_yoffset = 0;
 
+#if !defined(TARGET_SDL2)
+  /* determine required standard fullscreen mode for game screen size */
   for (i = 0; screen_xy[i][0] != -1; i++)
   {
     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
   for (i = 0; screen_xy[i][0] != -1; i++)
   {
     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
@@ -104,14 +423,52 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
 
   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
 
   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
+#endif
+
+  checked_free(video.fullscreen_modes);
+
+  video.fullscreen_modes = NULL;
+  video.fullscreen_mode_current = NULL;
+
+  video.window_scaling_percent = setup.window_scaling_percent;
+  video.window_scaling_quality = setup.window_scaling_quality;
+
+#if defined(TARGET_SDL2)
+  int num_displays = SDL_GetNumVideoDisplays();
+
+  if (num_displays > 0)
+  {
+    // currently only display modes of first display supported
+    int num_modes = SDL_GetNumDisplayModes(0);
+
+    if (num_modes > 0)
+    {
+      modes = checked_calloc((num_modes + 1) * sizeof(SDL_Rect *));
+
+      for (i = 0; i < num_modes; i++)
+      {
+       SDL_DisplayMode mode;
+
+       if (SDL_GetDisplayMode(0, i, &mode) < 0)
+         break;
+
+       modes[i] = checked_calloc(sizeof(SDL_Rect));
 
 
+       modes[i]->w = mode.w;
+       modes[i]->h = mode.h;
+      }
+    }
+  }
+#else
   /* get available hardware supported fullscreen modes */
   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
   /* get available hardware supported fullscreen modes */
   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
+#endif
 
   if (modes == NULL)
   {
 
   if (modes == NULL)
   {
-    /* no screen modes available => no fullscreen mode support */
-    video.fullscreen_available = FALSE;
+    /* no hardware screen modes available => no fullscreen mode support */
+    // video.fullscreen_available = FALSE;
+    hardware_fullscreen_available = FALSE;
   }
   else if (modes == (SDL_Rect **)-1)
   {
   }
   else if (modes == (SDL_Rect **)-1)
   {
@@ -130,7 +487,7 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     /* in this case, a certain number of screen modes is available */
     int num_modes = 0;
 
     /* in this case, a certain number of screen modes is available */
     int num_modes = 0;
 
-    for(i = 0; modes[i] != NULL; i++)
+    for (i = 0; modes[i] != NULL; i++)
     {
       boolean found_mode = FALSE;
 
     {
       boolean found_mode = FALSE;
 
@@ -165,16 +522,43 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     if (num_modes == 0)
     {
       /* no appropriate screen modes available => no fullscreen mode support */
     if (num_modes == 0)
     {
       /* no appropriate screen modes available => no fullscreen mode support */
-      video.fullscreen_available = FALSE;
+      // video.fullscreen_available = FALSE;
+      hardware_fullscreen_available = FALSE;
     }
   }
 
     }
   }
 
+  video.fullscreen_available = hardware_fullscreen_available;
+
+#if USE_DESKTOP_FULLSCREEN
+  // in SDL 2.0, there is always support for desktop fullscreen mode
+  // (in SDL 1.2, there is only support for "real" fullscreen mode)
+  video.fullscreen_available = TRUE;
+#endif
+
+#if defined(TARGET_SDL2)
+  if (modes)
+  {
+    for (i = 0; modes[i] != NULL; i++)
+      checked_free(modes[i]);
+
+    checked_free(modes);
+  }
+#endif
+
   /* open SDL video output device (window or fullscreen mode) */
   if (!SDLSetVideoMode(backbuffer, fullscreen))
     Error(ERR_EXIT, "setting video mode failed");
 
   /* open SDL video output device (window or fullscreen mode) */
   if (!SDLSetVideoMode(backbuffer, fullscreen))
     Error(ERR_EXIT, "setting video mode failed");
 
+  /* !!! SDL2 can only set the window icon if the window already exists !!! */
+  /* set window icon */
+  SDLSetWindowIcon(program.icon_filename);
+
   /* set window and icon title */
   /* 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);
   SDL_WM_SetCaption(program.window_title, program.window_title);
+#endif
 
   /* SDL cannot directly draw to the visible video framebuffer like X11,
      but always uses a backbuffer, which is then blitted to the visible
 
   /* SDL cannot directly draw to the visible video framebuffer like X11,
      but always uses a backbuffer, which is then blitted to the visible
@@ -185,95 +569,379 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
      'window', which indicates that the SDL backbuffer should be updated to
      the visible video framebuffer when attempting to blit to it.
 
      'window', which indicates that the SDL backbuffer should be updated to
      the visible video framebuffer when attempting to blit to it.
 
-     For convenience, it seems to be a good idea to create this symbolic
-     buffer 'window' at the same size as the SDL backbuffer. Although it
-     should never be drawn to directly, it would do no harm nevertheless. */
+     For convenience, it seems to be a good idea to create this symbolic
+     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 */
+  ReCreateBitmap(window, video.width, video.height, video.depth);
+}
+
+static SDL_Surface *SDLCreateScreen(DrawBuffer **backbuffer,
+                                   boolean fullscreen)
+{
+  SDL_Surface *new_surface = NULL;
+
+#if defined(TARGET_SDL2)
+  int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
+#if USE_DESKTOP_FULLSCREEN
+  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
+#else
+  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN;
+#endif
+
+#else
+  int surface_flags_window = SURFACE_FLAGS;
+  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
+#endif
+
+  int width  = (fullscreen ? fullscreen_width  : video.width);
+  int height = (fullscreen ? fullscreen_height : video.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)
+
+  // store if initial screen mode is fullscreen mode when changing screen size
+  video.fullscreen_initial = fullscreen;
+
+#if USE_RENDERER
+  float window_scaling_factor = (float)setup.window_scaling_percent / 100;
+#if !USE_DESKTOP_FULLSCREEN
+  float screen_scaling_factor = (fullscreen ? 1 : window_scaling_factor);
+#endif
+
+  video.window_width  = window_scaling_factor * width;
+  video.window_height = window_scaling_factor * height;
+
+  if ((*backbuffer)->surface)
+  {
+    SDL_FreeSurface((*backbuffer)->surface);
+    (*backbuffer)->surface = NULL;
+  }
+
+  if (sdl_texture)
+  {
+    SDL_DestroyTexture(sdl_texture);
+    sdl_texture = NULL;
+  }
+
+  if (!(fullscreen && fullscreen_enabled))
+  {
+    if (sdl_renderer)
+    {
+      SDL_DestroyRenderer(sdl_renderer);
+      sdl_renderer = NULL;
+    }
+
+    if (sdl_window)
+    {
+      SDL_DestroyWindow(sdl_window);
+      sdl_window = NULL;
+    }
+  }
+
+  if (sdl_window == NULL)
+    sdl_window = SDL_CreateWindow(program.window_title,
+                                 SDL_WINDOWPOS_CENTERED,
+                                 SDL_WINDOWPOS_CENTERED,
+#if USE_DESKTOP_FULLSCREEN
+                                 video.window_width,
+                                 video.window_height,
+#else
+                                 (int)(screen_scaling_factor * width),
+                                 (int)(screen_scaling_factor * height),
+#endif
+                                 surface_flags);
+
+  if (sdl_window != NULL)
+  {
+#if 0
+    /* if SDL_CreateRenderer() is called from within a VirtualBox Windows VM
+     *without* enabling 2D/3D acceleration and/or guest additions installed,
+     it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
+     it will try to use accelerated graphics and apparently fails miserably) */
+    if (sdl_renderer == NULL)
+      sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_SOFTWARE);
+#else
+    if (sdl_renderer == NULL)
+      sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
+#endif
+
+    if (sdl_renderer != NULL)
+    {
+      SDL_RenderSetLogicalSize(sdl_renderer, width, height);
+      // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+      SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
+
+      sdl_texture = SDL_CreateTexture(sdl_renderer,
+                                     SDL_PIXELFORMAT_ARGB8888,
+                                     SDL_TEXTUREACCESS_STREAMING,
+                                     width, height);
+
+      if (sdl_texture != NULL)
+      {
+       // use SDL default values for RGB masks and no alpha channel
+       new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
+
+       if (new_surface == NULL)
+         Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
+               SDL_GetError());
+      }
+      else
+      {
+       Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
+      }
+    }
+    else
+    {
+      Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
+    }
+  }
+  else
+  {
+    Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
+  }
+
+#else
+
+  if (sdl_window)
+    SDL_DestroyWindow(sdl_window);
+
+  sdl_window = SDL_CreateWindow(program.window_title,
+                               SDL_WINDOWPOS_CENTERED,
+                               SDL_WINDOWPOS_CENTERED,
+                               width, height,
+                               surface_flags);
+
+  if (sdl_window != NULL)
+    new_surface = SDL_GetWindowSurface(sdl_window);
+#endif
+
+#else
+  new_surface = SDL_SetVideoMode(width, height, video.depth, surface_flags);
+#endif
+
+#if defined(TARGET_SDL2)
+  // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
+  if (new_surface != NULL)
+    fullscreen_enabled = fullscreen;
+#endif
+
+  return new_surface;
+}
+
+boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
+{
+  boolean success = TRUE;
+  SDL_Surface *new_surface = NULL;
+
+  SetWindowTitle();
+
+  if (*backbuffer == NULL)
+    *backbuffer = CreateBitmapStruct();
+
+  /* (real bitmap might be larger in fullscreen mode with video offsets) */
+  (*backbuffer)->width  = video.width;
+  (*backbuffer)->height = video.height;
+
+  if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
+  {
+    setFullscreenParameters(setup.fullscreen_mode);
+
+    video_xoffset = fullscreen_xoffset;
+    video_yoffset = fullscreen_yoffset;
+
+    /* switch display to fullscreen mode, if available */
+    new_surface = SDLCreateScreen(backbuffer, TRUE);
+
+    if (new_surface == NULL)
+    {
+      /* switching display to fullscreen mode failed */
+      Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
+
+      /* do not try it again */
+      video.fullscreen_available = FALSE;
+
+      success = FALSE;
+    }
+    else
+    {
+      (*backbuffer)->surface = new_surface;
+
+      video.fullscreen_enabled = TRUE;
+      video.fullscreen_mode_current = setup.fullscreen_mode;
+
+      success = TRUE;
+    }
+  }
+
+  if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
+  {
+    video_xoffset = 0;
+    video_yoffset = 0;
+
+    /* switch display to window mode */
+    new_surface = SDLCreateScreen(backbuffer, FALSE);
+
+    if (new_surface == NULL)
+    {
+      /* switching display to window mode failed -- should not happen */
+      Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
+
+      success = FALSE;
+    }
+    else
+    {
+      (*backbuffer)->surface = new_surface;
+
+      video.fullscreen_enabled = FALSE;
+      video.window_scaling_percent = setup.window_scaling_percent;
+      video.window_scaling_quality = setup.window_scaling_quality;
+
+      success = TRUE;
+    }
+  }
+
+#if defined(TARGET_SDL2)
+  SDLRedrawWindow();                   // map window
+#endif
+
+#ifdef DEBUG
+#if defined(PLATFORM_WIN32)
+  // experimental drag and drop code
+
+  SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+
+  {
+    SDL_SysWMinfo wminfo;
+    HWND hwnd;
+    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);
+    }
+  }
+#endif
+#endif
+
+  return success;
+}
+
+void SDLSetWindowTitle()
+{
+#if defined(TARGET_SDL2)
+  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);
 
 
-  /* create additional (symbolic) buffer for double-buffering */
-  *window = CreateBitmap(video.width, video.height, video.depth);
+  SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
+
+  video.window_scaling_percent = window_scaling_percent;
+  video.window_width  = new_window_width;
+  video.window_height = new_window_height;
+
+  SetWindowTitle();
 }
 
 }
 
-boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
+void SDLSetWindowScalingQuality(char *window_scaling_quality)
 {
 {
-  boolean success = TRUE;
-  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
-  int surface_flags_window = SURFACE_FLAGS;
-  SDL_Surface *new_surface = NULL;
+  if (sdl_texture == NULL)
+    return;
 
 
-  if (*backbuffer == NULL)
-    *backbuffer = CreateBitmapStruct();
+  SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
 
 
-  if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
+  SDL_Texture *new_texture = SDL_CreateTexture(sdl_renderer,
+                                              SDL_PIXELFORMAT_ARGB8888,
+                                              SDL_TEXTUREACCESS_STREAMING,
+                                              video.width, video.height);
+
+  if (new_texture != NULL)
   {
   {
-    setFullscreenParameters();
+    SDL_DestroyTexture(sdl_texture);
 
 
-    video_xoffset = fullscreen_xoffset;
-    video_yoffset = fullscreen_yoffset;
+    sdl_texture = new_texture;
 
 
-    /* switch display to fullscreen mode, if available */
-    if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
-                                       video.depth, surface_flags_fullscreen))
-       == NULL)
-    {
-      /* switching display to fullscreen mode failed */
-      Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
+    SDLRedrawWindow();
+  }
 
 
-      /* do not try it again */
-      video.fullscreen_available = FALSE;
-      success = FALSE;
-    }
-    else
-    {
-      (*backbuffer)->surface = new_surface;
+  video.window_scaling_quality = window_scaling_quality;
+}
 
 
-      video.fullscreen_enabled = TRUE;
-      success = TRUE;
-    }
-  }
+void SDLSetWindowFullscreen(boolean fullscreen)
+{
+  if (sdl_window == NULL)
+    return;
 
 
-  if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
-  {
-    video_xoffset = 0;
-    video_yoffset = 0;
+#if USE_DESKTOP_FULLSCREEN
+  int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+#else
+  int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
+#endif
 
 
-    /* switch display to window mode */
-    if ((new_surface = SDL_SetVideoMode(video.width, video.height,
-                                       video.depth, surface_flags_window))
-       == NULL)
-    {
-      /* switching display to window mode failed -- should not happen */
-      Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
+  if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
+    video.fullscreen_enabled = fullscreen_enabled = fullscreen;
 
 
-      success = FALSE;
-    }
-    else
-    {
-      (*backbuffer)->surface = new_surface;
+  // if screen size was changed in fullscreen mode, correct desktop window size
+  if (!fullscreen && video.fullscreen_initial)
+  {
+    SDLSetWindowScaling(setup.window_scaling_percent);
+    SDL_SetWindowPosition(sdl_window,
+                         SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
 
 
-      video.fullscreen_enabled = FALSE;
-      success = TRUE;
-    }
+    video.fullscreen_initial = FALSE;
   }
   }
+}
 
 
-  return success;
+void SDLRedrawWindow()
+{
+  UpdateScreen(NULL);
 }
 }
+#endif
 
 
-void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
+void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
                            int depth)
 {
                            int depth)
 {
-  SDL_Surface *surface_tmp, *surface_native;
+  SDL_Surface *surface =
+    SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
 
 
-  if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
-                                         0, 0, 0, 0))
-      == NULL)
+  if (surface == NULL)
     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
 
     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
 
-  if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
-    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
-
-  SDL_FreeSurface(surface_tmp);
+  SDLSetNativeSurface(&surface);
 
 
-  new_bitmap->surface = surface_native;
+  bitmap->surface = surface;
 }
 
 void SDLFreeBitmapPointers(Bitmap *bitmap)
 }
 
 void SDLFreeBitmapPointers(Bitmap *bitmap)
@@ -282,8 +950,19 @@ void SDLFreeBitmapPointers(Bitmap *bitmap)
     SDL_FreeSurface(bitmap->surface);
   if (bitmap->surface_masked)
     SDL_FreeSurface(bitmap->surface_masked);
     SDL_FreeSurface(bitmap->surface);
   if (bitmap->surface_masked)
     SDL_FreeSurface(bitmap->surface_masked);
+
   bitmap->surface = NULL;
   bitmap->surface_masked = NULL;
   bitmap->surface = NULL;
   bitmap->surface_masked = NULL;
+
+#if defined(TARGET_SDL2)
+  if (bitmap->texture)
+    SDL_DestroyTexture(bitmap->texture);
+  if (bitmap->texture_masked)
+    SDL_DestroyTexture(bitmap->texture_masked);
+
+  bitmap->texture = NULL;
+  bitmap->texture_masked = NULL;
+#endif
 }
 
 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
 }
 
 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
@@ -315,17 +994,61 @@ void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   dst_rect.w = width;
   dst_rect.h = height;
 
   dst_rect.w = width;
   dst_rect.h = height;
 
-  if (src_bitmap != backbuffer || dst_bitmap != window)
+  // if (src_bitmap != backbuffer || dst_bitmap != window)
+  if (!(src_bitmap == backbuffer && dst_bitmap == window))
     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
                     src_bitmap->surface_masked : src_bitmap->surface),
                    &src_rect, real_dst_bitmap->surface, &dst_rect);
 
     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
                     src_bitmap->surface_masked : src_bitmap->surface),
                    &src_rect, real_dst_bitmap->surface, &dst_rect);
 
+#if defined(TARGET_SDL2)
+  if (dst_bitmap == window)
+  {
+    // SDL_UpdateWindowSurface(sdl_window);
+    // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
+    UpdateScreen(&dst_rect);
+  }
+#else
   if (dst_bitmap == window)
   if (dst_bitmap == window)
-    SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
+  {
+    // SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
+    UpdateScreen(&dst_rect);
+  }
+#endif
+}
+
+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)
+#if USE_RENDERER
+  SDL_Texture *texture;
+  SDL_Rect src_rect;
+  SDL_Rect dst_rect;
+
+  texture =
+    (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
+
+  if (texture == NULL)
+    return;
+
+  src_rect.x = src_x;
+  src_rect.y = src_y;
+  src_rect.w = width;
+  src_rect.h = height;
+
+  dst_rect.x = dst_x;
+  dst_rect.y = dst_y;
+  dst_rect.w = width;
+  dst_rect.h = height;
+
+  SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
+#endif
+#endif
 }
 
 }
 
-void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
-                     int width, int height, Uint32 color)
+void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
+                     Uint32 color)
 {
   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
   SDL_Rect rect;
 {
   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
   SDL_Rect rect;
@@ -343,56 +1066,69 @@ void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
 
   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
 
 
   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
 
+#if defined(TARGET_SDL2)
+  if (dst_bitmap == window)
+  {
+    // SDL_UpdateWindowSurface(sdl_window);
+    // SDL_UpdateWindowSurfaceRects(sdl_window, &rect, 1);
+    UpdateScreen(&rect);
+  }
+#else
   if (dst_bitmap == window)
   if (dst_bitmap == window)
-    SDL_UpdateRect(backbuffer->surface, x, y, width, height);
+  {
+    // SDL_UpdateRect(backbuffer->surface, x, y, width, height);
+    UpdateScreen(&rect);
+  }
+#endif
 }
 
 }
 
-void SDLFadeScreen(Bitmap *bitmap_cross, int fade_mode, int fade_delay,
-                  int post_delay)
+void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
+                     int fade_mode, int fade_delay, int post_delay,
+                     void (*draw_border_function)(void))
 {
   static boolean initialization_needed = TRUE;
 {
   static boolean initialization_needed = TRUE;
-  static SDL_Surface *surface_screen_copy = NULL;
+  static SDL_Surface *surface_source = NULL;
+  static SDL_Surface *surface_target = NULL;
   static SDL_Surface *surface_black = NULL;
   SDL_Surface *surface_screen = backbuffer->surface;
   static SDL_Surface *surface_black = NULL;
   SDL_Surface *surface_screen = backbuffer->surface;
-  SDL_Surface *surface_cross;          /* initialized later */
+  SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
   SDL_Rect src_rect, dst_rect;
   SDL_Rect src_rect, dst_rect;
-  int src_x = 0, src_y = 0;
-  int dst_x = 0, dst_y = 0;
-  boolean fade_reverse = (fade_mode == FADE_MODE_FADE_IN ? TRUE : FALSE);
+  SDL_Rect dst_rect2;
+  int src_x = x, src_y = y;
+  int dst_x = x, dst_y = y;
   unsigned int time_last, time_current;
   unsigned int time_last, time_current;
-  float alpha;
-  int alpha_final;
+
+  /* check if screen size has changed */
+  if (surface_source != NULL && (video.width  != surface_source->w ||
+                                video.height != surface_source->h))
+  {
+    SDL_FreeSurface(surface_source);
+    SDL_FreeSurface(surface_target);
+    SDL_FreeSurface(surface_black);
+
+    initialization_needed = TRUE;
+  }
 
   src_rect.x = src_x;
   src_rect.y = src_y;
 
   src_rect.x = src_x;
   src_rect.y = src_y;
-  src_rect.w = video.width;
-  src_rect.h = video.height;
+  src_rect.w = width;
+  src_rect.h = height;
 
   dst_x += video_xoffset;
   dst_y += video_yoffset;
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
 
   dst_x += video_xoffset;
   dst_y += video_yoffset;
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
-  dst_rect.w = video.width;
-  dst_rect.h = video.height;
-
-#if 0
-  if (!initialization_needed)
-  {
-    /* check if screen size has changed (can happen when toggling fullscreen) */
-    if (surface_screen_copy->w != surface_screen->w ||
-       surface_screen_copy->h != surface_screen->h)
-    {
-      SDL_FreeSurface(surface_screen_copy);
-      SDL_FreeSurface(surface_black);
+  dst_rect.w = width;          /* (ignored) */
+  dst_rect.h = height;         /* (ignored) */
 
 
-      initialization_needed = TRUE;
-    }
-  }
-#endif
+  dst_rect2 = dst_rect;
 
   if (initialization_needed)
   {
 
   if (initialization_needed)
   {
+#if defined(TARGET_SDL2)
+    unsigned int flags = 0;
+#else
     unsigned int flags = SDL_SRCALPHA;
 
     /* use same surface type as screen surface */
     unsigned int flags = SDL_SRCALPHA;
 
     /* use same surface type as screen surface */
@@ -400,40 +1136,43 @@ void SDLFadeScreen(Bitmap *bitmap_cross, int fade_mode, int fade_delay,
       flags |= SDL_HWSURFACE;
     else
       flags |= SDL_SWSURFACE;
       flags |= SDL_HWSURFACE;
     else
       flags |= SDL_SWSURFACE;
+#endif
 
 
-    /* create surface for temporary copy of screen buffer */
-    if ((surface_screen_copy =
+    /* create surface for temporary copy of screen buffer (source) */
+    if ((surface_source =
+        SDL_CreateRGBSurface(flags,
+                             video.width,
+                             video.height,
+                             surface_screen->format->BitsPerPixel,
+                             surface_screen->format->Rmask,
+                             surface_screen->format->Gmask,
+                             surface_screen->format->Bmask,
+                             surface_screen->format->Amask)) == NULL)
+      Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
+
+    /* create surface for cross-fading screen buffer (target) */
+    if ((surface_target =
         SDL_CreateRGBSurface(flags,
         SDL_CreateRGBSurface(flags,
-#if 1
                              video.width,
                              video.height,
                              video.width,
                              video.height,
-#else
-                             surface_screen->w,
-                             surface_screen->h,
-#endif
                              surface_screen->format->BitsPerPixel,
                              surface_screen->format->Rmask,
                              surface_screen->format->Gmask,
                              surface_screen->format->Bmask,
                              surface_screen->format->Amask)) == NULL)
                              surface_screen->format->BitsPerPixel,
                              surface_screen->format->Rmask,
                              surface_screen->format->Gmask,
                              surface_screen->format->Bmask,
                              surface_screen->format->Amask)) == NULL)
-      Error(ERR_EXIT, "SDL_CreateRGBSurface(   ) failed: %s", SDL_GetError());
+      Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
 
     /* create black surface for fading from/to black */
     if ((surface_black =
         SDL_CreateRGBSurface(flags,
 
     /* create black surface for fading from/to black */
     if ((surface_black =
         SDL_CreateRGBSurface(flags,
-#if 1
                              video.width,
                              video.height,
                              video.width,
                              video.height,
-#else
-                             surface_screen->w,
-                             surface_screen->h,
-#endif
                              surface_screen->format->BitsPerPixel,
                              surface_screen->format->Rmask,
                              surface_screen->format->Gmask,
                              surface_screen->format->Bmask,
                              surface_screen->format->Amask)) == NULL)
                              surface_screen->format->BitsPerPixel,
                              surface_screen->format->Rmask,
                              surface_screen->format->Gmask,
                              surface_screen->format->Bmask,
                              surface_screen->format->Amask)) == NULL)
-      Error(ERR_EXIT, "SDL_CreateRGBSurface(   ) failed: %s", SDL_GetError());
+      Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
 
     /* completely fill the surface with black color pixels */
     SDL_FillRect(surface_black, NULL,
 
     /* completely fill the surface with black color pixels */
     SDL_FillRect(surface_black, NULL,
@@ -442,34 +1181,255 @@ void SDLFadeScreen(Bitmap *bitmap_cross, int fade_mode, int fade_delay,
     initialization_needed = FALSE;
   }
 
     initialization_needed = FALSE;
   }
 
-  /* copy the current screen backbuffer to the temporary screen copy buffer */
-  SDL_BlitSurface(surface_screen, &dst_rect, surface_screen_copy, &src_rect);
-
-  surface_cross = (fade_mode == FADE_MODE_CROSSFADE ? bitmap_cross->surface :
-                  surface_black);
+  /* copy source and target surfaces to temporary surfaces for fading */
+  if (fade_mode & FADE_TYPE_TRANSFORM)
+  {
+    SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
+    SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
+  }
+  else if (fade_mode & FADE_TYPE_FADE_IN)
+  {
+    SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
+    SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
+  }
+  else         /* FADE_TYPE_FADE_OUT */
+  {
+    SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
+    SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
+  }
 
   time_current = SDL_GetTicks();
 
 
   time_current = SDL_GetTicks();
 
-  for (alpha = 0.0; alpha < 255.0;)
+  if (fade_mode == FADE_MODE_MELT)
   {
   {
-    time_last = time_current;
-    time_current = SDL_GetTicks();
-    alpha += 255 * ((float)(time_current - time_last) / fade_delay);
-    alpha_final = (int)(fade_reverse ? 255.0 - alpha : alpha);
-    alpha_final = MIN(MAX(0, alpha_final), 255);
+    boolean done = FALSE;
+    int melt_pixels = 2;
+    int melt_columns = width / melt_pixels;
+    int ypos[melt_columns];
+    int max_steps = height / 8 + 32;
+    int steps_done = 0;
+    float steps = 0;
+    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
+
+    ypos[0] = -GetSimpleRandom(16);
+
+    for (i = 1 ; i < melt_columns; i++)
+    {
+      int r = GetSimpleRandom(3) - 1;  /* randomly choose from { -1, 0, -1 } */
+
+      ypos[i] = ypos[i - 1] + r;
+
+      if (ypos[i] > 0)
+        ypos[i] = 0;
+      else
+        if (ypos[i] == -16)
+          ypos[i] = -15;
+    }
+
+    while (!done)
+    {
+      int steps_final;
+
+      time_last = time_current;
+      time_current = SDL_GetTicks();
+      steps += max_steps * ((float)(time_current - time_last) / fade_delay);
+      steps_final = MIN(MAX(0, steps), max_steps);
+
+      steps_done++;
+
+      done = (steps_done >= steps_final);
+
+      for (i = 0 ; i < melt_columns; i++)
+      {
+       if (ypos[i] < 0)
+        {
+          ypos[i]++;
+
+          done = FALSE;
+        }
+       else if (ypos[i] < height)
+       {
+         int y1 = 16;
+         int y2 = 8;
+         int y3 = 8;
+         int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
+
+         if (ypos[i] + dy >= height)
+           dy = height - ypos[i];
+
+         /* 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;
+         src_rect.w = melt_pixels;
+         // src_rect.h = dy;
+         src_rect.h = ypos[i] + dy;
+
+         dst_rect.x = dst_x + i * melt_pixels;
+         // dst_rect.y = dst_y + ypos[i];
+         dst_rect.y = dst_y;
+
+         if (steps_done >= steps_final)
+           SDL_BlitSurface(surface_target, &src_rect,
+                           surface_screen, &dst_rect);
+
+         ypos[i] += dy;
+
+         /* 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;
+         src_rect.h = height - ypos[i];
+
+         dst_rect.x = dst_x + i * melt_pixels;
+         dst_rect.y = dst_y + ypos[i];
+
+         if (steps_done >= steps_final)
+           SDL_BlitSurface(surface_source, &src_rect,
+                           surface_screen, &dst_rect);
+
+         done = FALSE;
+       }
+       else
+       {
+         src_rect.x = src_x + i * melt_pixels;
+         src_rect.y = src_y;
+         src_rect.w = melt_pixels;
+         src_rect.h = height;
+
+         dst_rect.x = dst_x + i * melt_pixels;
+         dst_rect.y = dst_y;
+
+         if (steps_done >= steps_final)
+           SDL_BlitSurface(surface_target, &src_rect,
+                           surface_screen, &dst_rect);
+       }
+      }
+
+      if (steps_done >= steps_final)
+      {
+       if (draw_border_function != NULL)
+         draw_border_function();
+
+       UpdateScreen(&dst_rect2);
+      }
+    }
+  }
+  else if (fade_mode == FADE_MODE_CURTAIN)
+  {
+    float xx;
+    int xx_final;
+    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
+
+    for (xx = 0; xx < xx_size;)
+    {
+      time_last = time_current;
+      time_current = SDL_GetTicks();
+      xx += xx_size * ((float)(time_current - time_last) / fade_delay);
+      xx_final = MIN(MAX(0, xx), xx_size);
+
+      src_rect.x = src_x;
+      src_rect.y = src_y;
+      src_rect.w = width;
+      src_rect.h = height;
+
+      dst_rect.x = dst_x;
+      dst_rect.y = dst_y;
+
+      /* draw new (target) image to screen buffer */
+      SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
+
+      if (xx_final < xx_size)
+      {
+       src_rect.w = xx_size - xx_final;
+       src_rect.h = height;
+
+       /* 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) */
+
+       src_rect.x = src_x + xx_size;
+       dst_rect.x = dst_x + xx_size + xx_final;
+
+       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
+      }
 
 
-    /* draw existing image to screen buffer */
-    SDL_BlitSurface(surface_screen_copy, &src_rect, surface_screen, &dst_rect);
+      if (draw_border_function != NULL)
+       draw_border_function();
 
 
-    /* draw new image to screen buffer using alpha blending */
-    SDL_SetAlpha(surface_cross, SDL_SRCALPHA, alpha_final);
-    SDL_BlitSurface(surface_cross, &src_rect, surface_screen, &dst_rect);
+      /* only update the region of the screen that is affected from fading */
+      UpdateScreen(&dst_rect2);
+    }
+  }
+  else         /* fading in, fading out or cross-fading */
+  {
+    float alpha;
+    int alpha_final;
+
+    for (alpha = 0.0; alpha < 255.0;)
+    {
+      time_last = time_current;
+      time_current = SDL_GetTicks();
+      alpha += 255 * ((float)(time_current - time_last) / fade_delay);
+      alpha_final = MIN(MAX(0, alpha), 255);
+
+      /* 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
+      SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
+
+      if (draw_border_function != NULL)
+       draw_border_function();
 
 
-    /* draw screen buffer to visible display */
-    SDL_Flip(surface_screen);
+      /* only update the region of the screen that is affected from fading */
+      UpdateScreen(&dst_rect);
+    }
   }
 
   }
 
-  Delay(post_delay);
+  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)
+    {
+      // do not wait longer than 10 ms at a time to be able to ...
+      Delay(MIN(10, time_post_delay - time_current));
+
+      // ... continue drawing global animations during post delay
+      UpdateScreen(NULL);
+
+      time_current = SDL_GetTicks();
+    }
+  }
 }
 
 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
 }
 
 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
@@ -512,7 +1472,7 @@ void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
 }
 
   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
 }
 
-#if 0
+#if ENABLE_UNUSED_CODE
 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
                  int num_points, Uint32 color)
 {
 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
                  int num_points, Uint32 color)
 {
@@ -597,7 +1557,7 @@ Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
 
 /* ========================================================================= */
 /* The following functions were taken from the SGE library                   */
 
 /* ========================================================================= */
 /* The following functions were taken from the SGE library                   */
-/* (SDL Graphics Extension Library) by Anders Lindström                      */
+/* (SDL Graphics Extension Library) by Anders Lindström                      */
 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
 /* ========================================================================= */
 
 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
 /* ========================================================================= */
 
@@ -1120,12 +2080,11 @@ int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
 {
   int x, y;
   tColorRGBA *sp, *csp, *dp;
 {
   int x, y;
   tColorRGBA *sp, *csp, *dp;
-  int sgap, dgap;
+  int dgap;
 
   /* pointer setup */
   sp = csp = (tColorRGBA *) src->pixels;
   dp = (tColorRGBA *) dst->pixels;
 
   /* pointer setup */
   sp = csp = (tColorRGBA *) src->pixels;
   dp = (tColorRGBA *) dst->pixels;
-  sgap = src->pitch - src->w * 4;
   dgap = dst->pitch - dst->w * 4;
 
   for (y = 0; y < dst->h; y++)
   dgap = dst->pitch - dst->w * 4;
 
   for (y = 0; y < dst->h; y++)
@@ -1170,9 +2129,10 @@ int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
 
 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 {
 
 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 {
-  int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
-  tColorRGBA *sp, *csp, *dp;
-  int sgap, dgap;
+  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 */
   if (src->w == 2 * dst->w &&
 
   /* use specialized zoom function when scaling down to exactly half size */
   if (src->w == 2 * dst->w &&
@@ -1180,38 +2140,23 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
 
   /* variable setup */
     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
 
   /* variable setup */
-  sx = (int) (65536.0 * (float) src->w / (float) dst->w);
-  sy = (int) (65536.0 * (float) src->h / (float) dst->h);
+  sx = (float) src->w / (float) dst->w;
+  sy = (float) src->h / (float) dst->h;
 
   /* allocate memory for row increments */
 
   /* allocate memory for row increments */
-  sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
-  say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
+  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 */
-  csx = 0;
-  csax = sax;
   for (x = 0; x <= dst->w; x++)
   for (x = 0; x <= dst->w; x++)
-  {
-    *csax = csx;
-    csax++;
-    csx &= 0xffff;
-    csx += sx;
-  }
+    *csax++ = (int)(sx * x);
 
 
-  csy = 0;
-  csay = say;
   for (y = 0; y <= dst->h; y++)
   for (y = 0; y <= dst->h; y++)
-  {
-    *csay = csy;
-    csay++;
-    csy &= 0xffff;
-    csy += sy;
-  }
+    *csay++ = (int)(sy * y);
 
   /* pointer setup */
 
   /* pointer setup */
-  sp = csp = (tColorRGBA *) src->pixels;
+  sp = csp = csp0 = (tColorRGBA *) src->pixels;
   dp = (tColorRGBA *) dst->pixels;
   dp = (tColorRGBA *) dst->pixels;
-  sgap = src->pitch - src->w * 4;
   dgap = dst->pitch - dst->w * 4;
 
   csay = say;
   dgap = dst->pitch - dst->w * 4;
 
   csay = say;
@@ -1227,7 +2172,7 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 
       /* advance source pointers */
       csax++;
 
       /* advance source pointers */
       csax++;
-      sp += (*csax >> 16);
+      sp = csp + *csax;
 
       /* advance destination pointer */
       dp++;
 
       /* advance destination pointer */
       dp++;
@@ -1235,7 +2180,7 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 
     /* advance source pointer */
     csay++;
 
     /* advance source pointer */
     csay++;
-    csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+    csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
 
     /* advance destination pointers */
     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
 
     /* advance destination pointers */
     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
@@ -1377,7 +2322,7 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   else
   {
     /* new source surface is 32 bit with a defined RGB ordering */
   else
   {
     /* new source surface is 32 bit with a defined RGB ordering */
-    zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
+    zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
                                    0x000000ff, 0x0000ff00, 0x00ff0000, 0);
     SDL_BlitSurface(src, NULL, zoom_src, NULL);
     is_32bit = TRUE;
                                    0x000000ff, 0x0000ff00, 0x00ff0000, 0);
     SDL_BlitSurface(src, NULL, zoom_src, NULL);
     is_32bit = TRUE;
@@ -1388,7 +2333,7 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   if (is_32bit)
   {
     /* target surface is 32 bit with source RGBA/ABGR ordering */
   if (is_32bit)
   {
     /* target surface is 32 bit with source RGBA/ABGR ordering */
-    zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
+    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->Rmask,
                                    zoom_src->format->Gmask,
                                    zoom_src->format->Bmask, 0);
@@ -1396,7 +2341,7 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   else
   {
     /* target surface is 8 bit */
   else
   {
     /* target surface is 8 bit */
-    zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
+    zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
                                    0, 0, 0, 0);
   }
 
                                    0, 0, 0, 0);
   }
 
@@ -1432,23 +2377,24 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   return zoom_dst;
 }
 
   return zoom_dst;
 }
 
-void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
+Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
 {
 {
-  SDL_Surface *sdl_surface_tmp;
-  int dst_width = dst_bitmap->width;
-  int dst_height = dst_bitmap->height;
+  Bitmap *dst_bitmap = CreateBitmapStruct();
+  SDL_Surface **dst_surface = &dst_bitmap->surface;
+
+  dst_width  = MAX(1, dst_width);      /* prevent zero bitmap width */
+  dst_height = MAX(1, dst_height);     /* prevent zero bitmap height */
 
 
-  /* throw away old destination surface */
-  SDL_FreeSurface(dst_bitmap->surface);
+  dst_bitmap->width  = dst_width;
+  dst_bitmap->height = dst_height;
 
   /* create zoomed temporary surface from source surface */
 
   /* create zoomed temporary surface from source surface */
-  sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
+  *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
 
   /* create native format destination surface from zoomed temporary surface */
 
   /* create native format destination surface from zoomed temporary surface */
-  dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
+  SDLSetNativeSurface(dst_surface);
 
 
-  /* free temporary surface */
-  SDL_FreeSurface(sdl_surface_tmp);
+  return dst_bitmap;
 }
 
 
 }
 
 
@@ -1461,35 +2407,57 @@ Bitmap *SDLLoadImage(char *filename)
   Bitmap *new_bitmap = CreateBitmapStruct();
   SDL_Surface *sdl_image_tmp;
 
   Bitmap *new_bitmap = CreateBitmapStruct();
   SDL_Surface *sdl_image_tmp;
 
+  print_timestamp_init("SDLLoadImage");
+
+  print_timestamp_time(getBaseNamePtr(filename));
+
   /* load image to temporary surface */
   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
   {
     SetError("IMG_Load(): %s", SDL_GetError());
   /* load image to temporary surface */
   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
   {
     SetError("IMG_Load(): %s", SDL_GetError());
+
     return NULL;
   }
 
     return NULL;
   }
 
+  print_timestamp_time("IMG_Load");
+
+  UPDATE_BUSY_STATE();
+
   /* create native non-transparent surface for current image */
   /* create native non-transparent surface for current image */
-  if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
+  if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+
     return NULL;
   }
 
     return NULL;
   }
 
+  print_timestamp_time("SDL_DisplayFormat (opaque)");
+
+  UPDATE_BUSY_STATE();
+
   /* create native transparent surface for current image */
   /* create native transparent surface for current image */
-  SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
+  SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
                  SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
                  SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
-  if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
+
+  if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+
     return NULL;
   }
 
     return NULL;
   }
 
+  print_timestamp_time("SDL_DisplayFormat (masked)");
+
+  UPDATE_BUSY_STATE();
+
   /* free temporary surface */
   SDL_FreeSurface(sdl_image_tmp);
 
   new_bitmap->width = new_bitmap->surface->w;
   new_bitmap->height = new_bitmap->surface->h;
 
   /* free temporary surface */
   SDL_FreeSurface(sdl_image_tmp);
 
   new_bitmap->width = new_bitmap->surface->w;
   new_bitmap->height = new_bitmap->surface->h;
 
+  print_timestamp_done("SDLLoadImage");
+
   return new_bitmap;
 }
 
   return new_bitmap;
 }
 
@@ -1537,8 +2505,10 @@ void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
 
 void SDLOpenAudio(void)
 {
 
 void SDLOpenAudio(void)
 {
+#if !defined(TARGET_SDL2)
   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
-    putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
+    SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
+#endif
 
   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
   {
 
   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
   {
@@ -1610,6 +2580,52 @@ void SDLNextEvent(Event *event)
   }
 }
 
   }
 }
 
+void SDLHandleWindowManagerEvent(Event *event)
+{
+#ifdef DEBUG
+#if defined(PLATFORM_WIN32)
+  // experimental drag and drop code
+
+  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");
+
+    num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
+
+    for (i = 0; i < num_files; i++)
+    {
+      int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
+      char buffer[buffer_len + 1];
+
+      DragQueryFile(hdrop, i, buffer, buffer_len + 1);
+
+      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                                                        */
@@ -1633,6 +2649,8 @@ static void SDLCloseJoystick(int nr)
     return;
 
   SDL_JoystickClose(sdl_joystick[nr]);
     return;
 
   SDL_JoystickClose(sdl_joystick[nr]);
+
+  sdl_joystick[nr] = NULL;
 }
 
 static boolean SDLCheckJoystickOpened(int nr)
 }
 
 static boolean SDLCheckJoystickOpened(int nr)
@@ -1640,7 +2658,11 @@ static boolean SDLCheckJoystickOpened(int nr)
   if (nr < 0 || nr > MAX_PLAYERS)
     return FALSE;
 
   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);
   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
+#endif
 }
 
 void HandleJoystickEvent(Event *event)
 }
 
 void HandleJoystickEvent(Event *event)
@@ -1677,7 +2699,7 @@ void SDLInitJoysticks()
   {
     sdl_joystick_subsystem_initialized = TRUE;
 
   {
     sdl_joystick_subsystem_initialized = TRUE;
 
-    if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
     {
       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
       return;
     {
       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
       return;
@@ -1740,5 +2762,3 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
 
   return TRUE;
 }
 
   return TRUE;
 }
-
-#endif /* TARGET_SDL */