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