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