added initialization of config and textures for global animations
[rocksndiamonds.git] / src / libgame / sdl.c
index 406c022638499282e91080006e5e328b7150e11c..71f6cf171ec796583b48dd40e09ddf05728cdc20 100644 (file)
@@ -1,34 +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;
 
 
-/* #ifdef PLATFORM_WIN32 */
-#define FULLSCREEN_BUG
-/* #endif */
+#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;
@@ -37,24 +39,302 @@ 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) */
+
+  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 defined(TARGET_SDL2)
+#if USE_RENDERER
+  SDL_Surface *screen = backbuffer->surface;
+
+  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);
+  }
+  SDL_RenderClear(sdl_renderer);
+  SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
+  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(backbuffer->surface, 1, rect);
+  else
+    SDL_UpdateRect(backbuffer->surface, 0, 0, 0, 0);
+#endif
+}
+
+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;
+
+  fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
+
+  if (fullscreen_mode == NULL)
+    return;
+
+  for (i = 0; video.fullscreen_modes[i].width != -1; i++)
+  {
+    if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
+       fullscreen_mode->height == video.fullscreen_modes[i].height)
+    {
+      fullscreen_width  = fullscreen_mode->width;
+      fullscreen_height = fullscreen_mode->height;
+
+      fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
+      fullscreen_yoffset = (fullscreen_height - video.height) / 2;
+
+      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;
+
+  bitmap->texture        = SDLCreateTextureFromSurface(bitmap->surface);
+  bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
+#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)
 {
-#ifdef FULLSCREEN_BUG
-  int i;
+#if !defined(TARGET_SDL2)
   static int screen_xy[][2] =
   {
     {  640, 480 },
   static int screen_xy[][2] =
   {
     {  640, 480 },
@@ -63,6 +343,9 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     {   -1,  -1 }
   };
 #endif
     {   -1,  -1 }
   };
 #endif
+  SDL_Rect **modes = NULL;
+  boolean hardware_fullscreen_available = TRUE;
+  int i, j;
 
   /* default: normal game window size */
   fullscreen_width = video.width;
 
   /* default: normal game window size */
   fullscreen_width = video.width;
@@ -70,27 +353,157 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
   fullscreen_xoffset = 0;
   fullscreen_yoffset = 0;
 
   fullscreen_xoffset = 0;
   fullscreen_yoffset = 0;
 
-#ifdef FULLSCREEN_BUG
+#if !defined(TARGET_SDL2)
+  /* determine required standard fullscreen mode for game screen size */
   for (i = 0; screen_xy[i][0] != -1; i++)
   {
   for (i = 0; screen_xy[i][0] != -1; i++)
   {
-    if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
+    if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
     {
     {
-      fullscreen_width = screen_xy[i][0];
+      fullscreen_width  = screen_xy[i][0];
       fullscreen_height = screen_xy[i][1];
       fullscreen_height = screen_xy[i][1];
+
       break;
     }
   }
 
       break;
     }
   }
 
-  fullscreen_xoffset = (fullscreen_width - video.width) / 2;
+  fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
 #endif
 
   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);
+#endif
+
+  if (modes == NULL)
+  {
+    /* no hardware screen modes available => no fullscreen mode support */
+    // video.fullscreen_available = FALSE;
+    hardware_fullscreen_available = FALSE;
+  }
+  else if (modes == (SDL_Rect **)-1)
+  {
+    /* fullscreen resolution is not restricted -- all resolutions available */
+    video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
+
+    /* use native video buffer size for fullscreen mode */
+    video.fullscreen_modes[0].width  = video.width;
+    video.fullscreen_modes[0].height = video.height;
+
+    video.fullscreen_modes[1].width  = -1;
+    video.fullscreen_modes[1].height = -1;
+  }
+  else
+  {
+    /* in this case, a certain number of screen modes is available */
+    int num_modes = 0;
+
+    for (i = 0; modes[i] != NULL; i++)
+    {
+      boolean found_mode = FALSE;
+
+      /* screen mode is smaller than video buffer size -- skip it */
+      if (modes[i]->w < video.width || modes[i]->h < video.height)
+       continue;
+
+      if (video.fullscreen_modes != NULL)
+       for (j = 0; video.fullscreen_modes[j].width != -1; j++)
+         if (modes[i]->w == video.fullscreen_modes[j].width &&
+             modes[i]->h == video.fullscreen_modes[j].height)
+           found_mode = TRUE;
+
+      if (found_mode)          /* screen mode already stored -- skip it */
+       continue;
+
+      /* new mode found; add it to list of available fullscreen modes */
+
+      num_modes++;
+
+      video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
+                                              (num_modes + 1) *
+                                              sizeof(struct ScreenModeInfo));
+
+      video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
+      video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
+
+      video.fullscreen_modes[num_modes].width  = -1;
+      video.fullscreen_modes[num_modes].height = -1;
+    }
+
+    if (num_modes == 0)
+    {
+      /* no appropriate screen modes available => no fullscreen mode support */
+      // 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
@@ -101,39 +514,204 @@ 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
 
 
-  /* create additional (symbolic) buffer for double-buffering */
-  *window = CreateBitmap(video.width, video.height, video.depth);
+  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;
 }
 
 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
 {
   boolean success = TRUE;
-  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
-  int surface_flags_window = SURFACE_FLAGS;
   SDL_Surface *new_surface = NULL;
 
   SDL_Surface *new_surface = NULL;
 
+  SetWindowTitle();
+
   if (*backbuffer == NULL)
     *backbuffer = CreateBitmapStruct();
 
   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)
   {
   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 */
     video_xoffset = fullscreen_xoffset;
     video_yoffset = fullscreen_yoffset;
 
     /* switch display to fullscreen mode, if available */
-    if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
-                                       video.depth, surface_flags_fullscreen))
-       == NULL)
+    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;
     {
       /* 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
       success = FALSE;
     }
     else
@@ -141,6 +719,8 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
       (*backbuffer)->surface = new_surface;
 
       video.fullscreen_enabled = TRUE;
       (*backbuffer)->surface = new_surface;
 
       video.fullscreen_enabled = TRUE;
+      video.fullscreen_mode_current = setup.fullscreen_mode;
+
       success = TRUE;
     }
   }
       success = TRUE;
     }
   }
@@ -151,9 +731,9 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     video_yoffset = 0;
 
     /* switch display to window mode */
     video_yoffset = 0;
 
     /* switch display to window mode */
-    if ((new_surface = SDL_SetVideoMode(video.width, video.height,
-                                       video.depth, surface_flags_window))
-       == NULL)
+    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());
     {
       /* switching display to window mode failed -- should not happen */
       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
@@ -165,29 +745,148 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
       (*backbuffer)->surface = new_surface;
 
       video.fullscreen_enabled = FALSE;
       (*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;
     }
   }
 
       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;
 }
 
   return success;
 }
 
-void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
+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);
+
+  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();
+}
+
+void SDLSetWindowScalingQuality(char *window_scaling_quality)
+{
+  if (sdl_texture == NULL)
+    return;
+
+  SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
+
+  SDL_Texture *new_texture = SDL_CreateTexture(sdl_renderer,
+                                              SDL_PIXELFORMAT_ARGB8888,
+                                              SDL_TEXTUREACCESS_STREAMING,
+                                              video.width, video.height);
+
+  if (new_texture != NULL)
+  {
+    SDL_DestroyTexture(sdl_texture);
+
+    sdl_texture = new_texture;
+
+    SDLRedrawWindow();
+  }
+
+  video.window_scaling_quality = window_scaling_quality;
+}
+
+void SDLSetWindowFullscreen(boolean fullscreen)
+{
+  if (sdl_window == NULL)
+    return;
+
+#if USE_DESKTOP_FULLSCREEN
+  int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+#else
+  int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
+#endif
+
+  if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
+    video.fullscreen_enabled = fullscreen_enabled = fullscreen;
+
+  // 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_initial = FALSE;
+  }
+}
+
+void SDLRedrawWindow()
+{
+  UpdateScreen(NULL);
+}
+#endif
+
+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)
@@ -196,8 +895,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,
@@ -207,54 +917,61 @@ void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
   SDL_Rect src_rect, dst_rect;
 
   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
   SDL_Rect src_rect, dst_rect;
 
-#ifdef FULLSCREEN_BUG
   if (src_bitmap == backbuffer)
   {
     src_x += video_xoffset;
     src_y += video_yoffset;
   }
   if (src_bitmap == backbuffer)
   {
     src_x += video_xoffset;
     src_y += video_yoffset;
   }
-#endif
 
   src_rect.x = src_x;
   src_rect.y = src_y;
   src_rect.w = width;
   src_rect.h = height;
 
 
   src_rect.x = src_x;
   src_rect.y = src_y;
   src_rect.w = width;
   src_rect.h = height;
 
-#ifdef FULLSCREEN_BUG
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     dst_x += video_xoffset;
     dst_y += video_yoffset;
   }
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     dst_x += video_xoffset;
     dst_y += video_yoffset;
   }
-#endif
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
   dst_rect.w = width;
   dst_rect.h = height;
 
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
   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 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;
 
-#ifdef FULLSCREEN_BUG
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
-#endif
 
   rect.x = x;
   rect.y = y;
 
   rect.x = x;
   rect.y = y;
@@ -263,58 +980,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;
 
 
-#ifdef FULLSCREEN_BUG
   dst_x += video_xoffset;
   dst_y += video_yoffset;
   dst_x += video_xoffset;
   dst_y += video_yoffset;
-#endif
 
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
 
   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 */
@@ -322,40 +1050,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,
@@ -364,31 +1095,235 @@ 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)
+  {
+    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);
+      }
+
+      if (draw_border_function != NULL)
+       draw_border_function();
+
+      /* only update the region of the screen that is affected from fading */
+      UpdateScreen(&dst_rect2);
+    }
+  }
+  else         /* fading in, fading out or cross-fading */
   {
   {
-    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);
+    float alpha;
+    int alpha_final;
 
 
-    /* draw existing image to screen buffer */
-    SDL_BlitSurface(surface_screen_copy, &src_rect, surface_screen, &dst_rect);
+    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);
 
 
-    /* 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);
+      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);
   }
 
   Delay(post_delay);
@@ -411,13 +1346,11 @@ void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
   rect.w = (to_x - from_x + 1);
   rect.h = (to_y - from_y + 1);
 
   rect.w = (to_x - from_x + 1);
   rect.h = (to_y - from_y + 1);
 
-#ifdef FULLSCREEN_BUG
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     rect.x += video_xoffset;
     rect.y += video_yoffset;
   }
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     rect.x += video_xoffset;
     rect.y += video_yoffset;
   }
-#endif
 
   SDL_FillRect(surface, &rect, color);
 }
 
   SDL_FillRect(surface, &rect, color);
 }
@@ -425,7 +1358,6 @@ void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
                 int to_x, int to_y, Uint32 color)
 {
 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
                 int to_x, int to_y, Uint32 color)
 {
-#ifdef FULLSCREEN_BUG
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     from_x += video_xoffset;
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     from_x += video_xoffset;
@@ -433,12 +1365,11 @@ void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
     to_x += video_xoffset;
     to_y += video_yoffset;
   }
     to_x += video_xoffset;
     to_y += video_yoffset;
   }
-#endif
 
   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)
 {
@@ -472,13 +1403,11 @@ Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
 {
   SDL_Surface *surface = src_bitmap->surface;
 
 {
   SDL_Surface *surface = src_bitmap->surface;
 
-#ifdef FULLSCREEN_BUG
   if (src_bitmap == backbuffer || src_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
   if (src_bitmap == backbuffer || src_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
-#endif
 
   switch (surface->format->BytesPerPixel)
   {
 
   switch (surface->format->BytesPerPixel)
   {
@@ -525,7 +1454,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                        */
 /* ========================================================================= */
 
@@ -971,13 +1900,11 @@ void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
 
 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
 {
 
 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
 {
-#ifdef FULLSCREEN_BUG
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
   if (dst_bitmap == backbuffer || dst_bitmap == window)
   {
     x += video_xoffset;
     y += video_yoffset;
   }
-#endif
 
   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
 }
 
   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
 }
@@ -1050,12 +1977,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++)
@@ -1100,9 +2026,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 &&
@@ -1110,38 +2037,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;
@@ -1157,7 +2069,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++;
@@ -1165,7 +2077,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);
@@ -1307,7 +2219,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;
@@ -1318,7 +2230,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);
@@ -1326,7 +2238,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);
   }
 
@@ -1362,23 +2274,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;
 
 
-  /* throw away old destination surface */
-  SDL_FreeSurface(dst_bitmap->surface);
+  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 */
 
   /* 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;
 }
 
 
 }
 
 
@@ -1391,35 +2304,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;
 }
 
@@ -1467,8 +2402,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)
   {
@@ -1515,7 +2452,6 @@ void SDLNextEvent(Event *event)
 {
   SDL_WaitEvent(event);
 
 {
   SDL_WaitEvent(event);
 
-#ifdef FULLSCREEN_BUG
   if (event->type == EVENT_BUTTONPRESS ||
       event->type == EVENT_BUTTONRELEASE)
   {
   if (event->type == EVENT_BUTTONPRESS ||
       event->type == EVENT_BUTTONRELEASE)
   {
@@ -1539,6 +2475,51 @@ void SDLNextEvent(Event *event)
     else
       ((MotionEvent *)event)->y = 0;
   }
     else
       ((MotionEvent *)event)->y = 0;
   }
+}
+
+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
 }
 
 #endif
 }
 
@@ -1565,6 +2546,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)
@@ -1572,7 +2555,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)
@@ -1609,7 +2596,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;
@@ -1672,5 +2659,3 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
 
   return TRUE;
 }
 
   return TRUE;
 }
-
-#endif /* TARGET_SDL */