updated contact info in source file headers
[rocksndiamonds.git] / src / libgame / sdl.c
index 52766b17d600c54f88dad072c26e258b0ca91022..da72bd490b965805ccff119494ab1c54de77734f 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Artsoft Retro-Game Library                               *
-*----------------------------------------------------------*
-* (c) 1994-2006 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"
@@ -29,6 +27,8 @@
 static SDL_Window *sdl_window = NULL;
 static SDL_Renderer *sdl_renderer = NULL;
 static SDL_Texture *sdl_texture = NULL;
+
+#define USE_RENDERER   TRUE
 #endif
 
 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
@@ -38,16 +38,54 @@ static int fullscreen_xoffset;
 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 1
+  if (limit_screen_updates &&
+      !DelayReached(&update_screen_delay, update_screen_delay_value))
+    return;
+
+  LimitScreenUpdates(FALSE);
+#endif
+
+#if defined(TARGET_SDL2)
+#if USE_RENDERER
   SDL_Surface *screen = backbuffer->surface;
 
+#if 1
+  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);
+  }
+#else
   SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
+#endif
   SDL_RenderClear(sdl_renderer);
   SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
   SDL_RenderPresent(sdl_renderer);
@@ -57,10 +95,25 @@ static void UpdateScreen(SDL_Rect *rect)
   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;
 
@@ -83,6 +136,7 @@ static void setFullscreenParameters(char *fullscreen_mode_string)
       break;
     }
   }
+#endif
 }
 
 static void SDLSetWindowIcon(char *basename)
@@ -121,14 +175,113 @@ static void SDLSetWindowIcon(char *basename)
 }
 
 #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);
+
+  SDL_FreeSurface(*surface);
+
+  *surface = new_surface;
+
+  return TRUE;
+}
+
+SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
+{
+  if (surface == NULL ||
+      backbuffer == NULL ||
+      backbuffer->surface == NULL)
+    return NULL;
+
+  return SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
+}
+
 SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface)
 {
-  if (backbuffer == NULL ||
+  if (surface == NULL ||
+      backbuffer == NULL ||
       backbuffer->surface == NULL)
     return NULL;
 
+#if 0
+  boolean same_pixel_format =
+    equalSDLPixelFormat(surface->format, backbuffer->surface->format);
+
+  printf("::: SDL_DisplayFormat: %08x -> %08x [%08x, %08x, %08x -> %08x, %08x, %08x] [%d, %d -> %d, %d] => %s\n",
+        surface->format->format,
+        backbuffer->surface->format->format,
+        surface->format->Rmask,
+        surface->format->Gmask,
+        surface->format->Bmask,
+        backbuffer->surface->format->Rmask,
+        backbuffer->surface->format->Gmask,
+        backbuffer->surface->format->Bmask,
+        surface->format->BitsPerPixel,
+        surface->format->BytesPerPixel,
+        backbuffer->surface->format->BitsPerPixel,
+        backbuffer->surface->format->BytesPerPixel,
+        (same_pixel_format ? "SAME" : "DIFF"));
+#endif
+
   return SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
 }
+
+#else
+
+boolean SDLSetNativeSurface(SDL_Surface **surface)
+{
+  SDL_Surface *new_surface;
+
+  if (surface == NULL)
+    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 = SDL_DisplayFormat(surface);
+
+  if (new_surface == NULL)
+    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+
+  return new_surface;
+}
+
 #endif
 
 void SDLInitVideoDisplay(void)
@@ -155,6 +308,7 @@ void SDLInitVideoDisplay(void)
 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
                        boolean fullscreen)
 {
+#if !defined(TARGET_SDL2)
   static int screen_xy[][2] =
   {
     {  640, 480 },
@@ -162,7 +316,9 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     { 1024, 768 },
     {   -1,  -1 }
   };
-  SDL_Rect **modes;
+#endif
+  SDL_Rect **modes = NULL;
+  boolean hardware_fullscreen_available = TRUE;
   int i, j;
 
   /* default: normal game window size */
@@ -171,6 +327,8 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
   fullscreen_xoffset = 0;
   fullscreen_yoffset = 0;
 
+#if !defined(TARGET_SDL2)
+  /* determine required standard fullscreen mode for game screen size */
   for (i = 0; screen_xy[i][0] != -1; i++)
   {
     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
@@ -184,6 +342,7 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
 
   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
+#endif
 
 #if 1
   checked_free(video.fullscreen_modes);
@@ -192,18 +351,45 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
   video.fullscreen_mode_current = NULL;
 #endif
 
-#if !defined(TARGET_SDL2)
+  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);
-#else
-  // (for now, no display modes in SDL2 -- change this later)
-  modes = NULL;
 #endif
 
   if (modes == NULL)
   {
-    /* no screen modes available => no fullscreen mode support */
-    video.fullscreen_available = FALSE;
+    /* no hardware screen modes available => no fullscreen mode support */
+    // video.fullscreen_available = FALSE;
+    hardware_fullscreen_available = FALSE;
   }
   else if (modes == (SDL_Rect **)-1)
   {
@@ -222,7 +408,7 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     /* in this case, a certain number of screen modes is available */
     int num_modes = 0;
 
-    for(i = 0; modes[i] != NULL; i++)
+    for (i = 0; modes[i] != NULL; i++)
     {
       boolean found_mode = FALSE;
 
@@ -257,10 +443,29 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     if (num_modes == 0)
     {
       /* no appropriate screen modes available => no fullscreen mode support */
-      video.fullscreen_available = FALSE;
+      // video.fullscreen_available = FALSE;
+      hardware_fullscreen_available = FALSE;
     }
   }
 
+  video.fullscreen_available = hardware_fullscreen_available;
+
+#if USE_DESKTOP_FULLSCREEN
+  // in SDL 2.0, there is always support for desktop fullscreen mode
+  // (in SDL 1.2, there is only support for "real" fullscreen mode)
+  video.fullscreen_available = TRUE;
+#endif
+
+#if defined(TARGET_SDL2)
+  if (modes)
+  {
+    for (i = 0; modes[i] != NULL; i++)
+      checked_free(modes[i]);
+
+    checked_free(modes);
+  }
+#endif
+
 #if 0
   /* set window icon */
   SDLSetWindowIcon(program.sdl_icon_filename);
@@ -271,6 +476,7 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
     Error(ERR_EXIT, "setting video mode failed");
 
 #if 1
+  /* !!! SDL2 can only set the window icon if the window already exists !!! */
   /* set window icon */
   SDLSetWindowIcon(program.sdl_icon_filename);
 #endif
@@ -303,9 +509,209 @@ void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
 #endif
 }
 
+static SDL_Surface *SDLCreateScreen(DrawBuffer **backbuffer,
+                                   boolean fullscreen)
+{
+  SDL_Surface *new_surface = NULL;
+
+#if defined(TARGET_SDL2)
+  static boolean fullscreen_enabled = FALSE;
+  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 on game start is fullscreen mode
+  if (sdl_window == NULL)
+  {
+#if 0
+    printf("::: GAME STARTS WITH FULLSCREEN %d\n", fullscreen);
+#endif
+
+    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 0
+  printf("::: use window scaling factor %f\n", screen_scaling_factor);
+#endif
+
+  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 0
+  Error(ERR_INFO, "::: checking 'sdl_window' ...");
+
+  if (sdl_window == NULL)
+    Error(ERR_INFO, "::: calling SDL_CreateWindow() [%d, %d, %d] ...",
+         setup.fullscreen, fullscreen, fullscreen_enabled);
+#endif
+
+  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)
+      {
+#if 1
+       // use SDL default values for RGB masks and no alpha channel
+       new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
+#else
+
+#if 1
+       // (do not use alpha channel)
+       new_surface = SDL_CreateRGBSurface(0, width, height, 32,
+                                          0x00FF0000,
+                                          0x0000FF00,
+                                          0x000000FF,
+                                          0x00000000);
+#else
+       // (this uses an alpha channel, which we don't want here)
+       new_surface = SDL_CreateRGBSurface(0, width, height, 32,
+                                          0x00FF0000,
+                                          0x0000FF00,
+                                          0x000000FF,
+                                          0xFF000000);
+#endif
+
+#endif
+
+       if (new_surface == NULL)
+         Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
+               SDL_GetError());
+      }
+      else
+      {
+       Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
+      }
+    }
+    else
+    {
+      Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
+    }
+  }
+  else
+  {
+    Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
+  }
+
+#else
+
+  if (sdl_window)
+    SDL_DestroyWindow(sdl_window);
+
+  sdl_window = SDL_CreateWindow(program.window_title,
+                               SDL_WINDOWPOS_CENTERED,
+                               SDL_WINDOWPOS_CENTERED,
+                               width, height,
+                               surface_flags);
+
+  if (sdl_window != NULL)
+    new_surface = SDL_GetWindowSurface(sdl_window);
+#endif
+
+#else
+  new_surface = SDL_SetVideoMode(width, height, video.depth, surface_flags);
+#endif
+
+#if defined(TARGET_SDL2)
+  // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
+  if (new_surface != NULL)
+    fullscreen_enabled = fullscreen;
+#endif
+
+  return new_surface;
+}
+
 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
 {
   boolean success = TRUE;
+#if 1
+#else
 #if defined(TARGET_SDL2)
   // int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN;
   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
@@ -313,9 +719,12 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
 #else
   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
   int surface_flags_window = SURFACE_FLAGS;
+#endif
 #endif
   SDL_Surface *new_surface = NULL;
 
+  SetWindowTitle();
+
   if (*backbuffer == NULL)
     *backbuffer = CreateBitmapStruct();
 
@@ -331,6 +740,10 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     video_yoffset = fullscreen_yoffset;
 
     /* switch display to fullscreen mode, if available */
+#if 1
+    new_surface = SDLCreateScreen(backbuffer, TRUE);
+#else
+
 #if defined(TARGET_SDL2)
     sdl_window = SDL_CreateWindow(program.window_title,
                                  SDL_WINDOWPOS_CENTERED,
@@ -342,11 +755,12 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
       new_surface = SDL_GetWindowSurface(sdl_window);
 
       // SDL_UpdateWindowSurface(sdl_window);  // immediately map window
-      UpdateScreen(NULL);      // immediately map window
+      // UpdateScreen(NULL);   // immediately map window
     }
 #else
     new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
                                   video.depth, surface_flags_fullscreen);
+#endif
 #endif
 
     if (new_surface == NULL)
@@ -376,19 +790,35 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     video_yoffset = 0;
 
     /* switch display to window mode */
+#if 1
+    new_surface = SDLCreateScreen(backbuffer, FALSE);
+#else
+
 #if defined(TARGET_SDL2)
 
-#if 1
-    float scale_factor = 1;
+#if USE_RENDERER
+    float screen_scaling_factor = 1.2;
     int test_fullscreen = 0;
     int surface_flags = (test_fullscreen ? surface_flags_fullscreen :
                         surface_flags_window);
 
+    if ((*backbuffer)->surface)
+      SDL_FreeSurface((*backbuffer)->surface);
+
+    if (sdl_texture)
+      SDL_DestroyTexture(sdl_texture);
+
+    if (sdl_renderer)
+      SDL_DestroyRenderer(sdl_renderer);
+
+    if (sdl_window)
+      SDL_DestroyWindow(sdl_window);
+
     sdl_window = SDL_CreateWindow(program.window_title,
                                  SDL_WINDOWPOS_CENTERED,
                                  SDL_WINDOWPOS_CENTERED,
-                                 (int)(scale_factor * video.width),
-                                 (int)(scale_factor * video.height),
+                                 (int)(screen_scaling_factor * video.width),
+                                 (int)(screen_scaling_factor * video.height),
                                  surface_flags);
 
     if (sdl_window != NULL)
@@ -407,11 +837,21 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
 
        if (sdl_texture != NULL)
        {
+#if 1
+         // (do not use alpha channel)
+         new_surface = SDL_CreateRGBSurface(0, video.width, video.height, 32,
+                                            0x00FF0000,
+                                            0x0000FF00,
+                                            0x000000FF,
+                                            0x00000000);
+#else
+         // (this uses an alpha channel, which we don't want here)
          new_surface = SDL_CreateRGBSurface(0, video.width, video.height, 32,
                                             0x00FF0000,
                                             0x0000FF00,
                                             0x000000FF,
                                             0xFF000000);
+#endif
 
          if (new_surface == NULL)
            Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
@@ -431,7 +871,12 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     {
       Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
     }
+
 #else
+
+    if (sdl_window)
+      SDL_DestroyWindow(sdl_window);
+
     sdl_window = SDL_CreateWindow(program.window_title,
                                  SDL_WINDOWPOS_CENTERED,
                                  SDL_WINDOWPOS_CENTERED,
@@ -443,13 +888,14 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
       new_surface = SDL_GetWindowSurface(sdl_window);
 
       // SDL_UpdateWindowSurface(sdl_window);  // immediately map window
-      UpdateScreen(NULL);      // immediately map window
+      // UpdateScreen(NULL);           // immediately map window
     }
 #endif
 
 #else
     new_surface = SDL_SetVideoMode(video.width, video.height,
                                   video.depth, surface_flags_window);
+#endif
 #endif
 
     if (new_surface == NULL)
@@ -464,11 +910,18 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
       (*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
+  // UpdateScreen(NULL);               // map window
+#endif
+
 #if 1
   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
 
@@ -476,24 +929,147 @@ boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
   {
     SDL_SysWMinfo wminfo;
     HWND hwnd;
+    boolean wminfo_success = FALSE;
 
     SDL_VERSION(&wminfo.version);
-    SDL_GetWMInfo(&wminfo);
+#if defined(TARGET_SDL2)
+    if (sdl_window)
+      wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
+#else
+    wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
+#endif
 
-    hwnd = wminfo.window;
+    if (wminfo_success)
+    {
+#if defined(TARGET_SDL2)
+      hwnd = wminfo.info.win.window;
+#else
+      hwnd = wminfo.window;
+#endif
 
-    DragAcceptFiles(hwnd, TRUE);
+      DragAcceptFiles(hwnd, TRUE);
+    }
   }
 #endif
 #endif
 
-
   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);
+
+#if 0
+  Error(ERR_DEBUG, "::: SDLSetWindowScaling(%d) ...", window_scaling_percent);
+#endif
+
+  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 0
+  Error(ERR_DEBUG, "::: SDL_SetWindowFullscreen(%d) ...", fullscreen);
+#endif
+
+  if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
+    video.fullscreen_enabled = fullscreen;
+
+#if 0
+  printf("::: SDLSetWindowFullscreen: %d, %d\n",
+        fullscreen, video.fullscreen_initial);
+#endif
+
+#if 1
+  // if game started in fullscreen mode, window will also get fullscreen 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;
+  }
+#endif
+}
+
+void SDLRedrawWindow()
+{
+  UpdateScreen(NULL);
+}
+#endif
+
+void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
                            int depth)
 {
+#if 1
+  SDL_Surface *surface =
+    SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
+
+  if (surface == NULL)
+    Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
+
+  SDLSetNativeSurface(&surface);
+
+  bitmap->surface = surface;
+
+#else
+
   SDL_Surface *surface_tmp, *surface_native;
 
   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
@@ -507,6 +1083,7 @@ void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
   SDL_FreeSurface(surface_tmp);
 
   new_bitmap->surface = surface_native;
+#endif
 }
 
 void SDLFreeBitmapPointers(Bitmap *bitmap)
@@ -548,7 +1125,8 @@ void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   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);
@@ -562,7 +1140,10 @@ void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   }
 #else
   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
 }
 
@@ -594,7 +1175,10 @@ void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
   }
 #else
   if (dst_bitmap == window)
-    SDL_UpdateRect(backbuffer->surface, x, y, width, height);
+  {
+    // SDL_UpdateRect(backbuffer->surface, x, y, width, height);
+    UpdateScreen(&rect);
+  }
 #endif
 }
 
@@ -609,9 +1193,7 @@ void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
   SDL_Surface *surface_screen = backbuffer->surface;
   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
   SDL_Rect src_rect, dst_rect;
-#if defined(TARGET_SDL2)
   SDL_Rect dst_rect2;
-#endif
   int src_x = x, src_y = y;
   int dst_x = x, dst_y = y;
   unsigned int time_last, time_current;
@@ -640,9 +1222,7 @@ void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
   dst_rect.w = width;          /* (ignored) */
   dst_rect.h = height;         /* (ignored) */
 
-#if defined(TARGET_SDL2)
   dst_rect2 = dst_rect;
-#endif
 
   if (initialization_needed)
   {
@@ -843,7 +1423,8 @@ void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
        // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect2, 1);
        UpdateScreen(&dst_rect2);
 #else
-       SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
+       // SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
+       UpdateScreen(&dst_rect2);
 #endif
       }
     }
@@ -882,7 +1463,8 @@ void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
       // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
       UpdateScreen(&dst_rect);
 #else
-      SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
+      // SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
+      UpdateScreen(&dst_rect);
 #endif
 #else
       SDL_Flip(surface_screen);
@@ -1594,6 +2176,80 @@ int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
   return 0;
 }
 
+#if 1
+
+int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
+{
+  int x, y, *sax, *say, *csax, *csay;
+  float sx, sy;
+  tColorRGBA *sp, *csp, *csp0, *dp;
+  int dgap;
+
+  /* use specialized zoom function when scaling down to exactly half size */
+  if (src->w == 2 * dst->w &&
+      src->h == 2 * dst->h)
+    return zoomSurfaceRGBA_scaleDownBy2(src, dst);
+
+#if 0
+  printf("::: zoomSurfaceRGBA:  %d, %d -> %d, %d\n",
+        src->w, src->h, dst->w, dst->h);
+#endif
+
+  /* variable setup */
+  sx = (float) src->w / (float) dst->w;
+  sy = (float) src->h / (float) dst->h;
+
+  /* allocate memory for row increments */
+  csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
+  csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
+
+  /* precalculate row increments */
+  for (x = 0; x <= dst->w; x++)
+    *csax++ = (int)(sx * x);
+
+  for (y = 0; y <= dst->h; y++)
+    *csay++ = (int)(sy * y);
+
+  /* pointer setup */
+  sp = csp = csp0 = (tColorRGBA *) src->pixels;
+  dp = (tColorRGBA *) dst->pixels;
+  dgap = dst->pitch - dst->w * 4;
+
+  csay = say;
+  for (y = 0; y < dst->h; y++)
+  {
+    sp = csp;
+    csax = sax;
+
+    for (x = 0; x < dst->w; x++)
+    {
+      /* draw */
+      *dp = *sp;
+
+      /* advance source pointers */
+      csax++;
+      sp = csp + *csax;
+
+      /* advance destination pointer */
+      dp++;
+    }
+
+    /* advance source pointer */
+    csay++;
+    csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
+
+    /* advance destination pointers */
+    dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
+  }
+
+  free(sax);
+  free(say);
+
+  return 0;
+}
+
+#else
+
 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
 {
   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
@@ -1678,6 +2334,8 @@ int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
   return 0;
 }
 
+#endif
+
 /*
   -----------------------------------------------------------------------------
   8 bit zoomer
@@ -1808,7 +2466,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 */
-    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;
@@ -1819,7 +2477,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 */
-    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);
@@ -1827,7 +2485,7 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   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);
   }
 
@@ -1863,25 +2521,92 @@ SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
   return zoom_dst;
 }
 
+#if 1
+
+Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
+{
+#if 1
+
+  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 */
+
+  dst_bitmap->width  = dst_width;
+  dst_bitmap->height = dst_height;
+
+  /* create zoomed temporary surface from source surface */
+  *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
+
+  /* create native format destination surface from zoomed temporary surface */
+  SDLSetNativeSurface(dst_surface);
+
+  return dst_bitmap;
+
+#else
+
+  Bitmap *dst_bitmap = CreateBitmapStruct();
+  SDL_Surface *sdl_surface_tmp;
+
+  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;
+
+  print_timestamp_init("SDLZoomBitmap");
+
+  /* create zoomed temporary surface from source surface */
+  sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
+  print_timestamp_time("zoomSurface");
+
+  /* create native format destination surface from zoomed temporary surface */
+  dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
+  print_timestamp_time("SDL_DisplayFormat");
+
+  /* free temporary surface */
+  SDL_FreeSurface(sdl_surface_tmp);
+  print_timestamp_time("SDL_FreeSurface");
+
+  print_timestamp_done("SDLZoomBitmap");
+
+  return dst_bitmap;
+
+#endif
+}
+
+#else
+
 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
 {
   SDL_Surface *sdl_surface_tmp;
   int dst_width = dst_bitmap->width;
   int dst_height = dst_bitmap->height;
 
+  print_timestamp_init("SDLZoomBitmap");
+
   /* throw away old destination surface */
   SDL_FreeSurface(dst_bitmap->surface);
+  print_timestamp_time("SDL_FreeSurface");
 
   /* create zoomed temporary surface from source surface */
   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
+  print_timestamp_time("zoomSurface");
 
   /* create native format destination surface from zoomed temporary surface */
   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
+  print_timestamp_time("SDL_DisplayFormat");
 
   /* free temporary surface */
   SDL_FreeSurface(sdl_surface_tmp);
+  print_timestamp_time("SDL_FreeSurface");
+
+  print_timestamp_done("SDLZoomBitmap");
 }
 
+#endif
+
 
 /* ========================================================================= */
 /* load image to bitmap                                                      */
@@ -1909,12 +2634,21 @@ Bitmap *SDLLoadImage(char *filename)
   UPDATE_BUSY_STATE();
 
   /* create native non-transparent surface for current image */
+#if 1
+  if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+
+    return NULL;
+  }
+#else
   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
 
     return NULL;
   }
+#endif
 
   print_timestamp_time("SDL_DisplayFormat (opaque)");
 
@@ -1923,12 +2657,21 @@ Bitmap *SDLLoadImage(char *filename)
   /* create native transparent surface for current image */
   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
                  SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
+#if 1
+  if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+
+    return NULL;
+  }
+#else
   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
   {
     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
 
     return NULL;
   }
+#endif
 
   print_timestamp_time("SDL_DisplayFormat (masked)");
 
@@ -2070,9 +2813,17 @@ void SDLHandleWindowManagerEvent(Event *event)
   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
 
+#if defined(TARGET_SDL2)
+  if (syswmmsg->msg.win.msg == WM_DROPFILES)
+#else
   if (syswmmsg->msg == WM_DROPFILES)
+#endif
   {
+#if defined(TARGET_SDL2)
+    HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
+#else
     HDROP hdrop = (HDROP)syswmmsg->wParam;
+#endif
     int i, num_files;
 
     printf("::: SDL_SYSWMEVENT:\n");
@@ -2089,7 +2840,11 @@ void SDLHandleWindowManagerEvent(Event *event)
       printf("::: - '%s'\n", buffer);
     }
 
+#if defined(TARGET_SDL2)
+    DragFinish((HDROP)syswmmsg->msg.win.wParam);
+#else
     DragFinish((HDROP)syswmmsg->wParam);
+#endif
   }
 #endif
 }