fixed (reactivated) global animations (toons) for SDL 1.2 target
[rocksndiamonds.git] / src / libgame / sdl.c
index 406c022638499282e91080006e5e328b7150e11c..a204e45ff13332cb09019531c551f78e3c7a2119 100644 (file)
-/***********************************************************
-* 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                                                           */
 /* ========================================================================= */
 
+/* SDL internal variables */
+#if defined(TARGET_SDL2)
+#define USE_TARGET_TEXTURE             TRUE
+#define USE_TARGET_TEXTURE_ONLY                FALSE
+
+static SDL_Window *sdl_window = NULL;
+static SDL_Renderer *sdl_renderer = NULL;
+#if USE_TARGET_TEXTURE
+static SDL_Texture *sdl_texture_stream = NULL;
+static SDL_Texture *sdl_texture_target = NULL;
+#else
+static SDL_Texture *sdl_texture = NULL;
+#endif
+static boolean fullscreen_enabled = FALSE;
+#endif
+
+static boolean limit_screen_updates = FALSE;
+
+
 /* functions from SGE library */
 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
 
 /* functions from SGE library */
 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
 
-/* #ifdef PLATFORM_WIN32 */
-#define FULLSCREEN_BUG
-/* #endif */
+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 = 50;         /* (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 (below border)
+    if (gfx.draw_global_anim_function != NULL)
+      gfx.draw_global_anim_function(DRAW_GLOBAL_ANIM_STAGE_1);
+
+    // copy global masked border to render target buffer, if defined
+    if (gfx.draw_global_border_function != NULL)
+      gfx.draw_global_border_function(DRAW_BORDER_TO_SCREEN);
+
+    // copy global animations to render target buffer, if defined (above border)
+    if (gfx.draw_global_anim_function != NULL)
+      gfx.draw_global_anim_function(DRAW_GLOBAL_ANIM_STAGE_2);
+
+    screen = gfx.final_screen_bitmap->surface;
+
+    // force full window redraw
+    rect = NULL;
+  }
+#endif
+
+#if USE_TARGET_TEXTURE
+#if USE_TARGET_TEXTURE_ONLY
+  SDL_Texture *sdl_texture = sdl_texture_target;
+#else
+  SDL_Texture *sdl_texture = sdl_texture_stream;
+#endif
+#endif
+
+#if defined(TARGET_SDL2)
+  if (rect)
+  {
+    int bytes_x = screen->pitch / video.width;
+    int bytes_y = screen->pitch;
+
+    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);
+
+#if USE_TARGET_TEXTURE
+  SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
+
+  // copy backbuffer to render target buffer
+  if (sdl_texture != sdl_texture_target)
+    SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
+#else
+  // copy backbuffer to render target buffer
+  SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
+#endif
+
+#if !USE_FINAL_SCREEN_BITMAP
+  // copy global animations to render target buffer, if defined (below border)
+  if (gfx.draw_global_anim_function != NULL)
+    gfx.draw_global_anim_function(DRAW_GLOBAL_ANIM_STAGE_1);
+
+  // copy global masked border to render target buffer, if defined
+  if (gfx.draw_global_border_function != NULL)
+    gfx.draw_global_border_function(DRAW_BORDER_TO_SCREEN);
+
+  // copy global animations to render target buffer, if defined (above border)
+  if (gfx.draw_global_anim_function != NULL)
+    gfx.draw_global_anim_function(DRAW_GLOBAL_ANIM_STAGE_2);
+#endif
+
+#if USE_TARGET_TEXTURE
+  SDL_SetRenderTarget(sdl_renderer, NULL);
+  SDL_RenderCopy(sdl_renderer, sdl_texture_target, NULL, NULL);
+#endif
+
+  // show render target buffer on screen
+  SDL_RenderPresent(sdl_renderer);
+
+#else  // TARGET_SDL
+  if (rect)
+    SDL_UpdateRects(screen, 1, rect);
+  else
+    SDL_UpdateRect(screen, 0, 0, 0, 0);
+#endif
+}
+
+static void 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_PixelFormat format;
+  SDL_Surface *new_surface;
+
+  if (surface == NULL)
+    return NULL;
+
+  if (backbuffer && backbuffer->surface)
+  {
+    format = *backbuffer->surface->format;
+    format.Amask = surface->format->Amask;     // keep alpha channel
+  }
+  else
+  {
+    format = *surface->format;
+  }
+
+  new_surface = SDL_ConvertSurface(surface, &format, 0);
+
+  if (new_surface == NULL)
+    Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
+
+  return new_surface;
+}
 
 
-/* stuff needed to work around SDL/Windows fullscreen drawing bug */
-static int fullscreen_width;
-static int fullscreen_height;
-static int fullscreen_xoffset;
-static int fullscreen_yoffset;
-static int video_xoffset;
-static int video_yoffset;
+#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)
 {
-#ifdef FULLSCREEN_BUG
-  int i;
-  static int screen_xy[][2] =
-  {
-    {  640, 480 },
-    {  800, 600 },
-    { 1024, 768 },
-    {   -1,  -1 }
-  };
-#endif
-
-  /* default: normal game window size */
-  fullscreen_width = video.width;
-  fullscreen_height = video.height;
-  fullscreen_xoffset = 0;
-  fullscreen_yoffset = 0;
-
-#ifdef FULLSCREEN_BUG
-  for (i = 0; screen_xy[i][0] != -1; i++)
-  {
-    if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
-    {
-      fullscreen_width = screen_xy[i][0];
-      fullscreen_height = screen_xy[i][1];
-      break;
-    }
-  }
+  video.window_scaling_percent = setup.window_scaling_percent;
+  video.window_scaling_quality = setup.window_scaling_quality;
 
 
-  fullscreen_xoffset = (fullscreen_width - video.width) / 2;
-  fullscreen_yoffset = (fullscreen_height - video.height) / 2;
+#if defined(TARGET_SDL2)
+  // SDL 2.0: support for (desktop) fullscreen mode available
+  video.fullscreen_available = TRUE;
+#else
+  // SDL 1.2: no support for fullscreen mode in R'n'D anymore
+  video.fullscreen_available = FALSE;
 #endif
 
   /* open SDL video output device (window or fullscreen mode) */
   if (!SDLSetVideoMode(backbuffer, fullscreen))
     Error(ERR_EXIT, "setting video mode failed");
 
 #endif
 
   /* 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,93 +421,433 @@ 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;
+  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
+#else
+  int surface_flags_window     = SURFACE_FLAGS;
+  int surface_flags_fullscreen = SURFACE_FLAGS;        // (no fullscreen in SDL 1.2)
+#endif
+
+  int width  = video.width;
+  int 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;
+
+  float window_scaling_factor = (float)setup.window_scaling_percent / 100;
+
+  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 USE_TARGET_TEXTURE
+  if (sdl_texture_stream)
+  {
+    SDL_DestroyTexture(sdl_texture_stream);
+    sdl_texture_stream = NULL;
+  }
+
+  if (sdl_texture_target)
+  {
+    SDL_DestroyTexture(sdl_texture_target);
+    sdl_texture_target = NULL;
+  }
+#else
+  if (sdl_texture)
+  {
+    SDL_DestroyTexture(sdl_texture);
+    sdl_texture = NULL;
+  }
+#endif
+
+  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,
+                                 video.window_width,
+                                 video.window_height,
+                                 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);
+
+#if USE_TARGET_TEXTURE
+      sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
+                                            SDL_PIXELFORMAT_ARGB8888,
+                                            SDL_TEXTUREACCESS_STREAMING,
+                                            width, height);
+
+      sdl_texture_target = SDL_CreateTexture(sdl_renderer,
+                                            SDL_PIXELFORMAT_ARGB8888,
+                                            SDL_TEXTUREACCESS_TARGET,
+                                            width, height);
+#else
+      sdl_texture = SDL_CreateTexture(sdl_renderer,
+                                     SDL_PIXELFORMAT_ARGB8888,
+                                     SDL_TEXTUREACCESS_STREAMING,
+                                     width, height);
+#endif
+
+#if USE_TARGET_TEXTURE
+      if (sdl_texture_stream != NULL &&
+         sdl_texture_target != NULL)
+#else
+      if (sdl_texture != NULL)
+#endif
+      {
+       // 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  // TARGET_SDL
+
+  if ((*backbuffer)->surface)
+  {
+    SDL_FreeSurface((*backbuffer)->surface);
+    (*backbuffer)->surface = NULL;
+  }
+
+  if (gfx.final_screen_bitmap == NULL)
+    gfx.final_screen_bitmap = CreateBitmapStruct();
+
+  gfx.final_screen_bitmap->width = width;
+  gfx.final_screen_bitmap->height = height;
+
+  gfx.final_screen_bitmap->surface =
+    SDL_SetVideoMode(width, height, video.depth, surface_flags);
+
+  if (gfx.final_screen_bitmap->surface != NULL)
+  {
+    new_surface =
+      SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
+
+    if (new_surface == NULL)
+      Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
+
+#if 0
+    new_surface = gfx.final_screen_bitmap->surface;
+    gfx.final_screen_bitmap = NULL;
+#endif
+
+  }
+  else
+  {
+    Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
+  }
+#endif
+
+#if defined(TARGET_SDL2)
+  // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
+  if (new_surface != NULL)
+    fullscreen_enabled = fullscreen;
+#endif
+
+  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)
+  {
+    /* switch display to fullscreen mode, if available */
+    new_surface = SDLCreateScreen(backbuffer, TRUE);
+
+    if (new_surface == NULL)
+    {
+      /* switching display to fullscreen mode failed -- do not try it again */
+      video.fullscreen_available = FALSE;
+
+      success = FALSE;
+    }
+    else
+    {
+      (*backbuffer)->surface = new_surface;
+
+      video.fullscreen_enabled = TRUE;
+
+      success = TRUE;
+    }
+  }
+
+  if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
+  {
+    /* switch display to window mode */
+    new_surface = SDLCreateScreen(backbuffer, FALSE);
+
+    if (new_surface == NULL)
+    {
+      /* switching display to window mode failed -- should not happen */
+
+      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;
 
 
-  /* create additional (symbolic) buffer for double-buffering */
-  *window = CreateBitmap(video.width, video.height, video.depth);
+  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();
 }
 
 }
 
-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 USE_TARGET_TEXTURE
+  SDL_Texture *new_texture;
 
 
-  if (*backbuffer == NULL)
-    *backbuffer = CreateBitmapStruct();
+  if (sdl_texture_stream == NULL ||
+      sdl_texture_target == NULL)
+    return;
 
 
-  if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
+  SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
+
+  new_texture = SDL_CreateTexture(sdl_renderer,
+                                 SDL_PIXELFORMAT_ARGB8888,
+                                 SDL_TEXTUREACCESS_STREAMING,
+                                 video.width, video.height);
+
+  if (new_texture != NULL)
   {
   {
-    video_xoffset = fullscreen_xoffset;
-    video_yoffset = fullscreen_yoffset;
+    SDL_DestroyTexture(sdl_texture_stream);
 
 
-    /* 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());
+    sdl_texture_stream = new_texture;
+  }
 
 
-      /* do not try it again */
-      video.fullscreen_available = FALSE;
-      success = FALSE;
-    }
-    else
-    {
-      (*backbuffer)->surface = new_surface;
+  new_texture = SDL_CreateTexture(sdl_renderer,
+                                 SDL_PIXELFORMAT_ARGB8888,
+                                 SDL_TEXTUREACCESS_TARGET,
+                                 video.width, video.height);
 
 
-      video.fullscreen_enabled = TRUE;
-      success = TRUE;
-    }
+  if (new_texture != NULL)
+  {
+    SDL_DestroyTexture(sdl_texture_target);
+
+    sdl_texture_target = new_texture;
   }
 
   }
 
-  if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
+  SDLRedrawWindow();
+
+#else
+  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)
   {
   {
-    video_xoffset = 0;
-    video_yoffset = 0;
+    SDL_DestroyTexture(sdl_texture);
 
 
-    /* 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());
+    sdl_texture = new_texture;
 
 
-      success = FALSE;
-    }
-    else
-    {
-      (*backbuffer)->surface = new_surface;
+    SDLRedrawWindow();
+  }
+#endif
 
 
-      video.fullscreen_enabled = FALSE;
-      success = TRUE;
-    }
+  video.window_scaling_quality = window_scaling_quality;
+}
+
+void SDLSetWindowFullscreen(boolean fullscreen)
+{
+  if (sdl_window == NULL)
+    return;
+
+  int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+
+  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;
   }
   }
+}
 
 
-  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)
@@ -196,8 +856,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 +878,60 @@ 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;
-  }
-#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;
-  }
-#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);
 
   if (dst_bitmap == window)
     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
                     src_bitmap->surface_masked : src_bitmap->surface),
                    &src_rect, real_dst_bitmap->surface, &dst_rect);
 
   if (dst_bitmap == window)
-    SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
+    UpdateScreen(&dst_rect);
 }
 
 }
 
-void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
-                     int width, int height, Uint32 color)
+void SDLBlitTexture(Bitmap *bitmap,
+                   int src_x, int src_y, int width, int height,
+                   int dst_x, int dst_y, int mask_mode)
 {
 {
-  Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
-  SDL_Rect rect;
+#if defined(TARGET_SDL2)
+  SDL_Texture *texture;
+  SDL_Rect src_rect;
+  SDL_Rect dst_rect;
 
 
-#ifdef FULLSCREEN_BUG
-  if (dst_bitmap == backbuffer || dst_bitmap == window)
-  {
-    x += video_xoffset;
-    y += video_yoffset;
-  }
+  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)
+{
+  Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
+  SDL_Rect rect;
 
   rect.x = x;
   rect.y = y;
 
   rect.x = x;
   rect.y = y;
@@ -263,135 +940,315 @@ 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 SDL_Surface *surface_screen_copy = NULL;
-  static SDL_Surface *surface_black = NULL;
+  SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
+  SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
+  SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
   SDL_Surface *surface_screen = backbuffer->surface;
   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;
+
+  // store function for drawing global masked border
+  void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
+
+  // deactivate drawing of global border while fading, if needed
+  if (draw_border_function == NULL)
+    gfx.draw_global_border_function = NULL;
 
   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;
-
-#ifdef FULLSCREEN_BUG
-  dst_x += video_xoffset;
-  dst_y += video_yoffset;
-#endif
+  src_rect.w = width;
+  src_rect.h = height;
 
   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;
+  dst_rect.w = width;          /* (ignored) */
+  dst_rect.h = height;         /* (ignored) */
 
 
-#if 0
-  if (!initialization_needed)
+  dst_rect2 = dst_rect;
+
+  /* copy source and target surfaces to temporary surfaces for fading */
+  if (fade_mode & FADE_TYPE_TRANSFORM)
   {
   {
-    /* 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);
+    SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
+    SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
 
 
-      initialization_needed = TRUE;
-    }
+    draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
+    draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
   }
   }
-#endif
+  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);
 
 
-  if (initialization_needed)
+    draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
+  }
+  else         /* FADE_TYPE_FADE_OUT */
   {
   {
-    unsigned int flags = SDL_SRCALPHA;
+    SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
+    SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
 
 
-    /* use same surface type as screen surface */
-    if ((surface_screen->flags & SDL_HWSURFACE))
-      flags |= SDL_HWSURFACE;
-    else
-      flags |= SDL_SWSURFACE;
-
-    /* create surface for temporary copy of screen buffer */
-    if ((surface_screen_copy =
-        SDL_CreateRGBSurface(flags,
-#if 1
-                             video.width,
-                             video.height,
+    draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
+  }
+
+  time_current = SDL_GetTicks();
+
+  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
 #else
-                             surface_screen->w,
-                             surface_screen->h,
+    SDL_SetAlpha(surface_target, 0, 0);                /* disable alpha blending */
 #endif
 #endif
-                             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 black surface for fading from/to black */
-    if ((surface_black =
-        SDL_CreateRGBSurface(flags,
-#if 1
-                             video.width,
-                             video.height,
+
+    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
 #else
-                             surface_screen->w,
-                             surface_screen->h,
+    SDL_SetAlpha(surface_source, 0, 0);                /* disable alpha blending */
 #endif
 #endif
-                             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());
 
 
-    /* completely fill the surface with black color pixels */
-    SDL_FillRect(surface_black, NULL,
-                SDL_MapRGB(surface_screen->format, 0, 0, 0));
+    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);
 
 
-    initialization_needed = FALSE;
+       /* 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 */
+  {
+    float alpha;
+    int alpha_final;
 
 
-  /* copy the current screen backbuffer to the temporary screen copy buffer */
-  SDL_BlitSurface(surface_screen, &dst_rect, surface_screen_copy, &src_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);
 
 
-  surface_cross = (fade_mode == FADE_MODE_CROSSFADE ? bitmap_cross->surface :
-                  surface_black);
+      if (draw_border_function != NULL)
+       draw_border_function();
 
 
-  time_current = SDL_GetTicks();
+      /* only update the region of the screen that is affected from fading */
+      UpdateScreen(&dst_rect);
+    }
+  }
 
 
-  for (alpha = 0.0; alpha < 255.0;)
+  if (post_delay > 0)
   {
   {
-    time_last = time_current;
+    unsigned int time_post_delay;
+
     time_current = SDL_GetTicks();
     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);
+    time_post_delay = time_current + post_delay;
 
 
-    /* draw existing image to screen buffer */
-    SDL_BlitSurface(surface_screen_copy, &src_rect, surface_screen, &dst_rect);
+    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));
 
 
-    /* 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);
+      // ... continue drawing global animations during post delay
+      UpdateScreen(NULL);
 
 
-    /* draw screen buffer to visible display */
-    SDL_Flip(surface_screen);
+      time_current = SDL_GetTicks();
+    }
   }
 
   }
 
-  Delay(post_delay);
+  // restore function for drawing global masked border
+  gfx.draw_global_border_function = draw_global_border_function;
 }
 
 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
 }
 
 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
@@ -411,34 +1268,16 @@ 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;
-  }
-#endif
-
   SDL_FillRect(surface, &rect, color);
 }
 
 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
                 int to_x, int to_y, Uint32 color)
 {
   SDL_FillRect(surface, &rect, 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;
-    from_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,14 +1311,6 @@ 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;
-  }
-#endif
-
   switch (surface->format->BytesPerPixel)
   {
     case 1:            /* assuming 8-bpp */
   switch (surface->format->BytesPerPixel)
   {
     case 1:            /* assuming 8-bpp */
@@ -525,7 +1356,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,14 +1802,6 @@ 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;
-  }
-#endif
-
   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
 }
 
   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
 }
 
@@ -1050,12 +1873,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 +1922,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 +1933,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 +1965,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 +1973,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 +2115,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 +2126,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 +2134,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 +2170,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;
 }
 
 
 }
 
 
@@ -1391,35 +2200,58 @@ 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_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
-  if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
+  if (sdl_image_tmp->format->Amask == 0)
+    SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
+                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
+
+  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 +2299,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)
   {
@@ -1514,32 +2348,52 @@ void SDLCloseAudio(void)
 void SDLNextEvent(Event *event)
 {
   SDL_WaitEvent(event);
 void SDLNextEvent(Event *event)
 {
   SDL_WaitEvent(event);
+}
 
 
-#ifdef FULLSCREEN_BUG
-  if (event->type == EVENT_BUTTONPRESS ||
-      event->type == EVENT_BUTTONRELEASE)
-  {
-    if (((ButtonEvent *)event)->x > video_xoffset)
-      ((ButtonEvent *)event)->x -= video_xoffset;
-    else
-      ((ButtonEvent *)event)->x = 0;
-    if (((ButtonEvent *)event)->y > video_yoffset)
-      ((ButtonEvent *)event)->y -= video_yoffset;
-    else
-      ((ButtonEvent *)event)->y = 0;
-  }
-  else if (event->type == EVENT_MOTIONNOTIFY)
+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 (((MotionEvent *)event)->x > video_xoffset)
-      ((MotionEvent *)event)->x -= video_xoffset;
-    else
-      ((MotionEvent *)event)->x = 0;
-    if (((MotionEvent *)event)->y > video_yoffset)
-      ((MotionEvent *)event)->y -= video_yoffset;
-    else
-      ((MotionEvent *)event)->y = 0;
+#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 +2419,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 +2428,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 +2469,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 +2532,3 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
 
   return TRUE;
 }
 
   return TRUE;
 }
-
-#endif /* TARGET_SDL */