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