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