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