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