removed support for 'real' (non-desktop) fullscreen mode for SDL2 target
[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 defined(TARGET_SDL2)
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   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
630 #else
631   int surface_flags_window = SURFACE_FLAGS;
632   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
633 #endif
634
635   int width  = (fullscreen ? fullscreen_width  : video.width);
636   int height = (fullscreen ? fullscreen_height : video.height);
637   int surface_flags = (fullscreen ? surface_flags_fullscreen :
638                        surface_flags_window);
639
640   // default window size is unscaled
641   video.window_width  = video.width;
642   video.window_height = video.height;
643
644 #if defined(TARGET_SDL2)
645
646   // store if initial screen mode is fullscreen mode when changing screen size
647   video.fullscreen_initial = fullscreen;
648
649   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
650
651   video.window_width  = window_scaling_factor * width;
652   video.window_height = window_scaling_factor * height;
653
654   if ((*backbuffer)->surface)
655   {
656     SDL_FreeSurface((*backbuffer)->surface);
657     (*backbuffer)->surface = NULL;
658   }
659
660 #if USE_TARGET_TEXTURE
661   if (sdl_texture_stream)
662   {
663     SDL_DestroyTexture(sdl_texture_stream);
664     sdl_texture_stream = NULL;
665   }
666
667   if (sdl_texture_target)
668   {
669     SDL_DestroyTexture(sdl_texture_target);
670     sdl_texture_target = NULL;
671   }
672 #else
673   if (sdl_texture)
674   {
675     SDL_DestroyTexture(sdl_texture);
676     sdl_texture = NULL;
677   }
678 #endif
679
680   if (!(fullscreen && fullscreen_enabled))
681   {
682     if (sdl_renderer)
683     {
684       SDL_DestroyRenderer(sdl_renderer);
685       sdl_renderer = NULL;
686     }
687
688     if (sdl_window)
689     {
690       SDL_DestroyWindow(sdl_window);
691       sdl_window = NULL;
692     }
693   }
694
695   if (sdl_window == NULL)
696     sdl_window = SDL_CreateWindow(program.window_title,
697                                   SDL_WINDOWPOS_CENTERED,
698                                   SDL_WINDOWPOS_CENTERED,
699                                   video.window_width,
700                                   video.window_height,
701                                   surface_flags);
702
703   if (sdl_window != NULL)
704   {
705 #if 0
706     /* if SDL_CreateRenderer() is called from within a VirtualBox Windows VM
707      *without* enabling 2D/3D acceleration and/or guest additions installed,
708      it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
709      it will try to use accelerated graphics and apparently fails miserably) */
710     if (sdl_renderer == NULL)
711       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_SOFTWARE);
712 #else
713     if (sdl_renderer == NULL)
714       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
715 #endif
716
717     if (sdl_renderer != NULL)
718     {
719       SDL_RenderSetLogicalSize(sdl_renderer, width, height);
720       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
721       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
722
723 #if USE_TARGET_TEXTURE
724       sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
725                                              SDL_PIXELFORMAT_ARGB8888,
726                                              SDL_TEXTUREACCESS_STREAMING,
727                                              width, height);
728
729       sdl_texture_target = SDL_CreateTexture(sdl_renderer,
730                                              SDL_PIXELFORMAT_ARGB8888,
731                                              SDL_TEXTUREACCESS_TARGET,
732                                              width, height);
733 #else
734       sdl_texture = SDL_CreateTexture(sdl_renderer,
735                                       SDL_PIXELFORMAT_ARGB8888,
736                                       SDL_TEXTUREACCESS_STREAMING,
737                                       width, height);
738 #endif
739
740 #if USE_TARGET_TEXTURE
741       if (sdl_texture_stream != NULL &&
742           sdl_texture_target != NULL)
743 #else
744       if (sdl_texture != NULL)
745 #endif
746       {
747         // use SDL default values for RGB masks and no alpha channel
748         new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
749
750         if (new_surface == NULL)
751           Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
752                 SDL_GetError());
753       }
754       else
755       {
756         Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
757       }
758     }
759     else
760     {
761       Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
762     }
763   }
764   else
765   {
766     Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
767   }
768
769 #else
770   new_surface = SDL_SetVideoMode(width, height, video.depth, surface_flags);
771 #endif
772
773 #if defined(TARGET_SDL2)
774   // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
775   if (new_surface != NULL)
776     fullscreen_enabled = fullscreen;
777 #endif
778
779   return new_surface;
780 }
781
782 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
783 {
784   boolean success = TRUE;
785   SDL_Surface *new_surface = NULL;
786
787   SetWindowTitle();
788
789   if (*backbuffer == NULL)
790     *backbuffer = CreateBitmapStruct();
791
792   /* (real bitmap might be larger in fullscreen mode with video offsets) */
793   (*backbuffer)->width  = video.width;
794   (*backbuffer)->height = video.height;
795
796   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
797   {
798     setFullscreenParameters(setup.fullscreen_mode);
799
800     video_xoffset = fullscreen_xoffset;
801     video_yoffset = fullscreen_yoffset;
802
803     /* switch display to fullscreen mode, if available */
804     new_surface = SDLCreateScreen(backbuffer, TRUE);
805
806     if (new_surface == NULL)
807     {
808       /* switching display to fullscreen mode failed */
809       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
810
811       /* do not try it again */
812       video.fullscreen_available = FALSE;
813
814       success = FALSE;
815     }
816     else
817     {
818       (*backbuffer)->surface = new_surface;
819
820       video.fullscreen_enabled = TRUE;
821       video.fullscreen_mode_current = setup.fullscreen_mode;
822
823       success = TRUE;
824     }
825   }
826
827   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
828   {
829     video_xoffset = 0;
830     video_yoffset = 0;
831
832     /* switch display to window mode */
833     new_surface = SDLCreateScreen(backbuffer, FALSE);
834
835     if (new_surface == NULL)
836     {
837       /* switching display to window mode failed -- should not happen */
838       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
839
840       success = FALSE;
841     }
842     else
843     {
844       (*backbuffer)->surface = new_surface;
845
846       video.fullscreen_enabled = FALSE;
847       video.window_scaling_percent = setup.window_scaling_percent;
848       video.window_scaling_quality = setup.window_scaling_quality;
849
850       success = TRUE;
851     }
852   }
853
854 #if defined(TARGET_SDL2)
855   SDLRedrawWindow();                    // map window
856 #endif
857
858 #ifdef DEBUG
859 #if defined(PLATFORM_WIN32)
860   // experimental drag and drop code
861
862   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
863
864   {
865     SDL_SysWMinfo wminfo;
866     HWND hwnd;
867     boolean wminfo_success = FALSE;
868
869     SDL_VERSION(&wminfo.version);
870 #if defined(TARGET_SDL2)
871     if (sdl_window)
872       wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
873 #else
874     wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
875 #endif
876
877     if (wminfo_success)
878     {
879 #if defined(TARGET_SDL2)
880       hwnd = wminfo.info.win.window;
881 #else
882       hwnd = wminfo.window;
883 #endif
884
885       DragAcceptFiles(hwnd, TRUE);
886     }
887   }
888 #endif
889 #endif
890
891   return success;
892 }
893
894 void SDLSetWindowTitle()
895 {
896 #if defined(TARGET_SDL2)
897   SDL_SetWindowTitle(sdl_window, program.window_title);
898 #else
899   SDL_WM_SetCaption(program.window_title, program.window_title);
900 #endif
901 }
902
903 #if defined(TARGET_SDL2)
904 void SDLSetWindowScaling(int window_scaling_percent)
905 {
906   if (sdl_window == NULL)
907     return;
908
909   float window_scaling_factor = (float)window_scaling_percent / 100;
910   int new_window_width  = (int)(window_scaling_factor * video.width);
911   int new_window_height = (int)(window_scaling_factor * video.height);
912
913   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
914
915   video.window_scaling_percent = window_scaling_percent;
916   video.window_width  = new_window_width;
917   video.window_height = new_window_height;
918
919   SetWindowTitle();
920 }
921
922 void SDLSetWindowScalingQuality(char *window_scaling_quality)
923 {
924 #if USE_TARGET_TEXTURE
925   SDL_Texture *new_texture;
926
927   if (sdl_texture_stream == NULL ||
928       sdl_texture_target == NULL)
929     return;
930
931   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
932
933   new_texture = SDL_CreateTexture(sdl_renderer,
934                                   SDL_PIXELFORMAT_ARGB8888,
935                                   SDL_TEXTUREACCESS_STREAMING,
936                                   video.width, video.height);
937
938   if (new_texture != NULL)
939   {
940     SDL_DestroyTexture(sdl_texture_stream);
941
942     sdl_texture_stream = new_texture;
943   }
944
945   new_texture = SDL_CreateTexture(sdl_renderer,
946                                   SDL_PIXELFORMAT_ARGB8888,
947                                   SDL_TEXTUREACCESS_TARGET,
948                                   video.width, video.height);
949
950   if (new_texture != NULL)
951   {
952     SDL_DestroyTexture(sdl_texture_target);
953
954     sdl_texture_target = new_texture;
955   }
956
957   SDLRedrawWindow();
958
959 #else
960   if (sdl_texture == NULL)
961     return;
962
963   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
964
965   SDL_Texture *new_texture = SDL_CreateTexture(sdl_renderer,
966                                                SDL_PIXELFORMAT_ARGB8888,
967                                                SDL_TEXTUREACCESS_STREAMING,
968                                                video.width, video.height);
969
970   if (new_texture != NULL)
971   {
972     SDL_DestroyTexture(sdl_texture);
973
974     sdl_texture = new_texture;
975
976     SDLRedrawWindow();
977   }
978 #endif
979
980   video.window_scaling_quality = window_scaling_quality;
981 }
982
983 void SDLSetWindowFullscreen(boolean fullscreen)
984 {
985   if (sdl_window == NULL)
986     return;
987
988   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
989
990   if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
991     video.fullscreen_enabled = fullscreen_enabled = fullscreen;
992
993   // if screen size was changed in fullscreen mode, correct desktop window size
994   if (!fullscreen && video.fullscreen_initial)
995   {
996     SDLSetWindowScaling(setup.window_scaling_percent);
997     SDL_SetWindowPosition(sdl_window,
998                           SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
999
1000     video.fullscreen_initial = FALSE;
1001   }
1002 }
1003
1004 void SDLRedrawWindow()
1005 {
1006   UpdateScreen(NULL);
1007 }
1008 #endif
1009
1010 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1011                             int depth)
1012 {
1013   SDL_Surface *surface =
1014     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1015
1016   if (surface == NULL)
1017     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1018
1019   SDLSetNativeSurface(&surface);
1020
1021   bitmap->surface = surface;
1022 }
1023
1024 void SDLFreeBitmapPointers(Bitmap *bitmap)
1025 {
1026   if (bitmap->surface)
1027     SDL_FreeSurface(bitmap->surface);
1028   if (bitmap->surface_masked)
1029     SDL_FreeSurface(bitmap->surface_masked);
1030
1031   bitmap->surface = NULL;
1032   bitmap->surface_masked = NULL;
1033
1034 #if defined(TARGET_SDL2)
1035   if (bitmap->texture)
1036     SDL_DestroyTexture(bitmap->texture);
1037   if (bitmap->texture_masked)
1038     SDL_DestroyTexture(bitmap->texture_masked);
1039
1040   bitmap->texture = NULL;
1041   bitmap->texture_masked = NULL;
1042 #endif
1043 }
1044
1045 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1046                  int src_x, int src_y, int width, int height,
1047                  int dst_x, int dst_y, int mask_mode)
1048 {
1049   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1050   SDL_Rect src_rect, dst_rect;
1051
1052   if (src_bitmap == backbuffer)
1053   {
1054     src_x += video_xoffset;
1055     src_y += video_yoffset;
1056   }
1057
1058   src_rect.x = src_x;
1059   src_rect.y = src_y;
1060   src_rect.w = width;
1061   src_rect.h = height;
1062
1063   if (dst_bitmap == backbuffer || dst_bitmap == window)
1064   {
1065     dst_x += video_xoffset;
1066     dst_y += video_yoffset;
1067   }
1068
1069   dst_rect.x = dst_x;
1070   dst_rect.y = dst_y;
1071   dst_rect.w = width;
1072   dst_rect.h = height;
1073
1074   // if (src_bitmap != backbuffer || dst_bitmap != window)
1075   if (!(src_bitmap == backbuffer && dst_bitmap == window))
1076     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1077                      src_bitmap->surface_masked : src_bitmap->surface),
1078                     &src_rect, real_dst_bitmap->surface, &dst_rect);
1079
1080 #if defined(TARGET_SDL2)
1081   if (dst_bitmap == window)
1082   {
1083     // SDL_UpdateWindowSurface(sdl_window);
1084     // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
1085     UpdateScreen(&dst_rect);
1086   }
1087 #else
1088   if (dst_bitmap == window)
1089   {
1090     // SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
1091     UpdateScreen(&dst_rect);
1092   }
1093 #endif
1094 }
1095
1096 void SDLBlitTexture(Bitmap *bitmap,
1097                     int src_x, int src_y, int width, int height,
1098                     int dst_x, int dst_y, int mask_mode)
1099 {
1100 #if defined(TARGET_SDL2)
1101   SDL_Texture *texture;
1102   SDL_Rect src_rect;
1103   SDL_Rect dst_rect;
1104
1105   texture =
1106     (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1107
1108   if (texture == NULL)
1109     return;
1110
1111   src_rect.x = src_x;
1112   src_rect.y = src_y;
1113   src_rect.w = width;
1114   src_rect.h = height;
1115
1116   dst_rect.x = dst_x;
1117   dst_rect.y = dst_y;
1118   dst_rect.w = width;
1119   dst_rect.h = height;
1120
1121   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1122 #endif
1123 }
1124
1125 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1126                       Uint32 color)
1127 {
1128   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1129   SDL_Rect rect;
1130
1131   if (dst_bitmap == backbuffer || dst_bitmap == window)
1132   {
1133     x += video_xoffset;
1134     y += video_yoffset;
1135   }
1136
1137   rect.x = x;
1138   rect.y = y;
1139   rect.w = width;
1140   rect.h = height;
1141
1142   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1143
1144 #if defined(TARGET_SDL2)
1145   if (dst_bitmap == window)
1146   {
1147     // SDL_UpdateWindowSurface(sdl_window);
1148     // SDL_UpdateWindowSurfaceRects(sdl_window, &rect, 1);
1149     UpdateScreen(&rect);
1150   }
1151 #else
1152   if (dst_bitmap == window)
1153   {
1154     // SDL_UpdateRect(backbuffer->surface, x, y, width, height);
1155     UpdateScreen(&rect);
1156   }
1157 #endif
1158 }
1159
1160 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
1161                       int fade_mode, int fade_delay, int post_delay,
1162                       void (*draw_border_function)(void))
1163 {
1164   SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1165   SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1166   SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
1167   SDL_Surface *surface_screen = backbuffer->surface;
1168   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
1169   SDL_Rect src_rect, dst_rect;
1170   SDL_Rect dst_rect2;
1171   int src_x = x, src_y = y;
1172   int dst_x = x, dst_y = y;
1173   unsigned int time_last, time_current;
1174
1175   // store function for drawing global masked border
1176   void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1177
1178   // deactivate drawing of global border while fading, if needed
1179   if (draw_border_function == NULL)
1180     gfx.draw_global_border_function = NULL;
1181
1182   src_rect.x = src_x;
1183   src_rect.y = src_y;
1184   src_rect.w = width;
1185   src_rect.h = height;
1186
1187   dst_x += video_xoffset;
1188   dst_y += video_yoffset;
1189
1190   dst_rect.x = dst_x;
1191   dst_rect.y = dst_y;
1192   dst_rect.w = width;           /* (ignored) */
1193   dst_rect.h = height;          /* (ignored) */
1194
1195   dst_rect2 = dst_rect;
1196
1197   /* copy source and target surfaces to temporary surfaces for fading */
1198   if (fade_mode & FADE_TYPE_TRANSFORM)
1199   {
1200     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
1201     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
1202
1203     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
1204     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
1205   }
1206   else if (fade_mode & FADE_TYPE_FADE_IN)
1207   {
1208     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
1209     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
1210
1211     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
1212   }
1213   else          /* FADE_TYPE_FADE_OUT */
1214   {
1215     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
1216     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
1217
1218     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
1219   }
1220
1221   time_current = SDL_GetTicks();
1222
1223   if (fade_mode == FADE_MODE_MELT)
1224   {
1225     boolean done = FALSE;
1226     int melt_pixels = 2;
1227     int melt_columns = width / melt_pixels;
1228     int ypos[melt_columns];
1229     int max_steps = height / 8 + 32;
1230     int steps_done = 0;
1231     float steps = 0;
1232     int i;
1233
1234     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1235 #if defined(TARGET_SDL2)
1236     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
1237 #else
1238     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
1239 #endif
1240
1241     ypos[0] = -GetSimpleRandom(16);
1242
1243     for (i = 1 ; i < melt_columns; i++)
1244     {
1245       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
1246
1247       ypos[i] = ypos[i - 1] + r;
1248
1249       if (ypos[i] > 0)
1250         ypos[i] = 0;
1251       else
1252         if (ypos[i] == -16)
1253           ypos[i] = -15;
1254     }
1255
1256     while (!done)
1257     {
1258       int steps_final;
1259
1260       time_last = time_current;
1261       time_current = SDL_GetTicks();
1262       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1263       steps_final = MIN(MAX(0, steps), max_steps);
1264
1265       steps_done++;
1266
1267       done = (steps_done >= steps_final);
1268
1269       for (i = 0 ; i < melt_columns; i++)
1270       {
1271         if (ypos[i] < 0)
1272         {
1273           ypos[i]++;
1274
1275           done = FALSE;
1276         }
1277         else if (ypos[i] < height)
1278         {
1279           int y1 = 16;
1280           int y2 = 8;
1281           int y3 = 8;
1282           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1283
1284           if (ypos[i] + dy >= height)
1285             dy = height - ypos[i];
1286
1287           /* copy part of (appearing) target surface to upper area */
1288           src_rect.x = src_x + i * melt_pixels;
1289           // src_rect.y = src_y + ypos[i];
1290           src_rect.y = src_y;
1291           src_rect.w = melt_pixels;
1292           // src_rect.h = dy;
1293           src_rect.h = ypos[i] + dy;
1294
1295           dst_rect.x = dst_x + i * melt_pixels;
1296           // dst_rect.y = dst_y + ypos[i];
1297           dst_rect.y = dst_y;
1298
1299           if (steps_done >= steps_final)
1300             SDL_BlitSurface(surface_target, &src_rect,
1301                             surface_screen, &dst_rect);
1302
1303           ypos[i] += dy;
1304
1305           /* copy part of (disappearing) source surface to lower area */
1306           src_rect.x = src_x + i * melt_pixels;
1307           src_rect.y = src_y;
1308           src_rect.w = melt_pixels;
1309           src_rect.h = height - ypos[i];
1310
1311           dst_rect.x = dst_x + i * melt_pixels;
1312           dst_rect.y = dst_y + ypos[i];
1313
1314           if (steps_done >= steps_final)
1315             SDL_BlitSurface(surface_source, &src_rect,
1316                             surface_screen, &dst_rect);
1317
1318           done = FALSE;
1319         }
1320         else
1321         {
1322           src_rect.x = src_x + i * melt_pixels;
1323           src_rect.y = src_y;
1324           src_rect.w = melt_pixels;
1325           src_rect.h = height;
1326
1327           dst_rect.x = dst_x + i * melt_pixels;
1328           dst_rect.y = dst_y;
1329
1330           if (steps_done >= steps_final)
1331             SDL_BlitSurface(surface_target, &src_rect,
1332                             surface_screen, &dst_rect);
1333         }
1334       }
1335
1336       if (steps_done >= steps_final)
1337       {
1338         if (draw_border_function != NULL)
1339           draw_border_function();
1340
1341         UpdateScreen(&dst_rect2);
1342       }
1343     }
1344   }
1345   else if (fade_mode == FADE_MODE_CURTAIN)
1346   {
1347     float xx;
1348     int xx_final;
1349     int xx_size = width / 2;
1350
1351     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1352 #if defined(TARGET_SDL2)
1353     SDL_SetSurfaceBlendMode(surface_source, SDL_BLENDMODE_NONE);
1354 #else
1355     SDL_SetAlpha(surface_source, 0, 0);         /* disable alpha blending */
1356 #endif
1357
1358     for (xx = 0; xx < xx_size;)
1359     {
1360       time_last = time_current;
1361       time_current = SDL_GetTicks();
1362       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1363       xx_final = MIN(MAX(0, xx), xx_size);
1364
1365       src_rect.x = src_x;
1366       src_rect.y = src_y;
1367       src_rect.w = width;
1368       src_rect.h = height;
1369
1370       dst_rect.x = dst_x;
1371       dst_rect.y = dst_y;
1372
1373       /* draw new (target) image to screen buffer */
1374       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1375
1376       if (xx_final < xx_size)
1377       {
1378         src_rect.w = xx_size - xx_final;
1379         src_rect.h = height;
1380
1381         /* draw old (source) image to screen buffer (left side) */
1382
1383         src_rect.x = src_x + xx_final;
1384         dst_rect.x = dst_x;
1385
1386         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1387
1388         /* draw old (source) image to screen buffer (right side) */
1389
1390         src_rect.x = src_x + xx_size;
1391         dst_rect.x = dst_x + xx_size + xx_final;
1392
1393         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1394       }
1395
1396       if (draw_border_function != NULL)
1397         draw_border_function();
1398
1399       /* only update the region of the screen that is affected from fading */
1400       UpdateScreen(&dst_rect2);
1401     }
1402   }
1403   else          /* fading in, fading out or cross-fading */
1404   {
1405     float alpha;
1406     int alpha_final;
1407
1408     for (alpha = 0.0; alpha < 255.0;)
1409     {
1410       time_last = time_current;
1411       time_current = SDL_GetTicks();
1412       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1413       alpha_final = MIN(MAX(0, alpha), 255);
1414
1415       /* draw existing (source) image to screen buffer */
1416       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1417
1418       /* draw new (target) image to screen buffer using alpha blending */
1419 #if defined(TARGET_SDL2)
1420       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
1421       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
1422 #else
1423       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
1424 #endif
1425       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1426
1427       if (draw_border_function != NULL)
1428         draw_border_function();
1429
1430       /* only update the region of the screen that is affected from fading */
1431       UpdateScreen(&dst_rect);
1432     }
1433   }
1434
1435   if (post_delay > 0)
1436   {
1437     unsigned int time_post_delay;
1438
1439     time_current = SDL_GetTicks();
1440     time_post_delay = time_current + post_delay;
1441
1442     while (time_current < time_post_delay)
1443     {
1444       // do not wait longer than 10 ms at a time to be able to ...
1445       Delay(MIN(10, time_post_delay - time_current));
1446
1447       // ... continue drawing global animations during post delay
1448       UpdateScreen(NULL);
1449
1450       time_current = SDL_GetTicks();
1451     }
1452   }
1453
1454   // restore function for drawing global masked border
1455   gfx.draw_global_border_function = draw_global_border_function;
1456 }
1457
1458 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1459                        int to_x, int to_y, Uint32 color)
1460 {
1461   SDL_Surface *surface = dst_bitmap->surface;
1462   SDL_Rect rect;
1463
1464   if (from_x > to_x)
1465     swap_numbers(&from_x, &to_x);
1466
1467   if (from_y > to_y)
1468     swap_numbers(&from_y, &to_y);
1469
1470   rect.x = from_x;
1471   rect.y = from_y;
1472   rect.w = (to_x - from_x + 1);
1473   rect.h = (to_y - from_y + 1);
1474
1475   if (dst_bitmap == backbuffer || dst_bitmap == window)
1476   {
1477     rect.x += video_xoffset;
1478     rect.y += video_yoffset;
1479   }
1480
1481   SDL_FillRect(surface, &rect, color);
1482 }
1483
1484 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1485                  int to_x, int to_y, Uint32 color)
1486 {
1487   if (dst_bitmap == backbuffer || dst_bitmap == window)
1488   {
1489     from_x += video_xoffset;
1490     from_y += video_yoffset;
1491     to_x += video_xoffset;
1492     to_y += video_yoffset;
1493   }
1494
1495   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1496 }
1497
1498 #if ENABLE_UNUSED_CODE
1499 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1500                   int num_points, Uint32 color)
1501 {
1502   int i, x, y;
1503   int line_width = 4;
1504
1505   for (i = 0; i < num_points - 1; i++)
1506   {
1507     for (x = 0; x < line_width; x++)
1508     {
1509       for (y = 0; y < line_width; y++)
1510       {
1511         int dx = x - line_width / 2;
1512         int dy = y - line_width / 2;
1513
1514         if ((x == 0 && y == 0) ||
1515             (x == 0 && y == line_width - 1) ||
1516             (x == line_width - 1 && y == 0) ||
1517             (x == line_width - 1 && y == line_width - 1))
1518           continue;
1519
1520         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1521                  points[i+1].x + dx, points[i+1].y + dy, color);
1522       }
1523     }
1524   }
1525 }
1526 #endif
1527
1528 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1529 {
1530   SDL_Surface *surface = src_bitmap->surface;
1531
1532   if (src_bitmap == backbuffer || src_bitmap == window)
1533   {
1534     x += video_xoffset;
1535     y += video_yoffset;
1536   }
1537
1538   switch (surface->format->BytesPerPixel)
1539   {
1540     case 1:             /* assuming 8-bpp */
1541     {
1542       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1543     }
1544     break;
1545
1546     case 2:             /* probably 15-bpp or 16-bpp */
1547     {
1548       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1549     }
1550     break;
1551
1552   case 3:               /* slow 24-bpp mode; usually not used */
1553     {
1554       /* does this work? */
1555       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1556       Uint32 color = 0;
1557       int shift;
1558
1559       shift = surface->format->Rshift;
1560       color |= *(pix + shift / 8) >> shift;
1561       shift = surface->format->Gshift;
1562       color |= *(pix + shift / 8) >> shift;
1563       shift = surface->format->Bshift;
1564       color |= *(pix + shift / 8) >> shift;
1565
1566       return color;
1567     }
1568     break;
1569
1570   case 4:               /* probably 32-bpp */
1571     {
1572       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1573     }
1574     break;
1575   }
1576
1577   return 0;
1578 }
1579
1580
1581 /* ========================================================================= */
1582 /* The following functions were taken from the SGE library                   */
1583 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1584 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1585 /* ========================================================================= */
1586
1587 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1588 {
1589   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1590   {
1591     switch (surface->format->BytesPerPixel)
1592     {
1593       case 1:
1594       {
1595         /* Assuming 8-bpp */
1596         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1597       }
1598       break;
1599
1600       case 2:
1601       {
1602         /* Probably 15-bpp or 16-bpp */
1603         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1604       }
1605       break;
1606
1607       case 3:
1608       {
1609         /* Slow 24-bpp mode, usually not used */
1610         Uint8 *pix;
1611         int shift;
1612
1613         /* Gack - slow, but endian correct */
1614         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1615         shift = surface->format->Rshift;
1616         *(pix+shift/8) = color>>shift;
1617         shift = surface->format->Gshift;
1618         *(pix+shift/8) = color>>shift;
1619         shift = surface->format->Bshift;
1620         *(pix+shift/8) = color>>shift;
1621       }
1622       break;
1623
1624       case 4:
1625       {
1626         /* Probably 32-bpp */
1627         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1628       }
1629       break;
1630     }
1631   }
1632 }
1633
1634 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1635                   Uint8 R, Uint8 G, Uint8 B)
1636 {
1637   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1638 }
1639
1640 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1641 {
1642   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1643 }
1644
1645 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1646 {
1647   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1648 }
1649
1650 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1651 {
1652   Uint8 *pix;
1653   int shift;
1654
1655   /* Gack - slow, but endian correct */
1656   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1657   shift = surface->format->Rshift;
1658   *(pix+shift/8) = color>>shift;
1659   shift = surface->format->Gshift;
1660   *(pix+shift/8) = color>>shift;
1661   shift = surface->format->Bshift;
1662   *(pix+shift/8) = color>>shift;
1663 }
1664
1665 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1666 {
1667   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1668 }
1669
1670 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1671 {
1672   switch (dest->format->BytesPerPixel)
1673   {
1674     case 1:
1675       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1676       break;
1677
1678     case 2:
1679       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1680       break;
1681
1682     case 3:
1683       _PutPixel24(dest,x,y,color);
1684       break;
1685
1686     case 4:
1687       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1688       break;
1689   }
1690 }
1691
1692 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1693 {
1694   if (SDL_MUSTLOCK(surface))
1695   {
1696     if (SDL_LockSurface(surface) < 0)
1697     {
1698       return;
1699     }
1700   }
1701
1702   _PutPixel(surface, x, y, color);
1703
1704   if (SDL_MUSTLOCK(surface))
1705   {
1706     SDL_UnlockSurface(surface);
1707   }
1708 }
1709
1710 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1711                   Uint8 r, Uint8 g, Uint8 b)
1712 {
1713   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1714 }
1715
1716 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1717 {
1718   if (y >= 0 && y <= dest->h - 1)
1719   {
1720     switch (dest->format->BytesPerPixel)
1721     {
1722       case 1:
1723         return y*dest->pitch;
1724         break;
1725
1726       case 2:
1727         return y*dest->pitch/2;
1728         break;
1729
1730       case 3:
1731         return y*dest->pitch;
1732         break;
1733
1734       case 4:
1735         return y*dest->pitch/4;
1736         break;
1737     }
1738   }
1739
1740   return -1;
1741 }
1742
1743 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1744 {
1745   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1746   {
1747     switch (surface->format->BytesPerPixel)
1748     {
1749       case 1:
1750       {
1751         /* Assuming 8-bpp */
1752         *((Uint8 *)surface->pixels + ypitch + x) = color;
1753       }
1754       break;
1755
1756       case 2:
1757       {
1758         /* Probably 15-bpp or 16-bpp */
1759         *((Uint16 *)surface->pixels + ypitch + x) = color;
1760       }
1761       break;
1762
1763       case 3:
1764       {
1765         /* Slow 24-bpp mode, usually not used */
1766         Uint8 *pix;
1767         int shift;
1768
1769         /* Gack - slow, but endian correct */
1770         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1771         shift = surface->format->Rshift;
1772         *(pix+shift/8) = color>>shift;
1773         shift = surface->format->Gshift;
1774         *(pix+shift/8) = color>>shift;
1775         shift = surface->format->Bshift;
1776         *(pix+shift/8) = color>>shift;
1777       }
1778       break;
1779
1780       case 4:
1781       {
1782         /* Probably 32-bpp */
1783         *((Uint32 *)surface->pixels + ypitch + x) = color;
1784       }
1785       break;
1786     }
1787   }
1788 }
1789
1790 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1791                Uint32 Color)
1792 {
1793   SDL_Rect l;
1794
1795   if (SDL_MUSTLOCK(Surface))
1796   {
1797     if (SDL_LockSurface(Surface) < 0)
1798     {
1799       return;
1800     }
1801   }
1802
1803   if (x1 > x2)
1804   {
1805     Sint16 tmp = x1;
1806     x1 = x2;
1807     x2 = tmp;
1808   }
1809
1810   /* Do the clipping */
1811   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1812     return;
1813   if (x1 < 0)
1814     x1 = 0;
1815   if (x2 > Surface->w - 1)
1816     x2 = Surface->w - 1;
1817
1818   l.x = x1;
1819   l.y = y;
1820   l.w = x2 - x1 + 1;
1821   l.h = 1;
1822
1823   SDL_FillRect(Surface, &l, Color);
1824
1825   if (SDL_MUSTLOCK(Surface))
1826   {
1827     SDL_UnlockSurface(Surface);
1828   }
1829 }
1830
1831 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1832                   Uint8 R, Uint8 G, Uint8 B)
1833 {
1834   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1835 }
1836
1837 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1838 {
1839   SDL_Rect l;
1840
1841   if (x1 > x2)
1842   {
1843     Sint16 tmp = x1;
1844     x1 = x2;
1845     x2 = tmp;
1846   }
1847
1848   /* Do the clipping */
1849   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1850     return;
1851   if (x1 < 0)
1852     x1 = 0;
1853   if (x2 > Surface->w - 1)
1854     x2 = Surface->w - 1;
1855
1856   l.x = x1;
1857   l.y = y;
1858   l.w = x2 - x1 + 1;
1859   l.h = 1;
1860
1861   SDL_FillRect(Surface, &l, Color);
1862 }
1863
1864 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1865                Uint32 Color)
1866 {
1867   SDL_Rect l;
1868
1869   if (SDL_MUSTLOCK(Surface))
1870   {
1871     if (SDL_LockSurface(Surface) < 0)
1872     {
1873       return;
1874     }
1875   }
1876
1877   if (y1 > y2)
1878   {
1879     Sint16 tmp = y1;
1880     y1 = y2;
1881     y2 = tmp;
1882   }
1883
1884   /* Do the clipping */
1885   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1886     return;
1887   if (y1 < 0)
1888     y1 = 0;
1889   if (y2 > Surface->h - 1)
1890     y2 = Surface->h - 1;
1891
1892   l.x = x;
1893   l.y = y1;
1894   l.w = 1;
1895   l.h = y2 - y1 + 1;
1896
1897   SDL_FillRect(Surface, &l, Color);
1898
1899   if (SDL_MUSTLOCK(Surface))
1900   {
1901     SDL_UnlockSurface(Surface);
1902   }
1903 }
1904
1905 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1906                   Uint8 R, Uint8 G, Uint8 B)
1907 {
1908   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1909 }
1910
1911 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1912 {
1913   SDL_Rect l;
1914
1915   if (y1 > y2)
1916   {
1917     Sint16 tmp = y1;
1918     y1 = y2;
1919     y2 = tmp;
1920   }
1921
1922   /* Do the clipping */
1923   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1924     return;
1925   if (y1 < 0)
1926     y1 = 0;
1927   if (y2 > Surface->h - 1)
1928     y2 = Surface->h - 1;
1929
1930   l.x = x;
1931   l.y = y1;
1932   l.w = 1;
1933   l.h = y2 - y1 + 1;
1934
1935   SDL_FillRect(Surface, &l, Color);
1936 }
1937
1938 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1939                 Sint16 x2, Sint16 y2, Uint32 Color,
1940                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1941                               Uint32 Color))
1942 {
1943   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1944
1945   dx = x2 - x1;
1946   dy = y2 - y1;
1947
1948   sdx = (dx < 0) ? -1 : 1;
1949   sdy = (dy < 0) ? -1 : 1;
1950
1951   dx = sdx * dx + 1;
1952   dy = sdy * dy + 1;
1953
1954   x = y = 0;
1955
1956   px = x1;
1957   py = y1;
1958
1959   if (dx >= dy)
1960   {
1961     for (x = 0; x < dx; x++)
1962     {
1963       Callback(Surface, px, py, Color);
1964
1965       y += dy;
1966       if (y >= dx)
1967       {
1968         y -= dx;
1969         py += sdy;
1970       }
1971
1972       px += sdx;
1973     }
1974   }
1975   else
1976   {
1977     for (y = 0; y < dy; y++)
1978     {
1979       Callback(Surface, px, py, Color);
1980
1981       x += dx;
1982       if (x >= dy)
1983       {
1984         x -= dy;
1985         px += sdx;
1986       }
1987
1988       py += sdy;
1989     }
1990   }
1991 }
1992
1993 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1994                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1995                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1996                                  Uint32 Color))
1997 {
1998   sge_DoLine(Surface, X1, Y1, X2, Y2,
1999              SDL_MapRGB(Surface->format, R, G, B), Callback);
2000 }
2001
2002 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
2003               Uint32 Color)
2004 {
2005   if (SDL_MUSTLOCK(Surface))
2006   {
2007     if (SDL_LockSurface(Surface) < 0)
2008       return;
2009    }
2010
2011    /* Draw the line */
2012    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
2013
2014    /* unlock the display */
2015    if (SDL_MUSTLOCK(Surface))
2016    {
2017       SDL_UnlockSurface(Surface);
2018    }
2019 }
2020
2021 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
2022                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
2023 {
2024   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
2025 }
2026
2027 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
2028 {
2029   if (dst_bitmap == backbuffer || dst_bitmap == window)
2030   {
2031     x += video_xoffset;
2032     y += video_yoffset;
2033   }
2034
2035   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
2036 }
2037
2038
2039 /*
2040   -----------------------------------------------------------------------------
2041   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2042   -----------------------------------------------------------------------------
2043 */
2044
2045 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
2046                    int width, int height, Uint32 color)
2047 {
2048   int x, y;
2049
2050   for (y = src_y; y < src_y + height; y++)
2051   {
2052     for (x = src_x; x < src_x + width; x++)
2053     {
2054       Uint32 pixel = SDLGetPixel(bitmap, x, y);
2055
2056       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2057     }
2058   }
2059 }
2060
2061 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2062                           int src_x, int src_y, int width, int height,
2063                           int dst_x, int dst_y)
2064 {
2065   int x, y;
2066
2067   for (y = 0; y < height; y++)
2068   {
2069     for (x = 0; x < width; x++)
2070     {
2071       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2072
2073       if (pixel != BLACK_PIXEL)
2074         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2075     }
2076   }
2077 }
2078
2079
2080 /* ========================================================================= */
2081 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2082 /* (Rotozoomer) by Andreas Schiffler                                         */
2083 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
2084 /* ========================================================================= */
2085
2086 /*
2087   -----------------------------------------------------------------------------
2088   32 bit zoomer
2089
2090   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2091   -----------------------------------------------------------------------------
2092 */
2093
2094 typedef struct
2095 {
2096   Uint8 r;
2097   Uint8 g;
2098   Uint8 b;
2099   Uint8 a;
2100 } tColorRGBA;
2101
2102 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2103 {
2104   int x, y;
2105   tColorRGBA *sp, *csp, *dp;
2106   int dgap;
2107
2108   /* pointer setup */
2109   sp = csp = (tColorRGBA *) src->pixels;
2110   dp = (tColorRGBA *) dst->pixels;
2111   dgap = dst->pitch - dst->w * 4;
2112
2113   for (y = 0; y < dst->h; y++)
2114   {
2115     sp = csp;
2116
2117     for (x = 0; x < dst->w; x++)
2118     {
2119       tColorRGBA *sp0 = sp;
2120       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2121       tColorRGBA *sp00 = &sp0[0];
2122       tColorRGBA *sp01 = &sp0[1];
2123       tColorRGBA *sp10 = &sp1[0];
2124       tColorRGBA *sp11 = &sp1[1];
2125       tColorRGBA new;
2126
2127       /* create new color pixel from all four source color pixels */
2128       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2129       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2130       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2131       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2132
2133       /* draw */
2134       *dp = new;
2135
2136       /* advance source pointers */
2137       sp += 2;
2138
2139       /* advance destination pointer */
2140       dp++;
2141     }
2142
2143     /* advance source pointer */
2144     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2145
2146     /* advance destination pointers */
2147     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2148   }
2149
2150   return 0;
2151 }
2152
2153 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2154 {
2155   int x, y, *sax, *say, *csax, *csay;
2156   float sx, sy;
2157   tColorRGBA *sp, *csp, *csp0, *dp;
2158   int dgap;
2159
2160   /* use specialized zoom function when scaling down to exactly half size */
2161   if (src->w == 2 * dst->w &&
2162       src->h == 2 * dst->h)
2163     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2164
2165   /* variable setup */
2166   sx = (float) src->w / (float) dst->w;
2167   sy = (float) src->h / (float) dst->h;
2168
2169   /* allocate memory for row increments */
2170   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2171   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2172
2173   /* precalculate row increments */
2174   for (x = 0; x <= dst->w; x++)
2175     *csax++ = (int)(sx * x);
2176
2177   for (y = 0; y <= dst->h; y++)
2178     *csay++ = (int)(sy * y);
2179
2180   /* pointer setup */
2181   sp = csp = csp0 = (tColorRGBA *) src->pixels;
2182   dp = (tColorRGBA *) dst->pixels;
2183   dgap = dst->pitch - dst->w * 4;
2184
2185   csay = say;
2186   for (y = 0; y < dst->h; y++)
2187   {
2188     sp = csp;
2189     csax = sax;
2190
2191     for (x = 0; x < dst->w; x++)
2192     {
2193       /* draw */
2194       *dp = *sp;
2195
2196       /* advance source pointers */
2197       csax++;
2198       sp = csp + *csax;
2199
2200       /* advance destination pointer */
2201       dp++;
2202     }
2203
2204     /* advance source pointer */
2205     csay++;
2206     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2207
2208     /* advance destination pointers */
2209     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2210   }
2211
2212   free(sax);
2213   free(say);
2214
2215   return 0;
2216 }
2217
2218 /*
2219   -----------------------------------------------------------------------------
2220   8 bit zoomer
2221
2222   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2223   -----------------------------------------------------------------------------
2224 */
2225
2226 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2227 {
2228   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2229   Uint8 *sp, *dp, *csp;
2230   int dgap;
2231
2232   /* variable setup */
2233   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2234   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2235
2236   /* allocate memory for row increments */
2237   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2238   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2239
2240   /* precalculate row increments */
2241   csx = 0;
2242   csax = sax;
2243   for (x = 0; x < dst->w; x++)
2244   {
2245     csx += sx;
2246     *csax = (csx >> 16);
2247     csx &= 0xffff;
2248     csax++;
2249   }
2250
2251   csy = 0;
2252   csay = say;
2253   for (y = 0; y < dst->h; y++)
2254   {
2255     csy += sy;
2256     *csay = (csy >> 16);
2257     csy &= 0xffff;
2258     csay++;
2259   }
2260
2261   csx = 0;
2262   csax = sax;
2263   for (x = 0; x < dst->w; x++)
2264   {
2265     csx += (*csax);
2266     csax++;
2267   }
2268
2269   csy = 0;
2270   csay = say;
2271   for (y = 0; y < dst->h; y++)
2272   {
2273     csy += (*csay);
2274     csay++;
2275   }
2276
2277   /* pointer setup */
2278   sp = csp = (Uint8 *) src->pixels;
2279   dp = (Uint8 *) dst->pixels;
2280   dgap = dst->pitch - dst->w;
2281
2282   /* draw */
2283   csay = say;
2284   for (y = 0; y < dst->h; y++)
2285   {
2286     csax = sax;
2287     sp = csp;
2288     for (x = 0; x < dst->w; x++)
2289     {
2290       /* draw */
2291       *dp = *sp;
2292
2293       /* advance source pointers */
2294       sp += (*csax);
2295       csax++;
2296
2297       /* advance destination pointer */
2298       dp++;
2299     }
2300
2301     /* advance source pointer (for row) */
2302     csp += ((*csay) * src->pitch);
2303     csay++;
2304
2305     /* advance destination pointers */
2306     dp += dgap;
2307   }
2308
2309   free(sax);
2310   free(say);
2311
2312   return 0;
2313 }
2314
2315 /*
2316   -----------------------------------------------------------------------------
2317   zoomSurface()
2318
2319   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2320   'zoomx' and 'zoomy' are scaling factors for width and height.
2321   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2322   into a 32bit RGBA format on the fly.
2323   -----------------------------------------------------------------------------
2324 */
2325
2326 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2327 {
2328   SDL_Surface *zoom_src = NULL;
2329   SDL_Surface *zoom_dst = NULL;
2330   boolean is_converted = FALSE;
2331   boolean is_32bit;
2332   int i;
2333
2334   if (src == NULL)
2335     return NULL;
2336
2337   /* determine if source surface is 32 bit or 8 bit */
2338   is_32bit = (src->format->BitsPerPixel == 32);
2339
2340   if (is_32bit || src->format->BitsPerPixel == 8)
2341   {
2342     /* use source surface 'as is' */
2343     zoom_src = src;
2344   }
2345   else
2346   {
2347     /* new source surface is 32 bit with a defined RGB ordering */
2348     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2349                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2350     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2351     is_32bit = TRUE;
2352     is_converted = TRUE;
2353   }
2354
2355   /* allocate surface to completely contain the zoomed surface */
2356   if (is_32bit)
2357   {
2358     /* target surface is 32 bit with source RGBA/ABGR ordering */
2359     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2360                                     zoom_src->format->Rmask,
2361                                     zoom_src->format->Gmask,
2362                                     zoom_src->format->Bmask, 0);
2363   }
2364   else
2365   {
2366     /* target surface is 8 bit */
2367     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2368                                     0, 0, 0, 0);
2369   }
2370
2371   /* lock source surface */
2372   SDL_LockSurface(zoom_src);
2373
2374   /* check which kind of surface we have */
2375   if (is_32bit)
2376   {
2377     /* call the 32 bit transformation routine to do the zooming */
2378     zoomSurfaceRGBA(zoom_src, zoom_dst);
2379   }
2380   else
2381   {
2382     /* copy palette */
2383     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2384       zoom_dst->format->palette->colors[i] =
2385         zoom_src->format->palette->colors[i];
2386     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2387
2388     /* call the 8 bit transformation routine to do the zooming */
2389     zoomSurfaceY(zoom_src, zoom_dst);
2390   }
2391
2392   /* unlock source surface */
2393   SDL_UnlockSurface(zoom_src);
2394
2395   /* free temporary surface */
2396   if (is_converted)
2397     SDL_FreeSurface(zoom_src);
2398
2399   /* return destination surface */
2400   return zoom_dst;
2401 }
2402
2403 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2404 {
2405   Bitmap *dst_bitmap = CreateBitmapStruct();
2406   SDL_Surface **dst_surface = &dst_bitmap->surface;
2407
2408   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2409   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2410
2411   dst_bitmap->width  = dst_width;
2412   dst_bitmap->height = dst_height;
2413
2414   /* create zoomed temporary surface from source surface */
2415   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2416
2417   /* create native format destination surface from zoomed temporary surface */
2418   SDLSetNativeSurface(dst_surface);
2419
2420   return dst_bitmap;
2421 }
2422
2423
2424 /* ========================================================================= */
2425 /* load image to bitmap                                                      */
2426 /* ========================================================================= */
2427
2428 Bitmap *SDLLoadImage(char *filename)
2429 {
2430   Bitmap *new_bitmap = CreateBitmapStruct();
2431   SDL_Surface *sdl_image_tmp;
2432
2433   print_timestamp_init("SDLLoadImage");
2434
2435   print_timestamp_time(getBaseNamePtr(filename));
2436
2437   /* load image to temporary surface */
2438   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2439   {
2440     SetError("IMG_Load(): %s", SDL_GetError());
2441
2442     return NULL;
2443   }
2444
2445   print_timestamp_time("IMG_Load");
2446
2447   UPDATE_BUSY_STATE();
2448
2449   /* create native non-transparent surface for current image */
2450   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2451   {
2452     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2453
2454     return NULL;
2455   }
2456
2457   print_timestamp_time("SDL_DisplayFormat (opaque)");
2458
2459   UPDATE_BUSY_STATE();
2460
2461   /* create native transparent surface for current image */
2462   if (sdl_image_tmp->format->Amask == 0)
2463     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2464                     SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2465
2466   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2467   {
2468     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2469
2470     return NULL;
2471   }
2472
2473   print_timestamp_time("SDL_DisplayFormat (masked)");
2474
2475   UPDATE_BUSY_STATE();
2476
2477   /* free temporary surface */
2478   SDL_FreeSurface(sdl_image_tmp);
2479
2480   new_bitmap->width = new_bitmap->surface->w;
2481   new_bitmap->height = new_bitmap->surface->h;
2482
2483   print_timestamp_done("SDLLoadImage");
2484
2485   return new_bitmap;
2486 }
2487
2488
2489 /* ------------------------------------------------------------------------- */
2490 /* custom cursor fuctions                                                    */
2491 /* ------------------------------------------------------------------------- */
2492
2493 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2494 {
2495   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2496                           cursor_info->width, cursor_info->height,
2497                           cursor_info->hot_x, cursor_info->hot_y);
2498 }
2499
2500 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2501 {
2502   static struct MouseCursorInfo *last_cursor_info = NULL;
2503   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2504   static SDL_Cursor *cursor_default = NULL;
2505   static SDL_Cursor *cursor_current = NULL;
2506
2507   /* if invoked for the first time, store the SDL default cursor */
2508   if (cursor_default == NULL)
2509     cursor_default = SDL_GetCursor();
2510
2511   /* only create new cursor if cursor info (custom only) has changed */
2512   if (cursor_info != NULL && cursor_info != last_cursor_info)
2513   {
2514     cursor_current = create_cursor(cursor_info);
2515     last_cursor_info = cursor_info;
2516   }
2517
2518   /* only set new cursor if cursor info (custom or NULL) has changed */
2519   if (cursor_info != last_cursor_info2)
2520     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2521
2522   last_cursor_info2 = cursor_info;
2523 }
2524
2525
2526 /* ========================================================================= */
2527 /* audio functions                                                           */
2528 /* ========================================================================= */
2529
2530 void SDLOpenAudio(void)
2531 {
2532 #if !defined(TARGET_SDL2)
2533   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2534     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2535 #endif
2536
2537   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2538   {
2539     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2540     return;
2541   }
2542
2543   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2544                     AUDIO_NUM_CHANNELS_STEREO,
2545                     setup.system.audio_fragment_size) < 0)
2546   {
2547     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2548     return;
2549   }
2550
2551   audio.sound_available = TRUE;
2552   audio.music_available = TRUE;
2553   audio.loops_available = TRUE;
2554   audio.sound_enabled = TRUE;
2555
2556   /* set number of available mixer channels */
2557   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2558   audio.music_channel = MUSIC_CHANNEL;
2559   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2560
2561   Mixer_InitChannels();
2562 }
2563
2564 void SDLCloseAudio(void)
2565 {
2566   Mix_HaltMusic();
2567   Mix_HaltChannel(-1);
2568
2569   Mix_CloseAudio();
2570   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2571 }
2572
2573
2574 /* ========================================================================= */
2575 /* event functions                                                           */
2576 /* ========================================================================= */
2577
2578 void SDLNextEvent(Event *event)
2579 {
2580   SDL_WaitEvent(event);
2581
2582   if (event->type == EVENT_BUTTONPRESS ||
2583       event->type == EVENT_BUTTONRELEASE)
2584   {
2585     if (((ButtonEvent *)event)->x > video_xoffset)
2586       ((ButtonEvent *)event)->x -= video_xoffset;
2587     else
2588       ((ButtonEvent *)event)->x = 0;
2589     if (((ButtonEvent *)event)->y > video_yoffset)
2590       ((ButtonEvent *)event)->y -= video_yoffset;
2591     else
2592       ((ButtonEvent *)event)->y = 0;
2593   }
2594   else if (event->type == EVENT_MOTIONNOTIFY)
2595   {
2596     if (((MotionEvent *)event)->x > video_xoffset)
2597       ((MotionEvent *)event)->x -= video_xoffset;
2598     else
2599       ((MotionEvent *)event)->x = 0;
2600     if (((MotionEvent *)event)->y > video_yoffset)
2601       ((MotionEvent *)event)->y -= video_yoffset;
2602     else
2603       ((MotionEvent *)event)->y = 0;
2604   }
2605 }
2606
2607 void SDLHandleWindowManagerEvent(Event *event)
2608 {
2609 #ifdef DEBUG
2610 #if defined(PLATFORM_WIN32)
2611   // experimental drag and drop code
2612
2613   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2614   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2615
2616 #if defined(TARGET_SDL2)
2617   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2618 #else
2619   if (syswmmsg->msg == WM_DROPFILES)
2620 #endif
2621   {
2622 #if defined(TARGET_SDL2)
2623     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2624 #else
2625     HDROP hdrop = (HDROP)syswmmsg->wParam;
2626 #endif
2627     int i, num_files;
2628
2629     printf("::: SDL_SYSWMEVENT:\n");
2630
2631     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2632
2633     for (i = 0; i < num_files; i++)
2634     {
2635       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2636       char buffer[buffer_len + 1];
2637
2638       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2639
2640       printf("::: - '%s'\n", buffer);
2641     }
2642
2643 #if defined(TARGET_SDL2)
2644     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2645 #else
2646     DragFinish((HDROP)syswmmsg->wParam);
2647 #endif
2648   }
2649 #endif
2650 #endif
2651 }
2652
2653
2654 /* ========================================================================= */
2655 /* joystick functions                                                        */
2656 /* ========================================================================= */
2657
2658 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2659 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2660 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2661
2662 static boolean SDLOpenJoystick(int nr)
2663 {
2664   if (nr < 0 || nr > MAX_PLAYERS)
2665     return FALSE;
2666
2667   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2668 }
2669
2670 static void SDLCloseJoystick(int nr)
2671 {
2672   if (nr < 0 || nr > MAX_PLAYERS)
2673     return;
2674
2675   SDL_JoystickClose(sdl_joystick[nr]);
2676
2677   sdl_joystick[nr] = NULL;
2678 }
2679
2680 static boolean SDLCheckJoystickOpened(int nr)
2681 {
2682   if (nr < 0 || nr > MAX_PLAYERS)
2683     return FALSE;
2684
2685 #if defined(TARGET_SDL2)
2686   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2687 #else
2688   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2689 #endif
2690 }
2691
2692 void HandleJoystickEvent(Event *event)
2693 {
2694   switch(event->type)
2695   {
2696     case SDL_JOYAXISMOTION:
2697       if (event->jaxis.axis < 2)
2698         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2699       break;
2700
2701     case SDL_JOYBUTTONDOWN:
2702       if (event->jbutton.button < 2)
2703         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2704       break;
2705
2706     case SDL_JOYBUTTONUP:
2707       if (event->jbutton.button < 2)
2708         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2709       break;
2710
2711     default:
2712       break;
2713   }
2714 }
2715
2716 void SDLInitJoysticks()
2717 {
2718   static boolean sdl_joystick_subsystem_initialized = FALSE;
2719   boolean print_warning = !sdl_joystick_subsystem_initialized;
2720   int i;
2721
2722   if (!sdl_joystick_subsystem_initialized)
2723   {
2724     sdl_joystick_subsystem_initialized = TRUE;
2725
2726     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2727     {
2728       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2729       return;
2730     }
2731   }
2732
2733   for (i = 0; i < MAX_PLAYERS; i++)
2734   {
2735     /* get configured joystick for this player */
2736     char *device_name = setup.input[i].joy.device_name;
2737     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2738
2739     if (joystick_nr >= SDL_NumJoysticks())
2740     {
2741       if (setup.input[i].use_joystick && print_warning)
2742         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2743
2744       joystick_nr = -1;
2745     }
2746
2747     /* misuse joystick file descriptor variable to store joystick number */
2748     joystick.fd[i] = joystick_nr;
2749
2750     if (joystick_nr == -1)
2751       continue;
2752
2753     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2754     if (SDLCheckJoystickOpened(joystick_nr))
2755       SDLCloseJoystick(joystick_nr);
2756
2757     if (!setup.input[i].use_joystick)
2758       continue;
2759
2760     if (!SDLOpenJoystick(joystick_nr))
2761     {
2762       if (print_warning)
2763         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2764
2765       continue;
2766     }
2767
2768     joystick.status = JOYSTICK_ACTIVATED;
2769   }
2770 }
2771
2772 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2773 {
2774   if (nr < 0 || nr >= MAX_PLAYERS)
2775     return FALSE;
2776
2777   if (x != NULL)
2778     *x = sdl_js_axis[nr][0];
2779   if (y != NULL)
2780     *y = sdl_js_axis[nr][1];
2781
2782   if (b1 != NULL)
2783     *b1 = sdl_js_button[nr][0];
2784   if (b2 != NULL)
2785     *b2 = sdl_js_button[nr][1];
2786
2787   return TRUE;
2788 }