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