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