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