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