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