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