changed function to scale SDL surface to be externally available
[rocksndiamonds.git] / src / libgame / sdl.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // sdl.c
10 // ============================================================================
11
12 #include "system.h"
13 #include "sound.h"
14 #include "joystick.h"
15 #include "misc.h"
16 #include "setup.h"
17 #include "gadgets.h"
18
19 #define ENABLE_UNUSED_CODE      0       // currently unused functions
20
21 #define DEBUG_JOYSTICKS         0
22
23
24 // ============================================================================
25 // video functions
26 // ============================================================================
27
28 // SDL internal variables
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
35
36
37 // functions from SGE library
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
39
40 #if defined(USE_TOUCH_INPUT_OVERLAY)
41 // functions to draw overlay graphics for touch device input
42 static void DrawTouchInputOverlay(void);
43 static void DrawTouchGadgetsOverlay(void);
44 #endif
45
46 void SDLLimitScreenUpdates(boolean enable)
47 {
48   limit_screen_updates = enable;
49 }
50
51 static void FinalizeScreen(int draw_target)
52 {
53   // copy global animations to render target buffer, if defined (below border)
54   if (gfx.draw_global_anim_function != NULL)
55     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
56
57   // copy global masked border to render target buffer, if defined
58   if (gfx.draw_global_border_function != NULL)
59     gfx.draw_global_border_function(draw_target);
60
61   // copy global animations to render target buffer, if defined (above border)
62   if (gfx.draw_global_anim_function != NULL)
63     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
64
65   // copy tile selection cursor to render target buffer, if defined (part 1)
66   if (gfx.draw_tile_cursor_function != NULL)
67     gfx.draw_tile_cursor_function(draw_target, TRUE);
68
69   // copy envelope request to render target buffer, if needed (above all)
70   if (gfx.draw_envelope_request_function != NULL)
71     gfx.draw_envelope_request_function(draw_target);
72
73   // copy tile selection cursor to render target buffer, if defined (part 2)
74   if (gfx.draw_tile_cursor_function != NULL)
75     gfx.draw_tile_cursor_function(draw_target, FALSE);
76
77   // copy global animations to render target buffer, if defined (mouse pointer)
78   if (gfx.draw_global_anim_function != NULL)
79     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_3);
80 }
81
82 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
83 {
84   if (program.headless)
85     return;
86
87   static DelayCounter update_screen_delay = { 50 };     // (milliseconds)
88   SDL_Surface *screen = backbuffer->surface;
89
90   if (limit_screen_updates &&
91       !DelayReached(&update_screen_delay))
92     return;
93
94   LimitScreenUpdates(FALSE);
95
96 #if 0
97   {
98     static int LastFrameCounter = 0;
99     boolean changed = (FrameCounter != LastFrameCounter);
100
101     Debug("internal:frame", "FrameCounter == %d [%s]", FrameCounter,
102           (changed ? "-" : "SAME FRAME UPDATED"));
103
104     LastFrameCounter = FrameCounter;
105
106     /*
107     if (FrameCounter % 2)
108       return;
109     */
110   }
111 #endif
112
113   if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
114       gfx.final_screen_bitmap != NULL)  // may not be initialized yet
115   {
116     // draw global animations using bitmaps instead of using textures
117     // to prevent texture scaling artefacts (this is potentially slower)
118
119     BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
120                gfx.win_xsize, gfx.win_ysize, 0, 0);
121
122     FinalizeScreen(DRAW_TO_SCREEN);
123
124     screen = gfx.final_screen_bitmap->surface;
125
126     // force full window redraw
127     rect = NULL;
128   }
129
130   SDL_Texture *sdl_texture = sdl_texture_stream;
131
132   // deactivate use of target texture if render targets are not supported
133   if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
134        video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
135       sdl_texture_target == NULL)
136     video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
137
138   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
139     sdl_texture = sdl_texture_target;
140
141   if (rect)
142   {
143     int bytes_x = screen->pitch / video.width;
144     int bytes_y = screen->pitch;
145
146     SDL_UpdateTexture(sdl_texture, rect,
147                       screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
148                       screen->pitch);
149   }
150   else
151   {
152     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
153   }
154
155   int xoff = video.screen_xoffset;
156   int yoff = video.screen_yoffset;
157   SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
158   SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
159   SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
160
161   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
162       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
163     dst_rect2 = &dst_rect_screen;
164   else
165     dst_rect1 = &dst_rect_screen;
166
167 #if defined(HAS_SCREEN_KEYBOARD)
168   SDL_Rect src_rect_up = { 0, 0,  video.width, video.height };
169   SDL_Rect dst_rect_up = dst_rect_screen;
170
171   if (video.shifted_up || video.shifted_up_delay.count)
172   {
173     int time_current = SDL_GetTicks();
174     int pos = video.shifted_up_pos;
175     int pos_last = video.shifted_up_pos_last;
176
177     if (!DelayReachedExt(&video.shifted_up_delay, time_current))
178     {
179       int delay_count = time_current - video.shifted_up_delay.count;
180       int delay_value = video.shifted_up_delay.value;
181
182       pos = pos_last + (pos - pos_last) * delay_count / delay_value;
183     }
184     else
185     {
186       video.shifted_up_pos_last = pos;
187       video.shifted_up_delay.count = 0;
188     }
189
190     src_rect_up.y = pos;
191     src_rect_up.h = video.height - pos;
192     dst_rect_up.h = video.height - pos;
193
194     if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
195         video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
196     {
197       src_rect2 = &src_rect_up;
198       dst_rect2 = &dst_rect_up;
199     }
200     else
201     {
202       src_rect1 = &src_rect_up;
203       dst_rect1 = &dst_rect_up;
204     }
205   }
206 #endif
207
208   // clear render target buffer
209   SDL_RenderClear(sdl_renderer);
210
211   // set renderer to use target texture for rendering
212   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
213       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
214     SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
215
216   // copy backbuffer texture to render target buffer
217   if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
218     SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
219
220   if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
221     FinalizeScreen(DRAW_TO_SCREEN);
222
223   // when using target texture, copy it to screen buffer
224   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
225       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
226   {
227     SDL_SetRenderTarget(sdl_renderer, NULL);
228     SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
229   }
230
231 #if defined(USE_TOUCH_INPUT_OVERLAY)
232   // draw overlay graphics for touch device input, if needed
233   DrawTouchInputOverlay();
234
235   // draw overlay gadgets for touch device input, if needed
236   DrawTouchGadgetsOverlay();
237 #endif
238
239   // global synchronization point of the game to align video frame delay
240   if (with_frame_delay)
241     WaitUntilDelayReached(&video.frame_delay);
242
243   video.frame_counter++;
244
245   // show render target buffer on screen
246   SDL_RenderPresent(sdl_renderer);
247 }
248
249 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
250 {
251   PumpEvents();         // execute event filter actions while waiting
252
253   UpdateScreenExt(rect, TRUE);
254 }
255
256 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
257 {
258   UpdateScreenExt(rect, FALSE);
259 }
260
261 void Delay_WithScreenUpdates(unsigned int delay)
262 {
263   unsigned int time_current = SDL_GetTicks();
264   unsigned int time_delayed = time_current + delay;
265
266   while (time_current < time_delayed)
267   {
268     // updating the screen contains waiting for frame delay (non-busy)
269     UpdateScreen_WithFrameDelay(NULL);
270
271     time_current = SDL_GetTicks();
272   }
273 }
274
275 static void SDLSetWindowIcon(char *basename)
276 {
277   // (setting the window icon on Mac OS X would replace the high-quality
278   // dock icon with the currently smaller (and uglier) icon from file)
279
280 #if !defined(PLATFORM_MAC)
281   char *filename = getCustomImageFilename(basename);
282   SDL_Surface *surface;
283
284   if (filename == NULL)
285   {
286     Warn("SDLSetWindowIcon(): cannot find file '%s'", basename);
287
288     return;
289   }
290
291   if ((surface = IMG_Load(filename)) == NULL)
292   {
293     Warn("IMG_Load('%s') failed: %s", basename, SDL_GetError());
294
295     return;
296   }
297
298   // set transparent color
299   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
300                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
301
302   SDL_SetWindowIcon(sdl_window, surface);
303 #endif
304 }
305
306 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
307                                    SDL_PixelFormat *format2)
308 {
309   return (format1->format        == format2->format &&
310           format1->BitsPerPixel  == format2->BitsPerPixel &&
311           format1->BytesPerPixel == format2->BytesPerPixel &&
312           format1->Rmask         == format2->Rmask &&
313           format1->Gmask         == format2->Gmask &&
314           format1->Bmask         == format2->Bmask);
315 }
316
317 static void SDLCopyColorKey(SDL_Surface *src_surface, SDL_Surface *dst_surface)
318 {
319   Pixel color_key;
320   Uint8 r, g, b;
321
322   // check if source surface has a color key
323   if (SDL_GetColorKey(src_surface, &color_key) == 0)
324   {
325     // get RGB values of color key of source surface
326     SDL_GetRGB(color_key, src_surface->format, &r, &g, &b);
327
328     // get color key from RGB values in destination surface format
329     color_key = SDL_MapRGB(dst_surface->format, r, g, b);
330
331     // set color key in destination surface
332     SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL, color_key);
333   }
334   else
335   {
336     // unset color key in destination surface
337     SDL_SetColorKey(dst_surface, UNSET_TRANSPARENT_PIXEL, 0);
338   }
339 }
340
341 static boolean SDLHasColorKey(SDL_Surface *surface)
342 {
343   Pixel color_key;
344
345   return (SDL_GetColorKey(surface, &color_key) == 0);
346 }
347
348 static boolean SDLHasAlpha(SDL_Surface *surface)
349 {
350   SDL_BlendMode blend_mode;
351
352   if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
353     return FALSE;
354
355   return (blend_mode == SDL_BLENDMODE_BLEND);
356 }
357
358 static void SDLSetSurfaceAlpha(SDL_Surface *surface, boolean set, int alpha)
359 {
360   SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
361
362   SDL_SetSurfaceBlendMode(surface, blend_mode);
363   SDL_SetSurfaceAlphaMod(surface, alpha);
364 }
365
366 static void SDLSetTextureAlpha(SDL_Texture *texture, boolean set, int alpha)
367 {
368   SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
369
370   SDL_SetTextureBlendMode(texture, blend_mode);
371   SDL_SetTextureAlphaMod(texture, alpha);
372 }
373
374 static void SDLSetBitmapAlpha(Bitmap *bitmap, boolean is_texture,
375                               boolean is_masked)
376 {
377   int alpha_next_blit = bitmap->alpha_next_blit;
378
379   // alpha value must be requested every time before blitting, if needed
380   bitmap->alpha_next_blit = -1;
381
382   // nothing to do if requested alpha value is already set
383   if (bitmap->alpha[is_texture][is_masked] == alpha_next_blit)
384     return;
385
386   // store requested alpha value for masked/unmasked surface/texture
387   bitmap->alpha[is_texture][is_masked] = alpha_next_blit;
388
389   // set blend mode if bitmap is masked or if alpha value is defined
390   boolean set_blend_mode = (is_masked || alpha_next_blit != -1);
391
392   // if alpha value is undefined, use default (opaque) alpha value
393   if (alpha_next_blit == -1)
394     alpha_next_blit = SDL_ALPHA_OPAQUE;
395
396   if (is_texture)
397     SDLSetTextureAlpha(is_masked ? bitmap->texture_masked : bitmap->texture,
398                        set_blend_mode, alpha_next_blit);
399   else
400     SDLSetSurfaceAlpha(is_masked ? bitmap->surface_masked : bitmap->surface,
401                        set_blend_mode, alpha_next_blit);
402 }
403
404 void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
405 {
406   SDLSetSurfaceAlpha(surface, set, alpha);
407 }
408
409 const char *SDLGetRendererName(void)
410 {
411   static SDL_RendererInfo renderer_info;
412
413   SDL_GetRendererInfo(sdl_renderer, &renderer_info);
414
415   return renderer_info.name;
416 }
417
418 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
419 {
420   SDL_PixelFormat format;
421   SDL_Surface *new_surface;
422
423   if (surface == NULL)
424     return NULL;
425
426   if (backbuffer && backbuffer->surface)
427   {
428     format = *backbuffer->surface->format;
429     format.Amask = surface->format->Amask;      // keep alpha channel
430   }
431   else
432   {
433     format = *surface->format;
434   }
435
436   new_surface = SDL_ConvertSurface(surface, &format, 0);
437
438   if (new_surface == NULL)
439     Fail("SDL_ConvertSurface() failed: %s", SDL_GetError());
440
441   // workaround for a bug in SDL 2.0.12 (which does not convert the color key)
442   if (SDLHasColorKey(surface) && !SDLHasColorKey(new_surface))
443     SDLCopyColorKey(surface, new_surface);
444
445   return new_surface;
446 }
447
448 boolean SDLSetNativeSurface(SDL_Surface **surface)
449 {
450   SDL_Surface *new_surface;
451
452   if (surface == NULL ||
453       *surface == NULL ||
454       backbuffer == NULL ||
455       backbuffer->surface == NULL)
456     return FALSE;
457
458   // if pixel format already optimized for destination surface, do nothing
459   if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
460     return FALSE;
461
462   new_surface = SDLGetNativeSurface(*surface);
463
464   SDL_FreeSurface(*surface);
465
466   *surface = new_surface;
467
468   return TRUE;
469 }
470
471 SDL_Surface *SDLCreateNativeSurface(int width, int height, int depth)
472 {
473   if (program.headless)
474     return NULL;
475
476   SDL_Surface *surface = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
477
478   if (surface == NULL)
479     Fail("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
480
481   SDLSetNativeSurface(&surface);
482
483   return surface;
484 }
485
486 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
487 {
488   if (program.headless)
489     return NULL;
490
491   SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
492
493   if (texture == NULL)
494     Fail("SDL_CreateTextureFromSurface() failed: %s", SDL_GetError());
495
496   return texture;
497 }
498
499 void SDLCreateBitmapTextures(Bitmap *bitmap)
500 {
501   if (bitmap == NULL)
502     return;
503
504   if (bitmap->texture)
505     SDL_DestroyTexture(bitmap->texture);
506   if (bitmap->texture_masked)
507     SDL_DestroyTexture(bitmap->texture_masked);
508
509   bitmap->texture        = SDLCreateTextureFromSurface(bitmap->surface);
510   bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
511 }
512
513 void SDLFreeBitmapTextures(Bitmap *bitmap)
514 {
515   if (bitmap == NULL)
516     return;
517
518   if (bitmap->texture)
519     SDL_DestroyTexture(bitmap->texture);
520   if (bitmap->texture_masked)
521     SDL_DestroyTexture(bitmap->texture_masked);
522
523   bitmap->texture = NULL;
524   bitmap->texture_masked = NULL;
525 }
526
527 void SDLInitVideoDisplay(void)
528 {
529   // set hint to select render driver as specified in setup config file
530   if (!strEqual(setup.system.sdl_renderdriver, ARG_DEFAULT))
531     SDL_SetHint(SDL_HINT_RENDER_DRIVER, setup.system.sdl_renderdriver);
532
533   // initialize SDL video
534   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
535     Fail("SDL_InitSubSystem() failed: %s", SDL_GetError());
536
537   // set default SDL depth
538   video.default_depth = 32;     // (how to determine video depth in SDL2?)
539   //
540   // Code used with SDL 1.2:
541   // video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
542 }
543
544 static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
545 {
546   if (program.headless)
547     return;
548
549   video.window_scaling_percent = setup.window_scaling_percent;
550   video.window_scaling_quality = setup.window_scaling_quality;
551
552   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
553
554   // SDL 2.0: support for (desktop) fullscreen mode available
555   video.fullscreen_available = TRUE;
556
557   // open SDL video output device (window or fullscreen mode)
558   if (!SDLSetVideoMode(fullscreen))
559     Fail("setting video mode failed");
560
561   // !!! SDL2 can only set the window icon if the window already exists !!!
562   // set window icon
563   SDLSetWindowIcon(program.icon_filename);
564
565   // set window and icon title
566   SDLSetWindowTitle();
567 }
568
569 static void SDLInitVideoBuffer_DrawBuffer(void)
570 {
571   /* SDL cannot directly draw to the visible video framebuffer like X11,
572      but always uses a backbuffer, which is then blitted to the visible
573      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
574      visible video framebuffer with 'SDL_Flip', if the hardware supports
575      this). Therefore do not use an additional backbuffer for drawing, but
576      use a symbolic buffer (distinguishable from the SDL backbuffer) called
577      'window', which indicates that the SDL backbuffer should be updated to
578      the visible video framebuffer when attempting to blit to it.
579
580      For convenience, it seems to be a good idea to create this symbolic
581      buffer 'window' at the same size as the SDL backbuffer. Although it
582      should never be drawn to directly, it would do no harm nevertheless. */
583
584   // create additional (symbolic) buffer for double-buffering
585   ReCreateBitmap(&window, video.width, video.height);
586
587   // create dummy drawing buffer for headless mode, if needed
588   if (program.headless)
589     ReCreateBitmap(&backbuffer, video.width, video.height);
590 }
591
592 void SDLInitVideoBuffer(boolean fullscreen)
593 {
594   SDLInitVideoBuffer_VideoBuffer(fullscreen);
595   SDLInitVideoBuffer_DrawBuffer();
596 }
597
598 static boolean SDLCreateScreen(boolean fullscreen)
599 {
600   SDL_Surface *new_surface = NULL;
601
602   int surface_flags_window     = SURFACE_FLAGS;
603   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
604
605 #if 1
606   int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
607
608   video.vsync_mode = VSYNC_MODE_OFF;
609
610   if (!strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF))
611   {
612     renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
613     video.vsync_mode = VSYNC_MODE_NORMAL;
614   }
615 #else
616   /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
617      _without_ enabling 2D/3D acceleration and/or guest additions installed,
618      it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
619      it will try to use accelerated graphics and apparently fails miserably) */
620   int renderer_flags = SDL_RENDERER_SOFTWARE;
621 #endif
622
623   int width  = video.width;
624   int height = video.height;
625   int screen_width  = video.screen_width;
626   int screen_height = video.screen_height;
627   int surface_flags = (fullscreen ? surface_flags_fullscreen :
628                        surface_flags_window);
629   int display_nr = options.display_nr;
630
631   // default window size is unscaled
632   video.window_width  = screen_width;
633   video.window_height = screen_height;
634
635   // store if initial screen mode is fullscreen mode when changing screen size
636   video.fullscreen_initial = fullscreen;
637
638   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
639
640   video.window_width  = window_scaling_factor * screen_width;
641   video.window_height = window_scaling_factor * screen_height;
642
643   if (sdl_texture_stream)
644   {
645     SDL_DestroyTexture(sdl_texture_stream);
646     sdl_texture_stream = NULL;
647   }
648
649   if (sdl_texture_target)
650   {
651     SDL_DestroyTexture(sdl_texture_target);
652     sdl_texture_target = NULL;
653   }
654
655   if (!(fullscreen && fullscreen_enabled))
656   {
657     if (sdl_renderer)
658     {
659       SDL_DestroyRenderer(sdl_renderer);
660       sdl_renderer = NULL;
661     }
662
663     if (sdl_window)
664     {
665       SDL_SetWindowSize(sdl_window, video.window_width, video.window_height);
666     }
667   }
668
669   if (sdl_window == NULL)
670     sdl_window = SDL_CreateWindow(program.window_title,
671                                   SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
672                                   SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr),
673                                   video.window_width,
674                                   video.window_height,
675                                   surface_flags);
676
677   if (sdl_window != NULL)
678   {
679     if (sdl_renderer == NULL)
680       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
681
682     if (sdl_renderer != NULL)
683     {
684       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
685       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
686
687       // required for setting adaptive vsync when using OpenGL renderer
688       SDLSetScreenVsyncMode(setup.vsync_mode);
689
690       sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
691                                              SDL_PIXELFORMAT_ARGB8888,
692                                              SDL_TEXTUREACCESS_STREAMING,
693                                              width, height);
694
695       if (SDL_RenderTargetSupported(sdl_renderer))
696         sdl_texture_target = SDL_CreateTexture(sdl_renderer,
697                                                SDL_PIXELFORMAT_ARGB8888,
698                                                SDL_TEXTUREACCESS_TARGET,
699                                                width, height);
700
701       if (sdl_texture_stream != NULL)
702       {
703         // use SDL default values for RGB masks and no alpha channel
704         new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
705
706         if (new_surface == NULL)
707           Warn("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
708       }
709       else
710       {
711         Warn("SDL_CreateTexture() failed: %s", SDL_GetError());
712       }
713     }
714     else
715     {
716       Warn("SDL_CreateRenderer() failed: %s", SDL_GetError());
717     }
718   }
719   else
720   {
721     Warn("SDL_CreateWindow() failed: %s", SDL_GetError());
722   }
723
724   SDLSetScreenProperties();
725
726   // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
727   if (new_surface != NULL)
728     fullscreen_enabled = fullscreen;
729
730   if (backbuffer == NULL)
731     backbuffer = CreateBitmapStruct();
732
733   backbuffer->width  = video.width;
734   backbuffer->height = video.height;
735
736   if (backbuffer->surface)
737     SDL_FreeSurface(backbuffer->surface);
738
739   backbuffer->surface = new_surface;
740
741   return (new_surface != NULL);
742 }
743
744 boolean SDLSetVideoMode(boolean fullscreen)
745 {
746   boolean success = FALSE;
747
748   SetWindowTitle();
749
750   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
751   {
752     // switch display to fullscreen mode, if available
753     success = SDLCreateScreen(TRUE);
754
755     if (!success)
756     {
757       // switching display to fullscreen mode failed -- do not try it again
758       video.fullscreen_available = FALSE;
759     }
760     else
761     {
762       video.fullscreen_enabled = TRUE;
763     }
764   }
765
766   if ((!fullscreen && video.fullscreen_enabled) || !success)
767   {
768     // switch display to window mode
769     success = SDLCreateScreen(FALSE);
770
771     if (!success)
772     {
773       // switching display to window mode failed -- should not happen
774     }
775     else
776     {
777       video.fullscreen_enabled = FALSE;
778       video.window_scaling_percent = setup.window_scaling_percent;
779       video.window_scaling_quality = setup.window_scaling_quality;
780
781       SDLSetScreenRenderingMode(setup.screen_rendering_mode);
782     }
783   }
784
785   SDLRedrawWindow();                    // map window
786
787   return success;
788 }
789
790 void SDLSetWindowTitle(void)
791 {
792   if (sdl_window == NULL)
793     return;
794
795   SDL_SetWindowTitle(sdl_window, program.window_title);
796 }
797
798 void SDLSetWindowScaling(int window_scaling_percent)
799 {
800   if (sdl_window == NULL)
801     return;
802
803   float window_scaling_factor = (float)window_scaling_percent / 100;
804   int new_window_width  = (int)(window_scaling_factor * video.screen_width);
805   int new_window_height = (int)(window_scaling_factor * video.screen_height);
806
807   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
808
809   video.window_scaling_percent = window_scaling_percent;
810   video.window_width  = new_window_width;
811   video.window_height = new_window_height;
812
813   SetWindowTitle();
814 }
815
816 void SDLSetWindowScalingQuality(char *window_scaling_quality)
817 {
818   SDL_Texture *new_texture;
819
820   if (sdl_texture_stream == NULL)
821     return;
822
823   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
824
825   new_texture = SDL_CreateTexture(sdl_renderer,
826                                   SDL_PIXELFORMAT_ARGB8888,
827                                   SDL_TEXTUREACCESS_STREAMING,
828                                   video.width, video.height);
829
830   if (new_texture != NULL)
831   {
832     SDL_DestroyTexture(sdl_texture_stream);
833
834     sdl_texture_stream = new_texture;
835   }
836
837   if (SDL_RenderTargetSupported(sdl_renderer))
838     new_texture = SDL_CreateTexture(sdl_renderer,
839                                     SDL_PIXELFORMAT_ARGB8888,
840                                     SDL_TEXTUREACCESS_TARGET,
841                                     video.width, video.height);
842   else
843     new_texture = NULL;
844
845   if (new_texture != NULL)
846   {
847     SDL_DestroyTexture(sdl_texture_target);
848
849     sdl_texture_target = new_texture;
850   }
851
852   SDLRedrawWindow();
853
854   video.window_scaling_quality = window_scaling_quality;
855 }
856
857 void SDLSetWindowFullscreen(boolean fullscreen)
858 {
859   if (sdl_window == NULL)
860     return;
861
862   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
863
864   if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
865     video.fullscreen_enabled = fullscreen_enabled = fullscreen;
866
867   // if screen size was changed in fullscreen mode, correct desktop window size
868   if (!fullscreen && video.fullscreen_initial)
869   {
870     SDLSetWindowScaling(setup.window_scaling_percent);
871     SDL_SetWindowPosition(sdl_window,
872                           SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr),
873                           SDL_WINDOWPOS_CENTERED_DISPLAY(options.display_nr));
874
875     video.fullscreen_initial = FALSE;
876   }
877 }
878
879 void SDLSetDisplaySize(void)
880 {
881   if (sdl_renderer != NULL)
882   {
883     int w, h;
884
885     SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
886
887     video.display_width  = w;
888     video.display_height = h;
889
890 #if 0
891     Debug("video", "SDL renderer size: %d x %d",
892           video.display_width, video.display_height);
893 #endif
894   }
895   else
896   {
897     SDL_Rect display_bounds;
898
899     SDL_GetDisplayBounds(0, &display_bounds);
900
901     video.display_width  = display_bounds.w;
902     video.display_height = display_bounds.h;
903
904 #if 0
905     Debug("video", "SDL display size: %d x %d",
906           video.display_width, video.display_height);
907 #endif
908   }
909 }
910
911 void SDLSetScreenSizeAndOffsets(int width, int height)
912 {
913   // set default video screen size and offsets
914   video.screen_width = width;
915   video.screen_height = height;
916   video.screen_xoffset = 0;
917   video.screen_yoffset = 0;
918
919 #if defined(USE_COMPLETE_DISPLAY)
920   float ratio_video   = (float) width / height;
921   float ratio_display = (float) video.display_width / video.display_height;
922
923   if (ratio_video != ratio_display)
924   {
925     // adjust drawable screen size to cover the whole device display
926
927     if (ratio_video < ratio_display)
928       video.screen_width  *= ratio_display / ratio_video;
929     else
930       video.screen_height *= ratio_video / ratio_display;
931
932     video.screen_xoffset = (video.screen_width  - width)  / 2;
933     video.screen_yoffset = (video.screen_height - height) / 2;
934
935 #if 0
936     Debug("video", "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
937           width, height,
938           video.screen_width, video.screen_height,
939           ratio_video, ratio_display);
940 #endif
941   }
942 #endif
943 }
944
945 void SDLSetScreenSizeForRenderer(int width, int height)
946 {
947   SDL_RenderSetLogicalSize(sdl_renderer, width, height);
948 }
949
950 void SDLSetScreenProperties(void)
951 {
952   SDLSetDisplaySize();
953   SDLSetScreenSizeAndOffsets(video.width, video.height);
954   SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
955
956   SetOverlayGridSizeAndButtons();
957 }
958
959 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
960 {
961   video.screen_rendering_mode =
962     (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
963      SPECIAL_RENDERING_BITMAP :
964      strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
965      SPECIAL_RENDERING_TARGET:
966      strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
967      SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
968 }
969
970 void SDLSetScreenVsyncMode(char *vsync_mode)
971 {
972   // changing vsync mode without re-creating renderer only supported by OpenGL
973   if (!strPrefixLower((char *)SDLGetRendererName(), "opengl"))
974     return;
975
976   int interval = VSYNC_MODE_STR_TO_INT(vsync_mode);
977   int result = SDL_GL_SetSwapInterval(interval);
978
979   // if adaptive vsync requested, but not supported, retry with normal vsync
980   if (result == -1 && interval == VSYNC_MODE_ADAPTIVE)
981   {
982     interval = VSYNC_MODE_NORMAL;
983
984     result = SDL_GL_SetSwapInterval(interval);
985   }
986
987   if (result == -1)
988     interval = VSYNC_MODE_OFF;
989
990   video.vsync_mode = interval;
991 }
992
993 void SDLRedrawWindow(void)
994 {
995   UpdateScreen_WithoutFrameDelay(NULL);
996 }
997
998 void SDLFreeBitmapPointers(Bitmap *bitmap)
999 {
1000   if (bitmap->surface)
1001     SDL_FreeSurface(bitmap->surface);
1002   if (bitmap->surface_masked)
1003     SDL_FreeSurface(bitmap->surface_masked);
1004
1005   bitmap->surface = NULL;
1006   bitmap->surface_masked = NULL;
1007
1008   if (bitmap->texture)
1009     SDL_DestroyTexture(bitmap->texture);
1010   if (bitmap->texture_masked)
1011     SDL_DestroyTexture(bitmap->texture_masked);
1012
1013   bitmap->texture = NULL;
1014   bitmap->texture_masked = NULL;
1015 }
1016
1017 void SDLBlitSurface(SDL_Surface *src_surface, SDL_Surface *dst_surface,
1018                     int src_x, int src_y, int width, int height,
1019                     int dst_x, int dst_y)
1020 {
1021   SDL_Rect src_rect, dst_rect;
1022
1023   src_rect.x = src_x;
1024   src_rect.y = src_y;
1025   src_rect.w = width;
1026   src_rect.h = height;
1027
1028   dst_rect.x = dst_x;
1029   dst_rect.y = dst_y;
1030   dst_rect.w = width;
1031   dst_rect.h = height;
1032
1033   SDL_BlitSurface(src_surface, &src_rect, dst_surface, &dst_rect);
1034 }
1035
1036 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1037                  int src_x, int src_y, int width, int height,
1038                  int dst_x, int dst_y, int mask_mode)
1039 {
1040   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1041   SDL_Rect src_rect, dst_rect;
1042
1043   src_rect.x = src_x;
1044   src_rect.y = src_y;
1045   src_rect.w = width;
1046   src_rect.h = height;
1047
1048   dst_rect.x = dst_x;
1049   dst_rect.y = dst_y;
1050   dst_rect.w = width;
1051   dst_rect.h = height;
1052
1053   SDLSetBitmapAlpha(src_bitmap, FALSE, mask_mode == BLIT_MASKED);
1054
1055   // if (src_bitmap != backbuffer || dst_bitmap != window)
1056   if (!(src_bitmap == backbuffer && dst_bitmap == window))
1057     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1058                      src_bitmap->surface_masked : src_bitmap->surface),
1059                     &src_rect, real_dst_bitmap->surface, &dst_rect);
1060
1061   if (dst_bitmap == window)
1062     UpdateScreen_WithFrameDelay(&dst_rect);
1063 }
1064
1065 void SDLBlitTexture(Bitmap *bitmap,
1066                     int src_x, int src_y, int width, int height,
1067                     int dst_x, int dst_y, int mask_mode)
1068 {
1069   SDL_Texture *texture;
1070   SDL_Rect src_rect;
1071   SDL_Rect dst_rect;
1072
1073   texture =
1074     (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1075
1076   if (texture == NULL)
1077     return;
1078
1079   src_rect.x = src_x;
1080   src_rect.y = src_y;
1081   src_rect.w = width;
1082   src_rect.h = height;
1083
1084   dst_rect.x = dst_x;
1085   dst_rect.y = dst_y;
1086   dst_rect.w = width;
1087   dst_rect.h = height;
1088
1089   SDLSetBitmapAlpha(bitmap, TRUE, mask_mode == BLIT_MASKED);
1090
1091   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1092 }
1093
1094 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1095                       Uint32 color)
1096 {
1097   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1098   SDL_Rect rect;
1099
1100   rect.x = x;
1101   rect.y = y;
1102   rect.w = width;
1103   rect.h = height;
1104
1105   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1106
1107   if (dst_bitmap == window)
1108     UpdateScreen_WithFrameDelay(&rect);
1109 }
1110
1111 void PrepareFadeBitmap(int draw_target)
1112 {
1113   Bitmap *fade_bitmap =
1114     (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1115      draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1116
1117   if (fade_bitmap == NULL)
1118     return;
1119
1120   // copy backbuffer to fading buffer
1121   BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1122
1123   // add border and animations to fading buffer
1124   FinalizeScreen(draw_target);
1125 }
1126
1127 void SDLFadeRectangle(int x, int y, int width, int height,
1128                       int fade_mode, int fade_delay, int post_delay,
1129                       void (*draw_border_function)(void))
1130 {
1131   SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1132   SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1133   SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1134   SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
1135   SDL_Surface *surface_screen = backbuffer->surface;
1136   SDL_Rect src_rect, dst_rect;
1137   SDL_Rect dst_rect2;
1138   int src_x = x, src_y = y;
1139   int dst_x = x, dst_y = y;
1140   unsigned int time_last, time_current;
1141
1142   // store function for drawing global masked border
1143   void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1144
1145   // deactivate drawing of global border while fading, if needed
1146   if (draw_border_function == NULL)
1147     gfx.draw_global_border_function = NULL;
1148
1149   src_rect.x = src_x;
1150   src_rect.y = src_y;
1151   src_rect.w = width;
1152   src_rect.h = height;
1153
1154   dst_rect.x = dst_x;
1155   dst_rect.y = dst_y;
1156   dst_rect.w = width;           // (ignored)
1157   dst_rect.h = height;          // (ignored)
1158
1159   dst_rect2 = dst_rect;
1160
1161   // before fading in, store backbuffer (without animation graphics)
1162   if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1163     SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1164
1165   // copy source and target surfaces to temporary surfaces for fading
1166   if (fade_mode & FADE_TYPE_TRANSFORM)
1167   {
1168     // (source and target fading buffer already prepared)
1169   }
1170   else if (fade_mode & FADE_TYPE_FADE_IN)
1171   {
1172     // (target fading buffer already prepared)
1173     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
1174   }
1175   else          // FADE_TYPE_FADE_OUT
1176   {
1177     // (source fading buffer already prepared)
1178     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
1179   }
1180
1181   time_current = SDL_GetTicks();
1182
1183   if (fade_delay <= 0)
1184   {
1185     // immediately draw final target frame without delay
1186     fade_mode &= (FADE_MODE_FADE | FADE_MODE_TRANSFORM);
1187     fade_delay = 1;
1188     time_current -= 1;
1189
1190     // when fading without delay, also skip post delay
1191     post_delay = 0;
1192   }
1193
1194   if (fade_mode == FADE_MODE_MELT)
1195   {
1196     boolean done = FALSE;
1197     int melt_pixels = 2;
1198     int melt_columns = width / melt_pixels;
1199     int ypos[melt_columns];
1200     int max_steps = height / 8 + 32;
1201     int steps_done = 0;
1202     float steps = 0;
1203     int i;
1204
1205     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1206
1207     SDLSetAlpha(surface_target, FALSE, 0);      // disable alpha blending
1208
1209     ypos[0] = -GetSimpleRandom(16);
1210
1211     for (i = 1 ; i < melt_columns; i++)
1212     {
1213       int r = GetSimpleRandom(3) - 1;   // randomly choose from { -1, 0, -1 }
1214
1215       ypos[i] = ypos[i - 1] + r;
1216
1217       if (ypos[i] > 0)
1218         ypos[i] = 0;
1219       else
1220         if (ypos[i] == -16)
1221           ypos[i] = -15;
1222     }
1223
1224     while (!done)
1225     {
1226       int steps_final;
1227
1228       time_last = time_current;
1229       time_current = SDL_GetTicks();
1230       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1231       steps_final = MIN(MAX(0, steps), max_steps);
1232
1233       steps_done++;
1234
1235       done = (steps_done >= steps_final);
1236
1237       for (i = 0 ; i < melt_columns; i++)
1238       {
1239         if (ypos[i] < 0)
1240         {
1241           ypos[i]++;
1242
1243           done = FALSE;
1244         }
1245         else if (ypos[i] < height)
1246         {
1247           int y1 = 16;
1248           int y2 = 8;
1249           int y3 = 8;
1250           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1251
1252           if (ypos[i] + dy >= height)
1253             dy = height - ypos[i];
1254
1255           // copy part of (appearing) target surface to upper area
1256           src_rect.x = src_x + i * melt_pixels;
1257           // src_rect.y = src_y + ypos[i];
1258           src_rect.y = src_y;
1259           src_rect.w = melt_pixels;
1260           // src_rect.h = dy;
1261           src_rect.h = ypos[i] + dy;
1262
1263           dst_rect.x = dst_x + i * melt_pixels;
1264           // dst_rect.y = dst_y + ypos[i];
1265           dst_rect.y = dst_y;
1266
1267           if (steps_done >= steps_final)
1268             SDL_BlitSurface(surface_target, &src_rect,
1269                             surface_screen, &dst_rect);
1270
1271           ypos[i] += dy;
1272
1273           // copy part of (disappearing) source surface to lower area
1274           src_rect.x = src_x + i * melt_pixels;
1275           src_rect.y = src_y;
1276           src_rect.w = melt_pixels;
1277           src_rect.h = height - ypos[i];
1278
1279           dst_rect.x = dst_x + i * melt_pixels;
1280           dst_rect.y = dst_y + ypos[i];
1281
1282           if (steps_done >= steps_final)
1283             SDL_BlitSurface(surface_source, &src_rect,
1284                             surface_screen, &dst_rect);
1285
1286           done = FALSE;
1287         }
1288         else
1289         {
1290           src_rect.x = src_x + i * melt_pixels;
1291           src_rect.y = src_y;
1292           src_rect.w = melt_pixels;
1293           src_rect.h = height;
1294
1295           dst_rect.x = dst_x + i * melt_pixels;
1296           dst_rect.y = dst_y;
1297
1298           if (steps_done >= steps_final)
1299             SDL_BlitSurface(surface_target, &src_rect,
1300                             surface_screen, &dst_rect);
1301         }
1302       }
1303
1304       if (steps_done >= steps_final)
1305       {
1306         if (draw_border_function != NULL)
1307           draw_border_function();
1308
1309         UpdateScreen_WithFrameDelay(&dst_rect2);
1310
1311         if (PendingEscapeKeyEvent())
1312           break;
1313       }
1314     }
1315   }
1316   else if (fade_mode == FADE_MODE_CURTAIN)
1317   {
1318     float xx;
1319     int xx_final;
1320     int xx_size = width / 2;
1321
1322     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1323
1324     SDLSetAlpha(surface_source, FALSE, 0);      // disable alpha blending
1325
1326     for (xx = 0; xx < xx_size;)
1327     {
1328       time_last = time_current;
1329       time_current = SDL_GetTicks();
1330       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1331       xx_final = MIN(MAX(0, xx), xx_size);
1332
1333       src_rect.x = src_x;
1334       src_rect.y = src_y;
1335       src_rect.w = width;
1336       src_rect.h = height;
1337
1338       dst_rect.x = dst_x;
1339       dst_rect.y = dst_y;
1340
1341       // draw new (target) image to screen buffer
1342       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1343
1344       if (xx_final < xx_size)
1345       {
1346         src_rect.w = xx_size - xx_final;
1347         src_rect.h = height;
1348
1349         // draw old (source) image to screen buffer (left side)
1350
1351         src_rect.x = src_x + xx_final;
1352         dst_rect.x = dst_x;
1353
1354         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1355
1356         // draw old (source) image to screen buffer (right side)
1357
1358         src_rect.x = src_x + xx_size;
1359         dst_rect.x = dst_x + xx_size + xx_final;
1360
1361         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1362       }
1363
1364       if (draw_border_function != NULL)
1365         draw_border_function();
1366
1367       // only update the region of the screen that is affected from fading
1368       UpdateScreen_WithFrameDelay(&dst_rect2);
1369
1370       if (PendingEscapeKeyEvent())
1371         break;
1372     }
1373   }
1374   else          // fading in, fading out or cross-fading
1375   {
1376     float alpha;
1377     int alpha_final;
1378
1379     for (alpha = 0.0; alpha < 255.0;)
1380     {
1381       time_last = time_current;
1382       time_current = SDL_GetTicks();
1383       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1384       alpha_final = MIN(MAX(0, alpha), 255);
1385
1386       // draw existing (source) image to screen buffer
1387       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1388
1389       // draw new (target) image to screen buffer using alpha blending
1390       SDLSetAlpha(surface_target, TRUE, alpha_final);
1391       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1392
1393       if (draw_border_function != NULL)
1394         draw_border_function();
1395
1396       // only update the region of the screen that is affected from fading
1397       UpdateScreen_WithFrameDelay(&dst_rect);
1398
1399       if (PendingEscapeKeyEvent())
1400         break;
1401     }
1402   }
1403
1404   if (post_delay > 0)
1405     Delay_WithScreenUpdates(post_delay);
1406
1407   // restore function for drawing global masked border
1408   gfx.draw_global_border_function = draw_global_border_function;
1409
1410   // after fading in, restore backbuffer (without animation graphics)
1411   if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1412     SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1413 }
1414
1415 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1416                        int to_x, int to_y, Uint32 color)
1417 {
1418   SDL_Surface *surface = dst_bitmap->surface;
1419   SDL_Rect rect;
1420
1421   if (from_x > to_x)
1422     swap_numbers(&from_x, &to_x);
1423
1424   if (from_y > to_y)
1425     swap_numbers(&from_y, &to_y);
1426
1427   rect.x = from_x;
1428   rect.y = from_y;
1429   rect.w = (to_x - from_x + 1);
1430   rect.h = (to_y - from_y + 1);
1431
1432   SDL_FillRect(surface, &rect, color);
1433 }
1434
1435 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1436                  int to_x, int to_y, Uint32 color)
1437 {
1438   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1439 }
1440
1441 #if ENABLE_UNUSED_CODE
1442 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1443                   int num_points, Uint32 color)
1444 {
1445   int i, x, y;
1446   int line_width = 4;
1447
1448   for (i = 0; i < num_points - 1; i++)
1449   {
1450     for (x = 0; x < line_width; x++)
1451     {
1452       for (y = 0; y < line_width; y++)
1453       {
1454         int dx = x - line_width / 2;
1455         int dy = y - line_width / 2;
1456
1457         if ((x == 0 && y == 0) ||
1458             (x == 0 && y == line_width - 1) ||
1459             (x == line_width - 1 && y == 0) ||
1460             (x == line_width - 1 && y == line_width - 1))
1461           continue;
1462
1463         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1464                  points[i + 1].x + dx, points[i + 1].y + dy, color);
1465       }
1466     }
1467   }
1468 }
1469 #endif
1470
1471 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1472 {
1473   SDL_Surface *surface = src_bitmap->surface;
1474
1475   switch (surface->format->BytesPerPixel)
1476   {
1477     case 1:             // assuming 8-bpp
1478     {
1479       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1480     }
1481     break;
1482
1483     case 2:             // probably 15-bpp or 16-bpp
1484     {
1485       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1486     }
1487     break;
1488
1489   case 3:               // slow 24-bpp mode; usually not used
1490     {
1491       // does this work?
1492       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1493       Uint32 color = 0;
1494       int shift;
1495
1496       shift = surface->format->Rshift;
1497       color |= *(pix + shift / 8) >> shift;
1498       shift = surface->format->Gshift;
1499       color |= *(pix + shift / 8) >> shift;
1500       shift = surface->format->Bshift;
1501       color |= *(pix + shift / 8) >> shift;
1502
1503       return color;
1504     }
1505     break;
1506
1507   case 4:               // probably 32-bpp
1508     {
1509       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1510     }
1511     break;
1512   }
1513
1514   return 0;
1515 }
1516
1517
1518 // ============================================================================
1519 // The following functions were taken from the SGE library
1520 // (SDL Graphics Extension Library) by Anders Lindström
1521 // http://www.etek.chalmers.se/~e8cal1/sge/index.html
1522 // ============================================================================
1523
1524 static void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1525 {
1526   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1527   {
1528     switch (surface->format->BytesPerPixel)
1529     {
1530       case 1:
1531       {
1532         // Assuming 8-bpp
1533         *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1534       }
1535       break;
1536
1537       case 2:
1538       {
1539         // Probably 15-bpp or 16-bpp
1540         *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1541       }
1542       break;
1543
1544       case 3:
1545       {
1546         // Slow 24-bpp mode, usually not used
1547         Uint8 *pix;
1548         int shift;
1549
1550         // Gack - slow, but endian correct
1551         pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1552         shift = surface->format->Rshift;
1553         *(pix + shift / 8) = color>>shift;
1554         shift = surface->format->Gshift;
1555         *(pix + shift / 8) = color>>shift;
1556         shift = surface->format->Bshift;
1557         *(pix + shift / 8) = color>>shift;
1558       }
1559       break;
1560
1561       case 4:
1562       {
1563         // Probably 32-bpp
1564         *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1565       }
1566       break;
1567     }
1568   }
1569 }
1570
1571 #if 0
1572 static void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1573                          Uint8 R, Uint8 G, Uint8 B)
1574 {
1575   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1576 }
1577
1578 static void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1579 {
1580   *((Uint8 *)surface->pixels + y * surface->pitch + x) = color;
1581 }
1582
1583 static void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1584 {
1585   *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x) = color;
1586 }
1587
1588 static void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1589 {
1590   Uint8 *pix;
1591   int shift;
1592
1593   // Gack - slow, but endian correct
1594   pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1595   shift = surface->format->Rshift;
1596   *(pix + shift / 8) = color>>shift;
1597   shift = surface->format->Gshift;
1598   *(pix + shift / 8) = color>>shift;
1599   shift = surface->format->Bshift;
1600   *(pix + shift / 8) = color>>shift;
1601 }
1602
1603 static void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1604 {
1605   *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x) = color;
1606 }
1607
1608 static void _PutPixelX(SDL_Surface *dest, Sint16 x, Sint16 y, Uint32 color)
1609 {
1610   switch (dest->format->BytesPerPixel)
1611   {
1612     case 1:
1613       *((Uint8 *)dest->pixels + y * dest->pitch + x) = color;
1614       break;
1615
1616     case 2:
1617       *((Uint16 *)dest->pixels + y * dest->pitch / 2 + x) = color;
1618       break;
1619
1620     case 3:
1621       _PutPixel24(dest, x, y, color);
1622       break;
1623
1624     case 4:
1625       *((Uint32 *)dest->pixels + y * dest->pitch / 4 + x) = color;
1626       break;
1627   }
1628 }
1629 #endif
1630
1631 static void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1632 {
1633   if (SDL_MUSTLOCK(surface))
1634   {
1635     if (SDL_LockSurface(surface) < 0)
1636     {
1637       return;
1638     }
1639   }
1640
1641   _PutPixel(surface, x, y, color);
1642
1643   if (SDL_MUSTLOCK(surface))
1644   {
1645     SDL_UnlockSurface(surface);
1646   }
1647 }
1648
1649 #if 0
1650 static void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1651                             Uint8 r, Uint8 g, Uint8 b)
1652 {
1653   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1654 }
1655
1656 static Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1657 {
1658   if (y >= 0 && y <= dest->h - 1)
1659   {
1660     switch (dest->format->BytesPerPixel)
1661     {
1662       case 1:
1663         return y * dest->pitch;
1664         break;
1665
1666       case 2:
1667         return y * dest->pitch / 2;
1668         break;
1669
1670       case 3:
1671         return y * dest->pitch;
1672         break;
1673
1674       case 4:
1675         return y * dest->pitch / 4;
1676         break;
1677     }
1678   }
1679
1680   return -1;
1681 }
1682
1683 static void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch,
1684                           Uint32 color)
1685 {
1686   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1687   {
1688     switch (surface->format->BytesPerPixel)
1689     {
1690       case 1:
1691       {
1692         // Assuming 8-bpp
1693         *((Uint8 *)surface->pixels + ypitch + x) = color;
1694       }
1695       break;
1696
1697       case 2:
1698       {
1699         // Probably 15-bpp or 16-bpp
1700         *((Uint16 *)surface->pixels + ypitch + x) = color;
1701       }
1702       break;
1703
1704       case 3:
1705       {
1706         // Slow 24-bpp mode, usually not used
1707         Uint8 *pix;
1708         int shift;
1709
1710         // Gack - slow, but endian correct
1711         pix = (Uint8 *)surface->pixels + ypitch + x * 3;
1712         shift = surface->format->Rshift;
1713         *(pix + shift / 8) = color>>shift;
1714         shift = surface->format->Gshift;
1715         *(pix + shift / 8) = color>>shift;
1716         shift = surface->format->Bshift;
1717         *(pix + shift / 8) = color>>shift;
1718       }
1719       break;
1720
1721       case 4:
1722       {
1723         // Probably 32-bpp
1724         *((Uint32 *)surface->pixels + ypitch + x) = color;
1725       }
1726       break;
1727     }
1728   }
1729 }
1730
1731 static void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1732                       Uint32 Color)
1733 {
1734   SDL_Rect l;
1735
1736   if (SDL_MUSTLOCK(Surface))
1737   {
1738     if (SDL_LockSurface(Surface) < 0)
1739     {
1740       return;
1741     }
1742   }
1743
1744   if (x1 > x2)
1745   {
1746     Sint16 tmp = x1;
1747     x1 = x2;
1748     x2 = tmp;
1749   }
1750
1751   // Do the clipping
1752   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1753     return;
1754   if (x1 < 0)
1755     x1 = 0;
1756   if (x2 > Surface->w - 1)
1757     x2 = Surface->w - 1;
1758
1759   l.x = x1;
1760   l.y = y;
1761   l.w = x2 - x1 + 1;
1762   l.h = 1;
1763
1764   SDL_FillRect(Surface, &l, Color);
1765
1766   if (SDL_MUSTLOCK(Surface))
1767   {
1768     SDL_UnlockSurface(Surface);
1769   }
1770 }
1771
1772 static void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1773                          Uint8 R, Uint8 G, Uint8 B)
1774 {
1775   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1776 }
1777
1778 static void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1779                    Uint32 Color)
1780 {
1781   SDL_Rect l;
1782
1783   if (x1 > x2)
1784   {
1785     Sint16 tmp = x1;
1786     x1 = x2;
1787     x2 = tmp;
1788   }
1789
1790   // Do the clipping
1791   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1792     return;
1793   if (x1 < 0)
1794     x1 = 0;
1795   if (x2 > Surface->w - 1)
1796     x2 = Surface->w - 1;
1797
1798   l.x = x1;
1799   l.y = y;
1800   l.w = x2 - x1 + 1;
1801   l.h = 1;
1802
1803   SDL_FillRect(Surface, &l, Color);
1804 }
1805
1806 static void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1807                       Uint32 Color)
1808 {
1809   SDL_Rect l;
1810
1811   if (SDL_MUSTLOCK(Surface))
1812   {
1813     if (SDL_LockSurface(Surface) < 0)
1814     {
1815       return;
1816     }
1817   }
1818
1819   if (y1 > y2)
1820   {
1821     Sint16 tmp = y1;
1822     y1 = y2;
1823     y2 = tmp;
1824   }
1825
1826   // Do the clipping
1827   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1828     return;
1829   if (y1 < 0)
1830     y1 = 0;
1831   if (y2 > Surface->h - 1)
1832     y2 = Surface->h - 1;
1833
1834   l.x = x;
1835   l.y = y1;
1836   l.w = 1;
1837   l.h = y2 - y1 + 1;
1838
1839   SDL_FillRect(Surface, &l, Color);
1840
1841   if (SDL_MUSTLOCK(Surface))
1842   {
1843     SDL_UnlockSurface(Surface);
1844   }
1845 }
1846
1847 static void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1848                          Uint8 R, Uint8 G, Uint8 B)
1849 {
1850   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1851 }
1852
1853 static void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1854                    Uint32 Color)
1855 {
1856   SDL_Rect l;
1857
1858   if (y1 > y2)
1859   {
1860     Sint16 tmp = y1;
1861     y1 = y2;
1862     y2 = tmp;
1863   }
1864
1865   // Do the clipping
1866   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1867     return;
1868   if (y1 < 0)
1869     y1 = 0;
1870   if (y2 > Surface->h - 1)
1871     y2 = Surface->h - 1;
1872
1873   l.x = x;
1874   l.y = y1;
1875   l.w = 1;
1876   l.h = y2 - y1 + 1;
1877
1878   SDL_FillRect(Surface, &l, Color);
1879 }
1880 #endif
1881
1882 static void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1883                        Sint16 x2, Sint16 y2, Uint32 Color,
1884                        void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1885                                      Uint32 Color))
1886 {
1887   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1888
1889   dx = x2 - x1;
1890   dy = y2 - y1;
1891
1892   sdx = (dx < 0) ? -1 : 1;
1893   sdy = (dy < 0) ? -1 : 1;
1894
1895   dx = sdx * dx + 1;
1896   dy = sdy * dy + 1;
1897
1898   x = y = 0;
1899
1900   px = x1;
1901   py = y1;
1902
1903   if (dx >= dy)
1904   {
1905     for (x = 0; x < dx; x++)
1906     {
1907       Callback(Surface, px, py, Color);
1908
1909       y += dy;
1910       if (y >= dx)
1911       {
1912         y -= dx;
1913         py += sdy;
1914       }
1915
1916       px += sdx;
1917     }
1918   }
1919   else
1920   {
1921     for (y = 0; y < dy; y++)
1922     {
1923       Callback(Surface, px, py, Color);
1924
1925       x += dx;
1926       if (x >= dy)
1927       {
1928         x -= dy;
1929         px += sdx;
1930       }
1931
1932       py += sdy;
1933     }
1934   }
1935 }
1936
1937 #if 0
1938 static void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1939                           Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1940                           void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1941                                         Uint32 Color))
1942 {
1943   sge_DoLine(Surface, X1, Y1, X2, Y2,
1944              SDL_MapRGB(Surface->format, R, G, B), Callback);
1945 }
1946 #endif
1947
1948 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1949               Uint32 Color)
1950 {
1951   if (SDL_MUSTLOCK(Surface))
1952   {
1953     if (SDL_LockSurface(Surface) < 0)
1954       return;
1955    }
1956
1957    // Draw the line
1958    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1959
1960    // unlock the display
1961    if (SDL_MUSTLOCK(Surface))
1962    {
1963       SDL_UnlockSurface(Surface);
1964    }
1965 }
1966
1967 #if 0
1968 static void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1969                         Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1970 {
1971   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1972 }
1973 #endif
1974
1975 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1976 {
1977   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1978 }
1979
1980
1981 // ----------------------------------------------------------------------------
1982 // quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1983 // ----------------------------------------------------------------------------
1984
1985 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1986                           int src_x, int src_y, int width, int height,
1987                           int dst_x, int dst_y)
1988 {
1989   int x, y;
1990
1991   for (y = 0; y < height; y++)
1992   {
1993     for (x = 0; x < width; x++)
1994     {
1995       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1996
1997       if (pixel != BLACK_PIXEL)
1998         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1999     }
2000   }
2001 }
2002
2003
2004 // ============================================================================
2005 // The following functions were taken from the SDL_gfx library version 2.0.3
2006 // (Rotozoomer) by Andreas Schiffler
2007 // http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html
2008 // ============================================================================
2009
2010 // ----------------------------------------------------------------------------
2011 // 32 bit zoomer
2012 //
2013 // zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2014 // ----------------------------------------------------------------------------
2015
2016 typedef struct
2017 {
2018   Uint8 r;
2019   Uint8 g;
2020   Uint8 b;
2021   Uint8 a;
2022 } tColorRGBA;
2023
2024 static int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2025 {
2026   int x, y;
2027   tColorRGBA *sp, *csp, *dp;
2028   int dgap;
2029
2030   // pointer setup
2031   sp = csp = (tColorRGBA *) src->pixels;
2032   dp = (tColorRGBA *) dst->pixels;
2033   dgap = dst->pitch - dst->w * 4;
2034
2035   for (y = 0; y < dst->h; y++)
2036   {
2037     sp = csp;
2038
2039     for (x = 0; x < dst->w; x++)
2040     {
2041       tColorRGBA *sp0 = sp;
2042       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2043       tColorRGBA *sp00 = &sp0[0];
2044       tColorRGBA *sp01 = &sp0[1];
2045       tColorRGBA *sp10 = &sp1[0];
2046       tColorRGBA *sp11 = &sp1[1];
2047       tColorRGBA new;
2048
2049       // create new color pixel from all four source color pixels
2050       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2051       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2052       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2053       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2054
2055       // draw
2056       *dp = new;
2057
2058       // advance source pointers
2059       sp += 2;
2060
2061       // advance destination pointer
2062       dp++;
2063     }
2064
2065     // advance source pointer
2066     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2067
2068     // advance destination pointers
2069     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2070   }
2071
2072   return 0;
2073 }
2074
2075 static int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2076 {
2077   int x, y, *sax, *say, *csax, *csay;
2078   float sx, sy;
2079   tColorRGBA *sp, *csp, *csp0, *dp;
2080   int dgap;
2081
2082   // use specialized zoom function when scaling down to exactly half size
2083   if (src->w == 2 * dst->w &&
2084       src->h == 2 * dst->h)
2085     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2086
2087   // variable setup
2088   sx = (float) src->w / (float) dst->w;
2089   sy = (float) src->h / (float) dst->h;
2090
2091   // allocate memory for row increments
2092   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2093   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2094
2095   // precalculate row increments
2096   for (x = 0; x <= dst->w; x++)
2097     *csax++ = (int)(sx * x);
2098
2099   for (y = 0; y <= dst->h; y++)
2100     *csay++ = (int)(sy * y);
2101
2102   // pointer setup
2103   sp = csp = csp0 = (tColorRGBA *) src->pixels;
2104   dp = (tColorRGBA *) dst->pixels;
2105   dgap = dst->pitch - dst->w * 4;
2106
2107   csay = say;
2108   for (y = 0; y < dst->h; y++)
2109   {
2110     sp = csp;
2111     csax = sax;
2112
2113     for (x = 0; x < dst->w; x++)
2114     {
2115       // draw
2116       *dp = *sp;
2117
2118       // advance source pointers
2119       csax++;
2120       sp = csp + *csax;
2121
2122       // advance destination pointer
2123       dp++;
2124     }
2125
2126     // advance source pointer
2127     csay++;
2128     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2129
2130     // advance destination pointers
2131     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2132   }
2133
2134   free(sax);
2135   free(say);
2136
2137   return 0;
2138 }
2139
2140 // ----------------------------------------------------------------------------
2141 // 8 bit zoomer
2142 //
2143 // zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2144 // ----------------------------------------------------------------------------
2145
2146 static int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2147 {
2148   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2149   Uint8 *sp, *dp, *csp;
2150   int dgap;
2151
2152   // variable setup
2153   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2154   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2155
2156   // allocate memory for row increments
2157   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2158   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2159
2160   // precalculate row increments
2161   csx = 0;
2162   csax = sax;
2163   for (x = 0; x < dst->w; x++)
2164   {
2165     csx += sx;
2166     *csax = (csx >> 16);
2167     csx &= 0xffff;
2168     csax++;
2169   }
2170
2171   csy = 0;
2172   csay = say;
2173   for (y = 0; y < dst->h; y++)
2174   {
2175     csy += sy;
2176     *csay = (csy >> 16);
2177     csy &= 0xffff;
2178     csay++;
2179   }
2180
2181   csx = 0;
2182   csax = sax;
2183   for (x = 0; x < dst->w; x++)
2184   {
2185     csx += (*csax);
2186     csax++;
2187   }
2188
2189   csy = 0;
2190   csay = say;
2191   for (y = 0; y < dst->h; y++)
2192   {
2193     csy += (*csay);
2194     csay++;
2195   }
2196
2197   // pointer setup
2198   sp = csp = (Uint8 *) src->pixels;
2199   dp = (Uint8 *) dst->pixels;
2200   dgap = dst->pitch - dst->w;
2201
2202   // draw
2203   csay = say;
2204   for (y = 0; y < dst->h; y++)
2205   {
2206     csax = sax;
2207     sp = csp;
2208     for (x = 0; x < dst->w; x++)
2209     {
2210       // draw
2211       *dp = *sp;
2212
2213       // advance source pointers
2214       sp += (*csax);
2215       csax++;
2216
2217       // advance destination pointer
2218       dp++;
2219     }
2220
2221     // advance source pointer (for row)
2222     csp += ((*csay) * src->pitch);
2223     csay++;
2224
2225     // advance destination pointers
2226     dp += dgap;
2227   }
2228
2229   free(sax);
2230   free(say);
2231
2232   return 0;
2233 }
2234
2235 // ----------------------------------------------------------------------------
2236 // SDLZoomSurface()
2237 //
2238 // Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2239 // 'zoomx' and 'zoomy' are scaling factors for width and height.
2240 // If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2241 // into a 32bit RGBA format on the fly.
2242 // ----------------------------------------------------------------------------
2243
2244 SDL_Surface *SDLZoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2245 {
2246   SDL_Surface *zoom_src = NULL;
2247   SDL_Surface *zoom_dst = NULL;
2248   boolean is_converted = FALSE;
2249   boolean is_32bit;
2250   int i;
2251
2252   if (src == NULL)
2253     return NULL;
2254
2255   // determine if source surface is 32 bit or 8 bit
2256   is_32bit = (src->format->BitsPerPixel == 32);
2257
2258   if (is_32bit || src->format->BitsPerPixel == 8)
2259   {
2260     // use source surface 'as is'
2261     zoom_src = src;
2262   }
2263   else
2264   {
2265     // new source surface is 32 bit with a defined RGB ordering
2266     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2267                                     0x000000ff, 0x0000ff00, 0x00ff0000,
2268                                     (src->format->Amask ? 0xff000000 : 0));
2269     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2270     is_32bit = TRUE;
2271     is_converted = TRUE;
2272   }
2273
2274   // allocate surface to completely contain the zoomed surface
2275   if (is_32bit)
2276   {
2277     // target surface is 32 bit with source RGBA/ABGR ordering
2278     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2279                                     zoom_src->format->Rmask,
2280                                     zoom_src->format->Gmask,
2281                                     zoom_src->format->Bmask,
2282                                     zoom_src->format->Amask);
2283   }
2284   else
2285   {
2286     // target surface is 8 bit
2287     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2288                                     0, 0, 0, 0);
2289   }
2290
2291   // lock source surface
2292   SDL_LockSurface(zoom_src);
2293
2294   // check which kind of surface we have
2295   if (is_32bit)
2296   {
2297     // call the 32 bit transformation routine to do the zooming
2298     zoomSurfaceRGBA(zoom_src, zoom_dst);
2299   }
2300   else
2301   {
2302     // copy palette
2303     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2304       zoom_dst->format->palette->colors[i] =
2305         zoom_src->format->palette->colors[i];
2306     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2307
2308     // call the 8 bit transformation routine to do the zooming
2309     zoomSurfaceY(zoom_src, zoom_dst);
2310   }
2311
2312   // unlock source surface
2313   SDL_UnlockSurface(zoom_src);
2314
2315   // free temporary surface
2316   if (is_converted)
2317     SDL_FreeSurface(zoom_src);
2318
2319   // return destination surface
2320   return zoom_dst;
2321 }
2322
2323 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2324 {
2325   SDL_Surface *new_surface;
2326
2327   if (surface == NULL)
2328     return NULL;
2329
2330   if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2331     Fail("SDLGetNativeSurface() failed");
2332
2333   // remove alpha channel from native non-transparent surface, if defined
2334   SDLSetAlpha(new_surface, FALSE, 0);
2335
2336   // remove transparent color from native non-transparent surface, if defined
2337   SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2338
2339   return new_surface;
2340 }
2341
2342 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2343 {
2344   Bitmap *dst_bitmap = CreateBitmapStruct();
2345   SDL_Surface *src_surface = src_bitmap->surface_masked;
2346   SDL_Surface *dst_surface;
2347
2348   dst_width  = MAX(1, dst_width);       // prevent zero bitmap width
2349   dst_height = MAX(1, dst_height);      // prevent zero bitmap height
2350
2351   dst_bitmap->width  = dst_width;
2352   dst_bitmap->height = dst_height;
2353
2354   // create zoomed temporary surface from source surface
2355   dst_surface = SDLZoomSurface(src_surface, dst_width, dst_height);
2356
2357   // create native format destination surface from zoomed temporary surface
2358   SDLSetNativeSurface(&dst_surface);
2359
2360   // set color key for zoomed surface from source surface, if defined
2361   if (SDLHasColorKey(src_surface))
2362     SDLCopyColorKey(src_surface, dst_surface);
2363
2364   // create native non-transparent surface for opaque blitting
2365   dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2366
2367   // set native transparent surface for masked blitting
2368   dst_bitmap->surface_masked = dst_surface;
2369
2370   return dst_bitmap;
2371 }
2372
2373
2374 // ============================================================================
2375 // load image to bitmap
2376 // ============================================================================
2377
2378 Bitmap *SDLLoadImage(char *filename)
2379 {
2380   Bitmap *new_bitmap = CreateBitmapStruct();
2381   SDL_Surface *sdl_image_tmp;
2382
2383   if (program.headless)
2384   {
2385     // prevent sanity check warnings at later stage
2386     new_bitmap->width = new_bitmap->height = 1;
2387
2388     return new_bitmap;
2389   }
2390
2391   print_timestamp_init("SDLLoadImage");
2392
2393   print_timestamp_time(getBaseNamePtr(filename));
2394
2395   // load image to temporary surface
2396   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2397     Fail("IMG_Load('%s') failed: %s", getBaseNamePtr(filename), SDL_GetError());
2398
2399   print_timestamp_time("IMG_Load");
2400
2401   UPDATE_BUSY_STATE();
2402
2403   // create native non-transparent surface for current image
2404   if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2405     Fail("SDLGetOpaqueSurface() failed");
2406
2407   print_timestamp_time("SDLGetNativeSurface (opaque)");
2408
2409   UPDATE_BUSY_STATE();
2410
2411   // set black pixel to transparent if no alpha channel / transparent color
2412   if (!SDLHasAlpha(sdl_image_tmp) &&
2413       !SDLHasColorKey(sdl_image_tmp))
2414     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2415                     SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2416
2417   // create native transparent surface for current image
2418   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2419     Fail("SDLGetNativeSurface() failed");
2420
2421   print_timestamp_time("SDLGetNativeSurface (masked)");
2422
2423   UPDATE_BUSY_STATE();
2424
2425   // free temporary surface
2426   SDL_FreeSurface(sdl_image_tmp);
2427
2428   new_bitmap->width = new_bitmap->surface->w;
2429   new_bitmap->height = new_bitmap->surface->h;
2430
2431   print_timestamp_done("SDLLoadImage");
2432
2433   return new_bitmap;
2434 }
2435
2436
2437 // ----------------------------------------------------------------------------
2438 // custom cursor fuctions
2439 // ----------------------------------------------------------------------------
2440
2441 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2442 {
2443   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2444                           cursor_info->width, cursor_info->height,
2445                           cursor_info->hot_x, cursor_info->hot_y);
2446 }
2447
2448 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2449 {
2450   static struct MouseCursorInfo *last_cursor_info = NULL;
2451   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2452   static SDL_Cursor *cursor_default = NULL;
2453   static SDL_Cursor *cursor_current = NULL;
2454
2455   // if invoked for the first time, store the SDL default cursor
2456   if (cursor_default == NULL)
2457     cursor_default = SDL_GetCursor();
2458
2459   // only create new cursor if cursor info (custom only) has changed
2460   if (cursor_info != NULL && cursor_info != last_cursor_info)
2461   {
2462     cursor_current = create_cursor(cursor_info);
2463     last_cursor_info = cursor_info;
2464   }
2465
2466   // only set new cursor if cursor info (custom or NULL) has changed
2467   if (cursor_info != last_cursor_info2)
2468     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2469
2470   last_cursor_info2 = cursor_info;
2471 }
2472
2473
2474 // ============================================================================
2475 // audio functions
2476 // ============================================================================
2477
2478 void SDLOpenAudio(void)
2479 {
2480   if (program.headless)
2481     return;
2482
2483   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2484   {
2485     Warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
2486
2487     return;
2488   }
2489
2490   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2491                     AUDIO_NUM_CHANNELS_STEREO,
2492                     setup.system.audio_fragment_size) < 0)
2493   {
2494     Warn("Mix_OpenAudio() failed: %s", SDL_GetError());
2495
2496     return;
2497   }
2498
2499   audio.sound_available = TRUE;
2500   audio.music_available = TRUE;
2501   audio.loops_available = TRUE;
2502   audio.sound_enabled = TRUE;
2503
2504   // set number of available mixer channels
2505   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2506   audio.music_channel = MUSIC_CHANNEL;
2507   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2508
2509   Mixer_InitChannels();
2510 }
2511
2512 void SDLCloseAudio(void)
2513 {
2514   Mix_HaltMusic();
2515   Mix_HaltChannel(-1);
2516
2517   Mix_CloseAudio();
2518   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2519 }
2520
2521
2522 // ============================================================================
2523 // event functions
2524 // ============================================================================
2525
2526 void SDLWaitEvent(Event *event)
2527 {
2528   SDL_WaitEvent(event);
2529 }
2530
2531 void SDLCorrectRawMousePosition(int *x, int *y)
2532 {
2533   if (sdl_renderer == NULL)
2534     return;
2535
2536   // this corrects the raw mouse position for logical screen size within event
2537   // filters (correction done later by SDL library when handling mouse events)
2538
2539   SDL_Rect viewport;
2540   float scale_x, scale_y;
2541
2542   SDL_RenderGetViewport(sdl_renderer, &viewport);
2543   SDL_RenderGetScale(sdl_renderer, &scale_x, &scale_y);
2544
2545   *x = (int)(*x / scale_x);
2546   *y = (int)(*y / scale_y);
2547
2548   *x -= viewport.x;
2549   *y -= viewport.y;
2550 }
2551
2552
2553 // ============================================================================
2554 // joystick functions
2555 // ============================================================================
2556
2557 static void *sdl_joystick[MAX_PLAYERS];         // game controller or joystick
2558 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2559 static int sdl_js_axis[MAX_PLAYERS][2];
2560 static int sdl_js_button[MAX_PLAYERS][2];
2561 static boolean sdl_is_controller[MAX_PLAYERS];
2562
2563 void SDLClearJoystickState(void)
2564 {
2565   int i, j;
2566
2567   for (i = 0; i < MAX_PLAYERS; i++)
2568   {
2569     for (j = 0; j < 2; j++)
2570     {
2571       sdl_js_axis_raw[i][j] = -1;
2572       sdl_js_axis[i][j] = 0;
2573       sdl_js_button[i][j] = 0;
2574     }
2575   }
2576 }
2577
2578 boolean SDLOpenJoystick(int nr)
2579 {
2580   if (nr < 0 || nr >= MAX_PLAYERS)
2581     return FALSE;
2582
2583   sdl_is_controller[nr] = SDL_IsGameController(nr);
2584
2585 #if DEBUG_JOYSTICKS
2586   Debug("joystick", "opening joystick %d (%s)",
2587         nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2588 #endif
2589
2590   if (sdl_is_controller[nr])
2591     sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2592   else
2593     sdl_joystick[nr] = SDL_JoystickOpen(nr);
2594
2595   return (sdl_joystick[nr] != NULL);
2596 }
2597
2598 void SDLCloseJoystick(int nr)
2599 {
2600   if (nr < 0 || nr >= MAX_PLAYERS)
2601     return;
2602
2603 #if DEBUG_JOYSTICKS
2604   Debug("joystick", "closing joystick %d", nr);
2605 #endif
2606
2607   if (sdl_is_controller[nr])
2608     SDL_GameControllerClose(sdl_joystick[nr]);
2609   else
2610     SDL_JoystickClose(sdl_joystick[nr]);
2611
2612   sdl_joystick[nr] = NULL;
2613 }
2614
2615 boolean SDLCheckJoystickOpened(int nr)
2616 {
2617   if (nr < 0 || nr >= MAX_PLAYERS)
2618     return FALSE;
2619
2620   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2621 }
2622
2623 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2624 {
2625   int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2626                  axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2627                  axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2628                  axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2629
2630   if (nr < 0 || nr >= MAX_PLAYERS)
2631     return;
2632
2633   if (axis_id == -1)
2634     return;
2635
2636   // prevent (slightly jittering, but centered) axis A from resetting axis B
2637   if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2638       axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2639     return;
2640
2641   sdl_js_axis[nr][axis_id] = axis_value;
2642   sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2643 }
2644
2645 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2646 {
2647   int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2648                    button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2649                    button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2650                    button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2651                    button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2652                    button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2653                    button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2654                    button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2655                    -1);
2656
2657   if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2658     sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2659   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2660     sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2661   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2662     sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2663   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2664     sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2665
2666   if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2667       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2668       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2669       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2670     sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2671
2672   if (nr < 0 || nr >= MAX_PLAYERS)
2673     return;
2674
2675   if (button_id == -1)
2676     return;
2677
2678   sdl_js_button[nr][button_id] = button_state;
2679 }
2680
2681 void HandleJoystickEvent(Event *event)
2682 {
2683   // when using joystick, disable overlay touch buttons
2684   runtime.uses_touch_device = FALSE;
2685
2686   switch (event->type)
2687   {
2688     case SDL_CONTROLLERDEVICEADDED:
2689 #if DEBUG_JOYSTICKS
2690       Debug("joystick", "SDL_CONTROLLERDEVICEADDED: device %d added",
2691             event->cdevice.which);
2692 #endif
2693       InitJoysticks();
2694       break;
2695
2696     case SDL_CONTROLLERDEVICEREMOVED:
2697 #if DEBUG_JOYSTICKS
2698       Debug("joystick", "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2699             event->cdevice.which);
2700 #endif
2701       InitJoysticks();
2702       break;
2703
2704     case SDL_CONTROLLERAXISMOTION:
2705 #if DEBUG_JOYSTICKS
2706       Debug("joystick", "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2707             event->caxis.which, event->caxis.axis, event->caxis.value);
2708 #endif
2709       setJoystickAxis(event->caxis.which,
2710                       event->caxis.axis,
2711                       event->caxis.value);
2712       break;
2713
2714     case SDL_CONTROLLERBUTTONDOWN:
2715 #if DEBUG_JOYSTICKS
2716       Debug("joystick", "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2717             event->cbutton.which, event->cbutton.button);
2718 #endif
2719       setJoystickButton(event->cbutton.which,
2720                         event->cbutton.button,
2721                         TRUE);
2722       break;
2723
2724     case SDL_CONTROLLERBUTTONUP:
2725 #if DEBUG_JOYSTICKS
2726       Debug("joystick", "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2727             event->cbutton.which, event->cbutton.button);
2728 #endif
2729       setJoystickButton(event->cbutton.which,
2730                         event->cbutton.button,
2731                         FALSE);
2732       break;
2733
2734     case SDL_JOYAXISMOTION:
2735       if (sdl_is_controller[event->jaxis.which])
2736         break;
2737
2738 #if DEBUG_JOYSTICKS
2739       Debug("joystick", "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2740             event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2741 #endif
2742       if (event->jaxis.axis < 4)
2743         setJoystickAxis(event->jaxis.which,
2744                         event->jaxis.axis,
2745                         event->jaxis.value);
2746       break;
2747
2748     case SDL_JOYBUTTONDOWN:
2749       if (sdl_is_controller[event->jaxis.which])
2750         break;
2751
2752 #if DEBUG_JOYSTICKS
2753       Debug("joystick", "SDL_JOYBUTTONDOWN: device %d, button %d",
2754             event->jbutton.which, event->jbutton.button);
2755 #endif
2756       if (event->jbutton.button < 4)
2757         setJoystickButton(event->jbutton.which,
2758                           event->jbutton.button,
2759                           TRUE);
2760       break;
2761
2762     case SDL_JOYBUTTONUP:
2763       if (sdl_is_controller[event->jaxis.which])
2764         break;
2765
2766 #if DEBUG_JOYSTICKS
2767       Debug("joystick", "SDL_JOYBUTTONUP: device %d, button %d",
2768             event->jbutton.which, event->jbutton.button);
2769 #endif
2770       if (event->jbutton.button < 4)
2771         setJoystickButton(event->jbutton.which,
2772                           event->jbutton.button,
2773                           FALSE);
2774       break;
2775
2776     default:
2777       break;
2778   }
2779 }
2780
2781 void SDLInitJoysticks(void)
2782 {
2783   static boolean sdl_joystick_subsystem_initialized = FALSE;
2784   boolean print_warning = !sdl_joystick_subsystem_initialized;
2785   char *mappings_file_base = getPath2(options.conf_directory,
2786                                       GAMECONTROLLER_BASENAME);
2787   char *mappings_file_user = getPath2(getMainUserGameDataDir(),
2788                                       GAMECONTROLLER_BASENAME);
2789   int num_mappings;
2790   int i;
2791
2792   if (!sdl_joystick_subsystem_initialized)
2793   {
2794     sdl_joystick_subsystem_initialized = TRUE;
2795
2796     SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2797
2798     if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2799       Fail("SDL_Init() failed: %s", SDL_GetError());
2800
2801     num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2802
2803     // the included game controller base mappings should always be found
2804     if (num_mappings == -1)
2805       Warn("no game controller base mappings found");
2806 #if DEBUG_JOYSTICKS
2807     else
2808       Debug("joystick", "%d game controller base mapping(s) added",
2809             num_mappings);
2810 #endif
2811
2812     num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2813
2814 #if DEBUG_JOYSTICKS
2815     // the personal game controller user mappings may or may not be found
2816     if (num_mappings == -1)
2817       Warn("no game controller user mappings found");
2818     else
2819       Debug("joystick", , "%d game controller user mapping(s) added",
2820             num_mappings);
2821
2822     Debug("joystick", "%d joystick(s) found:", SDL_NumJoysticks());
2823 #endif
2824
2825     checked_free(mappings_file_base);
2826     checked_free(mappings_file_user);
2827
2828 #if DEBUG_JOYSTICKS
2829     for (i = 0; i < SDL_NumJoysticks(); i++)
2830     {
2831       const char *name, *type;
2832
2833       if (SDL_IsGameController(i))
2834       {
2835         name = SDL_GameControllerNameForIndex(i);
2836         type = "game controller";
2837       }
2838       else
2839       {
2840         name = SDL_JoystickNameForIndex(i);
2841         type = "joystick";
2842       }
2843
2844       Debug("joystick", "- joystick %d (%s): '%s'",
2845             i, type, (name ? name : "(Unknown)"));
2846     }
2847 #endif
2848   }
2849
2850   // assign joysticks from configured to connected joystick for all players
2851   for (i = 0; i < MAX_PLAYERS; i++)
2852   {
2853     // get configured joystick for this player
2854     char *device_name = setup.input[i].joy.device_name;
2855     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2856
2857     if (joystick_nr >= SDL_NumJoysticks())
2858     {
2859       if (setup.input[i].use_joystick && print_warning)
2860         Warn("cannot find joystick %d", joystick_nr);
2861
2862       joystick_nr = -1;
2863     }
2864
2865     // store configured joystick number for each player
2866     joystick.nr[i] = joystick_nr;
2867   }
2868
2869   // now open all connected joysticks (regardless if configured or not)
2870   for (i = 0; i < SDL_NumJoysticks(); i++)
2871   {
2872     // this allows subsequent calls to 'InitJoysticks' for re-initialization
2873     if (SDLCheckJoystickOpened(i))
2874       SDLCloseJoystick(i);
2875
2876     if (SDLOpenJoystick(i))
2877       joystick.status = JOYSTICK_ACTIVATED;
2878     else if (print_warning)
2879       Warn("cannot open joystick %d", i);
2880   }
2881
2882   SDLClearJoystickState();
2883 }
2884
2885 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2886 {
2887   if (nr < 0 || nr >= MAX_PLAYERS)
2888     return FALSE;
2889
2890   if (x != NULL)
2891     *x = sdl_js_axis[nr][0];
2892   if (y != NULL)
2893     *y = sdl_js_axis[nr][1];
2894
2895   if (b1 != NULL)
2896     *b1 = sdl_js_button[nr][0];
2897   if (b2 != NULL)
2898     *b2 = sdl_js_button[nr][1];
2899
2900   return TRUE;
2901 }
2902
2903
2904 // ============================================================================
2905 // touch input overlay functions
2906 // ============================================================================
2907
2908 #if defined(USE_TOUCH_INPUT_OVERLAY)
2909 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2910 {
2911   SDL_Rect rect;
2912   int grid_xsize = overlay.grid_xsize;
2913   int grid_ysize = overlay.grid_ysize;
2914   int x, y;
2915
2916   SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2917   SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2918
2919   for (x = 0; x < grid_xsize; x++)
2920   {
2921     rect.x = (x + 0) * video.screen_width / grid_xsize;
2922     rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
2923
2924     for (y = 0; y < grid_ysize; y++)
2925     {
2926       rect.y = (y + 0) * video.screen_height / grid_ysize;
2927       rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
2928
2929       if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
2930         SDL_RenderDrawRect(sdl_renderer, &rect);
2931     }
2932   }
2933
2934   SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
2935 }
2936
2937 static void RenderFillRectangle(int x, int y, int width, int height)
2938 {
2939   SDL_Rect rect = { x, y, width, height };
2940
2941   SDL_RenderFillRect(sdl_renderer, &rect);
2942 }
2943
2944 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
2945 {
2946   static int alpha_direction = 0;
2947   static int alpha_highlight = 0;
2948   int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
2949   int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
2950   SDL_Rect rect;
2951   int grid_xsize = overlay.grid_xsize;
2952   int grid_ysize = overlay.grid_ysize;
2953   int x, y;
2954
2955   if (alpha == alpha_max)
2956   {
2957     if (alpha_direction < 0)
2958     {
2959       alpha_highlight = MAX(0, alpha_highlight - alpha_step);
2960
2961       if (alpha_highlight == 0)
2962         alpha_direction = 1;
2963     }
2964     else
2965     {
2966       alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
2967
2968       if (alpha_highlight == alpha_max)
2969         alpha_direction = -1;
2970     }
2971   }
2972   else
2973   {
2974     alpha_direction = 1;
2975     alpha_highlight = alpha;
2976   }
2977
2978   SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2979
2980   for (x = 0; x < grid_xsize; x++)
2981   {
2982     for (y = 0; y < grid_ysize; y++)
2983     {
2984       int grid_button = overlay.grid_button[x][y];
2985       int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
2986       int alpha_draw = alpha;
2987       int outline_border = MV_NONE;
2988       int border_size = 2;
2989       boolean draw_outlined = setup.touch.draw_outlined;
2990       boolean draw_pressed = setup.touch.draw_pressed;
2991
2992       if (grid_button == CHAR_GRID_BUTTON_NONE)
2993         continue;
2994
2995       if (grid_button == overlay.grid_button_highlight)
2996       {
2997         draw_outlined = FALSE;
2998         alpha_draw = MIN((float)alpha_highlight * 1.5, SDL_ALPHA_OPAQUE);
2999       }
3000
3001       if (draw_pressed && overlay.grid_button_action & grid_button_action)
3002       {
3003         if (draw_outlined)
3004           draw_outlined = FALSE;
3005         else
3006           alpha_draw = MIN((float)alpha_draw * 1.5, SDL_ALPHA_OPAQUE);
3007       }
3008
3009       SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_draw);
3010
3011       rect.x = (x + 0) * video.screen_width  / grid_xsize;
3012       rect.y = (y + 0) * video.screen_height / grid_ysize;
3013       rect.w = (x + 1) * video.screen_width  / grid_xsize - rect.x;
3014       rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3015
3016       if (x == 0 || overlay.grid_button[x - 1][y] != grid_button)
3017       {
3018         rect.x += border_size;
3019         rect.w -= border_size;
3020
3021         outline_border |= MV_LEFT;
3022       }
3023
3024       if (x == grid_xsize - 1 || overlay.grid_button[x + 1][y] != grid_button)
3025       {
3026         rect.w -= border_size;
3027
3028         outline_border |= MV_RIGHT;
3029       }
3030
3031       if (y == 0 || overlay.grid_button[x][y - 1] != grid_button)
3032       {
3033         rect.y += border_size;
3034         rect.h -= border_size;
3035
3036         outline_border |= MV_UP;
3037       }
3038
3039       if (y == grid_ysize - 1 || overlay.grid_button[x][y + 1] != grid_button)
3040       {
3041         rect.h -= border_size;
3042
3043         outline_border |= MV_DOWN;
3044       }
3045
3046       if (draw_outlined)
3047       {
3048         int rect_x = rect.x +
3049           (outline_border & MV_LEFT  ? border_size : 0);
3050         int rect_w = rect.w -
3051           (outline_border & MV_LEFT  ? border_size : 0) -
3052           (outline_border & MV_RIGHT ? border_size : 0);
3053
3054         if (outline_border & MV_LEFT)
3055           RenderFillRectangle(rect.x, rect.y, border_size, rect.h);
3056
3057         if (outline_border & MV_RIGHT)
3058           RenderFillRectangle(rect.x + rect.w - border_size, rect.y,
3059                               border_size, rect.h);
3060
3061         if (outline_border & MV_UP)
3062           RenderFillRectangle(rect_x, rect.y, rect_w, border_size);
3063
3064         if (outline_border & MV_DOWN)
3065           RenderFillRectangle(rect_x, rect.y + rect.h - border_size,
3066                               rect_w, border_size);
3067       }
3068       else
3069       {
3070         SDL_RenderFillRect(sdl_renderer, &rect);
3071       }
3072     }
3073   }
3074
3075   SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3076 }
3077
3078 static void DrawTouchInputOverlay(void)
3079 {
3080   static boolean deactivated = TRUE;
3081   static boolean show_grid = FALSE;
3082   static int alpha = 0;
3083   int alpha_max = ALPHA_FROM_TRANSPARENCY(setup.touch.transparency);
3084   int alpha_step = ALPHA_FADING_STEPSIZE(alpha_max);
3085   boolean active = (overlay.enabled && overlay.active);
3086
3087   if (!active && deactivated)
3088     return;
3089
3090   if (active)
3091   {
3092     if (alpha < alpha_max)
3093       alpha = MIN(alpha + alpha_step, alpha_max);
3094
3095     deactivated = FALSE;
3096   }
3097   else
3098   {
3099     alpha = MAX(0, alpha - alpha_step);
3100
3101     if (alpha == 0)
3102       deactivated = TRUE;
3103   }
3104
3105   if (overlay.show_grid)
3106     show_grid = TRUE;
3107   else if (deactivated)
3108     show_grid = FALSE;
3109
3110   if (show_grid)
3111     DrawTouchInputOverlay_ShowGrid(alpha);
3112
3113   DrawTouchInputOverlay_ShowGridButtons(alpha);
3114 }
3115
3116 static void DrawTouchGadgetsOverlay(void)
3117 {
3118   DrawGadgets_OverlayTouchButtons();
3119 }
3120 #endif