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