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