removed some global variables from function parameters
[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 defined(TARGET_SDL2)
921   if (dst_bitmap == window)
922   {
923     // SDL_UpdateWindowSurface(sdl_window);
924     // SDL_UpdateWindowSurfaceRects(sdl_window, &rect, 1);
925     UpdateScreen(&rect);
926   }
927 #else
928   if (dst_bitmap == window)
929   {
930     // SDL_UpdateRect(backbuffer->surface, x, y, width, height);
931     UpdateScreen(&rect);
932   }
933 #endif
934 }
935
936 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
937                       int fade_mode, int fade_delay, int post_delay,
938                       void (*draw_border_function)(void))
939 {
940   SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
941   SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
942   SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
943   SDL_Surface *surface_screen = backbuffer->surface;
944   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
945   SDL_Rect src_rect, dst_rect;
946   SDL_Rect dst_rect2;
947   int src_x = x, src_y = y;
948   int dst_x = x, dst_y = y;
949   unsigned int time_last, time_current;
950
951   // store function for drawing global masked border
952   void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
953
954   // deactivate drawing of global border while fading, if needed
955   if (draw_border_function == NULL)
956     gfx.draw_global_border_function = NULL;
957
958   src_rect.x = src_x;
959   src_rect.y = src_y;
960   src_rect.w = width;
961   src_rect.h = height;
962
963   dst_rect.x = dst_x;
964   dst_rect.y = dst_y;
965   dst_rect.w = width;           /* (ignored) */
966   dst_rect.h = height;          /* (ignored) */
967
968   dst_rect2 = dst_rect;
969
970   /* copy source and target surfaces to temporary surfaces for fading */
971   if (fade_mode & FADE_TYPE_TRANSFORM)
972   {
973     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
974     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
975
976     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
977     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
978   }
979   else if (fade_mode & FADE_TYPE_FADE_IN)
980   {
981     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
982     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
983
984     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
985   }
986   else          /* FADE_TYPE_FADE_OUT */
987   {
988     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
989     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
990
991     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
992   }
993
994   time_current = SDL_GetTicks();
995
996   if (fade_mode == FADE_MODE_MELT)
997   {
998     boolean done = FALSE;
999     int melt_pixels = 2;
1000     int melt_columns = width / melt_pixels;
1001     int ypos[melt_columns];
1002     int max_steps = height / 8 + 32;
1003     int steps_done = 0;
1004     float steps = 0;
1005     int i;
1006
1007     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1008 #if defined(TARGET_SDL2)
1009     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
1010 #else
1011     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
1012 #endif
1013
1014     ypos[0] = -GetSimpleRandom(16);
1015
1016     for (i = 1 ; i < melt_columns; i++)
1017     {
1018       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
1019
1020       ypos[i] = ypos[i - 1] + r;
1021
1022       if (ypos[i] > 0)
1023         ypos[i] = 0;
1024       else
1025         if (ypos[i] == -16)
1026           ypos[i] = -15;
1027     }
1028
1029     while (!done)
1030     {
1031       int steps_final;
1032
1033       time_last = time_current;
1034       time_current = SDL_GetTicks();
1035       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1036       steps_final = MIN(MAX(0, steps), max_steps);
1037
1038       steps_done++;
1039
1040       done = (steps_done >= steps_final);
1041
1042       for (i = 0 ; i < melt_columns; i++)
1043       {
1044         if (ypos[i] < 0)
1045         {
1046           ypos[i]++;
1047
1048           done = FALSE;
1049         }
1050         else if (ypos[i] < height)
1051         {
1052           int y1 = 16;
1053           int y2 = 8;
1054           int y3 = 8;
1055           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1056
1057           if (ypos[i] + dy >= height)
1058             dy = height - ypos[i];
1059
1060           /* copy part of (appearing) target surface to upper area */
1061           src_rect.x = src_x + i * melt_pixels;
1062           // src_rect.y = src_y + ypos[i];
1063           src_rect.y = src_y;
1064           src_rect.w = melt_pixels;
1065           // src_rect.h = dy;
1066           src_rect.h = ypos[i] + dy;
1067
1068           dst_rect.x = dst_x + i * melt_pixels;
1069           // dst_rect.y = dst_y + ypos[i];
1070           dst_rect.y = dst_y;
1071
1072           if (steps_done >= steps_final)
1073             SDL_BlitSurface(surface_target, &src_rect,
1074                             surface_screen, &dst_rect);
1075
1076           ypos[i] += dy;
1077
1078           /* copy part of (disappearing) source surface to lower area */
1079           src_rect.x = src_x + i * melt_pixels;
1080           src_rect.y = src_y;
1081           src_rect.w = melt_pixels;
1082           src_rect.h = height - ypos[i];
1083
1084           dst_rect.x = dst_x + i * melt_pixels;
1085           dst_rect.y = dst_y + ypos[i];
1086
1087           if (steps_done >= steps_final)
1088             SDL_BlitSurface(surface_source, &src_rect,
1089                             surface_screen, &dst_rect);
1090
1091           done = FALSE;
1092         }
1093         else
1094         {
1095           src_rect.x = src_x + i * melt_pixels;
1096           src_rect.y = src_y;
1097           src_rect.w = melt_pixels;
1098           src_rect.h = height;
1099
1100           dst_rect.x = dst_x + i * melt_pixels;
1101           dst_rect.y = dst_y;
1102
1103           if (steps_done >= steps_final)
1104             SDL_BlitSurface(surface_target, &src_rect,
1105                             surface_screen, &dst_rect);
1106         }
1107       }
1108
1109       if (steps_done >= steps_final)
1110       {
1111         if (draw_border_function != NULL)
1112           draw_border_function();
1113
1114         UpdateScreen(&dst_rect2);
1115       }
1116     }
1117   }
1118   else if (fade_mode == FADE_MODE_CURTAIN)
1119   {
1120     float xx;
1121     int xx_final;
1122     int xx_size = width / 2;
1123
1124     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1125 #if defined(TARGET_SDL2)
1126     SDL_SetSurfaceBlendMode(surface_source, SDL_BLENDMODE_NONE);
1127 #else
1128     SDL_SetAlpha(surface_source, 0, 0);         /* disable alpha blending */
1129 #endif
1130
1131     for (xx = 0; xx < xx_size;)
1132     {
1133       time_last = time_current;
1134       time_current = SDL_GetTicks();
1135       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1136       xx_final = MIN(MAX(0, xx), xx_size);
1137
1138       src_rect.x = src_x;
1139       src_rect.y = src_y;
1140       src_rect.w = width;
1141       src_rect.h = height;
1142
1143       dst_rect.x = dst_x;
1144       dst_rect.y = dst_y;
1145
1146       /* draw new (target) image to screen buffer */
1147       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1148
1149       if (xx_final < xx_size)
1150       {
1151         src_rect.w = xx_size - xx_final;
1152         src_rect.h = height;
1153
1154         /* draw old (source) image to screen buffer (left side) */
1155
1156         src_rect.x = src_x + xx_final;
1157         dst_rect.x = dst_x;
1158
1159         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1160
1161         /* draw old (source) image to screen buffer (right side) */
1162
1163         src_rect.x = src_x + xx_size;
1164         dst_rect.x = dst_x + xx_size + xx_final;
1165
1166         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1167       }
1168
1169       if (draw_border_function != NULL)
1170         draw_border_function();
1171
1172       /* only update the region of the screen that is affected from fading */
1173       UpdateScreen(&dst_rect2);
1174     }
1175   }
1176   else          /* fading in, fading out or cross-fading */
1177   {
1178     float alpha;
1179     int alpha_final;
1180
1181     for (alpha = 0.0; alpha < 255.0;)
1182     {
1183       time_last = time_current;
1184       time_current = SDL_GetTicks();
1185       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1186       alpha_final = MIN(MAX(0, alpha), 255);
1187
1188       /* draw existing (source) image to screen buffer */
1189       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1190
1191       /* draw new (target) image to screen buffer using alpha blending */
1192 #if defined(TARGET_SDL2)
1193       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
1194       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
1195 #else
1196       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
1197 #endif
1198       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1199
1200       if (draw_border_function != NULL)
1201         draw_border_function();
1202
1203       /* only update the region of the screen that is affected from fading */
1204       UpdateScreen(&dst_rect);
1205     }
1206   }
1207
1208   if (post_delay > 0)
1209   {
1210     unsigned int time_post_delay;
1211
1212     time_current = SDL_GetTicks();
1213     time_post_delay = time_current + post_delay;
1214
1215     while (time_current < time_post_delay)
1216     {
1217       // do not wait longer than 10 ms at a time to be able to ...
1218       Delay(MIN(10, time_post_delay - time_current));
1219
1220       // ... continue drawing global animations during post delay
1221       UpdateScreen(NULL);
1222
1223       time_current = SDL_GetTicks();
1224     }
1225   }
1226
1227   // restore function for drawing global masked border
1228   gfx.draw_global_border_function = draw_global_border_function;
1229 }
1230
1231 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1232                        int to_x, int to_y, Uint32 color)
1233 {
1234   SDL_Surface *surface = dst_bitmap->surface;
1235   SDL_Rect rect;
1236
1237   if (from_x > to_x)
1238     swap_numbers(&from_x, &to_x);
1239
1240   if (from_y > to_y)
1241     swap_numbers(&from_y, &to_y);
1242
1243   rect.x = from_x;
1244   rect.y = from_y;
1245   rect.w = (to_x - from_x + 1);
1246   rect.h = (to_y - from_y + 1);
1247
1248   SDL_FillRect(surface, &rect, color);
1249 }
1250
1251 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1252                  int to_x, int to_y, Uint32 color)
1253 {
1254   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1255 }
1256
1257 #if ENABLE_UNUSED_CODE
1258 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1259                   int num_points, Uint32 color)
1260 {
1261   int i, x, y;
1262   int line_width = 4;
1263
1264   for (i = 0; i < num_points - 1; i++)
1265   {
1266     for (x = 0; x < line_width; x++)
1267     {
1268       for (y = 0; y < line_width; y++)
1269       {
1270         int dx = x - line_width / 2;
1271         int dy = y - line_width / 2;
1272
1273         if ((x == 0 && y == 0) ||
1274             (x == 0 && y == line_width - 1) ||
1275             (x == line_width - 1 && y == 0) ||
1276             (x == line_width - 1 && y == line_width - 1))
1277           continue;
1278
1279         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1280                  points[i+1].x + dx, points[i+1].y + dy, color);
1281       }
1282     }
1283   }
1284 }
1285 #endif
1286
1287 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1288 {
1289   SDL_Surface *surface = src_bitmap->surface;
1290
1291   switch (surface->format->BytesPerPixel)
1292   {
1293     case 1:             /* assuming 8-bpp */
1294     {
1295       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1296     }
1297     break;
1298
1299     case 2:             /* probably 15-bpp or 16-bpp */
1300     {
1301       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1302     }
1303     break;
1304
1305   case 3:               /* slow 24-bpp mode; usually not used */
1306     {
1307       /* does this work? */
1308       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1309       Uint32 color = 0;
1310       int shift;
1311
1312       shift = surface->format->Rshift;
1313       color |= *(pix + shift / 8) >> shift;
1314       shift = surface->format->Gshift;
1315       color |= *(pix + shift / 8) >> shift;
1316       shift = surface->format->Bshift;
1317       color |= *(pix + shift / 8) >> shift;
1318
1319       return color;
1320     }
1321     break;
1322
1323   case 4:               /* probably 32-bpp */
1324     {
1325       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1326     }
1327     break;
1328   }
1329
1330   return 0;
1331 }
1332
1333
1334 /* ========================================================================= */
1335 /* The following functions were taken from the SGE library                   */
1336 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1337 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1338 /* ========================================================================= */
1339
1340 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1341 {
1342   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1343   {
1344     switch (surface->format->BytesPerPixel)
1345     {
1346       case 1:
1347       {
1348         /* Assuming 8-bpp */
1349         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1350       }
1351       break;
1352
1353       case 2:
1354       {
1355         /* Probably 15-bpp or 16-bpp */
1356         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1357       }
1358       break;
1359
1360       case 3:
1361       {
1362         /* Slow 24-bpp mode, usually not used */
1363         Uint8 *pix;
1364         int shift;
1365
1366         /* Gack - slow, but endian correct */
1367         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1368         shift = surface->format->Rshift;
1369         *(pix+shift/8) = color>>shift;
1370         shift = surface->format->Gshift;
1371         *(pix+shift/8) = color>>shift;
1372         shift = surface->format->Bshift;
1373         *(pix+shift/8) = color>>shift;
1374       }
1375       break;
1376
1377       case 4:
1378       {
1379         /* Probably 32-bpp */
1380         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1381       }
1382       break;
1383     }
1384   }
1385 }
1386
1387 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1388                   Uint8 R, Uint8 G, Uint8 B)
1389 {
1390   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1391 }
1392
1393 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1394 {
1395   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1396 }
1397
1398 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1399 {
1400   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1401 }
1402
1403 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1404 {
1405   Uint8 *pix;
1406   int shift;
1407
1408   /* Gack - slow, but endian correct */
1409   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1410   shift = surface->format->Rshift;
1411   *(pix+shift/8) = color>>shift;
1412   shift = surface->format->Gshift;
1413   *(pix+shift/8) = color>>shift;
1414   shift = surface->format->Bshift;
1415   *(pix+shift/8) = color>>shift;
1416 }
1417
1418 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1419 {
1420   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1421 }
1422
1423 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1424 {
1425   switch (dest->format->BytesPerPixel)
1426   {
1427     case 1:
1428       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1429       break;
1430
1431     case 2:
1432       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1433       break;
1434
1435     case 3:
1436       _PutPixel24(dest,x,y,color);
1437       break;
1438
1439     case 4:
1440       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1441       break;
1442   }
1443 }
1444
1445 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1446 {
1447   if (SDL_MUSTLOCK(surface))
1448   {
1449     if (SDL_LockSurface(surface) < 0)
1450     {
1451       return;
1452     }
1453   }
1454
1455   _PutPixel(surface, x, y, color);
1456
1457   if (SDL_MUSTLOCK(surface))
1458   {
1459     SDL_UnlockSurface(surface);
1460   }
1461 }
1462
1463 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1464                   Uint8 r, Uint8 g, Uint8 b)
1465 {
1466   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1467 }
1468
1469 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1470 {
1471   if (y >= 0 && y <= dest->h - 1)
1472   {
1473     switch (dest->format->BytesPerPixel)
1474     {
1475       case 1:
1476         return y*dest->pitch;
1477         break;
1478
1479       case 2:
1480         return y*dest->pitch/2;
1481         break;
1482
1483       case 3:
1484         return y*dest->pitch;
1485         break;
1486
1487       case 4:
1488         return y*dest->pitch/4;
1489         break;
1490     }
1491   }
1492
1493   return -1;
1494 }
1495
1496 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1497 {
1498   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1499   {
1500     switch (surface->format->BytesPerPixel)
1501     {
1502       case 1:
1503       {
1504         /* Assuming 8-bpp */
1505         *((Uint8 *)surface->pixels + ypitch + x) = color;
1506       }
1507       break;
1508
1509       case 2:
1510       {
1511         /* Probably 15-bpp or 16-bpp */
1512         *((Uint16 *)surface->pixels + ypitch + x) = color;
1513       }
1514       break;
1515
1516       case 3:
1517       {
1518         /* Slow 24-bpp mode, usually not used */
1519         Uint8 *pix;
1520         int shift;
1521
1522         /* Gack - slow, but endian correct */
1523         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1524         shift = surface->format->Rshift;
1525         *(pix+shift/8) = color>>shift;
1526         shift = surface->format->Gshift;
1527         *(pix+shift/8) = color>>shift;
1528         shift = surface->format->Bshift;
1529         *(pix+shift/8) = color>>shift;
1530       }
1531       break;
1532
1533       case 4:
1534       {
1535         /* Probably 32-bpp */
1536         *((Uint32 *)surface->pixels + ypitch + x) = color;
1537       }
1538       break;
1539     }
1540   }
1541 }
1542
1543 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1544                Uint32 Color)
1545 {
1546   SDL_Rect l;
1547
1548   if (SDL_MUSTLOCK(Surface))
1549   {
1550     if (SDL_LockSurface(Surface) < 0)
1551     {
1552       return;
1553     }
1554   }
1555
1556   if (x1 > x2)
1557   {
1558     Sint16 tmp = x1;
1559     x1 = x2;
1560     x2 = tmp;
1561   }
1562
1563   /* Do the clipping */
1564   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1565     return;
1566   if (x1 < 0)
1567     x1 = 0;
1568   if (x2 > Surface->w - 1)
1569     x2 = Surface->w - 1;
1570
1571   l.x = x1;
1572   l.y = y;
1573   l.w = x2 - x1 + 1;
1574   l.h = 1;
1575
1576   SDL_FillRect(Surface, &l, Color);
1577
1578   if (SDL_MUSTLOCK(Surface))
1579   {
1580     SDL_UnlockSurface(Surface);
1581   }
1582 }
1583
1584 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1585                   Uint8 R, Uint8 G, Uint8 B)
1586 {
1587   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1588 }
1589
1590 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1591 {
1592   SDL_Rect l;
1593
1594   if (x1 > x2)
1595   {
1596     Sint16 tmp = x1;
1597     x1 = x2;
1598     x2 = tmp;
1599   }
1600
1601   /* Do the clipping */
1602   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1603     return;
1604   if (x1 < 0)
1605     x1 = 0;
1606   if (x2 > Surface->w - 1)
1607     x2 = Surface->w - 1;
1608
1609   l.x = x1;
1610   l.y = y;
1611   l.w = x2 - x1 + 1;
1612   l.h = 1;
1613
1614   SDL_FillRect(Surface, &l, Color);
1615 }
1616
1617 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1618                Uint32 Color)
1619 {
1620   SDL_Rect l;
1621
1622   if (SDL_MUSTLOCK(Surface))
1623   {
1624     if (SDL_LockSurface(Surface) < 0)
1625     {
1626       return;
1627     }
1628   }
1629
1630   if (y1 > y2)
1631   {
1632     Sint16 tmp = y1;
1633     y1 = y2;
1634     y2 = tmp;
1635   }
1636
1637   /* Do the clipping */
1638   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1639     return;
1640   if (y1 < 0)
1641     y1 = 0;
1642   if (y2 > Surface->h - 1)
1643     y2 = Surface->h - 1;
1644
1645   l.x = x;
1646   l.y = y1;
1647   l.w = 1;
1648   l.h = y2 - y1 + 1;
1649
1650   SDL_FillRect(Surface, &l, Color);
1651
1652   if (SDL_MUSTLOCK(Surface))
1653   {
1654     SDL_UnlockSurface(Surface);
1655   }
1656 }
1657
1658 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1659                   Uint8 R, Uint8 G, Uint8 B)
1660 {
1661   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1662 }
1663
1664 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1665 {
1666   SDL_Rect l;
1667
1668   if (y1 > y2)
1669   {
1670     Sint16 tmp = y1;
1671     y1 = y2;
1672     y2 = tmp;
1673   }
1674
1675   /* Do the clipping */
1676   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1677     return;
1678   if (y1 < 0)
1679     y1 = 0;
1680   if (y2 > Surface->h - 1)
1681     y2 = Surface->h - 1;
1682
1683   l.x = x;
1684   l.y = y1;
1685   l.w = 1;
1686   l.h = y2 - y1 + 1;
1687
1688   SDL_FillRect(Surface, &l, Color);
1689 }
1690
1691 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1692                 Sint16 x2, Sint16 y2, Uint32 Color,
1693                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1694                               Uint32 Color))
1695 {
1696   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1697
1698   dx = x2 - x1;
1699   dy = y2 - y1;
1700
1701   sdx = (dx < 0) ? -1 : 1;
1702   sdy = (dy < 0) ? -1 : 1;
1703
1704   dx = sdx * dx + 1;
1705   dy = sdy * dy + 1;
1706
1707   x = y = 0;
1708
1709   px = x1;
1710   py = y1;
1711
1712   if (dx >= dy)
1713   {
1714     for (x = 0; x < dx; x++)
1715     {
1716       Callback(Surface, px, py, Color);
1717
1718       y += dy;
1719       if (y >= dx)
1720       {
1721         y -= dx;
1722         py += sdy;
1723       }
1724
1725       px += sdx;
1726     }
1727   }
1728   else
1729   {
1730     for (y = 0; y < dy; y++)
1731     {
1732       Callback(Surface, px, py, Color);
1733
1734       x += dx;
1735       if (x >= dy)
1736       {
1737         x -= dy;
1738         px += sdx;
1739       }
1740
1741       py += sdy;
1742     }
1743   }
1744 }
1745
1746 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1747                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1748                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1749                                  Uint32 Color))
1750 {
1751   sge_DoLine(Surface, X1, Y1, X2, Y2,
1752              SDL_MapRGB(Surface->format, R, G, B), Callback);
1753 }
1754
1755 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1756               Uint32 Color)
1757 {
1758   if (SDL_MUSTLOCK(Surface))
1759   {
1760     if (SDL_LockSurface(Surface) < 0)
1761       return;
1762    }
1763
1764    /* Draw the line */
1765    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1766
1767    /* unlock the display */
1768    if (SDL_MUSTLOCK(Surface))
1769    {
1770       SDL_UnlockSurface(Surface);
1771    }
1772 }
1773
1774 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1775                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1776 {
1777   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1778 }
1779
1780 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1781 {
1782   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1783 }
1784
1785
1786 /*
1787   -----------------------------------------------------------------------------
1788   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1789   -----------------------------------------------------------------------------
1790 */
1791
1792 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1793                    int width, int height, Uint32 color)
1794 {
1795   int x, y;
1796
1797   for (y = src_y; y < src_y + height; y++)
1798   {
1799     for (x = src_x; x < src_x + width; x++)
1800     {
1801       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1802
1803       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1804     }
1805   }
1806 }
1807
1808 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1809                           int src_x, int src_y, int width, int height,
1810                           int dst_x, int dst_y)
1811 {
1812   int x, y;
1813
1814   for (y = 0; y < height; y++)
1815   {
1816     for (x = 0; x < width; x++)
1817     {
1818       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1819
1820       if (pixel != BLACK_PIXEL)
1821         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1822     }
1823   }
1824 }
1825
1826
1827 /* ========================================================================= */
1828 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1829 /* (Rotozoomer) by Andreas Schiffler                                         */
1830 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1831 /* ========================================================================= */
1832
1833 /*
1834   -----------------------------------------------------------------------------
1835   32 bit zoomer
1836
1837   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1838   -----------------------------------------------------------------------------
1839 */
1840
1841 typedef struct
1842 {
1843   Uint8 r;
1844   Uint8 g;
1845   Uint8 b;
1846   Uint8 a;
1847 } tColorRGBA;
1848
1849 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1850 {
1851   int x, y;
1852   tColorRGBA *sp, *csp, *dp;
1853   int dgap;
1854
1855   /* pointer setup */
1856   sp = csp = (tColorRGBA *) src->pixels;
1857   dp = (tColorRGBA *) dst->pixels;
1858   dgap = dst->pitch - dst->w * 4;
1859
1860   for (y = 0; y < dst->h; y++)
1861   {
1862     sp = csp;
1863
1864     for (x = 0; x < dst->w; x++)
1865     {
1866       tColorRGBA *sp0 = sp;
1867       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1868       tColorRGBA *sp00 = &sp0[0];
1869       tColorRGBA *sp01 = &sp0[1];
1870       tColorRGBA *sp10 = &sp1[0];
1871       tColorRGBA *sp11 = &sp1[1];
1872       tColorRGBA new;
1873
1874       /* create new color pixel from all four source color pixels */
1875       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1876       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1877       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1878       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1879
1880       /* draw */
1881       *dp = new;
1882
1883       /* advance source pointers */
1884       sp += 2;
1885
1886       /* advance destination pointer */
1887       dp++;
1888     }
1889
1890     /* advance source pointer */
1891     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1892
1893     /* advance destination pointers */
1894     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1895   }
1896
1897   return 0;
1898 }
1899
1900 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1901 {
1902   int x, y, *sax, *say, *csax, *csay;
1903   float sx, sy;
1904   tColorRGBA *sp, *csp, *csp0, *dp;
1905   int dgap;
1906
1907   /* use specialized zoom function when scaling down to exactly half size */
1908   if (src->w == 2 * dst->w &&
1909       src->h == 2 * dst->h)
1910     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1911
1912   /* variable setup */
1913   sx = (float) src->w / (float) dst->w;
1914   sy = (float) src->h / (float) dst->h;
1915
1916   /* allocate memory for row increments */
1917   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1918   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1919
1920   /* precalculate row increments */
1921   for (x = 0; x <= dst->w; x++)
1922     *csax++ = (int)(sx * x);
1923
1924   for (y = 0; y <= dst->h; y++)
1925     *csay++ = (int)(sy * y);
1926
1927   /* pointer setup */
1928   sp = csp = csp0 = (tColorRGBA *) src->pixels;
1929   dp = (tColorRGBA *) dst->pixels;
1930   dgap = dst->pitch - dst->w * 4;
1931
1932   csay = say;
1933   for (y = 0; y < dst->h; y++)
1934   {
1935     sp = csp;
1936     csax = sax;
1937
1938     for (x = 0; x < dst->w; x++)
1939     {
1940       /* draw */
1941       *dp = *sp;
1942
1943       /* advance source pointers */
1944       csax++;
1945       sp = csp + *csax;
1946
1947       /* advance destination pointer */
1948       dp++;
1949     }
1950
1951     /* advance source pointer */
1952     csay++;
1953     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1954
1955     /* advance destination pointers */
1956     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1957   }
1958
1959   free(sax);
1960   free(say);
1961
1962   return 0;
1963 }
1964
1965 /*
1966   -----------------------------------------------------------------------------
1967   8 bit zoomer
1968
1969   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1970   -----------------------------------------------------------------------------
1971 */
1972
1973 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1974 {
1975   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1976   Uint8 *sp, *dp, *csp;
1977   int dgap;
1978
1979   /* variable setup */
1980   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1981   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1982
1983   /* allocate memory for row increments */
1984   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1985   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1986
1987   /* precalculate row increments */
1988   csx = 0;
1989   csax = sax;
1990   for (x = 0; x < dst->w; x++)
1991   {
1992     csx += sx;
1993     *csax = (csx >> 16);
1994     csx &= 0xffff;
1995     csax++;
1996   }
1997
1998   csy = 0;
1999   csay = say;
2000   for (y = 0; y < dst->h; y++)
2001   {
2002     csy += sy;
2003     *csay = (csy >> 16);
2004     csy &= 0xffff;
2005     csay++;
2006   }
2007
2008   csx = 0;
2009   csax = sax;
2010   for (x = 0; x < dst->w; x++)
2011   {
2012     csx += (*csax);
2013     csax++;
2014   }
2015
2016   csy = 0;
2017   csay = say;
2018   for (y = 0; y < dst->h; y++)
2019   {
2020     csy += (*csay);
2021     csay++;
2022   }
2023
2024   /* pointer setup */
2025   sp = csp = (Uint8 *) src->pixels;
2026   dp = (Uint8 *) dst->pixels;
2027   dgap = dst->pitch - dst->w;
2028
2029   /* draw */
2030   csay = say;
2031   for (y = 0; y < dst->h; y++)
2032   {
2033     csax = sax;
2034     sp = csp;
2035     for (x = 0; x < dst->w; x++)
2036     {
2037       /* draw */
2038       *dp = *sp;
2039
2040       /* advance source pointers */
2041       sp += (*csax);
2042       csax++;
2043
2044       /* advance destination pointer */
2045       dp++;
2046     }
2047
2048     /* advance source pointer (for row) */
2049     csp += ((*csay) * src->pitch);
2050     csay++;
2051
2052     /* advance destination pointers */
2053     dp += dgap;
2054   }
2055
2056   free(sax);
2057   free(say);
2058
2059   return 0;
2060 }
2061
2062 /*
2063   -----------------------------------------------------------------------------
2064   zoomSurface()
2065
2066   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2067   'zoomx' and 'zoomy' are scaling factors for width and height.
2068   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2069   into a 32bit RGBA format on the fly.
2070   -----------------------------------------------------------------------------
2071 */
2072
2073 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2074 {
2075   SDL_Surface *zoom_src = NULL;
2076   SDL_Surface *zoom_dst = NULL;
2077   boolean is_converted = FALSE;
2078   boolean is_32bit;
2079   int i;
2080
2081   if (src == NULL)
2082     return NULL;
2083
2084   /* determine if source surface is 32 bit or 8 bit */
2085   is_32bit = (src->format->BitsPerPixel == 32);
2086
2087   if (is_32bit || src->format->BitsPerPixel == 8)
2088   {
2089     /* use source surface 'as is' */
2090     zoom_src = src;
2091   }
2092   else
2093   {
2094     /* new source surface is 32 bit with a defined RGB ordering */
2095     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2096                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2097     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2098     is_32bit = TRUE;
2099     is_converted = TRUE;
2100   }
2101
2102   /* allocate surface to completely contain the zoomed surface */
2103   if (is_32bit)
2104   {
2105     /* target surface is 32 bit with source RGBA/ABGR ordering */
2106     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2107                                     zoom_src->format->Rmask,
2108                                     zoom_src->format->Gmask,
2109                                     zoom_src->format->Bmask, 0);
2110   }
2111   else
2112   {
2113     /* target surface is 8 bit */
2114     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2115                                     0, 0, 0, 0);
2116   }
2117
2118   /* lock source surface */
2119   SDL_LockSurface(zoom_src);
2120
2121   /* check which kind of surface we have */
2122   if (is_32bit)
2123   {
2124     /* call the 32 bit transformation routine to do the zooming */
2125     zoomSurfaceRGBA(zoom_src, zoom_dst);
2126   }
2127   else
2128   {
2129     /* copy palette */
2130     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2131       zoom_dst->format->palette->colors[i] =
2132         zoom_src->format->palette->colors[i];
2133     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2134
2135     /* call the 8 bit transformation routine to do the zooming */
2136     zoomSurfaceY(zoom_src, zoom_dst);
2137   }
2138
2139   /* unlock source surface */
2140   SDL_UnlockSurface(zoom_src);
2141
2142   /* free temporary surface */
2143   if (is_converted)
2144     SDL_FreeSurface(zoom_src);
2145
2146   /* return destination surface */
2147   return zoom_dst;
2148 }
2149
2150 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2151 {
2152   Bitmap *dst_bitmap = CreateBitmapStruct();
2153   SDL_Surface **dst_surface = &dst_bitmap->surface;
2154
2155   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2156   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2157
2158   dst_bitmap->width  = dst_width;
2159   dst_bitmap->height = dst_height;
2160
2161   /* create zoomed temporary surface from source surface */
2162   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2163
2164   /* create native format destination surface from zoomed temporary surface */
2165   SDLSetNativeSurface(dst_surface);
2166
2167   return dst_bitmap;
2168 }
2169
2170
2171 /* ========================================================================= */
2172 /* load image to bitmap                                                      */
2173 /* ========================================================================= */
2174
2175 Bitmap *SDLLoadImage(char *filename)
2176 {
2177   Bitmap *new_bitmap = CreateBitmapStruct();
2178   SDL_Surface *sdl_image_tmp;
2179
2180   print_timestamp_init("SDLLoadImage");
2181
2182   print_timestamp_time(getBaseNamePtr(filename));
2183
2184   /* load image to temporary surface */
2185   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2186   {
2187     SetError("IMG_Load(): %s", SDL_GetError());
2188
2189     return NULL;
2190   }
2191
2192   print_timestamp_time("IMG_Load");
2193
2194   UPDATE_BUSY_STATE();
2195
2196   /* create native non-transparent surface for current image */
2197   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2198   {
2199     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2200
2201     return NULL;
2202   }
2203
2204   print_timestamp_time("SDL_DisplayFormat (opaque)");
2205
2206   UPDATE_BUSY_STATE();
2207
2208   /* create native transparent surface for current image */
2209   if (sdl_image_tmp->format->Amask == 0)
2210     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2211                     SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2212
2213   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2214   {
2215     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2216
2217     return NULL;
2218   }
2219
2220   print_timestamp_time("SDL_DisplayFormat (masked)");
2221
2222   UPDATE_BUSY_STATE();
2223
2224   /* free temporary surface */
2225   SDL_FreeSurface(sdl_image_tmp);
2226
2227   new_bitmap->width = new_bitmap->surface->w;
2228   new_bitmap->height = new_bitmap->surface->h;
2229
2230   print_timestamp_done("SDLLoadImage");
2231
2232   return new_bitmap;
2233 }
2234
2235
2236 /* ------------------------------------------------------------------------- */
2237 /* custom cursor fuctions                                                    */
2238 /* ------------------------------------------------------------------------- */
2239
2240 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2241 {
2242   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2243                           cursor_info->width, cursor_info->height,
2244                           cursor_info->hot_x, cursor_info->hot_y);
2245 }
2246
2247 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2248 {
2249   static struct MouseCursorInfo *last_cursor_info = NULL;
2250   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2251   static SDL_Cursor *cursor_default = NULL;
2252   static SDL_Cursor *cursor_current = NULL;
2253
2254   /* if invoked for the first time, store the SDL default cursor */
2255   if (cursor_default == NULL)
2256     cursor_default = SDL_GetCursor();
2257
2258   /* only create new cursor if cursor info (custom only) has changed */
2259   if (cursor_info != NULL && cursor_info != last_cursor_info)
2260   {
2261     cursor_current = create_cursor(cursor_info);
2262     last_cursor_info = cursor_info;
2263   }
2264
2265   /* only set new cursor if cursor info (custom or NULL) has changed */
2266   if (cursor_info != last_cursor_info2)
2267     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2268
2269   last_cursor_info2 = cursor_info;
2270 }
2271
2272
2273 /* ========================================================================= */
2274 /* audio functions                                                           */
2275 /* ========================================================================= */
2276
2277 void SDLOpenAudio(void)
2278 {
2279 #if !defined(TARGET_SDL2)
2280   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2281     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2282 #endif
2283
2284   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2285   {
2286     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2287     return;
2288   }
2289
2290   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2291                     AUDIO_NUM_CHANNELS_STEREO,
2292                     setup.system.audio_fragment_size) < 0)
2293   {
2294     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2295     return;
2296   }
2297
2298   audio.sound_available = TRUE;
2299   audio.music_available = TRUE;
2300   audio.loops_available = TRUE;
2301   audio.sound_enabled = TRUE;
2302
2303   /* set number of available mixer channels */
2304   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2305   audio.music_channel = MUSIC_CHANNEL;
2306   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2307
2308   Mixer_InitChannels();
2309 }
2310
2311 void SDLCloseAudio(void)
2312 {
2313   Mix_HaltMusic();
2314   Mix_HaltChannel(-1);
2315
2316   Mix_CloseAudio();
2317   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2318 }
2319
2320
2321 /* ========================================================================= */
2322 /* event functions                                                           */
2323 /* ========================================================================= */
2324
2325 void SDLNextEvent(Event *event)
2326 {
2327   SDL_WaitEvent(event);
2328 }
2329
2330 void SDLHandleWindowManagerEvent(Event *event)
2331 {
2332 #ifdef DEBUG
2333 #if defined(PLATFORM_WIN32)
2334   // experimental drag and drop code
2335
2336   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2337   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2338
2339 #if defined(TARGET_SDL2)
2340   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2341 #else
2342   if (syswmmsg->msg == WM_DROPFILES)
2343 #endif
2344   {
2345 #if defined(TARGET_SDL2)
2346     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2347 #else
2348     HDROP hdrop = (HDROP)syswmmsg->wParam;
2349 #endif
2350     int i, num_files;
2351
2352     printf("::: SDL_SYSWMEVENT:\n");
2353
2354     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2355
2356     for (i = 0; i < num_files; i++)
2357     {
2358       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2359       char buffer[buffer_len + 1];
2360
2361       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2362
2363       printf("::: - '%s'\n", buffer);
2364     }
2365
2366 #if defined(TARGET_SDL2)
2367     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2368 #else
2369     DragFinish((HDROP)syswmmsg->wParam);
2370 #endif
2371   }
2372 #endif
2373 #endif
2374 }
2375
2376
2377 /* ========================================================================= */
2378 /* joystick functions                                                        */
2379 /* ========================================================================= */
2380
2381 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2382 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2383 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2384
2385 static boolean SDLOpenJoystick(int nr)
2386 {
2387   if (nr < 0 || nr > MAX_PLAYERS)
2388     return FALSE;
2389
2390   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2391 }
2392
2393 static void SDLCloseJoystick(int nr)
2394 {
2395   if (nr < 0 || nr > MAX_PLAYERS)
2396     return;
2397
2398   SDL_JoystickClose(sdl_joystick[nr]);
2399
2400   sdl_joystick[nr] = NULL;
2401 }
2402
2403 static boolean SDLCheckJoystickOpened(int nr)
2404 {
2405   if (nr < 0 || nr > MAX_PLAYERS)
2406     return FALSE;
2407
2408 #if defined(TARGET_SDL2)
2409   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2410 #else
2411   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2412 #endif
2413 }
2414
2415 void HandleJoystickEvent(Event *event)
2416 {
2417   switch(event->type)
2418   {
2419     case SDL_JOYAXISMOTION:
2420       if (event->jaxis.axis < 2)
2421         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2422       break;
2423
2424     case SDL_JOYBUTTONDOWN:
2425       if (event->jbutton.button < 2)
2426         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2427       break;
2428
2429     case SDL_JOYBUTTONUP:
2430       if (event->jbutton.button < 2)
2431         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2432       break;
2433
2434     default:
2435       break;
2436   }
2437 }
2438
2439 void SDLInitJoysticks()
2440 {
2441   static boolean sdl_joystick_subsystem_initialized = FALSE;
2442   boolean print_warning = !sdl_joystick_subsystem_initialized;
2443   int i;
2444
2445   if (!sdl_joystick_subsystem_initialized)
2446   {
2447     sdl_joystick_subsystem_initialized = TRUE;
2448
2449     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2450     {
2451       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2452       return;
2453     }
2454   }
2455
2456   for (i = 0; i < MAX_PLAYERS; i++)
2457   {
2458     /* get configured joystick for this player */
2459     char *device_name = setup.input[i].joy.device_name;
2460     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2461
2462     if (joystick_nr >= SDL_NumJoysticks())
2463     {
2464       if (setup.input[i].use_joystick && print_warning)
2465         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2466
2467       joystick_nr = -1;
2468     }
2469
2470     /* misuse joystick file descriptor variable to store joystick number */
2471     joystick.fd[i] = joystick_nr;
2472
2473     if (joystick_nr == -1)
2474       continue;
2475
2476     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2477     if (SDLCheckJoystickOpened(joystick_nr))
2478       SDLCloseJoystick(joystick_nr);
2479
2480     if (!setup.input[i].use_joystick)
2481       continue;
2482
2483     if (!SDLOpenJoystick(joystick_nr))
2484     {
2485       if (print_warning)
2486         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2487
2488       continue;
2489     }
2490
2491     joystick.status = JOYSTICK_ACTIVATED;
2492   }
2493 }
2494
2495 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2496 {
2497   if (nr < 0 || nr >= MAX_PLAYERS)
2498     return FALSE;
2499
2500   if (x != NULL)
2501     *x = sdl_js_axis[nr][0];
2502   if (y != NULL)
2503     *y = sdl_js_axis[nr][1];
2504
2505   if (b1 != NULL)
2506     *b1 = sdl_js_button[nr][0];
2507   if (b2 != NULL)
2508     *b2 = sdl_js_button[nr][1];
2509
2510   return TRUE;
2511 }