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