fixed switching from fullscreen to window when using different screen sizes
[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
1205   {
1206     float alpha;
1207     int alpha_final;
1208
1209     for (alpha = 0.0; alpha < 255.0;)
1210     {
1211       time_last = time_current;
1212       time_current = SDL_GetTicks();
1213       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1214       alpha_final = MIN(MAX(0, alpha), 255);
1215
1216       /* draw existing (source) image to screen buffer */
1217       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1218
1219       /* draw new (target) image to screen buffer using alpha blending */
1220 #if defined(TARGET_SDL2)
1221       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
1222       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
1223 #else
1224       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
1225 #endif
1226       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1227
1228       if (draw_border_function != NULL)
1229         draw_border_function();
1230
1231       /* only update the region of the screen that is affected from fading */
1232       UpdateScreen(&dst_rect);
1233     }
1234   }
1235
1236   Delay(post_delay);
1237 }
1238
1239 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1240                        int to_x, int to_y, Uint32 color)
1241 {
1242   SDL_Surface *surface = dst_bitmap->surface;
1243   SDL_Rect rect;
1244
1245   if (from_x > to_x)
1246     swap_numbers(&from_x, &to_x);
1247
1248   if (from_y > to_y)
1249     swap_numbers(&from_y, &to_y);
1250
1251   rect.x = from_x;
1252   rect.y = from_y;
1253   rect.w = (to_x - from_x + 1);
1254   rect.h = (to_y - from_y + 1);
1255
1256   if (dst_bitmap == backbuffer || dst_bitmap == window)
1257   {
1258     rect.x += video_xoffset;
1259     rect.y += video_yoffset;
1260   }
1261
1262   SDL_FillRect(surface, &rect, color);
1263 }
1264
1265 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1266                  int to_x, int to_y, Uint32 color)
1267 {
1268   if (dst_bitmap == backbuffer || dst_bitmap == window)
1269   {
1270     from_x += video_xoffset;
1271     from_y += video_yoffset;
1272     to_x += video_xoffset;
1273     to_y += video_yoffset;
1274   }
1275
1276   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1277 }
1278
1279 #if ENABLE_UNUSED_CODE
1280 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1281                   int num_points, Uint32 color)
1282 {
1283   int i, x, y;
1284   int line_width = 4;
1285
1286   for (i = 0; i < num_points - 1; i++)
1287   {
1288     for (x = 0; x < line_width; x++)
1289     {
1290       for (y = 0; y < line_width; y++)
1291       {
1292         int dx = x - line_width / 2;
1293         int dy = y - line_width / 2;
1294
1295         if ((x == 0 && y == 0) ||
1296             (x == 0 && y == line_width - 1) ||
1297             (x == line_width - 1 && y == 0) ||
1298             (x == line_width - 1 && y == line_width - 1))
1299           continue;
1300
1301         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1302                  points[i+1].x + dx, points[i+1].y + dy, color);
1303       }
1304     }
1305   }
1306 }
1307 #endif
1308
1309 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1310 {
1311   SDL_Surface *surface = src_bitmap->surface;
1312
1313   if (src_bitmap == backbuffer || src_bitmap == window)
1314   {
1315     x += video_xoffset;
1316     y += video_yoffset;
1317   }
1318
1319   switch (surface->format->BytesPerPixel)
1320   {
1321     case 1:             /* assuming 8-bpp */
1322     {
1323       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1324     }
1325     break;
1326
1327     case 2:             /* probably 15-bpp or 16-bpp */
1328     {
1329       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1330     }
1331     break;
1332
1333   case 3:               /* slow 24-bpp mode; usually not used */
1334     {
1335       /* does this work? */
1336       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1337       Uint32 color = 0;
1338       int shift;
1339
1340       shift = surface->format->Rshift;
1341       color |= *(pix + shift / 8) >> shift;
1342       shift = surface->format->Gshift;
1343       color |= *(pix + shift / 8) >> shift;
1344       shift = surface->format->Bshift;
1345       color |= *(pix + shift / 8) >> shift;
1346
1347       return color;
1348     }
1349     break;
1350
1351   case 4:               /* probably 32-bpp */
1352     {
1353       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1354     }
1355     break;
1356   }
1357
1358   return 0;
1359 }
1360
1361
1362 /* ========================================================================= */
1363 /* The following functions were taken from the SGE library                   */
1364 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1365 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1366 /* ========================================================================= */
1367
1368 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1369 {
1370   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1371   {
1372     switch (surface->format->BytesPerPixel)
1373     {
1374       case 1:
1375       {
1376         /* Assuming 8-bpp */
1377         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1378       }
1379       break;
1380
1381       case 2:
1382       {
1383         /* Probably 15-bpp or 16-bpp */
1384         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1385       }
1386       break;
1387
1388       case 3:
1389       {
1390         /* Slow 24-bpp mode, usually not used */
1391         Uint8 *pix;
1392         int shift;
1393
1394         /* Gack - slow, but endian correct */
1395         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1396         shift = surface->format->Rshift;
1397         *(pix+shift/8) = color>>shift;
1398         shift = surface->format->Gshift;
1399         *(pix+shift/8) = color>>shift;
1400         shift = surface->format->Bshift;
1401         *(pix+shift/8) = color>>shift;
1402       }
1403       break;
1404
1405       case 4:
1406       {
1407         /* Probably 32-bpp */
1408         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1409       }
1410       break;
1411     }
1412   }
1413 }
1414
1415 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1416                   Uint8 R, Uint8 G, Uint8 B)
1417 {
1418   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1419 }
1420
1421 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1422 {
1423   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1424 }
1425
1426 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1427 {
1428   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1429 }
1430
1431 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1432 {
1433   Uint8 *pix;
1434   int shift;
1435
1436   /* Gack - slow, but endian correct */
1437   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1438   shift = surface->format->Rshift;
1439   *(pix+shift/8) = color>>shift;
1440   shift = surface->format->Gshift;
1441   *(pix+shift/8) = color>>shift;
1442   shift = surface->format->Bshift;
1443   *(pix+shift/8) = color>>shift;
1444 }
1445
1446 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1447 {
1448   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1449 }
1450
1451 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1452 {
1453   switch (dest->format->BytesPerPixel)
1454   {
1455     case 1:
1456       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1457       break;
1458
1459     case 2:
1460       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1461       break;
1462
1463     case 3:
1464       _PutPixel24(dest,x,y,color);
1465       break;
1466
1467     case 4:
1468       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1469       break;
1470   }
1471 }
1472
1473 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1474 {
1475   if (SDL_MUSTLOCK(surface))
1476   {
1477     if (SDL_LockSurface(surface) < 0)
1478     {
1479       return;
1480     }
1481   }
1482
1483   _PutPixel(surface, x, y, color);
1484
1485   if (SDL_MUSTLOCK(surface))
1486   {
1487     SDL_UnlockSurface(surface);
1488   }
1489 }
1490
1491 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1492                   Uint8 r, Uint8 g, Uint8 b)
1493 {
1494   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1495 }
1496
1497 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1498 {
1499   if (y >= 0 && y <= dest->h - 1)
1500   {
1501     switch (dest->format->BytesPerPixel)
1502     {
1503       case 1:
1504         return y*dest->pitch;
1505         break;
1506
1507       case 2:
1508         return y*dest->pitch/2;
1509         break;
1510
1511       case 3:
1512         return y*dest->pitch;
1513         break;
1514
1515       case 4:
1516         return y*dest->pitch/4;
1517         break;
1518     }
1519   }
1520
1521   return -1;
1522 }
1523
1524 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1525 {
1526   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1527   {
1528     switch (surface->format->BytesPerPixel)
1529     {
1530       case 1:
1531       {
1532         /* Assuming 8-bpp */
1533         *((Uint8 *)surface->pixels + ypitch + x) = color;
1534       }
1535       break;
1536
1537       case 2:
1538       {
1539         /* Probably 15-bpp or 16-bpp */
1540         *((Uint16 *)surface->pixels + ypitch + x) = color;
1541       }
1542       break;
1543
1544       case 3:
1545       {
1546         /* Slow 24-bpp mode, usually not used */
1547         Uint8 *pix;
1548         int shift;
1549
1550         /* Gack - slow, but endian correct */
1551         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1552         shift = surface->format->Rshift;
1553         *(pix+shift/8) = color>>shift;
1554         shift = surface->format->Gshift;
1555         *(pix+shift/8) = color>>shift;
1556         shift = surface->format->Bshift;
1557         *(pix+shift/8) = color>>shift;
1558       }
1559       break;
1560
1561       case 4:
1562       {
1563         /* Probably 32-bpp */
1564         *((Uint32 *)surface->pixels + ypitch + x) = color;
1565       }
1566       break;
1567     }
1568   }
1569 }
1570
1571 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1572                Uint32 Color)
1573 {
1574   SDL_Rect l;
1575
1576   if (SDL_MUSTLOCK(Surface))
1577   {
1578     if (SDL_LockSurface(Surface) < 0)
1579     {
1580       return;
1581     }
1582   }
1583
1584   if (x1 > x2)
1585   {
1586     Sint16 tmp = x1;
1587     x1 = x2;
1588     x2 = tmp;
1589   }
1590
1591   /* Do the clipping */
1592   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1593     return;
1594   if (x1 < 0)
1595     x1 = 0;
1596   if (x2 > Surface->w - 1)
1597     x2 = Surface->w - 1;
1598
1599   l.x = x1;
1600   l.y = y;
1601   l.w = x2 - x1 + 1;
1602   l.h = 1;
1603
1604   SDL_FillRect(Surface, &l, Color);
1605
1606   if (SDL_MUSTLOCK(Surface))
1607   {
1608     SDL_UnlockSurface(Surface);
1609   }
1610 }
1611
1612 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1613                   Uint8 R, Uint8 G, Uint8 B)
1614 {
1615   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1616 }
1617
1618 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1619 {
1620   SDL_Rect l;
1621
1622   if (x1 > x2)
1623   {
1624     Sint16 tmp = x1;
1625     x1 = x2;
1626     x2 = tmp;
1627   }
1628
1629   /* Do the clipping */
1630   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1631     return;
1632   if (x1 < 0)
1633     x1 = 0;
1634   if (x2 > Surface->w - 1)
1635     x2 = Surface->w - 1;
1636
1637   l.x = x1;
1638   l.y = y;
1639   l.w = x2 - x1 + 1;
1640   l.h = 1;
1641
1642   SDL_FillRect(Surface, &l, Color);
1643 }
1644
1645 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1646                Uint32 Color)
1647 {
1648   SDL_Rect l;
1649
1650   if (SDL_MUSTLOCK(Surface))
1651   {
1652     if (SDL_LockSurface(Surface) < 0)
1653     {
1654       return;
1655     }
1656   }
1657
1658   if (y1 > y2)
1659   {
1660     Sint16 tmp = y1;
1661     y1 = y2;
1662     y2 = tmp;
1663   }
1664
1665   /* Do the clipping */
1666   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1667     return;
1668   if (y1 < 0)
1669     y1 = 0;
1670   if (y2 > Surface->h - 1)
1671     y2 = Surface->h - 1;
1672
1673   l.x = x;
1674   l.y = y1;
1675   l.w = 1;
1676   l.h = y2 - y1 + 1;
1677
1678   SDL_FillRect(Surface, &l, Color);
1679
1680   if (SDL_MUSTLOCK(Surface))
1681   {
1682     SDL_UnlockSurface(Surface);
1683   }
1684 }
1685
1686 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1687                   Uint8 R, Uint8 G, Uint8 B)
1688 {
1689   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1690 }
1691
1692 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1693 {
1694   SDL_Rect l;
1695
1696   if (y1 > y2)
1697   {
1698     Sint16 tmp = y1;
1699     y1 = y2;
1700     y2 = tmp;
1701   }
1702
1703   /* Do the clipping */
1704   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1705     return;
1706   if (y1 < 0)
1707     y1 = 0;
1708   if (y2 > Surface->h - 1)
1709     y2 = Surface->h - 1;
1710
1711   l.x = x;
1712   l.y = y1;
1713   l.w = 1;
1714   l.h = y2 - y1 + 1;
1715
1716   SDL_FillRect(Surface, &l, Color);
1717 }
1718
1719 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1720                 Sint16 x2, Sint16 y2, Uint32 Color,
1721                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1722                               Uint32 Color))
1723 {
1724   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1725
1726   dx = x2 - x1;
1727   dy = y2 - y1;
1728
1729   sdx = (dx < 0) ? -1 : 1;
1730   sdy = (dy < 0) ? -1 : 1;
1731
1732   dx = sdx * dx + 1;
1733   dy = sdy * dy + 1;
1734
1735   x = y = 0;
1736
1737   px = x1;
1738   py = y1;
1739
1740   if (dx >= dy)
1741   {
1742     for (x = 0; x < dx; x++)
1743     {
1744       Callback(Surface, px, py, Color);
1745
1746       y += dy;
1747       if (y >= dx)
1748       {
1749         y -= dx;
1750         py += sdy;
1751       }
1752
1753       px += sdx;
1754     }
1755   }
1756   else
1757   {
1758     for (y = 0; y < dy; y++)
1759     {
1760       Callback(Surface, px, py, Color);
1761
1762       x += dx;
1763       if (x >= dy)
1764       {
1765         x -= dy;
1766         px += sdx;
1767       }
1768
1769       py += sdy;
1770     }
1771   }
1772 }
1773
1774 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1775                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1776                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1777                                  Uint32 Color))
1778 {
1779   sge_DoLine(Surface, X1, Y1, X2, Y2,
1780              SDL_MapRGB(Surface->format, R, G, B), Callback);
1781 }
1782
1783 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1784               Uint32 Color)
1785 {
1786   if (SDL_MUSTLOCK(Surface))
1787   {
1788     if (SDL_LockSurface(Surface) < 0)
1789       return;
1790    }
1791
1792    /* Draw the line */
1793    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1794
1795    /* unlock the display */
1796    if (SDL_MUSTLOCK(Surface))
1797    {
1798       SDL_UnlockSurface(Surface);
1799    }
1800 }
1801
1802 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1803                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1804 {
1805   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1806 }
1807
1808 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1809 {
1810   if (dst_bitmap == backbuffer || dst_bitmap == window)
1811   {
1812     x += video_xoffset;
1813     y += video_yoffset;
1814   }
1815
1816   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1817 }
1818
1819
1820 /*
1821   -----------------------------------------------------------------------------
1822   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1823   -----------------------------------------------------------------------------
1824 */
1825
1826 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1827                    int width, int height, Uint32 color)
1828 {
1829   int x, y;
1830
1831   for (y = src_y; y < src_y + height; y++)
1832   {
1833     for (x = src_x; x < src_x + width; x++)
1834     {
1835       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1836
1837       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1838     }
1839   }
1840 }
1841
1842 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1843                           int src_x, int src_y, int width, int height,
1844                           int dst_x, int dst_y)
1845 {
1846   int x, y;
1847
1848   for (y = 0; y < height; y++)
1849   {
1850     for (x = 0; x < width; x++)
1851     {
1852       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1853
1854       if (pixel != BLACK_PIXEL)
1855         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1856     }
1857   }
1858 }
1859
1860
1861 /* ========================================================================= */
1862 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1863 /* (Rotozoomer) by Andreas Schiffler                                         */
1864 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1865 /* ========================================================================= */
1866
1867 /*
1868   -----------------------------------------------------------------------------
1869   32 bit zoomer
1870
1871   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1872   -----------------------------------------------------------------------------
1873 */
1874
1875 typedef struct
1876 {
1877   Uint8 r;
1878   Uint8 g;
1879   Uint8 b;
1880   Uint8 a;
1881 } tColorRGBA;
1882
1883 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1884 {
1885   int x, y;
1886   tColorRGBA *sp, *csp, *dp;
1887   int dgap;
1888
1889   /* pointer setup */
1890   sp = csp = (tColorRGBA *) src->pixels;
1891   dp = (tColorRGBA *) dst->pixels;
1892   dgap = dst->pitch - dst->w * 4;
1893
1894   for (y = 0; y < dst->h; y++)
1895   {
1896     sp = csp;
1897
1898     for (x = 0; x < dst->w; x++)
1899     {
1900       tColorRGBA *sp0 = sp;
1901       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1902       tColorRGBA *sp00 = &sp0[0];
1903       tColorRGBA *sp01 = &sp0[1];
1904       tColorRGBA *sp10 = &sp1[0];
1905       tColorRGBA *sp11 = &sp1[1];
1906       tColorRGBA new;
1907
1908       /* create new color pixel from all four source color pixels */
1909       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1910       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1911       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1912       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1913
1914       /* draw */
1915       *dp = new;
1916
1917       /* advance source pointers */
1918       sp += 2;
1919
1920       /* advance destination pointer */
1921       dp++;
1922     }
1923
1924     /* advance source pointer */
1925     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1926
1927     /* advance destination pointers */
1928     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1929   }
1930
1931   return 0;
1932 }
1933
1934 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1935 {
1936   int x, y, *sax, *say, *csax, *csay;
1937   float sx, sy;
1938   tColorRGBA *sp, *csp, *csp0, *dp;
1939   int dgap;
1940
1941   /* use specialized zoom function when scaling down to exactly half size */
1942   if (src->w == 2 * dst->w &&
1943       src->h == 2 * dst->h)
1944     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1945
1946   /* variable setup */
1947   sx = (float) src->w / (float) dst->w;
1948   sy = (float) src->h / (float) dst->h;
1949
1950   /* allocate memory for row increments */
1951   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1952   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1953
1954   /* precalculate row increments */
1955   for (x = 0; x <= dst->w; x++)
1956     *csax++ = (int)(sx * x);
1957
1958   for (y = 0; y <= dst->h; y++)
1959     *csay++ = (int)(sy * y);
1960
1961   /* pointer setup */
1962   sp = csp = csp0 = (tColorRGBA *) src->pixels;
1963   dp = (tColorRGBA *) dst->pixels;
1964   dgap = dst->pitch - dst->w * 4;
1965
1966   csay = say;
1967   for (y = 0; y < dst->h; y++)
1968   {
1969     sp = csp;
1970     csax = sax;
1971
1972     for (x = 0; x < dst->w; x++)
1973     {
1974       /* draw */
1975       *dp = *sp;
1976
1977       /* advance source pointers */
1978       csax++;
1979       sp = csp + *csax;
1980
1981       /* advance destination pointer */
1982       dp++;
1983     }
1984
1985     /* advance source pointer */
1986     csay++;
1987     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1988
1989     /* advance destination pointers */
1990     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1991   }
1992
1993   free(sax);
1994   free(say);
1995
1996   return 0;
1997 }
1998
1999 /*
2000   -----------------------------------------------------------------------------
2001   8 bit zoomer
2002
2003   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2004   -----------------------------------------------------------------------------
2005 */
2006
2007 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2008 {
2009   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2010   Uint8 *sp, *dp, *csp;
2011   int dgap;
2012
2013   /* variable setup */
2014   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2015   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2016
2017   /* allocate memory for row increments */
2018   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2019   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2020
2021   /* precalculate row increments */
2022   csx = 0;
2023   csax = sax;
2024   for (x = 0; x < dst->w; x++)
2025   {
2026     csx += sx;
2027     *csax = (csx >> 16);
2028     csx &= 0xffff;
2029     csax++;
2030   }
2031
2032   csy = 0;
2033   csay = say;
2034   for (y = 0; y < dst->h; y++)
2035   {
2036     csy += sy;
2037     *csay = (csy >> 16);
2038     csy &= 0xffff;
2039     csay++;
2040   }
2041
2042   csx = 0;
2043   csax = sax;
2044   for (x = 0; x < dst->w; x++)
2045   {
2046     csx += (*csax);
2047     csax++;
2048   }
2049
2050   csy = 0;
2051   csay = say;
2052   for (y = 0; y < dst->h; y++)
2053   {
2054     csy += (*csay);
2055     csay++;
2056   }
2057
2058   /* pointer setup */
2059   sp = csp = (Uint8 *) src->pixels;
2060   dp = (Uint8 *) dst->pixels;
2061   dgap = dst->pitch - dst->w;
2062
2063   /* draw */
2064   csay = say;
2065   for (y = 0; y < dst->h; y++)
2066   {
2067     csax = sax;
2068     sp = csp;
2069     for (x = 0; x < dst->w; x++)
2070     {
2071       /* draw */
2072       *dp = *sp;
2073
2074       /* advance source pointers */
2075       sp += (*csax);
2076       csax++;
2077
2078       /* advance destination pointer */
2079       dp++;
2080     }
2081
2082     /* advance source pointer (for row) */
2083     csp += ((*csay) * src->pitch);
2084     csay++;
2085
2086     /* advance destination pointers */
2087     dp += dgap;
2088   }
2089
2090   free(sax);
2091   free(say);
2092
2093   return 0;
2094 }
2095
2096 /*
2097   -----------------------------------------------------------------------------
2098   zoomSurface()
2099
2100   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2101   'zoomx' and 'zoomy' are scaling factors for width and height.
2102   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2103   into a 32bit RGBA format on the fly.
2104   -----------------------------------------------------------------------------
2105 */
2106
2107 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2108 {
2109   SDL_Surface *zoom_src = NULL;
2110   SDL_Surface *zoom_dst = NULL;
2111   boolean is_converted = FALSE;
2112   boolean is_32bit;
2113   int i;
2114
2115   if (src == NULL)
2116     return NULL;
2117
2118   /* determine if source surface is 32 bit or 8 bit */
2119   is_32bit = (src->format->BitsPerPixel == 32);
2120
2121   if (is_32bit || src->format->BitsPerPixel == 8)
2122   {
2123     /* use source surface 'as is' */
2124     zoom_src = src;
2125   }
2126   else
2127   {
2128     /* new source surface is 32 bit with a defined RGB ordering */
2129     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2130                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2131     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2132     is_32bit = TRUE;
2133     is_converted = TRUE;
2134   }
2135
2136   /* allocate surface to completely contain the zoomed surface */
2137   if (is_32bit)
2138   {
2139     /* target surface is 32 bit with source RGBA/ABGR ordering */
2140     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2141                                     zoom_src->format->Rmask,
2142                                     zoom_src->format->Gmask,
2143                                     zoom_src->format->Bmask, 0);
2144   }
2145   else
2146   {
2147     /* target surface is 8 bit */
2148     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2149                                     0, 0, 0, 0);
2150   }
2151
2152   /* lock source surface */
2153   SDL_LockSurface(zoom_src);
2154
2155   /* check which kind of surface we have */
2156   if (is_32bit)
2157   {
2158     /* call the 32 bit transformation routine to do the zooming */
2159     zoomSurfaceRGBA(zoom_src, zoom_dst);
2160   }
2161   else
2162   {
2163     /* copy palette */
2164     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2165       zoom_dst->format->palette->colors[i] =
2166         zoom_src->format->palette->colors[i];
2167     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2168
2169     /* call the 8 bit transformation routine to do the zooming */
2170     zoomSurfaceY(zoom_src, zoom_dst);
2171   }
2172
2173   /* unlock source surface */
2174   SDL_UnlockSurface(zoom_src);
2175
2176   /* free temporary surface */
2177   if (is_converted)
2178     SDL_FreeSurface(zoom_src);
2179
2180   /* return destination surface */
2181   return zoom_dst;
2182 }
2183
2184 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2185 {
2186   Bitmap *dst_bitmap = CreateBitmapStruct();
2187   SDL_Surface **dst_surface = &dst_bitmap->surface;
2188
2189   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2190   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2191
2192   dst_bitmap->width  = dst_width;
2193   dst_bitmap->height = dst_height;
2194
2195   /* create zoomed temporary surface from source surface */
2196   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2197
2198   /* create native format destination surface from zoomed temporary surface */
2199   SDLSetNativeSurface(dst_surface);
2200
2201   return dst_bitmap;
2202 }
2203
2204
2205 /* ========================================================================= */
2206 /* load image to bitmap                                                      */
2207 /* ========================================================================= */
2208
2209 Bitmap *SDLLoadImage(char *filename)
2210 {
2211   Bitmap *new_bitmap = CreateBitmapStruct();
2212   SDL_Surface *sdl_image_tmp;
2213
2214   print_timestamp_init("SDLLoadImage");
2215
2216   print_timestamp_time(getBaseNamePtr(filename));
2217
2218   /* load image to temporary surface */
2219   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2220   {
2221     SetError("IMG_Load(): %s", SDL_GetError());
2222
2223     return NULL;
2224   }
2225
2226   print_timestamp_time("IMG_Load");
2227
2228   UPDATE_BUSY_STATE();
2229
2230   /* create native non-transparent surface for current image */
2231   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2232   {
2233     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2234
2235     return NULL;
2236   }
2237
2238   print_timestamp_time("SDL_DisplayFormat (opaque)");
2239
2240   UPDATE_BUSY_STATE();
2241
2242   /* create native transparent surface for current image */
2243   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2244                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2245
2246   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2247   {
2248     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2249
2250     return NULL;
2251   }
2252
2253   print_timestamp_time("SDL_DisplayFormat (masked)");
2254
2255   UPDATE_BUSY_STATE();
2256
2257   /* free temporary surface */
2258   SDL_FreeSurface(sdl_image_tmp);
2259
2260   new_bitmap->width = new_bitmap->surface->w;
2261   new_bitmap->height = new_bitmap->surface->h;
2262
2263   print_timestamp_done("SDLLoadImage");
2264
2265   return new_bitmap;
2266 }
2267
2268
2269 /* ------------------------------------------------------------------------- */
2270 /* custom cursor fuctions                                                    */
2271 /* ------------------------------------------------------------------------- */
2272
2273 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2274 {
2275   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2276                           cursor_info->width, cursor_info->height,
2277                           cursor_info->hot_x, cursor_info->hot_y);
2278 }
2279
2280 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2281 {
2282   static struct MouseCursorInfo *last_cursor_info = NULL;
2283   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2284   static SDL_Cursor *cursor_default = NULL;
2285   static SDL_Cursor *cursor_current = NULL;
2286
2287   /* if invoked for the first time, store the SDL default cursor */
2288   if (cursor_default == NULL)
2289     cursor_default = SDL_GetCursor();
2290
2291   /* only create new cursor if cursor info (custom only) has changed */
2292   if (cursor_info != NULL && cursor_info != last_cursor_info)
2293   {
2294     cursor_current = create_cursor(cursor_info);
2295     last_cursor_info = cursor_info;
2296   }
2297
2298   /* only set new cursor if cursor info (custom or NULL) has changed */
2299   if (cursor_info != last_cursor_info2)
2300     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2301
2302   last_cursor_info2 = cursor_info;
2303 }
2304
2305
2306 /* ========================================================================= */
2307 /* audio functions                                                           */
2308 /* ========================================================================= */
2309
2310 void SDLOpenAudio(void)
2311 {
2312 #if !defined(TARGET_SDL2)
2313   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2314     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2315 #endif
2316
2317   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2318   {
2319     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2320     return;
2321   }
2322
2323   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2324                     AUDIO_NUM_CHANNELS_STEREO,
2325                     setup.system.audio_fragment_size) < 0)
2326   {
2327     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2328     return;
2329   }
2330
2331   audio.sound_available = TRUE;
2332   audio.music_available = TRUE;
2333   audio.loops_available = TRUE;
2334   audio.sound_enabled = TRUE;
2335
2336   /* set number of available mixer channels */
2337   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2338   audio.music_channel = MUSIC_CHANNEL;
2339   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2340
2341   Mixer_InitChannels();
2342 }
2343
2344 void SDLCloseAudio(void)
2345 {
2346   Mix_HaltMusic();
2347   Mix_HaltChannel(-1);
2348
2349   Mix_CloseAudio();
2350   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2351 }
2352
2353
2354 /* ========================================================================= */
2355 /* event functions                                                           */
2356 /* ========================================================================= */
2357
2358 void SDLNextEvent(Event *event)
2359 {
2360   SDL_WaitEvent(event);
2361
2362   if (event->type == EVENT_BUTTONPRESS ||
2363       event->type == EVENT_BUTTONRELEASE)
2364   {
2365     if (((ButtonEvent *)event)->x > video_xoffset)
2366       ((ButtonEvent *)event)->x -= video_xoffset;
2367     else
2368       ((ButtonEvent *)event)->x = 0;
2369     if (((ButtonEvent *)event)->y > video_yoffset)
2370       ((ButtonEvent *)event)->y -= video_yoffset;
2371     else
2372       ((ButtonEvent *)event)->y = 0;
2373   }
2374   else if (event->type == EVENT_MOTIONNOTIFY)
2375   {
2376     if (((MotionEvent *)event)->x > video_xoffset)
2377       ((MotionEvent *)event)->x -= video_xoffset;
2378     else
2379       ((MotionEvent *)event)->x = 0;
2380     if (((MotionEvent *)event)->y > video_yoffset)
2381       ((MotionEvent *)event)->y -= video_yoffset;
2382     else
2383       ((MotionEvent *)event)->y = 0;
2384   }
2385 }
2386
2387 void SDLHandleWindowManagerEvent(Event *event)
2388 {
2389 #ifdef DEBUG
2390 #if defined(PLATFORM_WIN32)
2391   // experimental drag and drop code
2392
2393   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2394   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2395
2396 #if defined(TARGET_SDL2)
2397   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2398 #else
2399   if (syswmmsg->msg == WM_DROPFILES)
2400 #endif
2401   {
2402 #if defined(TARGET_SDL2)
2403     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2404 #else
2405     HDROP hdrop = (HDROP)syswmmsg->wParam;
2406 #endif
2407     int i, num_files;
2408
2409     printf("::: SDL_SYSWMEVENT:\n");
2410
2411     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2412
2413     for (i = 0; i < num_files; i++)
2414     {
2415       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2416       char buffer[buffer_len + 1];
2417
2418       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2419
2420       printf("::: - '%s'\n", buffer);
2421     }
2422
2423 #if defined(TARGET_SDL2)
2424     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2425 #else
2426     DragFinish((HDROP)syswmmsg->wParam);
2427 #endif
2428   }
2429 #endif
2430 #endif
2431 }
2432
2433
2434 /* ========================================================================= */
2435 /* joystick functions                                                        */
2436 /* ========================================================================= */
2437
2438 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2439 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2440 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2441
2442 static boolean SDLOpenJoystick(int nr)
2443 {
2444   if (nr < 0 || nr > MAX_PLAYERS)
2445     return FALSE;
2446
2447   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2448 }
2449
2450 static void SDLCloseJoystick(int nr)
2451 {
2452   if (nr < 0 || nr > MAX_PLAYERS)
2453     return;
2454
2455   SDL_JoystickClose(sdl_joystick[nr]);
2456
2457   sdl_joystick[nr] = NULL;
2458 }
2459
2460 static boolean SDLCheckJoystickOpened(int nr)
2461 {
2462   if (nr < 0 || nr > MAX_PLAYERS)
2463     return FALSE;
2464
2465 #if defined(TARGET_SDL2)
2466   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2467 #else
2468   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2469 #endif
2470 }
2471
2472 void HandleJoystickEvent(Event *event)
2473 {
2474   switch(event->type)
2475   {
2476     case SDL_JOYAXISMOTION:
2477       if (event->jaxis.axis < 2)
2478         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2479       break;
2480
2481     case SDL_JOYBUTTONDOWN:
2482       if (event->jbutton.button < 2)
2483         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2484       break;
2485
2486     case SDL_JOYBUTTONUP:
2487       if (event->jbutton.button < 2)
2488         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2489       break;
2490
2491     default:
2492       break;
2493   }
2494 }
2495
2496 void SDLInitJoysticks()
2497 {
2498   static boolean sdl_joystick_subsystem_initialized = FALSE;
2499   boolean print_warning = !sdl_joystick_subsystem_initialized;
2500   int i;
2501
2502   if (!sdl_joystick_subsystem_initialized)
2503   {
2504     sdl_joystick_subsystem_initialized = TRUE;
2505
2506     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2507     {
2508       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2509       return;
2510     }
2511   }
2512
2513   for (i = 0; i < MAX_PLAYERS; i++)
2514   {
2515     /* get configured joystick for this player */
2516     char *device_name = setup.input[i].joy.device_name;
2517     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2518
2519     if (joystick_nr >= SDL_NumJoysticks())
2520     {
2521       if (setup.input[i].use_joystick && print_warning)
2522         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2523
2524       joystick_nr = -1;
2525     }
2526
2527     /* misuse joystick file descriptor variable to store joystick number */
2528     joystick.fd[i] = joystick_nr;
2529
2530     if (joystick_nr == -1)
2531       continue;
2532
2533     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2534     if (SDLCheckJoystickOpened(joystick_nr))
2535       SDLCloseJoystick(joystick_nr);
2536
2537     if (!setup.input[i].use_joystick)
2538       continue;
2539
2540     if (!SDLOpenJoystick(joystick_nr))
2541     {
2542       if (print_warning)
2543         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2544
2545       continue;
2546     }
2547
2548     joystick.status = JOYSTICK_ACTIVATED;
2549   }
2550 }
2551
2552 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2553 {
2554   if (nr < 0 || nr >= MAX_PLAYERS)
2555     return FALSE;
2556
2557   if (x != NULL)
2558     *x = sdl_js_axis[nr][0];
2559   if (y != NULL)
2560     *y = sdl_js_axis[nr][1];
2561
2562   if (b1 != NULL)
2563     *b1 = sdl_js_button[nr][0];
2564   if (b2 != NULL)
2565     *b2 = sdl_js_button[nr][1];
2566
2567   return TRUE;
2568 }