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