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