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