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