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