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