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