+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 = 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;
+}
+
+#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)
+{
+#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 */
+#if !defined(TARGET_SDL2)
+ video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
+#else
+ video.default_depth = 32; // (how to determine video depth in SDL2?)
+#endif
+}
+
+void SDLInitVideoBuffer(boolean fullscreen)
+{
+ video.window_scaling_percent = setup.window_scaling_percent;
+ video.window_scaling_quality = setup.window_scaling_quality;
+
+#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(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 */
+#if defined(TARGET_SDL2)
+ SDL_SetWindowTitle(sdl_window, program.window_title);
+#else
+ 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
+ video framebuffer with 'SDL_UpdateRect' (or replaced with the current
+ visible video framebuffer with 'SDL_Flip', if the hardware supports
+ this). Therefore do not use an additional backbuffer for drawing, but
+ use a symbolic buffer (distinguishable from the SDL backbuffer) called
+ '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. */
+
+ /* create additional (symbolic) buffer for double-buffering */
+ ReCreateBitmap(&window, video.width, video.height, video.depth);
+}
+
+static boolean SDLCreateScreen(boolean fullscreen)
+{
+ SDL_Surface *new_surface = NULL;
+
+#if defined(TARGET_SDL2)
+ int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
+ int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
+#else
+ int surface_flags_window = SURFACE_FLAGS;
+ int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
+#endif
+
+ 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 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