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