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