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