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