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