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