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