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