rnd-20140103-1-src
[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    1
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
44 /* functions from SGE library */
45 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
46
47 #if defined(TARGET_SDL2)
48 static void UpdateScreen(SDL_Rect *rect)
49 {
50 #if USE_RENDERER
51   SDL_Surface *screen = backbuffer->surface;
52
53 #if 1
54   if (rect)
55   {
56     int bytes_x = screen->pitch / video.width;
57     int bytes_y = screen->pitch;
58
59     SDL_UpdateTexture(sdl_texture, rect,
60                       screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
61                       screen->pitch);
62   }
63   else
64   {
65     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
66   }
67 #else
68   SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
69 #endif
70   SDL_RenderClear(sdl_renderer);
71   SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
72   SDL_RenderPresent(sdl_renderer);
73 #else
74   if (rect)
75     SDL_UpdateWindowSurfaceRects(sdl_window, rect, 1);
76   else
77     SDL_UpdateWindowSurface(sdl_window);
78 #endif
79 }
80 #endif
81
82 static void setFullscreenParameters(char *fullscreen_mode_string)
83 {
84   struct ScreenModeInfo *fullscreen_mode;
85   int i;
86
87   fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
88
89   if (fullscreen_mode == NULL)
90     return;
91
92   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
93   {
94     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
95         fullscreen_mode->height == video.fullscreen_modes[i].height)
96     {
97       fullscreen_width  = fullscreen_mode->width;
98       fullscreen_height = fullscreen_mode->height;
99
100       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
101       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
102
103       break;
104     }
105   }
106 }
107
108 static void SDLSetWindowIcon(char *basename)
109 {
110   /* (setting the window icon on Mac OS X would replace the high-quality
111      dock icon with the currently smaller (and uglier) icon from file) */
112
113 #if !defined(PLATFORM_MACOSX)
114   char *filename = getCustomImageFilename(basename);
115   SDL_Surface *surface;
116
117   if (filename == NULL)
118   {
119     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
120
121     return;
122   }
123
124   if ((surface = IMG_Load(filename)) == NULL)
125   {
126     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
127
128     return;
129   }
130
131   /* set transparent color */
132   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
133                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
134
135 #if defined(TARGET_SDL2)
136   SDL_SetWindowIcon(sdl_window, surface);
137 #else
138   SDL_WM_SetIcon(surface, NULL);
139 #endif
140 #endif
141 }
142
143 #if defined(TARGET_SDL2)
144 SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface)
145 {
146   if (backbuffer == NULL ||
147       backbuffer->surface == NULL)
148     return NULL;
149
150   return SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
151 }
152 #endif
153
154 void SDLInitVideoDisplay(void)
155 {
156 #if !defined(TARGET_SDL2)
157   if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
158     SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
159
160   SDL_putenv("SDL_VIDEO_CENTERED=1");
161 #endif
162
163   /* initialize SDL video */
164   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
165     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
166
167   /* set default SDL depth */
168 #if !defined(TARGET_SDL2)
169   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
170 #else
171   video.default_depth = 32;     // (how to determine video depth in SDL2?)
172 #endif
173 }
174
175 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
176                         boolean fullscreen)
177 {
178   static int screen_xy[][2] =
179   {
180     {  640, 480 },
181     {  800, 600 },
182     { 1024, 768 },
183     {   -1,  -1 }
184   };
185   SDL_Rect **modes;
186   int i, j;
187
188   /* default: normal game window size */
189   fullscreen_width = video.width;
190   fullscreen_height = video.height;
191   fullscreen_xoffset = 0;
192   fullscreen_yoffset = 0;
193
194   for (i = 0; screen_xy[i][0] != -1; i++)
195   {
196     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
197     {
198       fullscreen_width  = screen_xy[i][0];
199       fullscreen_height = screen_xy[i][1];
200
201       break;
202     }
203   }
204
205   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
206   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
207
208 #if 1
209   checked_free(video.fullscreen_modes);
210
211   video.fullscreen_modes = NULL;
212   video.fullscreen_mode_current = NULL;
213 #endif
214
215 #if !defined(TARGET_SDL2)
216   /* get available hardware supported fullscreen modes */
217   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
218 #else
219   // (for now, no display modes in SDL2 -- change this later)
220   modes = NULL;
221 #endif
222
223   if (modes == NULL)
224   {
225     /* no screen modes available => no fullscreen mode support */
226     video.fullscreen_available = FALSE;
227   }
228   else if (modes == (SDL_Rect **)-1)
229   {
230     /* fullscreen resolution is not restricted -- all resolutions available */
231     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
232
233     /* use native video buffer size for fullscreen mode */
234     video.fullscreen_modes[0].width  = video.width;
235     video.fullscreen_modes[0].height = video.height;
236
237     video.fullscreen_modes[1].width  = -1;
238     video.fullscreen_modes[1].height = -1;
239   }
240   else
241   {
242     /* in this case, a certain number of screen modes is available */
243     int num_modes = 0;
244
245     for(i = 0; modes[i] != NULL; i++)
246     {
247       boolean found_mode = FALSE;
248
249       /* screen mode is smaller than video buffer size -- skip it */
250       if (modes[i]->w < video.width || modes[i]->h < video.height)
251         continue;
252
253       if (video.fullscreen_modes != NULL)
254         for (j = 0; video.fullscreen_modes[j].width != -1; j++)
255           if (modes[i]->w == video.fullscreen_modes[j].width &&
256               modes[i]->h == video.fullscreen_modes[j].height)
257             found_mode = TRUE;
258
259       if (found_mode)           /* screen mode already stored -- skip it */
260         continue;
261
262       /* new mode found; add it to list of available fullscreen modes */
263
264       num_modes++;
265
266       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
267                                                (num_modes + 1) *
268                                                sizeof(struct ScreenModeInfo));
269
270       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
271       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
272
273       video.fullscreen_modes[num_modes].width  = -1;
274       video.fullscreen_modes[num_modes].height = -1;
275     }
276
277     if (num_modes == 0)
278     {
279       /* no appropriate screen modes available => no fullscreen mode support */
280       video.fullscreen_available = FALSE;
281     }
282   }
283
284 #if 0
285   /* set window icon */
286   SDLSetWindowIcon(program.sdl_icon_filename);
287 #endif
288
289   /* open SDL video output device (window or fullscreen mode) */
290   if (!SDLSetVideoMode(backbuffer, fullscreen))
291     Error(ERR_EXIT, "setting video mode failed");
292
293 #if 1
294   /* !!! SDL2 can only set the window icon if the window already exists !!! */
295   /* set window icon */
296   SDLSetWindowIcon(program.sdl_icon_filename);
297 #endif
298
299   /* set window and icon title */
300 #if defined(TARGET_SDL2)
301   SDL_SetWindowTitle(sdl_window, program.window_title);
302 #else
303   SDL_WM_SetCaption(program.window_title, program.window_title);
304 #endif
305
306   /* SDL cannot directly draw to the visible video framebuffer like X11,
307      but always uses a backbuffer, which is then blitted to the visible
308      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
309      visible video framebuffer with 'SDL_Flip', if the hardware supports
310      this). Therefore do not use an additional backbuffer for drawing, but
311      use a symbolic buffer (distinguishable from the SDL backbuffer) called
312      'window', which indicates that the SDL backbuffer should be updated to
313      the visible video framebuffer when attempting to blit to it.
314
315      For convenience, it seems to be a good idea to create this symbolic
316      buffer 'window' at the same size as the SDL backbuffer. Although it
317      should never be drawn to directly, it would do no harm nevertheless. */
318
319   /* create additional (symbolic) buffer for double-buffering */
320 #if 1
321   ReCreateBitmap(window, video.width, video.height, video.depth);
322 #else
323   *window = CreateBitmap(video.width, video.height, video.depth);
324 #endif
325 }
326
327 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
328 {
329   boolean success = TRUE;
330 #if defined(TARGET_SDL2)
331   // int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN;
332   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
333   int surface_flags_window = SURFACE_FLAGS;
334 #else
335   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
336   int surface_flags_window = SURFACE_FLAGS;
337 #endif
338   SDL_Surface *new_surface = NULL;
339
340   if (*backbuffer == NULL)
341     *backbuffer = CreateBitmapStruct();
342
343   /* (real bitmap might be larger in fullscreen mode with video offsets) */
344   (*backbuffer)->width  = video.width;
345   (*backbuffer)->height = video.height;
346
347   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
348   {
349     setFullscreenParameters(setup.fullscreen_mode);
350
351     video_xoffset = fullscreen_xoffset;
352     video_yoffset = fullscreen_yoffset;
353
354     /* switch display to fullscreen mode, if available */
355 #if defined(TARGET_SDL2)
356     sdl_window = SDL_CreateWindow(program.window_title,
357                                   SDL_WINDOWPOS_CENTERED,
358                                   SDL_WINDOWPOS_CENTERED,
359                                   fullscreen_width, fullscreen_height,
360                                   surface_flags_fullscreen);
361     if (sdl_window != NULL)
362     {
363       new_surface = SDL_GetWindowSurface(sdl_window);
364
365       // SDL_UpdateWindowSurface(sdl_window);   // immediately map window
366       // UpdateScreen(NULL);    // immediately map window
367     }
368 #else
369     new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
370                                    video.depth, surface_flags_fullscreen);
371 #endif
372
373     if (new_surface == NULL)
374     {
375       /* switching display to fullscreen mode failed */
376       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
377
378       /* do not try it again */
379       video.fullscreen_available = FALSE;
380
381       success = FALSE;
382     }
383     else
384     {
385       (*backbuffer)->surface = new_surface;
386
387       video.fullscreen_enabled = TRUE;
388       video.fullscreen_mode_current = setup.fullscreen_mode;
389
390       success = TRUE;
391     }
392   }
393
394   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
395   {
396     video_xoffset = 0;
397     video_yoffset = 0;
398
399     /* switch display to window mode */
400 #if defined(TARGET_SDL2)
401
402 #if USE_RENDERER
403     float scale_factor = 1.2;
404     int test_fullscreen = 0;
405     int surface_flags = (test_fullscreen ? surface_flags_fullscreen :
406                          surface_flags_window);
407
408     sdl_window = SDL_CreateWindow(program.window_title,
409                                   SDL_WINDOWPOS_CENTERED,
410                                   SDL_WINDOWPOS_CENTERED,
411                                   (int)(scale_factor * video.width),
412                                   (int)(scale_factor * video.height),
413                                   surface_flags);
414
415     if (sdl_window != NULL)
416     {
417       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
418
419       if (sdl_renderer != NULL)
420       {
421         SDL_RenderSetLogicalSize(sdl_renderer, video.width, video.height);
422         SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
423
424         sdl_texture = SDL_CreateTexture(sdl_renderer,
425                                         SDL_PIXELFORMAT_ARGB8888,
426                                         SDL_TEXTUREACCESS_STREAMING,
427                                         video.width, video.height);
428
429         if (sdl_texture != NULL)
430         {
431 #if 1
432           // (do not use alpha channel)
433           new_surface = SDL_CreateRGBSurface(0, video.width, video.height, 32,
434                                              0x00FF0000,
435                                              0x0000FF00,
436                                              0x000000FF,
437                                              0x00000000);
438 #else
439           // (this uses an alpha channel, which we don't want here)
440           new_surface = SDL_CreateRGBSurface(0, video.width, video.height, 32,
441                                              0x00FF0000,
442                                              0x0000FF00,
443                                              0x000000FF,
444                                              0xFF000000);
445 #endif
446
447           if (new_surface == NULL)
448             Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
449                   SDL_GetError());
450         }
451         else
452         {
453           Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
454         }
455       }
456       else
457       {
458         Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
459       }
460     }
461     else
462     {
463       Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
464     }
465 #else
466     sdl_window = SDL_CreateWindow(program.window_title,
467                                   SDL_WINDOWPOS_CENTERED,
468                                   SDL_WINDOWPOS_CENTERED,
469                                   video.width, video.height,
470                                   surface_flags_window);
471
472     if (sdl_window != NULL)
473     {
474       new_surface = SDL_GetWindowSurface(sdl_window);
475
476       // SDL_UpdateWindowSurface(sdl_window);   // immediately map window
477       // UpdateScreen(NULL);            // immediately map window
478     }
479 #endif
480
481 #else
482     new_surface = SDL_SetVideoMode(video.width, video.height,
483                                    video.depth, surface_flags_window);
484 #endif
485
486     if (new_surface == NULL)
487     {
488       /* switching display to window mode failed -- should not happen */
489       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
490
491       success = FALSE;
492     }
493     else
494     {
495       (*backbuffer)->surface = new_surface;
496
497       video.fullscreen_enabled = FALSE;
498
499       success = TRUE;
500     }
501   }
502
503 #if defined(TARGET_SDL2)
504   UpdateScreen(NULL);           // map window
505 #endif
506
507 #if 1
508   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
509
510 #if defined(PLATFORM_WIN32)
511   {
512     SDL_SysWMinfo wminfo;
513     HWND hwnd;
514
515     SDL_VERSION(&wminfo.version);
516     SDL_GetWMInfo(&wminfo);
517
518     hwnd = wminfo.window;
519
520     DragAcceptFiles(hwnd, TRUE);
521   }
522 #endif
523 #endif
524
525
526   return success;
527 }
528
529 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
530                             int depth)
531 {
532   SDL_Surface *surface_tmp, *surface_native;
533
534   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
535                                           0, 0, 0, 0))
536       == NULL)
537     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
538
539   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
540     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
541
542   SDL_FreeSurface(surface_tmp);
543
544   new_bitmap->surface = surface_native;
545 }
546
547 void SDLFreeBitmapPointers(Bitmap *bitmap)
548 {
549   if (bitmap->surface)
550     SDL_FreeSurface(bitmap->surface);
551   if (bitmap->surface_masked)
552     SDL_FreeSurface(bitmap->surface_masked);
553   bitmap->surface = NULL;
554   bitmap->surface_masked = NULL;
555 }
556
557 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
558                  int src_x, int src_y, int width, int height,
559                  int dst_x, int dst_y, int mask_mode)
560 {
561   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
562   SDL_Rect src_rect, dst_rect;
563
564   if (src_bitmap == backbuffer)
565   {
566     src_x += video_xoffset;
567     src_y += video_yoffset;
568   }
569
570   src_rect.x = src_x;
571   src_rect.y = src_y;
572   src_rect.w = width;
573   src_rect.h = height;
574
575   if (dst_bitmap == backbuffer || dst_bitmap == window)
576   {
577     dst_x += video_xoffset;
578     dst_y += video_yoffset;
579   }
580
581   dst_rect.x = dst_x;
582   dst_rect.y = dst_y;
583   dst_rect.w = width;
584   dst_rect.h = height;
585
586   // if (src_bitmap != backbuffer || dst_bitmap != window)
587   if (!(src_bitmap == backbuffer && dst_bitmap == window))
588     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
589                      src_bitmap->surface_masked : src_bitmap->surface),
590                     &src_rect, real_dst_bitmap->surface, &dst_rect);
591
592 #if defined(TARGET_SDL2)
593   if (dst_bitmap == window)
594   {
595     // SDL_UpdateWindowSurface(sdl_window);
596     // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
597     UpdateScreen(&dst_rect);
598   }
599 #else
600   if (dst_bitmap == window)
601     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
602 #endif
603 }
604
605 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
606                       Uint32 color)
607 {
608   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
609   SDL_Rect rect;
610
611   if (dst_bitmap == backbuffer || dst_bitmap == window)
612   {
613     x += video_xoffset;
614     y += video_yoffset;
615   }
616
617   rect.x = x;
618   rect.y = y;
619   rect.w = width;
620   rect.h = height;
621
622   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
623
624 #if defined(TARGET_SDL2)
625   if (dst_bitmap == window)
626   {
627     // SDL_UpdateWindowSurface(sdl_window);
628     // SDL_UpdateWindowSurfaceRects(sdl_window, &rect, 1);
629     UpdateScreen(&rect);
630   }
631 #else
632   if (dst_bitmap == window)
633     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
634 #endif
635 }
636
637 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
638                       int fade_mode, int fade_delay, int post_delay,
639                       void (*draw_border_function)(void))
640 {
641   static boolean initialization_needed = TRUE;
642   static SDL_Surface *surface_source = NULL;
643   static SDL_Surface *surface_target = NULL;
644   static SDL_Surface *surface_black = NULL;
645   SDL_Surface *surface_screen = backbuffer->surface;
646   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
647   SDL_Rect src_rect, dst_rect;
648 #if defined(TARGET_SDL2)
649   SDL_Rect dst_rect2;
650 #endif
651   int src_x = x, src_y = y;
652   int dst_x = x, dst_y = y;
653   unsigned int time_last, time_current;
654
655   /* check if screen size has changed */
656   if (surface_source != NULL && (video.width  != surface_source->w ||
657                                  video.height != surface_source->h))
658   {
659     SDL_FreeSurface(surface_source);
660     SDL_FreeSurface(surface_target);
661     SDL_FreeSurface(surface_black);
662
663     initialization_needed = TRUE;
664   }
665
666   src_rect.x = src_x;
667   src_rect.y = src_y;
668   src_rect.w = width;
669   src_rect.h = height;
670
671   dst_x += video_xoffset;
672   dst_y += video_yoffset;
673
674   dst_rect.x = dst_x;
675   dst_rect.y = dst_y;
676   dst_rect.w = width;           /* (ignored) */
677   dst_rect.h = height;          /* (ignored) */
678
679 #if defined(TARGET_SDL2)
680   dst_rect2 = dst_rect;
681 #endif
682
683   if (initialization_needed)
684   {
685 #if defined(TARGET_SDL2)
686     unsigned int flags = 0;
687 #else
688     unsigned int flags = SDL_SRCALPHA;
689
690     /* use same surface type as screen surface */
691     if ((surface_screen->flags & SDL_HWSURFACE))
692       flags |= SDL_HWSURFACE;
693     else
694       flags |= SDL_SWSURFACE;
695 #endif
696
697     /* create surface for temporary copy of screen buffer (source) */
698     if ((surface_source =
699          SDL_CreateRGBSurface(flags,
700                               video.width,
701                               video.height,
702                               surface_screen->format->BitsPerPixel,
703                               surface_screen->format->Rmask,
704                               surface_screen->format->Gmask,
705                               surface_screen->format->Bmask,
706                               surface_screen->format->Amask)) == NULL)
707       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
708
709     /* create surface for cross-fading screen buffer (target) */
710     if ((surface_target =
711          SDL_CreateRGBSurface(flags,
712                               video.width,
713                               video.height,
714                               surface_screen->format->BitsPerPixel,
715                               surface_screen->format->Rmask,
716                               surface_screen->format->Gmask,
717                               surface_screen->format->Bmask,
718                               surface_screen->format->Amask)) == NULL)
719       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
720
721     /* create black surface for fading from/to black */
722     if ((surface_black =
723          SDL_CreateRGBSurface(flags,
724                               video.width,
725                               video.height,
726                               surface_screen->format->BitsPerPixel,
727                               surface_screen->format->Rmask,
728                               surface_screen->format->Gmask,
729                               surface_screen->format->Bmask,
730                               surface_screen->format->Amask)) == NULL)
731       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
732
733     /* completely fill the surface with black color pixels */
734     SDL_FillRect(surface_black, NULL,
735                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
736
737     initialization_needed = FALSE;
738   }
739
740   /* copy source and target surfaces to temporary surfaces for fading */
741   if (fade_mode & FADE_TYPE_TRANSFORM)
742   {
743     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
744     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
745   }
746   else if (fade_mode & FADE_TYPE_FADE_IN)
747   {
748     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
749     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
750   }
751   else          /* FADE_TYPE_FADE_OUT */
752   {
753     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
754     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
755   }
756
757   time_current = SDL_GetTicks();
758
759   if (fade_mode == FADE_MODE_MELT)
760   {
761     boolean done = FALSE;
762     int melt_pixels = 2;
763     int melt_columns = width / melt_pixels;
764     int ypos[melt_columns];
765     int max_steps = height / 8 + 32;
766     int steps_done = 0;
767     float steps = 0;
768     int i;
769
770     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
771 #if defined(TARGET_SDL2)
772     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
773 #else
774     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
775 #endif
776
777     ypos[0] = -GetSimpleRandom(16);
778
779     for (i = 1 ; i < melt_columns; i++)
780     {
781       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
782
783       ypos[i] = ypos[i - 1] + r;
784
785       if (ypos[i] > 0)
786         ypos[i] = 0;
787       else
788         if (ypos[i] == -16)
789           ypos[i] = -15;
790     }
791
792     while (!done)
793     {
794       int steps_final;
795
796       time_last = time_current;
797       time_current = SDL_GetTicks();
798       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
799       steps_final = MIN(MAX(0, steps), max_steps);
800
801       steps_done++;
802
803       done = (steps_done >= steps_final);
804
805       for (i = 0 ; i < melt_columns; i++)
806       {
807         if (ypos[i] < 0)
808         {
809           ypos[i]++;
810
811           done = FALSE;
812         }
813         else if (ypos[i] < height)
814         {
815           int y1 = 16;
816           int y2 = 8;
817           int y3 = 8;
818           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
819
820           if (ypos[i] + dy >= height)
821             dy = height - ypos[i];
822
823           /* copy part of (appearing) target surface to upper area */
824           src_rect.x = src_x + i * melt_pixels;
825           // src_rect.y = src_y + ypos[i];
826           src_rect.y = src_y;
827           src_rect.w = melt_pixels;
828           // src_rect.h = dy;
829           src_rect.h = ypos[i] + dy;
830
831           dst_rect.x = dst_x + i * melt_pixels;
832           // dst_rect.y = dst_y + ypos[i];
833           dst_rect.y = dst_y;
834
835           if (steps_done >= steps_final)
836             SDL_BlitSurface(surface_target, &src_rect,
837                             surface_screen, &dst_rect);
838
839           ypos[i] += dy;
840
841           /* copy part of (disappearing) source surface to lower area */
842           src_rect.x = src_x + i * melt_pixels;
843           src_rect.y = src_y;
844           src_rect.w = melt_pixels;
845           src_rect.h = height - ypos[i];
846
847           dst_rect.x = dst_x + i * melt_pixels;
848           dst_rect.y = dst_y + ypos[i];
849
850           if (steps_done >= steps_final)
851             SDL_BlitSurface(surface_source, &src_rect,
852                             surface_screen, &dst_rect);
853
854           done = FALSE;
855         }
856         else
857         {
858           src_rect.x = src_x + i * melt_pixels;
859           src_rect.y = src_y;
860           src_rect.w = melt_pixels;
861           src_rect.h = height;
862
863           dst_rect.x = dst_x + i * melt_pixels;
864           dst_rect.y = dst_y;
865
866           if (steps_done >= steps_final)
867             SDL_BlitSurface(surface_target, &src_rect,
868                             surface_screen, &dst_rect);
869         }
870       }
871
872       if (steps_done >= steps_final)
873       {
874         if (draw_border_function != NULL)
875           draw_border_function();
876
877 #if defined(TARGET_SDL2)
878         // SDL_UpdateWindowSurface(sdl_window);
879         // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect2, 1);
880         UpdateScreen(&dst_rect2);
881 #else
882         SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
883 #endif
884       }
885     }
886   }
887   else
888   {
889     float alpha;
890     int alpha_final;
891
892     for (alpha = 0.0; alpha < 255.0;)
893     {
894       time_last = time_current;
895       time_current = SDL_GetTicks();
896       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
897       alpha_final = MIN(MAX(0, alpha), 255);
898
899       /* draw existing (source) image to screen buffer */
900       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
901
902       /* draw new (target) image to screen buffer using alpha blending */
903 #if defined(TARGET_SDL2)
904       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
905       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
906 #else
907       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
908 #endif
909       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
910
911       if (draw_border_function != NULL)
912         draw_border_function();
913
914 #if 1
915       /* only update the region of the screen that is affected from fading */
916 #if defined(TARGET_SDL2)
917       // SDL_UpdateWindowSurface(sdl_window);
918       // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
919       UpdateScreen(&dst_rect);
920 #else
921       SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
922 #endif
923 #else
924       SDL_Flip(surface_screen);
925 #endif
926     }
927   }
928
929   Delay(post_delay);
930 }
931
932 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
933                        int to_x, int to_y, Uint32 color)
934 {
935   SDL_Surface *surface = dst_bitmap->surface;
936   SDL_Rect rect;
937
938   if (from_x > to_x)
939     swap_numbers(&from_x, &to_x);
940
941   if (from_y > to_y)
942     swap_numbers(&from_y, &to_y);
943
944   rect.x = from_x;
945   rect.y = from_y;
946   rect.w = (to_x - from_x + 1);
947   rect.h = (to_y - from_y + 1);
948
949   if (dst_bitmap == backbuffer || dst_bitmap == window)
950   {
951     rect.x += video_xoffset;
952     rect.y += video_yoffset;
953   }
954
955   SDL_FillRect(surface, &rect, color);
956 }
957
958 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
959                  int to_x, int to_y, Uint32 color)
960 {
961   if (dst_bitmap == backbuffer || dst_bitmap == window)
962   {
963     from_x += video_xoffset;
964     from_y += video_yoffset;
965     to_x += video_xoffset;
966     to_y += video_yoffset;
967   }
968
969   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
970 }
971
972 #if 0
973 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
974                   int num_points, Uint32 color)
975 {
976   int i, x, y;
977   int line_width = 4;
978
979   for (i = 0; i < num_points - 1; i++)
980   {
981     for (x = 0; x < line_width; x++)
982     {
983       for (y = 0; y < line_width; y++)
984       {
985         int dx = x - line_width / 2;
986         int dy = y - line_width / 2;
987
988         if ((x == 0 && y == 0) ||
989             (x == 0 && y == line_width - 1) ||
990             (x == line_width - 1 && y == 0) ||
991             (x == line_width - 1 && y == line_width - 1))
992           continue;
993
994         sge_Line(surface, points[i].x + dx, points[i].y + dy,
995                  points[i+1].x + dx, points[i+1].y + dy, color);
996       }
997     }
998   }
999 }
1000 #endif
1001
1002 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1003 {
1004   SDL_Surface *surface = src_bitmap->surface;
1005
1006   if (src_bitmap == backbuffer || src_bitmap == window)
1007   {
1008     x += video_xoffset;
1009     y += video_yoffset;
1010   }
1011
1012   switch (surface->format->BytesPerPixel)
1013   {
1014     case 1:             /* assuming 8-bpp */
1015     {
1016       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1017     }
1018     break;
1019
1020     case 2:             /* probably 15-bpp or 16-bpp */
1021     {
1022       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1023     }
1024     break;
1025
1026   case 3:               /* slow 24-bpp mode; usually not used */
1027     {
1028       /* does this work? */
1029       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1030       Uint32 color = 0;
1031       int shift;
1032
1033       shift = surface->format->Rshift;
1034       color |= *(pix + shift / 8) >> shift;
1035       shift = surface->format->Gshift;
1036       color |= *(pix + shift / 8) >> shift;
1037       shift = surface->format->Bshift;
1038       color |= *(pix + shift / 8) >> shift;
1039
1040       return color;
1041     }
1042     break;
1043
1044   case 4:               /* probably 32-bpp */
1045     {
1046       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1047     }
1048     break;
1049   }
1050
1051   return 0;
1052 }
1053
1054
1055 /* ========================================================================= */
1056 /* The following functions were taken from the SGE library                   */
1057 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1058 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1059 /* ========================================================================= */
1060
1061 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1062 {
1063   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1064   {
1065     switch (surface->format->BytesPerPixel)
1066     {
1067       case 1:
1068       {
1069         /* Assuming 8-bpp */
1070         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1071       }
1072       break;
1073
1074       case 2:
1075       {
1076         /* Probably 15-bpp or 16-bpp */
1077         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1078       }
1079       break;
1080
1081       case 3:
1082       {
1083         /* Slow 24-bpp mode, usually not used */
1084         Uint8 *pix;
1085         int shift;
1086
1087         /* Gack - slow, but endian correct */
1088         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1089         shift = surface->format->Rshift;
1090         *(pix+shift/8) = color>>shift;
1091         shift = surface->format->Gshift;
1092         *(pix+shift/8) = color>>shift;
1093         shift = surface->format->Bshift;
1094         *(pix+shift/8) = color>>shift;
1095       }
1096       break;
1097
1098       case 4:
1099       {
1100         /* Probably 32-bpp */
1101         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1102       }
1103       break;
1104     }
1105   }
1106 }
1107
1108 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1109                   Uint8 R, Uint8 G, Uint8 B)
1110 {
1111   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1112 }
1113
1114 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1115 {
1116   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1117 }
1118
1119 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1120 {
1121   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1122 }
1123
1124 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1125 {
1126   Uint8 *pix;
1127   int shift;
1128
1129   /* Gack - slow, but endian correct */
1130   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1131   shift = surface->format->Rshift;
1132   *(pix+shift/8) = color>>shift;
1133   shift = surface->format->Gshift;
1134   *(pix+shift/8) = color>>shift;
1135   shift = surface->format->Bshift;
1136   *(pix+shift/8) = color>>shift;
1137 }
1138
1139 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1140 {
1141   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1142 }
1143
1144 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1145 {
1146   switch (dest->format->BytesPerPixel)
1147   {
1148     case 1:
1149       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1150       break;
1151
1152     case 2:
1153       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1154       break;
1155
1156     case 3:
1157       _PutPixel24(dest,x,y,color);
1158       break;
1159
1160     case 4:
1161       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1162       break;
1163   }
1164 }
1165
1166 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1167 {
1168   if (SDL_MUSTLOCK(surface))
1169   {
1170     if (SDL_LockSurface(surface) < 0)
1171     {
1172       return;
1173     }
1174   }
1175
1176   _PutPixel(surface, x, y, color);
1177
1178   if (SDL_MUSTLOCK(surface))
1179   {
1180     SDL_UnlockSurface(surface);
1181   }
1182 }
1183
1184 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1185                   Uint8 r, Uint8 g, Uint8 b)
1186 {
1187   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1188 }
1189
1190 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1191 {
1192   if (y >= 0 && y <= dest->h - 1)
1193   {
1194     switch (dest->format->BytesPerPixel)
1195     {
1196       case 1:
1197         return y*dest->pitch;
1198         break;
1199
1200       case 2:
1201         return y*dest->pitch/2;
1202         break;
1203
1204       case 3:
1205         return y*dest->pitch;
1206         break;
1207
1208       case 4:
1209         return y*dest->pitch/4;
1210         break;
1211     }
1212   }
1213
1214   return -1;
1215 }
1216
1217 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1218 {
1219   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1220   {
1221     switch (surface->format->BytesPerPixel)
1222     {
1223       case 1:
1224       {
1225         /* Assuming 8-bpp */
1226         *((Uint8 *)surface->pixels + ypitch + x) = color;
1227       }
1228       break;
1229
1230       case 2:
1231       {
1232         /* Probably 15-bpp or 16-bpp */
1233         *((Uint16 *)surface->pixels + ypitch + x) = color;
1234       }
1235       break;
1236
1237       case 3:
1238       {
1239         /* Slow 24-bpp mode, usually not used */
1240         Uint8 *pix;
1241         int shift;
1242
1243         /* Gack - slow, but endian correct */
1244         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1245         shift = surface->format->Rshift;
1246         *(pix+shift/8) = color>>shift;
1247         shift = surface->format->Gshift;
1248         *(pix+shift/8) = color>>shift;
1249         shift = surface->format->Bshift;
1250         *(pix+shift/8) = color>>shift;
1251       }
1252       break;
1253
1254       case 4:
1255       {
1256         /* Probably 32-bpp */
1257         *((Uint32 *)surface->pixels + ypitch + x) = color;
1258       }
1259       break;
1260     }
1261   }
1262 }
1263
1264 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1265                Uint32 Color)
1266 {
1267   SDL_Rect l;
1268
1269   if (SDL_MUSTLOCK(Surface))
1270   {
1271     if (SDL_LockSurface(Surface) < 0)
1272     {
1273       return;
1274     }
1275   }
1276
1277   if (x1 > x2)
1278   {
1279     Sint16 tmp = x1;
1280     x1 = x2;
1281     x2 = tmp;
1282   }
1283
1284   /* Do the clipping */
1285   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1286     return;
1287   if (x1 < 0)
1288     x1 = 0;
1289   if (x2 > Surface->w - 1)
1290     x2 = Surface->w - 1;
1291
1292   l.x = x1;
1293   l.y = y;
1294   l.w = x2 - x1 + 1;
1295   l.h = 1;
1296
1297   SDL_FillRect(Surface, &l, Color);
1298
1299   if (SDL_MUSTLOCK(Surface))
1300   {
1301     SDL_UnlockSurface(Surface);
1302   }
1303 }
1304
1305 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1306                   Uint8 R, Uint8 G, Uint8 B)
1307 {
1308   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1309 }
1310
1311 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1312 {
1313   SDL_Rect l;
1314
1315   if (x1 > x2)
1316   {
1317     Sint16 tmp = x1;
1318     x1 = x2;
1319     x2 = tmp;
1320   }
1321
1322   /* Do the clipping */
1323   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1324     return;
1325   if (x1 < 0)
1326     x1 = 0;
1327   if (x2 > Surface->w - 1)
1328     x2 = Surface->w - 1;
1329
1330   l.x = x1;
1331   l.y = y;
1332   l.w = x2 - x1 + 1;
1333   l.h = 1;
1334
1335   SDL_FillRect(Surface, &l, Color);
1336 }
1337
1338 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1339                Uint32 Color)
1340 {
1341   SDL_Rect l;
1342
1343   if (SDL_MUSTLOCK(Surface))
1344   {
1345     if (SDL_LockSurface(Surface) < 0)
1346     {
1347       return;
1348     }
1349   }
1350
1351   if (y1 > y2)
1352   {
1353     Sint16 tmp = y1;
1354     y1 = y2;
1355     y2 = tmp;
1356   }
1357
1358   /* Do the clipping */
1359   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1360     return;
1361   if (y1 < 0)
1362     y1 = 0;
1363   if (y2 > Surface->h - 1)
1364     y2 = Surface->h - 1;
1365
1366   l.x = x;
1367   l.y = y1;
1368   l.w = 1;
1369   l.h = y2 - y1 + 1;
1370
1371   SDL_FillRect(Surface, &l, Color);
1372
1373   if (SDL_MUSTLOCK(Surface))
1374   {
1375     SDL_UnlockSurface(Surface);
1376   }
1377 }
1378
1379 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1380                   Uint8 R, Uint8 G, Uint8 B)
1381 {
1382   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1383 }
1384
1385 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1386 {
1387   SDL_Rect l;
1388
1389   if (y1 > y2)
1390   {
1391     Sint16 tmp = y1;
1392     y1 = y2;
1393     y2 = tmp;
1394   }
1395
1396   /* Do the clipping */
1397   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1398     return;
1399   if (y1 < 0)
1400     y1 = 0;
1401   if (y2 > Surface->h - 1)
1402     y2 = Surface->h - 1;
1403
1404   l.x = x;
1405   l.y = y1;
1406   l.w = 1;
1407   l.h = y2 - y1 + 1;
1408
1409   SDL_FillRect(Surface, &l, Color);
1410 }
1411
1412 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1413                 Sint16 x2, Sint16 y2, Uint32 Color,
1414                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1415                               Uint32 Color))
1416 {
1417   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1418
1419   dx = x2 - x1;
1420   dy = y2 - y1;
1421
1422   sdx = (dx < 0) ? -1 : 1;
1423   sdy = (dy < 0) ? -1 : 1;
1424
1425   dx = sdx * dx + 1;
1426   dy = sdy * dy + 1;
1427
1428   x = y = 0;
1429
1430   px = x1;
1431   py = y1;
1432
1433   if (dx >= dy)
1434   {
1435     for (x = 0; x < dx; x++)
1436     {
1437       Callback(Surface, px, py, Color);
1438
1439       y += dy;
1440       if (y >= dx)
1441       {
1442         y -= dx;
1443         py += sdy;
1444       }
1445
1446       px += sdx;
1447     }
1448   }
1449   else
1450   {
1451     for (y = 0; y < dy; y++)
1452     {
1453       Callback(Surface, px, py, Color);
1454
1455       x += dx;
1456       if (x >= dy)
1457       {
1458         x -= dy;
1459         px += sdx;
1460       }
1461
1462       py += sdy;
1463     }
1464   }
1465 }
1466
1467 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1468                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1469                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1470                                  Uint32 Color))
1471 {
1472   sge_DoLine(Surface, X1, Y1, X2, Y2,
1473              SDL_MapRGB(Surface->format, R, G, B), Callback);
1474 }
1475
1476 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1477               Uint32 Color)
1478 {
1479   if (SDL_MUSTLOCK(Surface))
1480   {
1481     if (SDL_LockSurface(Surface) < 0)
1482       return;
1483    }
1484
1485    /* Draw the line */
1486    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1487
1488    /* unlock the display */
1489    if (SDL_MUSTLOCK(Surface))
1490    {
1491       SDL_UnlockSurface(Surface);
1492    }
1493 }
1494
1495 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1496                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1497 {
1498   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1499 }
1500
1501 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1502 {
1503   if (dst_bitmap == backbuffer || dst_bitmap == window)
1504   {
1505     x += video_xoffset;
1506     y += video_yoffset;
1507   }
1508
1509   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1510 }
1511
1512
1513 /*
1514   -----------------------------------------------------------------------------
1515   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1516   -----------------------------------------------------------------------------
1517 */
1518
1519 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1520                    int width, int height, Uint32 color)
1521 {
1522   int x, y;
1523
1524   for (y = src_y; y < src_y + height; y++)
1525   {
1526     for (x = src_x; x < src_x + width; x++)
1527     {
1528       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1529
1530       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1531     }
1532   }
1533 }
1534
1535 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1536                           int src_x, int src_y, int width, int height,
1537                           int dst_x, int dst_y)
1538 {
1539   int x, y;
1540
1541   for (y = 0; y < height; y++)
1542   {
1543     for (x = 0; x < width; x++)
1544     {
1545       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1546
1547       if (pixel != BLACK_PIXEL)
1548         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1549     }
1550   }
1551 }
1552
1553
1554 /* ========================================================================= */
1555 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1556 /* (Rotozoomer) by Andreas Schiffler                                         */
1557 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1558 /* ========================================================================= */
1559
1560 /*
1561   -----------------------------------------------------------------------------
1562   32 bit zoomer
1563
1564   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1565   -----------------------------------------------------------------------------
1566 */
1567
1568 typedef struct
1569 {
1570   Uint8 r;
1571   Uint8 g;
1572   Uint8 b;
1573   Uint8 a;
1574 } tColorRGBA;
1575
1576 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1577 {
1578   int x, y;
1579   tColorRGBA *sp, *csp, *dp;
1580 #if 0
1581   int sgap;
1582 #endif
1583   int dgap;
1584
1585   /* pointer setup */
1586   sp = csp = (tColorRGBA *) src->pixels;
1587   dp = (tColorRGBA *) dst->pixels;
1588 #if 0
1589   sgap = src->pitch - src->w * 4;
1590 #endif
1591   dgap = dst->pitch - dst->w * 4;
1592
1593   for (y = 0; y < dst->h; y++)
1594   {
1595     sp = csp;
1596
1597     for (x = 0; x < dst->w; x++)
1598     {
1599       tColorRGBA *sp0 = sp;
1600       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1601       tColorRGBA *sp00 = &sp0[0];
1602       tColorRGBA *sp01 = &sp0[1];
1603       tColorRGBA *sp10 = &sp1[0];
1604       tColorRGBA *sp11 = &sp1[1];
1605       tColorRGBA new;
1606
1607       /* create new color pixel from all four source color pixels */
1608       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1609       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1610       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1611       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1612
1613       /* draw */
1614       *dp = new;
1615
1616       /* advance source pointers */
1617       sp += 2;
1618
1619       /* advance destination pointer */
1620       dp++;
1621     }
1622
1623     /* advance source pointer */
1624     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1625
1626     /* advance destination pointers */
1627     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1628   }
1629
1630   return 0;
1631 }
1632
1633 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1634 {
1635   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1636   tColorRGBA *sp, *csp, *dp;
1637 #if 0
1638   int sgap;
1639 #endif
1640   int dgap;
1641
1642   /* use specialized zoom function when scaling down to exactly half size */
1643   if (src->w == 2 * dst->w &&
1644       src->h == 2 * dst->h)
1645     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1646
1647   /* variable setup */
1648   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1649   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1650
1651   /* allocate memory for row increments */
1652   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1653   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1654
1655   /* precalculate row increments */
1656   csx = 0;
1657   csax = sax;
1658   for (x = 0; x <= dst->w; x++)
1659   {
1660     *csax = csx;
1661     csax++;
1662     csx &= 0xffff;
1663     csx += sx;
1664   }
1665
1666   csy = 0;
1667   csay = say;
1668   for (y = 0; y <= dst->h; y++)
1669   {
1670     *csay = csy;
1671     csay++;
1672     csy &= 0xffff;
1673     csy += sy;
1674   }
1675
1676   /* pointer setup */
1677   sp = csp = (tColorRGBA *) src->pixels;
1678   dp = (tColorRGBA *) dst->pixels;
1679 #if 0
1680   sgap = src->pitch - src->w * 4;
1681 #endif
1682   dgap = dst->pitch - dst->w * 4;
1683
1684   csay = say;
1685   for (y = 0; y < dst->h; y++)
1686   {
1687     sp = csp;
1688     csax = sax;
1689
1690     for (x = 0; x < dst->w; x++)
1691     {
1692       /* draw */
1693       *dp = *sp;
1694
1695       /* advance source pointers */
1696       csax++;
1697       sp += (*csax >> 16);
1698
1699       /* advance destination pointer */
1700       dp++;
1701     }
1702
1703     /* advance source pointer */
1704     csay++;
1705     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1706
1707     /* advance destination pointers */
1708     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1709   }
1710
1711   free(sax);
1712   free(say);
1713
1714   return 0;
1715 }
1716
1717 /*
1718   -----------------------------------------------------------------------------
1719   8 bit zoomer
1720
1721   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1722   -----------------------------------------------------------------------------
1723 */
1724
1725 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1726 {
1727   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1728   Uint8 *sp, *dp, *csp;
1729   int dgap;
1730
1731   /* variable setup */
1732   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1733   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1734
1735   /* allocate memory for row increments */
1736   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1737   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1738
1739   /* precalculate row increments */
1740   csx = 0;
1741   csax = sax;
1742   for (x = 0; x < dst->w; x++)
1743   {
1744     csx += sx;
1745     *csax = (csx >> 16);
1746     csx &= 0xffff;
1747     csax++;
1748   }
1749
1750   csy = 0;
1751   csay = say;
1752   for (y = 0; y < dst->h; y++)
1753   {
1754     csy += sy;
1755     *csay = (csy >> 16);
1756     csy &= 0xffff;
1757     csay++;
1758   }
1759
1760   csx = 0;
1761   csax = sax;
1762   for (x = 0; x < dst->w; x++)
1763   {
1764     csx += (*csax);
1765     csax++;
1766   }
1767
1768   csy = 0;
1769   csay = say;
1770   for (y = 0; y < dst->h; y++)
1771   {
1772     csy += (*csay);
1773     csay++;
1774   }
1775
1776   /* pointer setup */
1777   sp = csp = (Uint8 *) src->pixels;
1778   dp = (Uint8 *) dst->pixels;
1779   dgap = dst->pitch - dst->w;
1780
1781   /* draw */
1782   csay = say;
1783   for (y = 0; y < dst->h; y++)
1784   {
1785     csax = sax;
1786     sp = csp;
1787     for (x = 0; x < dst->w; x++)
1788     {
1789       /* draw */
1790       *dp = *sp;
1791
1792       /* advance source pointers */
1793       sp += (*csax);
1794       csax++;
1795
1796       /* advance destination pointer */
1797       dp++;
1798     }
1799
1800     /* advance source pointer (for row) */
1801     csp += ((*csay) * src->pitch);
1802     csay++;
1803
1804     /* advance destination pointers */
1805     dp += dgap;
1806   }
1807
1808   free(sax);
1809   free(say);
1810
1811   return 0;
1812 }
1813
1814 /*
1815   -----------------------------------------------------------------------------
1816   zoomSurface()
1817
1818   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1819   'zoomx' and 'zoomy' are scaling factors for width and height.
1820   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1821   into a 32bit RGBA format on the fly.
1822   -----------------------------------------------------------------------------
1823 */
1824
1825 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1826 {
1827   SDL_Surface *zoom_src = NULL;
1828   SDL_Surface *zoom_dst = NULL;
1829   boolean is_converted = FALSE;
1830   boolean is_32bit;
1831   int i;
1832
1833   if (src == NULL)
1834     return NULL;
1835
1836   /* determine if source surface is 32 bit or 8 bit */
1837   is_32bit = (src->format->BitsPerPixel == 32);
1838
1839   if (is_32bit || src->format->BitsPerPixel == 8)
1840   {
1841     /* use source surface 'as is' */
1842     zoom_src = src;
1843   }
1844   else
1845   {
1846     /* new source surface is 32 bit with a defined RGB ordering */
1847     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1848                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1849     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1850     is_32bit = TRUE;
1851     is_converted = TRUE;
1852   }
1853
1854   /* allocate surface to completely contain the zoomed surface */
1855   if (is_32bit)
1856   {
1857     /* target surface is 32 bit with source RGBA/ABGR ordering */
1858     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1859                                     zoom_src->format->Rmask,
1860                                     zoom_src->format->Gmask,
1861                                     zoom_src->format->Bmask, 0);
1862   }
1863   else
1864   {
1865     /* target surface is 8 bit */
1866     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1867                                     0, 0, 0, 0);
1868   }
1869
1870   /* lock source surface */
1871   SDL_LockSurface(zoom_src);
1872
1873   /* check which kind of surface we have */
1874   if (is_32bit)
1875   {
1876     /* call the 32 bit transformation routine to do the zooming */
1877     zoomSurfaceRGBA(zoom_src, zoom_dst);
1878   }
1879   else
1880   {
1881     /* copy palette */
1882     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1883       zoom_dst->format->palette->colors[i] =
1884         zoom_src->format->palette->colors[i];
1885     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1886
1887     /* call the 8 bit transformation routine to do the zooming */
1888     zoomSurfaceY(zoom_src, zoom_dst);
1889   }
1890
1891   /* unlock source surface */
1892   SDL_UnlockSurface(zoom_src);
1893
1894   /* free temporary surface */
1895   if (is_converted)
1896     SDL_FreeSurface(zoom_src);
1897
1898   /* return destination surface */
1899   return zoom_dst;
1900 }
1901
1902 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1903 {
1904   SDL_Surface *sdl_surface_tmp;
1905   int dst_width = dst_bitmap->width;
1906   int dst_height = dst_bitmap->height;
1907
1908   /* throw away old destination surface */
1909   SDL_FreeSurface(dst_bitmap->surface);
1910
1911   /* create zoomed temporary surface from source surface */
1912   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1913
1914   /* create native format destination surface from zoomed temporary surface */
1915   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1916
1917   /* free temporary surface */
1918   SDL_FreeSurface(sdl_surface_tmp);
1919 }
1920
1921
1922 /* ========================================================================= */
1923 /* load image to bitmap                                                      */
1924 /* ========================================================================= */
1925
1926 Bitmap *SDLLoadImage(char *filename)
1927 {
1928   Bitmap *new_bitmap = CreateBitmapStruct();
1929   SDL_Surface *sdl_image_tmp;
1930
1931   print_timestamp_init("SDLLoadImage");
1932
1933   print_timestamp_time(getBaseNamePtr(filename));
1934
1935   /* load image to temporary surface */
1936   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1937   {
1938     SetError("IMG_Load(): %s", SDL_GetError());
1939
1940     return NULL;
1941   }
1942
1943   print_timestamp_time("IMG_Load");
1944
1945   UPDATE_BUSY_STATE();
1946
1947   /* create native non-transparent surface for current image */
1948   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1949   {
1950     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1951
1952     return NULL;
1953   }
1954
1955   print_timestamp_time("SDL_DisplayFormat (opaque)");
1956
1957   UPDATE_BUSY_STATE();
1958
1959   /* create native transparent surface for current image */
1960   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
1961                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1962   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1963   {
1964     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1965
1966     return NULL;
1967   }
1968
1969   print_timestamp_time("SDL_DisplayFormat (masked)");
1970
1971   UPDATE_BUSY_STATE();
1972
1973   /* free temporary surface */
1974   SDL_FreeSurface(sdl_image_tmp);
1975
1976   new_bitmap->width = new_bitmap->surface->w;
1977   new_bitmap->height = new_bitmap->surface->h;
1978
1979   print_timestamp_done("SDLLoadImage");
1980
1981   return new_bitmap;
1982 }
1983
1984
1985 /* ------------------------------------------------------------------------- */
1986 /* custom cursor fuctions                                                    */
1987 /* ------------------------------------------------------------------------- */
1988
1989 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1990 {
1991   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1992                           cursor_info->width, cursor_info->height,
1993                           cursor_info->hot_x, cursor_info->hot_y);
1994 }
1995
1996 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1997 {
1998   static struct MouseCursorInfo *last_cursor_info = NULL;
1999   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2000   static SDL_Cursor *cursor_default = NULL;
2001   static SDL_Cursor *cursor_current = NULL;
2002
2003   /* if invoked for the first time, store the SDL default cursor */
2004   if (cursor_default == NULL)
2005     cursor_default = SDL_GetCursor();
2006
2007   /* only create new cursor if cursor info (custom only) has changed */
2008   if (cursor_info != NULL && cursor_info != last_cursor_info)
2009   {
2010     cursor_current = create_cursor(cursor_info);
2011     last_cursor_info = cursor_info;
2012   }
2013
2014   /* only set new cursor if cursor info (custom or NULL) has changed */
2015   if (cursor_info != last_cursor_info2)
2016     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2017
2018   last_cursor_info2 = cursor_info;
2019 }
2020
2021
2022 /* ========================================================================= */
2023 /* audio functions                                                           */
2024 /* ========================================================================= */
2025
2026 void SDLOpenAudio(void)
2027 {
2028 #if !defined(TARGET_SDL2)
2029   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2030     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2031 #endif
2032
2033   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2034   {
2035     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2036     return;
2037   }
2038
2039   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2040                     AUDIO_NUM_CHANNELS_STEREO,
2041                     setup.system.audio_fragment_size) < 0)
2042   {
2043     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2044     return;
2045   }
2046
2047   audio.sound_available = TRUE;
2048   audio.music_available = TRUE;
2049   audio.loops_available = TRUE;
2050   audio.sound_enabled = TRUE;
2051
2052   /* set number of available mixer channels */
2053   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2054   audio.music_channel = MUSIC_CHANNEL;
2055   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2056
2057   Mixer_InitChannels();
2058 }
2059
2060 void SDLCloseAudio(void)
2061 {
2062   Mix_HaltMusic();
2063   Mix_HaltChannel(-1);
2064
2065   Mix_CloseAudio();
2066   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2067 }
2068
2069
2070 /* ========================================================================= */
2071 /* event functions                                                           */
2072 /* ========================================================================= */
2073
2074 void SDLNextEvent(Event *event)
2075 {
2076   SDL_WaitEvent(event);
2077
2078   if (event->type == EVENT_BUTTONPRESS ||
2079       event->type == EVENT_BUTTONRELEASE)
2080   {
2081     if (((ButtonEvent *)event)->x > video_xoffset)
2082       ((ButtonEvent *)event)->x -= video_xoffset;
2083     else
2084       ((ButtonEvent *)event)->x = 0;
2085     if (((ButtonEvent *)event)->y > video_yoffset)
2086       ((ButtonEvent *)event)->y -= video_yoffset;
2087     else
2088       ((ButtonEvent *)event)->y = 0;
2089   }
2090   else if (event->type == EVENT_MOTIONNOTIFY)
2091   {
2092     if (((MotionEvent *)event)->x > video_xoffset)
2093       ((MotionEvent *)event)->x -= video_xoffset;
2094     else
2095       ((MotionEvent *)event)->x = 0;
2096     if (((MotionEvent *)event)->y > video_yoffset)
2097       ((MotionEvent *)event)->y -= video_yoffset;
2098     else
2099       ((MotionEvent *)event)->y = 0;
2100   }
2101 }
2102
2103 void SDLHandleWindowManagerEvent(Event *event)
2104 {
2105 #if defined(PLATFORM_WIN32)
2106   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2107   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2108
2109   if (syswmmsg->msg == WM_DROPFILES)
2110   {
2111     HDROP hdrop = (HDROP)syswmmsg->wParam;
2112     int i, num_files;
2113
2114     printf("::: SDL_SYSWMEVENT:\n");
2115
2116     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2117
2118     for (i = 0; i < num_files; i++)
2119     {
2120       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2121       char buffer[buffer_len + 1];
2122
2123       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2124
2125       printf("::: - '%s'\n", buffer);
2126     }
2127
2128     DragFinish((HDROP)syswmmsg->wParam);
2129   }
2130 #endif
2131 }
2132
2133
2134 /* ========================================================================= */
2135 /* joystick functions                                                        */
2136 /* ========================================================================= */
2137
2138 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2139 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2140 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2141
2142 static boolean SDLOpenJoystick(int nr)
2143 {
2144   if (nr < 0 || nr > MAX_PLAYERS)
2145     return FALSE;
2146
2147   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2148 }
2149
2150 static void SDLCloseJoystick(int nr)
2151 {
2152   if (nr < 0 || nr > MAX_PLAYERS)
2153     return;
2154
2155   SDL_JoystickClose(sdl_joystick[nr]);
2156
2157   sdl_joystick[nr] = NULL;
2158 }
2159
2160 static boolean SDLCheckJoystickOpened(int nr)
2161 {
2162   if (nr < 0 || nr > MAX_PLAYERS)
2163     return FALSE;
2164
2165 #if defined(TARGET_SDL2)
2166   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2167 #else
2168   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2169 #endif
2170 }
2171
2172 void HandleJoystickEvent(Event *event)
2173 {
2174   switch(event->type)
2175   {
2176     case SDL_JOYAXISMOTION:
2177       if (event->jaxis.axis < 2)
2178         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2179       break;
2180
2181     case SDL_JOYBUTTONDOWN:
2182       if (event->jbutton.button < 2)
2183         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2184       break;
2185
2186     case SDL_JOYBUTTONUP:
2187       if (event->jbutton.button < 2)
2188         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2189       break;
2190
2191     default:
2192       break;
2193   }
2194 }
2195
2196 void SDLInitJoysticks()
2197 {
2198   static boolean sdl_joystick_subsystem_initialized = FALSE;
2199   boolean print_warning = !sdl_joystick_subsystem_initialized;
2200   int i;
2201
2202   if (!sdl_joystick_subsystem_initialized)
2203   {
2204     sdl_joystick_subsystem_initialized = TRUE;
2205
2206     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2207     {
2208       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2209       return;
2210     }
2211   }
2212
2213   for (i = 0; i < MAX_PLAYERS; i++)
2214   {
2215     /* get configured joystick for this player */
2216     char *device_name = setup.input[i].joy.device_name;
2217     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2218
2219     if (joystick_nr >= SDL_NumJoysticks())
2220     {
2221       if (setup.input[i].use_joystick && print_warning)
2222         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2223
2224       joystick_nr = -1;
2225     }
2226
2227     /* misuse joystick file descriptor variable to store joystick number */
2228     joystick.fd[i] = joystick_nr;
2229
2230     if (joystick_nr == -1)
2231       continue;
2232
2233     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2234     if (SDLCheckJoystickOpened(joystick_nr))
2235       SDLCloseJoystick(joystick_nr);
2236
2237     if (!setup.input[i].use_joystick)
2238       continue;
2239
2240     if (!SDLOpenJoystick(joystick_nr))
2241     {
2242       if (print_warning)
2243         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2244
2245       continue;
2246     }
2247
2248     joystick.status = JOYSTICK_ACTIVATED;
2249   }
2250 }
2251
2252 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2253 {
2254   if (nr < 0 || nr >= MAX_PLAYERS)
2255     return FALSE;
2256
2257   if (x != NULL)
2258     *x = sdl_js_axis[nr][0];
2259   if (y != NULL)
2260     *y = sdl_js_axis[nr][1];
2261
2262   if (b1 != NULL)
2263     *b1 = sdl_js_button[nr][0];
2264   if (b2 != NULL)
2265     *b2 = sdl_js_button[nr][1];
2266
2267   return TRUE;
2268 }
2269
2270 #endif /* TARGET_SDL */