added using configurable virtual buttons while playing
[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 #define DEBUG_JOYSTICKS         0
21
22
23 /* ========================================================================= */
24 /* video functions                                                           */
25 /* ========================================================================= */
26
27 /* SDL internal variables */
28 #if defined(TARGET_SDL2)
29 static SDL_Window *sdl_window = NULL;
30 static SDL_Renderer *sdl_renderer = NULL;
31 static SDL_Texture *sdl_texture_stream = NULL;
32 static SDL_Texture *sdl_texture_target = NULL;
33 static boolean fullscreen_enabled = FALSE;
34 #endif
35
36 static boolean limit_screen_updates = FALSE;
37
38
39 /* functions from SGE library */
40 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
41
42 #if defined(USE_TOUCH_INPUT_OVERLAY)
43 /* functions to draw overlay graphics for touch device input */
44 static void DrawTouchInputOverlay();
45 #endif
46
47 void SDLLimitScreenUpdates(boolean enable)
48 {
49   limit_screen_updates = enable;
50 }
51
52 static void FinalizeScreen(int draw_target)
53 {
54   // copy global animations to render target buffer, if defined (below border)
55   if (gfx.draw_global_anim_function != NULL)
56     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
57
58   // copy global masked border to render target buffer, if defined
59   if (gfx.draw_global_border_function != NULL)
60     gfx.draw_global_border_function(draw_target);
61
62   // copy global animations to render target buffer, if defined (above border)
63   if (gfx.draw_global_anim_function != NULL)
64     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
65
66   // copy tile selection cursor to render target buffer, if defined (above all)
67   if (gfx.draw_tile_cursor_function != NULL)
68     gfx.draw_tile_cursor_function(draw_target);
69 }
70
71 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
72 {
73   static unsigned int update_screen_delay = 0;
74   unsigned int update_screen_delay_value = 50;          /* (milliseconds) */
75   SDL_Surface *screen = backbuffer->surface;
76
77   if (limit_screen_updates &&
78       !DelayReached(&update_screen_delay, update_screen_delay_value))
79     return;
80
81   LimitScreenUpdates(FALSE);
82
83 #if 0
84   {
85     static int LastFrameCounter = 0;
86     boolean changed = (FrameCounter != LastFrameCounter);
87
88     printf("::: FrameCounter == %d [%s]\n", FrameCounter,
89            (changed ? "-" : "SAME FRAME UPDATED"));
90
91     LastFrameCounter = FrameCounter;
92
93     /*
94     if (FrameCounter % 2)
95       return;
96     */
97   }
98 #endif
99
100   if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
101       gfx.final_screen_bitmap != NULL)  // may not be initialized yet
102   {
103     // draw global animations using bitmaps instead of using textures
104     // to prevent texture scaling artefacts (this is potentially slower)
105
106     BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
107                gfx.win_xsize, gfx.win_ysize, 0, 0);
108
109     FinalizeScreen(DRAW_TO_SCREEN);
110
111     screen = gfx.final_screen_bitmap->surface;
112
113     // force full window redraw
114     rect = NULL;
115   }
116
117 #if defined(TARGET_SDL2)
118   SDL_Texture *sdl_texture = sdl_texture_stream;
119
120   // deactivate use of target texture if render targets are not supported
121   if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
122        video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
123       sdl_texture_target == NULL)
124     video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
125
126   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
127     sdl_texture = sdl_texture_target;
128
129   if (rect)
130   {
131     int bytes_x = screen->pitch / video.width;
132     int bytes_y = screen->pitch;
133
134     SDL_UpdateTexture(sdl_texture, rect,
135                       screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
136                       screen->pitch);
137   }
138   else
139   {
140     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
141   }
142
143   int xoff = video.screen_xoffset;
144   int yoff = video.screen_yoffset;
145   SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
146   SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
147   SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
148
149   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
150       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
151     dst_rect2 = &dst_rect_screen;
152   else
153     dst_rect1 = &dst_rect_screen;
154
155 #if defined(HAS_SCREEN_KEYBOARD)
156   if (video.shifted_up || video.shifted_up_delay)
157   {
158     int time_current = SDL_GetTicks();
159     int pos = video.shifted_up_pos;
160     int pos_last = video.shifted_up_pos_last;
161
162     if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
163                          time_current))
164     {
165       int delay = time_current - video.shifted_up_delay;
166       int delay_value = video.shifted_up_delay_value;
167
168       pos = pos_last + (pos - pos_last) * delay / delay_value;
169     }
170     else
171     {
172       video.shifted_up_pos_last = pos;
173       video.shifted_up_delay = 0;
174     }
175
176     SDL_Rect src_rect_up = { 0,    pos,  video.width, video.height - pos };
177     SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
178
179     if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
180         video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
181     {
182       src_rect2 = &src_rect_up;
183       dst_rect2 = &dst_rect_up;
184     }
185     else
186     {
187       src_rect1 = &src_rect_up;
188       dst_rect1 = &dst_rect_up;
189     }
190   }
191 #endif
192
193   // clear render target buffer
194   SDL_RenderClear(sdl_renderer);
195
196   // set renderer to use target texture for rendering
197   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
198       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
199     SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
200
201   // copy backbuffer texture to render target buffer
202   if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
203     SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
204
205   if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
206     FinalizeScreen(DRAW_TO_SCREEN);
207
208   // when using target texture, copy it to screen buffer
209   if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
210       video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
211   {
212     SDL_SetRenderTarget(sdl_renderer, NULL);
213     SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
214   }
215
216 #if defined(USE_TOUCH_INPUT_OVERLAY)
217   // draw overlay graphics for touch device input, if needed
218   DrawTouchInputOverlay();
219 #endif
220
221 #endif
222
223   // global synchronization point of the game to align video frame delay
224   if (with_frame_delay)
225     WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
226
227 #if defined(TARGET_SDL2)
228  // show render target buffer on screen
229   SDL_RenderPresent(sdl_renderer);
230 #else   // TARGET_SDL
231   if (rect)
232     SDL_UpdateRects(screen, 1, rect);
233   else
234     SDL_UpdateRect(screen, 0, 0, 0, 0);
235 #endif
236 }
237
238 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
239 {
240   UpdateScreenExt(rect, TRUE);
241 }
242
243 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
244 {
245   UpdateScreenExt(rect, FALSE);
246 }
247
248 static void SDLSetWindowIcon(char *basename)
249 {
250   /* (setting the window icon on Mac OS X would replace the high-quality
251      dock icon with the currently smaller (and uglier) icon from file) */
252
253 #if !defined(PLATFORM_MACOSX)
254   char *filename = getCustomImageFilename(basename);
255   SDL_Surface *surface;
256
257   if (filename == NULL)
258   {
259     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
260
261     return;
262   }
263
264   if ((surface = IMG_Load(filename)) == NULL)
265   {
266     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
267
268     return;
269   }
270
271   /* set transparent color */
272   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
273                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
274
275 #if defined(TARGET_SDL2)
276   SDL_SetWindowIcon(sdl_window, surface);
277 #else
278   SDL_WM_SetIcon(surface, NULL);
279 #endif
280 #endif
281 }
282
283 #if defined(TARGET_SDL2)
284
285 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
286                                    SDL_PixelFormat *format2)
287 {
288   return (format1->format        == format2->format &&
289           format1->BitsPerPixel  == format2->BitsPerPixel &&
290           format1->BytesPerPixel == format2->BytesPerPixel &&
291           format1->Rmask         == format2->Rmask &&
292           format1->Gmask         == format2->Gmask &&
293           format1->Bmask         == format2->Bmask);
294 }
295
296 static Pixel SDLGetColorKey(SDL_Surface *surface)
297 {
298   Pixel color_key;
299
300   if (SDL_GetColorKey(surface, &color_key) != 0)
301     return -1;
302
303   return color_key;
304 }
305
306 static boolean SDLHasColorKey(SDL_Surface *surface)
307 {
308   return (SDLGetColorKey(surface) != -1);
309 }
310
311 static boolean SDLHasAlpha(SDL_Surface *surface)
312 {
313   SDL_BlendMode blend_mode;
314
315   if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
316     return FALSE;
317
318   return (blend_mode == SDL_BLENDMODE_BLEND);
319 }
320
321 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
322 {
323   SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
324
325   SDL_SetSurfaceBlendMode(surface, blend_mode);
326   SDL_SetSurfaceAlphaMod(surface, alpha);
327 }
328
329 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
330 {
331   SDL_PixelFormat format;
332   SDL_Surface *new_surface;
333
334   if (surface == NULL)
335     return NULL;
336
337   if (backbuffer && backbuffer->surface)
338   {
339     format = *backbuffer->surface->format;
340     format.Amask = surface->format->Amask;      // keep alpha channel
341   }
342   else
343   {
344     format = *surface->format;
345   }
346
347   new_surface = SDL_ConvertSurface(surface, &format, 0);
348
349   if (new_surface == NULL)
350     Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
351
352   return new_surface;
353 }
354
355 boolean SDLSetNativeSurface(SDL_Surface **surface)
356 {
357   SDL_Surface *new_surface;
358
359   if (surface == NULL ||
360       *surface == NULL ||
361       backbuffer == NULL ||
362       backbuffer->surface == NULL)
363     return FALSE;
364
365   // if pixel format already optimized for destination surface, do nothing
366   if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
367     return FALSE;
368
369   new_surface = SDLGetNativeSurface(*surface);
370
371   SDL_FreeSurface(*surface);
372
373   *surface = new_surface;
374
375   return TRUE;
376 }
377
378 #else
379
380 static Pixel SDLGetColorKey(SDL_Surface *surface)
381 {
382   if ((surface->flags & SDL_SRCCOLORKEY) == 0)
383     return -1;
384
385   return surface->format->colorkey;
386 }
387
388 static boolean SDLHasColorKey(SDL_Surface *surface)
389 {
390   return (SDLGetColorKey(surface) != -1);
391 }
392
393 static boolean SDLHasAlpha(SDL_Surface *surface)
394 {
395   return ((surface->flags & SDL_SRCALPHA) != 0);
396 }
397
398 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
399 {
400   SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
401 }
402
403 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
404 {
405   SDL_Surface *new_surface;
406
407   if (surface == NULL)
408     return NULL;
409
410   if (!video.initialized)
411     new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
412   else if (SDLHasAlpha(surface))
413     new_surface = SDL_DisplayFormatAlpha(surface);
414   else
415     new_surface = SDL_DisplayFormat(surface);
416
417   if (new_surface == NULL)
418     Error(ERR_EXIT, "%s() failed: %s",
419           (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
420           SDL_GetError());
421
422   return new_surface;
423 }
424
425 boolean SDLSetNativeSurface(SDL_Surface **surface)
426 {
427   SDL_Surface *new_surface;
428
429   if (surface == NULL ||
430       *surface == NULL ||
431       !video.initialized)
432     return FALSE;
433
434   new_surface = SDLGetNativeSurface(*surface);
435
436   SDL_FreeSurface(*surface);
437
438   *surface = new_surface;
439
440   return TRUE;
441 }
442
443 #endif
444
445 #if defined(TARGET_SDL2)
446 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
447 {
448   if (program.headless)
449     return NULL;
450
451   SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
452
453   if (texture == NULL)
454     Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
455           SDL_GetError());
456
457   return texture;
458 }
459 #endif
460
461 void SDLCreateBitmapTextures(Bitmap *bitmap)
462 {
463 #if defined(TARGET_SDL2)
464   if (bitmap == NULL)
465     return;
466
467   if (bitmap->texture)
468     SDL_DestroyTexture(bitmap->texture);
469   if (bitmap->texture_masked)
470     SDL_DestroyTexture(bitmap->texture_masked);
471
472   bitmap->texture        = SDLCreateTextureFromSurface(bitmap->surface);
473   bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
474 #endif
475 }
476
477 void SDLFreeBitmapTextures(Bitmap *bitmap)
478 {
479 #if defined(TARGET_SDL2)
480   if (bitmap == NULL)
481     return;
482
483   if (bitmap->texture)
484     SDL_DestroyTexture(bitmap->texture);
485   if (bitmap->texture_masked)
486     SDL_DestroyTexture(bitmap->texture_masked);
487
488   bitmap->texture = NULL;
489   bitmap->texture_masked = NULL;
490 #endif
491 }
492
493 void SDLInitVideoDisplay(void)
494 {
495 #if !defined(TARGET_SDL2)
496   if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
497     SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
498
499   SDL_putenv("SDL_VIDEO_CENTERED=1");
500 #endif
501
502   /* initialize SDL video */
503   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
504     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
505
506   /* set default SDL depth */
507 #if !defined(TARGET_SDL2)
508   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
509 #else
510   video.default_depth = 32;     // (how to determine video depth in SDL2?)
511 #endif
512 }
513
514 inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
515 {
516   if (program.headless)
517     return;
518
519   video.window_scaling_percent = setup.window_scaling_percent;
520   video.window_scaling_quality = setup.window_scaling_quality;
521
522   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
523
524 #if defined(TARGET_SDL2)
525   // SDL 2.0: support for (desktop) fullscreen mode available
526   video.fullscreen_available = TRUE;
527 #else
528   // SDL 1.2: no support for fullscreen mode in R'n'D anymore
529   video.fullscreen_available = FALSE;
530 #endif
531
532   /* open SDL video output device (window or fullscreen mode) */
533   if (!SDLSetVideoMode(fullscreen))
534     Error(ERR_EXIT, "setting video mode failed");
535
536   /* !!! SDL2 can only set the window icon if the window already exists !!! */
537   /* set window icon */
538   SDLSetWindowIcon(program.icon_filename);
539
540   /* set window and icon title */
541   SDLSetWindowTitle();
542 }
543
544 inline static void SDLInitVideoBuffer_DrawBuffer()
545 {
546   /* SDL cannot directly draw to the visible video framebuffer like X11,
547      but always uses a backbuffer, which is then blitted to the visible
548      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
549      visible video framebuffer with 'SDL_Flip', if the hardware supports
550      this). Therefore do not use an additional backbuffer for drawing, but
551      use a symbolic buffer (distinguishable from the SDL backbuffer) called
552      'window', which indicates that the SDL backbuffer should be updated to
553      the visible video framebuffer when attempting to blit to it.
554
555      For convenience, it seems to be a good idea to create this symbolic
556      buffer 'window' at the same size as the SDL backbuffer. Although it
557      should never be drawn to directly, it would do no harm nevertheless. */
558
559   /* create additional (symbolic) buffer for double-buffering */
560   ReCreateBitmap(&window, video.width, video.height);
561
562   /* create dummy drawing buffer for headless mode, if needed */
563   if (program.headless)
564     ReCreateBitmap(&backbuffer, video.width, video.height);
565 }
566
567 void SDLInitVideoBuffer(boolean fullscreen)
568 {
569   SDLInitVideoBuffer_VideoBuffer(fullscreen);
570   SDLInitVideoBuffer_DrawBuffer();
571 }
572
573 static boolean SDLCreateScreen(boolean fullscreen)
574 {
575   SDL_Surface *new_surface = NULL;
576
577 #if defined(TARGET_SDL2)
578   int surface_flags_window     = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
579   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
580 #else
581   int surface_flags_window     = SURFACE_FLAGS;
582   int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
583 #endif
584
585 #if defined(TARGET_SDL2)
586 #if 1
587   int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
588 #else
589   /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
590      _without_ enabling 2D/3D acceleration and/or guest additions installed,
591      it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
592      it will try to use accelerated graphics and apparently fails miserably) */
593   int renderer_flags = SDL_RENDERER_SOFTWARE;
594 #endif
595
596   SDLSetScreenSizeAndOffsets(video.width, video.height);
597 #endif
598
599   int width  = video.width;
600   int height = video.height;
601   int screen_width  = video.screen_width;
602   int screen_height = video.screen_height;
603   int surface_flags = (fullscreen ? surface_flags_fullscreen :
604                        surface_flags_window);
605
606   // default window size is unscaled
607   video.window_width  = screen_width;
608   video.window_height = screen_height;
609
610 #if defined(TARGET_SDL2)
611
612   // store if initial screen mode is fullscreen mode when changing screen size
613   video.fullscreen_initial = fullscreen;
614
615   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
616
617   video.window_width  = window_scaling_factor * screen_width;
618   video.window_height = window_scaling_factor * screen_height;
619
620   if (sdl_texture_stream)
621   {
622     SDL_DestroyTexture(sdl_texture_stream);
623     sdl_texture_stream = NULL;
624   }
625
626   if (sdl_texture_target)
627   {
628     SDL_DestroyTexture(sdl_texture_target);
629     sdl_texture_target = NULL;
630   }
631
632   if (!(fullscreen && fullscreen_enabled))
633   {
634     if (sdl_renderer)
635     {
636       SDL_DestroyRenderer(sdl_renderer);
637       sdl_renderer = NULL;
638     }
639
640     if (sdl_window)
641     {
642       SDL_DestroyWindow(sdl_window);
643       sdl_window = NULL;
644     }
645   }
646
647   if (sdl_window == NULL)
648     sdl_window = SDL_CreateWindow(program.window_title,
649                                   SDL_WINDOWPOS_CENTERED,
650                                   SDL_WINDOWPOS_CENTERED,
651                                   video.window_width,
652                                   video.window_height,
653                                   surface_flags);
654
655   if (sdl_window != NULL)
656   {
657     if (sdl_renderer == NULL)
658       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
659
660     if (sdl_renderer != NULL)
661     {
662       SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
663       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
664       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
665
666       sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
667                                              SDL_PIXELFORMAT_ARGB8888,
668                                              SDL_TEXTUREACCESS_STREAMING,
669                                              width, height);
670
671       if (SDL_RenderTargetSupported(sdl_renderer))
672         sdl_texture_target = SDL_CreateTexture(sdl_renderer,
673                                                SDL_PIXELFORMAT_ARGB8888,
674                                                SDL_TEXTUREACCESS_TARGET,
675                                                width, height);
676
677       if (sdl_texture_stream != NULL)
678       {
679         // use SDL default values for RGB masks and no alpha channel
680         new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
681
682         if (new_surface == NULL)
683           Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
684       }
685       else
686       {
687         Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
688       }
689     }
690     else
691     {
692       Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
693     }
694   }
695   else
696   {
697     Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
698   }
699
700 #else   // TARGET_SDL
701
702   if (gfx.final_screen_bitmap == NULL)
703     gfx.final_screen_bitmap = CreateBitmapStruct();
704
705   gfx.final_screen_bitmap->width = width;
706   gfx.final_screen_bitmap->height = height;
707
708   gfx.final_screen_bitmap->surface =
709     SDL_SetVideoMode(width, height, video.depth, surface_flags);
710
711   if (gfx.final_screen_bitmap->surface != NULL)
712   {
713     new_surface =
714       SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
715
716     if (new_surface == NULL)
717       Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
718
719 #if 0
720     new_surface = gfx.final_screen_bitmap->surface;
721     gfx.final_screen_bitmap = NULL;
722 #endif
723
724   }
725   else
726   {
727     Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
728   }
729 #endif
730
731 #if defined(TARGET_SDL2)
732   // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
733   if (new_surface != NULL)
734     fullscreen_enabled = fullscreen;
735 #endif
736
737   if (backbuffer == NULL)
738     backbuffer = CreateBitmapStruct();
739
740   backbuffer->width  = video.width;
741   backbuffer->height = video.height;
742
743   if (backbuffer->surface)
744     SDL_FreeSurface(backbuffer->surface);
745
746   backbuffer->surface = new_surface;
747
748   return (new_surface != NULL);
749 }
750
751 boolean SDLSetVideoMode(boolean fullscreen)
752 {
753   boolean success = FALSE;
754
755   SetWindowTitle();
756
757   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
758   {
759     /* switch display to fullscreen mode, if available */
760     success = SDLCreateScreen(TRUE);
761
762     if (!success)
763     {
764       /* switching display to fullscreen mode failed -- do not try it again */
765       video.fullscreen_available = FALSE;
766     }
767     else
768     {
769       video.fullscreen_enabled = TRUE;
770     }
771   }
772
773   if ((!fullscreen && video.fullscreen_enabled) || !success)
774   {
775     /* switch display to window mode */
776     success = SDLCreateScreen(FALSE);
777
778     if (!success)
779     {
780       /* switching display to window mode failed -- should not happen */
781     }
782     else
783     {
784       video.fullscreen_enabled = FALSE;
785       video.window_scaling_percent = setup.window_scaling_percent;
786       video.window_scaling_quality = setup.window_scaling_quality;
787
788       SDLSetScreenRenderingMode(setup.screen_rendering_mode);
789     }
790   }
791
792 #if defined(TARGET_SDL2)
793   SDLRedrawWindow();                    // map window
794 #endif
795
796 #ifdef DEBUG
797 #if defined(PLATFORM_WIN32)
798   // experimental drag and drop code
799
800   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
801
802   {
803     SDL_SysWMinfo wminfo;
804     HWND hwnd;
805     boolean wminfo_success = FALSE;
806
807     SDL_VERSION(&wminfo.version);
808 #if defined(TARGET_SDL2)
809     if (sdl_window)
810       wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
811 #else
812     wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
813 #endif
814
815     if (wminfo_success)
816     {
817 #if defined(TARGET_SDL2)
818       hwnd = wminfo.info.win.window;
819 #else
820       hwnd = wminfo.window;
821 #endif
822
823       DragAcceptFiles(hwnd, TRUE);
824     }
825   }
826 #endif
827 #endif
828
829   return success;
830 }
831
832 void SDLSetWindowTitle()
833 {
834 #if defined(TARGET_SDL2)
835   if (sdl_window == NULL)
836     return;
837
838   SDL_SetWindowTitle(sdl_window, program.window_title);
839 #else
840   SDL_WM_SetCaption(program.window_title, program.window_title);
841 #endif
842 }
843
844 #if defined(TARGET_SDL2)
845 void SDLSetWindowScaling(int window_scaling_percent)
846 {
847   if (sdl_window == NULL)
848     return;
849
850   float window_scaling_factor = (float)window_scaling_percent / 100;
851   int new_window_width  = (int)(window_scaling_factor * video.screen_width);
852   int new_window_height = (int)(window_scaling_factor * video.screen_height);
853
854   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
855
856   video.window_scaling_percent = window_scaling_percent;
857   video.window_width  = new_window_width;
858   video.window_height = new_window_height;
859
860   SetWindowTitle();
861 }
862
863 void SDLSetWindowScalingQuality(char *window_scaling_quality)
864 {
865   SDL_Texture *new_texture;
866
867   if (sdl_texture_stream == NULL)
868     return;
869
870   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
871
872   new_texture = SDL_CreateTexture(sdl_renderer,
873                                   SDL_PIXELFORMAT_ARGB8888,
874                                   SDL_TEXTUREACCESS_STREAMING,
875                                   video.width, video.height);
876
877   if (new_texture != NULL)
878   {
879     SDL_DestroyTexture(sdl_texture_stream);
880
881     sdl_texture_stream = new_texture;
882   }
883
884   if (SDL_RenderTargetSupported(sdl_renderer))
885     new_texture = SDL_CreateTexture(sdl_renderer,
886                                     SDL_PIXELFORMAT_ARGB8888,
887                                     SDL_TEXTUREACCESS_TARGET,
888                                     video.width, video.height);
889   else
890     new_texture = NULL;
891
892   if (new_texture != NULL)
893   {
894     SDL_DestroyTexture(sdl_texture_target);
895
896     sdl_texture_target = new_texture;
897   }
898
899   SDLRedrawWindow();
900
901   video.window_scaling_quality = window_scaling_quality;
902 }
903
904 void SDLSetWindowFullscreen(boolean fullscreen)
905 {
906   if (sdl_window == NULL)
907     return;
908
909   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
910
911   if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
912     video.fullscreen_enabled = fullscreen_enabled = fullscreen;
913
914   // if screen size was changed in fullscreen mode, correct desktop window size
915   if (!fullscreen && video.fullscreen_initial)
916   {
917     SDLSetWindowScaling(setup.window_scaling_percent);
918     SDL_SetWindowPosition(sdl_window,
919                           SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
920
921     video.fullscreen_initial = FALSE;
922   }
923 }
924
925 void SDLSetDisplaySize()
926 {
927   SDL_Rect display_bounds;
928
929   SDL_GetDisplayBounds(0, &display_bounds);
930
931   video.display_width  = display_bounds.w;
932   video.display_height = display_bounds.h;
933
934 #if 0
935   Error(ERR_DEBUG, "SDL real screen size: %d x %d",
936         video.display_width, video.display_height);
937 #endif
938 }
939
940 void SDLSetScreenSizeAndOffsets(int width, int height)
941 {
942   // set default video screen size and offsets
943   video.screen_width = width;
944   video.screen_height = height;
945   video.screen_xoffset = 0;
946   video.screen_yoffset = 0;
947
948 #if defined(USE_COMPLETE_DISPLAY)
949   float ratio_video   = (float) width / height;
950   float ratio_display = (float) video.display_width / video.display_height;
951
952   if (ratio_video != ratio_display)
953   {
954     // adjust drawable screen size to cover the whole device display
955
956     if (ratio_video < ratio_display)
957       video.screen_width  *= ratio_display / ratio_video;
958     else
959       video.screen_height *= ratio_video / ratio_display;
960
961     video.screen_xoffset = (video.screen_width  - width)  / 2;
962     video.screen_yoffset = (video.screen_height - height) / 2;
963
964 #if 0
965     Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
966           width, height,
967           video.screen_width, video.screen_height,
968           ratio_video, ratio_display);
969 #endif
970   }
971 #endif
972 }
973
974 void SDLSetScreenSizeForRenderer(int width, int height)
975 {
976   SDL_RenderSetLogicalSize(sdl_renderer, width, height);
977 }
978
979 void SDLSetScreenProperties()
980 {
981   SDLSetScreenSizeAndOffsets(video.width, video.height);
982   SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
983 }
984
985 #endif
986
987 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
988 {
989 #if defined(TARGET_SDL2)
990   video.screen_rendering_mode =
991     (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
992      SPECIAL_RENDERING_BITMAP :
993      strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
994      SPECIAL_RENDERING_TARGET:
995      strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
996      SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
997 #else
998   video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
999 #endif
1000 }
1001
1002 void SDLRedrawWindow()
1003 {
1004   UpdateScreen_WithoutFrameDelay(NULL);
1005 }
1006
1007 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
1008                             int depth)
1009 {
1010   if (program.headless)
1011     return;
1012
1013   SDL_Surface *surface =
1014     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
1015
1016   if (surface == NULL)
1017     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1018
1019   SDLSetNativeSurface(&surface);
1020
1021   bitmap->surface = surface;
1022 }
1023
1024 void SDLFreeBitmapPointers(Bitmap *bitmap)
1025 {
1026   if (bitmap->surface)
1027     SDL_FreeSurface(bitmap->surface);
1028   if (bitmap->surface_masked)
1029     SDL_FreeSurface(bitmap->surface_masked);
1030
1031   bitmap->surface = NULL;
1032   bitmap->surface_masked = NULL;
1033
1034 #if defined(TARGET_SDL2)
1035   if (bitmap->texture)
1036     SDL_DestroyTexture(bitmap->texture);
1037   if (bitmap->texture_masked)
1038     SDL_DestroyTexture(bitmap->texture_masked);
1039
1040   bitmap->texture = NULL;
1041   bitmap->texture_masked = NULL;
1042 #endif
1043 }
1044
1045 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1046                  int src_x, int src_y, int width, int height,
1047                  int dst_x, int dst_y, int mask_mode)
1048 {
1049   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1050   SDL_Rect src_rect, dst_rect;
1051
1052   src_rect.x = src_x;
1053   src_rect.y = src_y;
1054   src_rect.w = width;
1055   src_rect.h = height;
1056
1057   dst_rect.x = dst_x;
1058   dst_rect.y = dst_y;
1059   dst_rect.w = width;
1060   dst_rect.h = height;
1061
1062   // if (src_bitmap != backbuffer || dst_bitmap != window)
1063   if (!(src_bitmap == backbuffer && dst_bitmap == window))
1064     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
1065                      src_bitmap->surface_masked : src_bitmap->surface),
1066                     &src_rect, real_dst_bitmap->surface, &dst_rect);
1067
1068   if (dst_bitmap == window)
1069     UpdateScreen_WithFrameDelay(&dst_rect);
1070 }
1071
1072 void SDLBlitTexture(Bitmap *bitmap,
1073                     int src_x, int src_y, int width, int height,
1074                     int dst_x, int dst_y, int mask_mode)
1075 {
1076 #if defined(TARGET_SDL2)
1077   SDL_Texture *texture;
1078   SDL_Rect src_rect;
1079   SDL_Rect dst_rect;
1080
1081   texture =
1082     (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1083
1084   if (texture == NULL)
1085     return;
1086
1087   src_rect.x = src_x;
1088   src_rect.y = src_y;
1089   src_rect.w = width;
1090   src_rect.h = height;
1091
1092   dst_rect.x = dst_x;
1093   dst_rect.y = dst_y;
1094   dst_rect.w = width;
1095   dst_rect.h = height;
1096
1097   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1098 #endif
1099 }
1100
1101 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1102                       Uint32 color)
1103 {
1104   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1105   SDL_Rect rect;
1106
1107   rect.x = x;
1108   rect.y = y;
1109   rect.w = width;
1110   rect.h = height;
1111
1112   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1113
1114   if (dst_bitmap == window)
1115     UpdateScreen_WithFrameDelay(&rect);
1116 }
1117
1118 void PrepareFadeBitmap(int draw_target)
1119 {
1120   Bitmap *fade_bitmap =
1121     (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1122      draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1123
1124   if (fade_bitmap == NULL)
1125     return;
1126
1127   // copy backbuffer to fading buffer
1128   BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
1129
1130   // add border and animations to fading buffer
1131   FinalizeScreen(draw_target);
1132 }
1133
1134 void SDLFadeRectangle(int x, int y, int width, int height,
1135                       int fade_mode, int fade_delay, int post_delay,
1136                       void (*draw_border_function)(void))
1137 {
1138   SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1139   SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1140   SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1141   SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
1142   SDL_Surface *surface_screen = backbuffer->surface;
1143   SDL_Rect src_rect, dst_rect;
1144   SDL_Rect dst_rect2;
1145   int src_x = x, src_y = y;
1146   int dst_x = x, dst_y = y;
1147   unsigned int time_last, time_current;
1148
1149   // store function for drawing global masked border
1150   void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1151
1152   // deactivate drawing of global border while fading, if needed
1153   if (draw_border_function == NULL)
1154     gfx.draw_global_border_function = NULL;
1155
1156   src_rect.x = src_x;
1157   src_rect.y = src_y;
1158   src_rect.w = width;
1159   src_rect.h = height;
1160
1161   dst_rect.x = dst_x;
1162   dst_rect.y = dst_y;
1163   dst_rect.w = width;           /* (ignored) */
1164   dst_rect.h = height;          /* (ignored) */
1165
1166   dst_rect2 = dst_rect;
1167
1168   // before fading in, store backbuffer (without animation graphics)
1169   if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1170     SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1171
1172   /* copy source and target surfaces to temporary surfaces for fading */
1173   if (fade_mode & FADE_TYPE_TRANSFORM)
1174   {
1175     // (source and target fading buffer already prepared)
1176   }
1177   else if (fade_mode & FADE_TYPE_FADE_IN)
1178   {
1179     // (target fading buffer already prepared)
1180     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
1181   }
1182   else          /* FADE_TYPE_FADE_OUT */
1183   {
1184     // (source fading buffer already prepared)
1185     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
1186   }
1187
1188   time_current = SDL_GetTicks();
1189
1190   if (fade_mode == FADE_MODE_MELT)
1191   {
1192     boolean done = FALSE;
1193     int melt_pixels = 2;
1194     int melt_columns = width / melt_pixels;
1195     int ypos[melt_columns];
1196     int max_steps = height / 8 + 32;
1197     int steps_done = 0;
1198     float steps = 0;
1199     int i;
1200
1201     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1202
1203     SDLSetAlpha(surface_target, FALSE, 0);      /* disable alpha blending */
1204
1205     ypos[0] = -GetSimpleRandom(16);
1206
1207     for (i = 1 ; i < melt_columns; i++)
1208     {
1209       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
1210
1211       ypos[i] = ypos[i - 1] + r;
1212
1213       if (ypos[i] > 0)
1214         ypos[i] = 0;
1215       else
1216         if (ypos[i] == -16)
1217           ypos[i] = -15;
1218     }
1219
1220     while (!done)
1221     {
1222       int steps_final;
1223
1224       time_last = time_current;
1225       time_current = SDL_GetTicks();
1226       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1227       steps_final = MIN(MAX(0, steps), max_steps);
1228
1229       steps_done++;
1230
1231       done = (steps_done >= steps_final);
1232
1233       for (i = 0 ; i < melt_columns; i++)
1234       {
1235         if (ypos[i] < 0)
1236         {
1237           ypos[i]++;
1238
1239           done = FALSE;
1240         }
1241         else if (ypos[i] < height)
1242         {
1243           int y1 = 16;
1244           int y2 = 8;
1245           int y3 = 8;
1246           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1247
1248           if (ypos[i] + dy >= height)
1249             dy = height - ypos[i];
1250
1251           /* copy part of (appearing) target surface to upper area */
1252           src_rect.x = src_x + i * melt_pixels;
1253           // src_rect.y = src_y + ypos[i];
1254           src_rect.y = src_y;
1255           src_rect.w = melt_pixels;
1256           // src_rect.h = dy;
1257           src_rect.h = ypos[i] + dy;
1258
1259           dst_rect.x = dst_x + i * melt_pixels;
1260           // dst_rect.y = dst_y + ypos[i];
1261           dst_rect.y = dst_y;
1262
1263           if (steps_done >= steps_final)
1264             SDL_BlitSurface(surface_target, &src_rect,
1265                             surface_screen, &dst_rect);
1266
1267           ypos[i] += dy;
1268
1269           /* copy part of (disappearing) source surface to lower area */
1270           src_rect.x = src_x + i * melt_pixels;
1271           src_rect.y = src_y;
1272           src_rect.w = melt_pixels;
1273           src_rect.h = height - ypos[i];
1274
1275           dst_rect.x = dst_x + i * melt_pixels;
1276           dst_rect.y = dst_y + ypos[i];
1277
1278           if (steps_done >= steps_final)
1279             SDL_BlitSurface(surface_source, &src_rect,
1280                             surface_screen, &dst_rect);
1281
1282           done = FALSE;
1283         }
1284         else
1285         {
1286           src_rect.x = src_x + i * melt_pixels;
1287           src_rect.y = src_y;
1288           src_rect.w = melt_pixels;
1289           src_rect.h = height;
1290
1291           dst_rect.x = dst_x + i * melt_pixels;
1292           dst_rect.y = dst_y;
1293
1294           if (steps_done >= steps_final)
1295             SDL_BlitSurface(surface_target, &src_rect,
1296                             surface_screen, &dst_rect);
1297         }
1298       }
1299
1300       if (steps_done >= steps_final)
1301       {
1302         if (draw_border_function != NULL)
1303           draw_border_function();
1304
1305         UpdateScreen_WithFrameDelay(&dst_rect2);
1306       }
1307     }
1308   }
1309   else if (fade_mode == FADE_MODE_CURTAIN)
1310   {
1311     float xx;
1312     int xx_final;
1313     int xx_size = width / 2;
1314
1315     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1316
1317     SDLSetAlpha(surface_source, FALSE, 0);      /* disable alpha blending */
1318
1319     for (xx = 0; xx < xx_size;)
1320     {
1321       time_last = time_current;
1322       time_current = SDL_GetTicks();
1323       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1324       xx_final = MIN(MAX(0, xx), xx_size);
1325
1326       src_rect.x = src_x;
1327       src_rect.y = src_y;
1328       src_rect.w = width;
1329       src_rect.h = height;
1330
1331       dst_rect.x = dst_x;
1332       dst_rect.y = dst_y;
1333
1334       /* draw new (target) image to screen buffer */
1335       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1336
1337       if (xx_final < xx_size)
1338       {
1339         src_rect.w = xx_size - xx_final;
1340         src_rect.h = height;
1341
1342         /* draw old (source) image to screen buffer (left side) */
1343
1344         src_rect.x = src_x + xx_final;
1345         dst_rect.x = dst_x;
1346
1347         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1348
1349         /* draw old (source) image to screen buffer (right side) */
1350
1351         src_rect.x = src_x + xx_size;
1352         dst_rect.x = dst_x + xx_size + xx_final;
1353
1354         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1355       }
1356
1357       if (draw_border_function != NULL)
1358         draw_border_function();
1359
1360       /* only update the region of the screen that is affected from fading */
1361       UpdateScreen_WithFrameDelay(&dst_rect2);
1362     }
1363   }
1364   else          /* fading in, fading out or cross-fading */
1365   {
1366     float alpha;
1367     int alpha_final;
1368
1369     for (alpha = 0.0; alpha < 255.0;)
1370     {
1371       time_last = time_current;
1372       time_current = SDL_GetTicks();
1373       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1374       alpha_final = MIN(MAX(0, alpha), 255);
1375
1376       /* draw existing (source) image to screen buffer */
1377       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1378
1379       /* draw new (target) image to screen buffer using alpha blending */
1380       SDLSetAlpha(surface_target, TRUE, alpha_final);
1381       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1382
1383       if (draw_border_function != NULL)
1384         draw_border_function();
1385
1386       /* only update the region of the screen that is affected from fading */
1387       UpdateScreen_WithFrameDelay(&dst_rect);
1388     }
1389   }
1390
1391   if (post_delay > 0)
1392   {
1393     unsigned int time_post_delay;
1394
1395     time_current = SDL_GetTicks();
1396     time_post_delay = time_current + post_delay;
1397
1398     while (time_current < time_post_delay)
1399     {
1400       // updating the screen contains waiting for frame delay (non-busy)
1401       UpdateScreen_WithFrameDelay(NULL);
1402
1403       time_current = SDL_GetTicks();
1404     }
1405   }
1406
1407   // restore function for drawing global masked border
1408   gfx.draw_global_border_function = draw_global_border_function;
1409
1410   // after fading in, restore backbuffer (without animation graphics)
1411   if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1412     SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1413 }
1414
1415 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1416                        int to_x, int to_y, Uint32 color)
1417 {
1418   SDL_Surface *surface = dst_bitmap->surface;
1419   SDL_Rect rect;
1420
1421   if (from_x > to_x)
1422     swap_numbers(&from_x, &to_x);
1423
1424   if (from_y > to_y)
1425     swap_numbers(&from_y, &to_y);
1426
1427   rect.x = from_x;
1428   rect.y = from_y;
1429   rect.w = (to_x - from_x + 1);
1430   rect.h = (to_y - from_y + 1);
1431
1432   SDL_FillRect(surface, &rect, color);
1433 }
1434
1435 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1436                  int to_x, int to_y, Uint32 color)
1437 {
1438   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1439 }
1440
1441 #if ENABLE_UNUSED_CODE
1442 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1443                   int num_points, Uint32 color)
1444 {
1445   int i, x, y;
1446   int line_width = 4;
1447
1448   for (i = 0; i < num_points - 1; i++)
1449   {
1450     for (x = 0; x < line_width; x++)
1451     {
1452       for (y = 0; y < line_width; y++)
1453       {
1454         int dx = x - line_width / 2;
1455         int dy = y - line_width / 2;
1456
1457         if ((x == 0 && y == 0) ||
1458             (x == 0 && y == line_width - 1) ||
1459             (x == line_width - 1 && y == 0) ||
1460             (x == line_width - 1 && y == line_width - 1))
1461           continue;
1462
1463         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1464                  points[i+1].x + dx, points[i+1].y + dy, color);
1465       }
1466     }
1467   }
1468 }
1469 #endif
1470
1471 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1472 {
1473   SDL_Surface *surface = src_bitmap->surface;
1474
1475   switch (surface->format->BytesPerPixel)
1476   {
1477     case 1:             /* assuming 8-bpp */
1478     {
1479       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1480     }
1481     break;
1482
1483     case 2:             /* probably 15-bpp or 16-bpp */
1484     {
1485       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1486     }
1487     break;
1488
1489   case 3:               /* slow 24-bpp mode; usually not used */
1490     {
1491       /* does this work? */
1492       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1493       Uint32 color = 0;
1494       int shift;
1495
1496       shift = surface->format->Rshift;
1497       color |= *(pix + shift / 8) >> shift;
1498       shift = surface->format->Gshift;
1499       color |= *(pix + shift / 8) >> shift;
1500       shift = surface->format->Bshift;
1501       color |= *(pix + shift / 8) >> shift;
1502
1503       return color;
1504     }
1505     break;
1506
1507   case 4:               /* probably 32-bpp */
1508     {
1509       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1510     }
1511     break;
1512   }
1513
1514   return 0;
1515 }
1516
1517
1518 /* ========================================================================= */
1519 /* The following functions were taken from the SGE library                   */
1520 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1521 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1522 /* ========================================================================= */
1523
1524 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1525 {
1526   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1527   {
1528     switch (surface->format->BytesPerPixel)
1529     {
1530       case 1:
1531       {
1532         /* Assuming 8-bpp */
1533         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1534       }
1535       break;
1536
1537       case 2:
1538       {
1539         /* Probably 15-bpp or 16-bpp */
1540         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1541       }
1542       break;
1543
1544       case 3:
1545       {
1546         /* Slow 24-bpp mode, usually not used */
1547         Uint8 *pix;
1548         int shift;
1549
1550         /* Gack - slow, but endian correct */
1551         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1552         shift = surface->format->Rshift;
1553         *(pix+shift/8) = color>>shift;
1554         shift = surface->format->Gshift;
1555         *(pix+shift/8) = color>>shift;
1556         shift = surface->format->Bshift;
1557         *(pix+shift/8) = color>>shift;
1558       }
1559       break;
1560
1561       case 4:
1562       {
1563         /* Probably 32-bpp */
1564         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1565       }
1566       break;
1567     }
1568   }
1569 }
1570
1571 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1572                   Uint8 R, Uint8 G, Uint8 B)
1573 {
1574   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1575 }
1576
1577 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1578 {
1579   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1580 }
1581
1582 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1583 {
1584   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1585 }
1586
1587 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1588 {
1589   Uint8 *pix;
1590   int shift;
1591
1592   /* Gack - slow, but endian correct */
1593   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1594   shift = surface->format->Rshift;
1595   *(pix+shift/8) = color>>shift;
1596   shift = surface->format->Gshift;
1597   *(pix+shift/8) = color>>shift;
1598   shift = surface->format->Bshift;
1599   *(pix+shift/8) = color>>shift;
1600 }
1601
1602 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1603 {
1604   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1605 }
1606
1607 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1608 {
1609   switch (dest->format->BytesPerPixel)
1610   {
1611     case 1:
1612       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1613       break;
1614
1615     case 2:
1616       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1617       break;
1618
1619     case 3:
1620       _PutPixel24(dest,x,y,color);
1621       break;
1622
1623     case 4:
1624       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1625       break;
1626   }
1627 }
1628
1629 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1630 {
1631   if (SDL_MUSTLOCK(surface))
1632   {
1633     if (SDL_LockSurface(surface) < 0)
1634     {
1635       return;
1636     }
1637   }
1638
1639   _PutPixel(surface, x, y, color);
1640
1641   if (SDL_MUSTLOCK(surface))
1642   {
1643     SDL_UnlockSurface(surface);
1644   }
1645 }
1646
1647 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1648                   Uint8 r, Uint8 g, Uint8 b)
1649 {
1650   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1651 }
1652
1653 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1654 {
1655   if (y >= 0 && y <= dest->h - 1)
1656   {
1657     switch (dest->format->BytesPerPixel)
1658     {
1659       case 1:
1660         return y*dest->pitch;
1661         break;
1662
1663       case 2:
1664         return y*dest->pitch/2;
1665         break;
1666
1667       case 3:
1668         return y*dest->pitch;
1669         break;
1670
1671       case 4:
1672         return y*dest->pitch/4;
1673         break;
1674     }
1675   }
1676
1677   return -1;
1678 }
1679
1680 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1681 {
1682   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1683   {
1684     switch (surface->format->BytesPerPixel)
1685     {
1686       case 1:
1687       {
1688         /* Assuming 8-bpp */
1689         *((Uint8 *)surface->pixels + ypitch + x) = color;
1690       }
1691       break;
1692
1693       case 2:
1694       {
1695         /* Probably 15-bpp or 16-bpp */
1696         *((Uint16 *)surface->pixels + ypitch + x) = color;
1697       }
1698       break;
1699
1700       case 3:
1701       {
1702         /* Slow 24-bpp mode, usually not used */
1703         Uint8 *pix;
1704         int shift;
1705
1706         /* Gack - slow, but endian correct */
1707         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1708         shift = surface->format->Rshift;
1709         *(pix+shift/8) = color>>shift;
1710         shift = surface->format->Gshift;
1711         *(pix+shift/8) = color>>shift;
1712         shift = surface->format->Bshift;
1713         *(pix+shift/8) = color>>shift;
1714       }
1715       break;
1716
1717       case 4:
1718       {
1719         /* Probably 32-bpp */
1720         *((Uint32 *)surface->pixels + ypitch + x) = color;
1721       }
1722       break;
1723     }
1724   }
1725 }
1726
1727 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1728                Uint32 Color)
1729 {
1730   SDL_Rect l;
1731
1732   if (SDL_MUSTLOCK(Surface))
1733   {
1734     if (SDL_LockSurface(Surface) < 0)
1735     {
1736       return;
1737     }
1738   }
1739
1740   if (x1 > x2)
1741   {
1742     Sint16 tmp = x1;
1743     x1 = x2;
1744     x2 = tmp;
1745   }
1746
1747   /* Do the clipping */
1748   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1749     return;
1750   if (x1 < 0)
1751     x1 = 0;
1752   if (x2 > Surface->w - 1)
1753     x2 = Surface->w - 1;
1754
1755   l.x = x1;
1756   l.y = y;
1757   l.w = x2 - x1 + 1;
1758   l.h = 1;
1759
1760   SDL_FillRect(Surface, &l, Color);
1761
1762   if (SDL_MUSTLOCK(Surface))
1763   {
1764     SDL_UnlockSurface(Surface);
1765   }
1766 }
1767
1768 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1769                   Uint8 R, Uint8 G, Uint8 B)
1770 {
1771   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1772 }
1773
1774 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1775 {
1776   SDL_Rect l;
1777
1778   if (x1 > x2)
1779   {
1780     Sint16 tmp = x1;
1781     x1 = x2;
1782     x2 = tmp;
1783   }
1784
1785   /* Do the clipping */
1786   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1787     return;
1788   if (x1 < 0)
1789     x1 = 0;
1790   if (x2 > Surface->w - 1)
1791     x2 = Surface->w - 1;
1792
1793   l.x = x1;
1794   l.y = y;
1795   l.w = x2 - x1 + 1;
1796   l.h = 1;
1797
1798   SDL_FillRect(Surface, &l, Color);
1799 }
1800
1801 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1802                Uint32 Color)
1803 {
1804   SDL_Rect l;
1805
1806   if (SDL_MUSTLOCK(Surface))
1807   {
1808     if (SDL_LockSurface(Surface) < 0)
1809     {
1810       return;
1811     }
1812   }
1813
1814   if (y1 > y2)
1815   {
1816     Sint16 tmp = y1;
1817     y1 = y2;
1818     y2 = tmp;
1819   }
1820
1821   /* Do the clipping */
1822   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1823     return;
1824   if (y1 < 0)
1825     y1 = 0;
1826   if (y2 > Surface->h - 1)
1827     y2 = Surface->h - 1;
1828
1829   l.x = x;
1830   l.y = y1;
1831   l.w = 1;
1832   l.h = y2 - y1 + 1;
1833
1834   SDL_FillRect(Surface, &l, Color);
1835
1836   if (SDL_MUSTLOCK(Surface))
1837   {
1838     SDL_UnlockSurface(Surface);
1839   }
1840 }
1841
1842 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1843                   Uint8 R, Uint8 G, Uint8 B)
1844 {
1845   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1846 }
1847
1848 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1849 {
1850   SDL_Rect l;
1851
1852   if (y1 > y2)
1853   {
1854     Sint16 tmp = y1;
1855     y1 = y2;
1856     y2 = tmp;
1857   }
1858
1859   /* Do the clipping */
1860   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1861     return;
1862   if (y1 < 0)
1863     y1 = 0;
1864   if (y2 > Surface->h - 1)
1865     y2 = Surface->h - 1;
1866
1867   l.x = x;
1868   l.y = y1;
1869   l.w = 1;
1870   l.h = y2 - y1 + 1;
1871
1872   SDL_FillRect(Surface, &l, Color);
1873 }
1874
1875 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1876                 Sint16 x2, Sint16 y2, Uint32 Color,
1877                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1878                               Uint32 Color))
1879 {
1880   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1881
1882   dx = x2 - x1;
1883   dy = y2 - y1;
1884
1885   sdx = (dx < 0) ? -1 : 1;
1886   sdy = (dy < 0) ? -1 : 1;
1887
1888   dx = sdx * dx + 1;
1889   dy = sdy * dy + 1;
1890
1891   x = y = 0;
1892
1893   px = x1;
1894   py = y1;
1895
1896   if (dx >= dy)
1897   {
1898     for (x = 0; x < dx; x++)
1899     {
1900       Callback(Surface, px, py, Color);
1901
1902       y += dy;
1903       if (y >= dx)
1904       {
1905         y -= dx;
1906         py += sdy;
1907       }
1908
1909       px += sdx;
1910     }
1911   }
1912   else
1913   {
1914     for (y = 0; y < dy; y++)
1915     {
1916       Callback(Surface, px, py, Color);
1917
1918       x += dx;
1919       if (x >= dy)
1920       {
1921         x -= dy;
1922         px += sdx;
1923       }
1924
1925       py += sdy;
1926     }
1927   }
1928 }
1929
1930 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1931                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1932                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1933                                  Uint32 Color))
1934 {
1935   sge_DoLine(Surface, X1, Y1, X2, Y2,
1936              SDL_MapRGB(Surface->format, R, G, B), Callback);
1937 }
1938
1939 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1940               Uint32 Color)
1941 {
1942   if (SDL_MUSTLOCK(Surface))
1943   {
1944     if (SDL_LockSurface(Surface) < 0)
1945       return;
1946    }
1947
1948    /* Draw the line */
1949    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1950
1951    /* unlock the display */
1952    if (SDL_MUSTLOCK(Surface))
1953    {
1954       SDL_UnlockSurface(Surface);
1955    }
1956 }
1957
1958 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1959                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1960 {
1961   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1962 }
1963
1964 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1965 {
1966   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1967 }
1968
1969
1970 /*
1971   -----------------------------------------------------------------------------
1972   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1973   -----------------------------------------------------------------------------
1974 */
1975
1976 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1977                    int width, int height, Uint32 color)
1978 {
1979   int x, y;
1980
1981   for (y = src_y; y < src_y + height; y++)
1982   {
1983     for (x = src_x; x < src_x + width; x++)
1984     {
1985       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1986
1987       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1988     }
1989   }
1990 }
1991
1992 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1993                           int src_x, int src_y, int width, int height,
1994                           int dst_x, int dst_y)
1995 {
1996   int x, y;
1997
1998   for (y = 0; y < height; y++)
1999   {
2000     for (x = 0; x < width; x++)
2001     {
2002       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2003
2004       if (pixel != BLACK_PIXEL)
2005         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2006     }
2007   }
2008 }
2009
2010
2011 /* ========================================================================= */
2012 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2013 /* (Rotozoomer) by Andreas Schiffler                                         */
2014 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
2015 /* ========================================================================= */
2016
2017 /*
2018   -----------------------------------------------------------------------------
2019   32 bit zoomer
2020
2021   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2022   -----------------------------------------------------------------------------
2023 */
2024
2025 typedef struct
2026 {
2027   Uint8 r;
2028   Uint8 g;
2029   Uint8 b;
2030   Uint8 a;
2031 } tColorRGBA;
2032
2033 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2034 {
2035   int x, y;
2036   tColorRGBA *sp, *csp, *dp;
2037   int dgap;
2038
2039   /* pointer setup */
2040   sp = csp = (tColorRGBA *) src->pixels;
2041   dp = (tColorRGBA *) dst->pixels;
2042   dgap = dst->pitch - dst->w * 4;
2043
2044   for (y = 0; y < dst->h; y++)
2045   {
2046     sp = csp;
2047
2048     for (x = 0; x < dst->w; x++)
2049     {
2050       tColorRGBA *sp0 = sp;
2051       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2052       tColorRGBA *sp00 = &sp0[0];
2053       tColorRGBA *sp01 = &sp0[1];
2054       tColorRGBA *sp10 = &sp1[0];
2055       tColorRGBA *sp11 = &sp1[1];
2056       tColorRGBA new;
2057
2058       /* create new color pixel from all four source color pixels */
2059       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2060       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2061       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2062       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2063
2064       /* draw */
2065       *dp = new;
2066
2067       /* advance source pointers */
2068       sp += 2;
2069
2070       /* advance destination pointer */
2071       dp++;
2072     }
2073
2074     /* advance source pointer */
2075     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2076
2077     /* advance destination pointers */
2078     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2079   }
2080
2081   return 0;
2082 }
2083
2084 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2085 {
2086   int x, y, *sax, *say, *csax, *csay;
2087   float sx, sy;
2088   tColorRGBA *sp, *csp, *csp0, *dp;
2089   int dgap;
2090
2091   /* use specialized zoom function when scaling down to exactly half size */
2092   if (src->w == 2 * dst->w &&
2093       src->h == 2 * dst->h)
2094     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2095
2096   /* variable setup */
2097   sx = (float) src->w / (float) dst->w;
2098   sy = (float) src->h / (float) dst->h;
2099
2100   /* allocate memory for row increments */
2101   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2102   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2103
2104   /* precalculate row increments */
2105   for (x = 0; x <= dst->w; x++)
2106     *csax++ = (int)(sx * x);
2107
2108   for (y = 0; y <= dst->h; y++)
2109     *csay++ = (int)(sy * y);
2110
2111   /* pointer setup */
2112   sp = csp = csp0 = (tColorRGBA *) src->pixels;
2113   dp = (tColorRGBA *) dst->pixels;
2114   dgap = dst->pitch - dst->w * 4;
2115
2116   csay = say;
2117   for (y = 0; y < dst->h; y++)
2118   {
2119     sp = csp;
2120     csax = sax;
2121
2122     for (x = 0; x < dst->w; x++)
2123     {
2124       /* draw */
2125       *dp = *sp;
2126
2127       /* advance source pointers */
2128       csax++;
2129       sp = csp + *csax;
2130
2131       /* advance destination pointer */
2132       dp++;
2133     }
2134
2135     /* advance source pointer */
2136     csay++;
2137     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2138
2139     /* advance destination pointers */
2140     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2141   }
2142
2143   free(sax);
2144   free(say);
2145
2146   return 0;
2147 }
2148
2149 /*
2150   -----------------------------------------------------------------------------
2151   8 bit zoomer
2152
2153   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2154   -----------------------------------------------------------------------------
2155 */
2156
2157 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2158 {
2159   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2160   Uint8 *sp, *dp, *csp;
2161   int dgap;
2162
2163   /* variable setup */
2164   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2165   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2166
2167   /* allocate memory for row increments */
2168   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2169   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2170
2171   /* precalculate row increments */
2172   csx = 0;
2173   csax = sax;
2174   for (x = 0; x < dst->w; x++)
2175   {
2176     csx += sx;
2177     *csax = (csx >> 16);
2178     csx &= 0xffff;
2179     csax++;
2180   }
2181
2182   csy = 0;
2183   csay = say;
2184   for (y = 0; y < dst->h; y++)
2185   {
2186     csy += sy;
2187     *csay = (csy >> 16);
2188     csy &= 0xffff;
2189     csay++;
2190   }
2191
2192   csx = 0;
2193   csax = sax;
2194   for (x = 0; x < dst->w; x++)
2195   {
2196     csx += (*csax);
2197     csax++;
2198   }
2199
2200   csy = 0;
2201   csay = say;
2202   for (y = 0; y < dst->h; y++)
2203   {
2204     csy += (*csay);
2205     csay++;
2206   }
2207
2208   /* pointer setup */
2209   sp = csp = (Uint8 *) src->pixels;
2210   dp = (Uint8 *) dst->pixels;
2211   dgap = dst->pitch - dst->w;
2212
2213   /* draw */
2214   csay = say;
2215   for (y = 0; y < dst->h; y++)
2216   {
2217     csax = sax;
2218     sp = csp;
2219     for (x = 0; x < dst->w; x++)
2220     {
2221       /* draw */
2222       *dp = *sp;
2223
2224       /* advance source pointers */
2225       sp += (*csax);
2226       csax++;
2227
2228       /* advance destination pointer */
2229       dp++;
2230     }
2231
2232     /* advance source pointer (for row) */
2233     csp += ((*csay) * src->pitch);
2234     csay++;
2235
2236     /* advance destination pointers */
2237     dp += dgap;
2238   }
2239
2240   free(sax);
2241   free(say);
2242
2243   return 0;
2244 }
2245
2246 /*
2247   -----------------------------------------------------------------------------
2248   zoomSurface()
2249
2250   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2251   'zoomx' and 'zoomy' are scaling factors for width and height.
2252   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2253   into a 32bit RGBA format on the fly.
2254   -----------------------------------------------------------------------------
2255 */
2256
2257 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2258 {
2259   SDL_Surface *zoom_src = NULL;
2260   SDL_Surface *zoom_dst = NULL;
2261   boolean is_converted = FALSE;
2262   boolean is_32bit;
2263   int i;
2264
2265   if (src == NULL)
2266     return NULL;
2267
2268   /* determine if source surface is 32 bit or 8 bit */
2269   is_32bit = (src->format->BitsPerPixel == 32);
2270
2271   if (is_32bit || src->format->BitsPerPixel == 8)
2272   {
2273     /* use source surface 'as is' */
2274     zoom_src = src;
2275   }
2276   else
2277   {
2278     /* new source surface is 32 bit with a defined RGB ordering */
2279     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2280                                     0x000000ff, 0x0000ff00, 0x00ff0000,
2281                                     (src->format->Amask ? 0xff000000 : 0));
2282     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2283     is_32bit = TRUE;
2284     is_converted = TRUE;
2285   }
2286
2287   /* allocate surface to completely contain the zoomed surface */
2288   if (is_32bit)
2289   {
2290     /* target surface is 32 bit with source RGBA/ABGR ordering */
2291     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2292                                     zoom_src->format->Rmask,
2293                                     zoom_src->format->Gmask,
2294                                     zoom_src->format->Bmask,
2295                                     zoom_src->format->Amask);
2296   }
2297   else
2298   {
2299     /* target surface is 8 bit */
2300     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2301                                     0, 0, 0, 0);
2302   }
2303
2304   /* lock source surface */
2305   SDL_LockSurface(zoom_src);
2306
2307   /* check which kind of surface we have */
2308   if (is_32bit)
2309   {
2310     /* call the 32 bit transformation routine to do the zooming */
2311     zoomSurfaceRGBA(zoom_src, zoom_dst);
2312   }
2313   else
2314   {
2315     /* copy palette */
2316     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2317       zoom_dst->format->palette->colors[i] =
2318         zoom_src->format->palette->colors[i];
2319     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2320
2321     /* call the 8 bit transformation routine to do the zooming */
2322     zoomSurfaceY(zoom_src, zoom_dst);
2323   }
2324
2325   /* unlock source surface */
2326   SDL_UnlockSurface(zoom_src);
2327
2328   /* free temporary surface */
2329   if (is_converted)
2330     SDL_FreeSurface(zoom_src);
2331
2332   /* return destination surface */
2333   return zoom_dst;
2334 }
2335
2336 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2337 {
2338   SDL_Surface *new_surface;
2339
2340   if (surface == NULL)
2341     return NULL;
2342
2343   if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2344     Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2345
2346   /* remove alpha channel from native non-transparent surface, if defined */
2347   SDLSetAlpha(new_surface, FALSE, 0);
2348
2349   /* remove transparent color from native non-transparent surface, if defined */
2350   SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2351
2352   return new_surface;
2353 }
2354
2355 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2356 {
2357   Bitmap *dst_bitmap = CreateBitmapStruct();
2358   SDL_Surface *src_surface = src_bitmap->surface_masked;
2359   SDL_Surface *dst_surface;
2360
2361   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2362   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2363
2364   dst_bitmap->width  = dst_width;
2365   dst_bitmap->height = dst_height;
2366
2367   /* create zoomed temporary surface from source surface */
2368   dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2369
2370   /* create native format destination surface from zoomed temporary surface */
2371   SDLSetNativeSurface(&dst_surface);
2372
2373   /* set color key for zoomed surface from source surface, if defined */
2374   if (SDLHasColorKey(src_surface))
2375     SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2376                     SDLGetColorKey(src_surface));
2377
2378   /* create native non-transparent surface for opaque blitting */
2379   dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2380
2381   /* set native transparent surface for masked blitting */
2382   dst_bitmap->surface_masked = dst_surface;
2383
2384   return dst_bitmap;
2385 }
2386
2387
2388 /* ========================================================================= */
2389 /* load image to bitmap                                                      */
2390 /* ========================================================================= */
2391
2392 Bitmap *SDLLoadImage(char *filename)
2393 {
2394   Bitmap *new_bitmap = CreateBitmapStruct();
2395   SDL_Surface *sdl_image_tmp;
2396
2397   if (program.headless)
2398   {
2399     /* prevent sanity check warnings at later stage */
2400     new_bitmap->width = new_bitmap->height = 1;
2401
2402     return new_bitmap;
2403   }
2404
2405   print_timestamp_init("SDLLoadImage");
2406
2407   print_timestamp_time(getBaseNamePtr(filename));
2408
2409   /* load image to temporary surface */
2410   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2411     Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2412
2413   print_timestamp_time("IMG_Load");
2414
2415   UPDATE_BUSY_STATE();
2416
2417   /* create native non-transparent surface for current image */
2418   if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2419     Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2420
2421   print_timestamp_time("SDLGetNativeSurface (opaque)");
2422
2423   UPDATE_BUSY_STATE();
2424
2425   /* set black pixel to transparent if no alpha channel / transparent color */
2426   if (!SDLHasAlpha(sdl_image_tmp) &&
2427       !SDLHasColorKey(sdl_image_tmp))
2428     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2429                     SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2430
2431   /* create native transparent surface for current image */
2432   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2433     Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2434
2435   print_timestamp_time("SDLGetNativeSurface (masked)");
2436
2437   UPDATE_BUSY_STATE();
2438
2439   /* free temporary surface */
2440   SDL_FreeSurface(sdl_image_tmp);
2441
2442   new_bitmap->width = new_bitmap->surface->w;
2443   new_bitmap->height = new_bitmap->surface->h;
2444
2445   print_timestamp_done("SDLLoadImage");
2446
2447   return new_bitmap;
2448 }
2449
2450
2451 /* ------------------------------------------------------------------------- */
2452 /* custom cursor fuctions                                                    */
2453 /* ------------------------------------------------------------------------- */
2454
2455 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2456 {
2457   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2458                           cursor_info->width, cursor_info->height,
2459                           cursor_info->hot_x, cursor_info->hot_y);
2460 }
2461
2462 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2463 {
2464   static struct MouseCursorInfo *last_cursor_info = NULL;
2465   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2466   static SDL_Cursor *cursor_default = NULL;
2467   static SDL_Cursor *cursor_current = NULL;
2468
2469   /* if invoked for the first time, store the SDL default cursor */
2470   if (cursor_default == NULL)
2471     cursor_default = SDL_GetCursor();
2472
2473   /* only create new cursor if cursor info (custom only) has changed */
2474   if (cursor_info != NULL && cursor_info != last_cursor_info)
2475   {
2476     cursor_current = create_cursor(cursor_info);
2477     last_cursor_info = cursor_info;
2478   }
2479
2480   /* only set new cursor if cursor info (custom or NULL) has changed */
2481   if (cursor_info != last_cursor_info2)
2482     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2483
2484   last_cursor_info2 = cursor_info;
2485 }
2486
2487
2488 /* ========================================================================= */
2489 /* audio functions                                                           */
2490 /* ========================================================================= */
2491
2492 void SDLOpenAudio(void)
2493 {
2494   if (program.headless)
2495     return;
2496
2497 #if !defined(TARGET_SDL2)
2498   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2499     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2500 #endif
2501
2502   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2503   {
2504     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2505     return;
2506   }
2507
2508   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2509                     AUDIO_NUM_CHANNELS_STEREO,
2510                     setup.system.audio_fragment_size) < 0)
2511   {
2512     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2513     return;
2514   }
2515
2516   audio.sound_available = TRUE;
2517   audio.music_available = TRUE;
2518   audio.loops_available = TRUE;
2519   audio.sound_enabled = TRUE;
2520
2521   /* set number of available mixer channels */
2522   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2523   audio.music_channel = MUSIC_CHANNEL;
2524   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2525
2526   Mixer_InitChannels();
2527 }
2528
2529 void SDLCloseAudio(void)
2530 {
2531   Mix_HaltMusic();
2532   Mix_HaltChannel(-1);
2533
2534   Mix_CloseAudio();
2535   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2536 }
2537
2538
2539 /* ========================================================================= */
2540 /* event functions                                                           */
2541 /* ========================================================================= */
2542
2543 void SDLWaitEvent(Event *event)
2544 {
2545   SDL_WaitEvent(event);
2546 }
2547
2548 void SDLHandleWindowManagerEvent(Event *event)
2549 {
2550 #ifdef DEBUG
2551 #if defined(PLATFORM_WIN32)
2552   // experimental drag and drop code
2553
2554   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2555   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2556
2557 #if defined(TARGET_SDL2)
2558   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2559 #else
2560   if (syswmmsg->msg == WM_DROPFILES)
2561 #endif
2562   {
2563 #if defined(TARGET_SDL2)
2564     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2565 #else
2566     HDROP hdrop = (HDROP)syswmmsg->wParam;
2567 #endif
2568     int i, num_files;
2569
2570     printf("::: SDL_SYSWMEVENT:\n");
2571
2572     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2573
2574     for (i = 0; i < num_files; i++)
2575     {
2576       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2577       char buffer[buffer_len + 1];
2578
2579       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2580
2581       printf("::: - '%s'\n", buffer);
2582     }
2583
2584 #if defined(TARGET_SDL2)
2585     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2586 #else
2587     DragFinish((HDROP)syswmmsg->wParam);
2588 #endif
2589   }
2590 #endif
2591 #endif
2592 }
2593
2594
2595 /* ========================================================================= */
2596 /* joystick functions                                                        */
2597 /* ========================================================================= */
2598
2599 #if defined(TARGET_SDL2)
2600 static void *sdl_joystick[MAX_PLAYERS];         // game controller or joystick
2601 #else
2602 static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported
2603 #endif
2604 static int sdl_js_axis_raw[MAX_PLAYERS][2];
2605 static int sdl_js_axis[MAX_PLAYERS][2];
2606 static int sdl_js_button[MAX_PLAYERS][2];
2607 static boolean sdl_is_controller[MAX_PLAYERS];
2608
2609 void SDLClearJoystickState()
2610 {
2611   int i, j;
2612
2613   for (i = 0; i < MAX_PLAYERS; i++)
2614   {
2615     for (j = 0; j < 2; j++)
2616     {
2617       sdl_js_axis_raw[i][j] = -1;
2618       sdl_js_axis[i][j] = 0;
2619       sdl_js_button[i][j] = 0;
2620     }
2621   }
2622 }
2623
2624 boolean SDLOpenJoystick(int nr)
2625 {
2626   if (nr < 0 || nr >= MAX_PLAYERS)
2627     return FALSE;
2628
2629 #if defined(TARGET_SDL2)
2630   sdl_is_controller[nr] = SDL_IsGameController(nr);
2631 #else
2632   sdl_is_controller[nr] = FALSE;
2633 #endif
2634
2635 #if DEBUG_JOYSTICKS
2636   Error(ERR_DEBUG, "opening joystick %d (%s)",
2637         nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
2638 #endif
2639
2640 #if defined(TARGET_SDL2)
2641   if (sdl_is_controller[nr])
2642     sdl_joystick[nr] = SDL_GameControllerOpen(nr);
2643   else
2644     sdl_joystick[nr] = SDL_JoystickOpen(nr);
2645 #else
2646   sdl_joystick[nr] = SDL_JoystickOpen(nr);
2647 #endif
2648
2649   return (sdl_joystick[nr] != NULL);
2650 }
2651
2652 void SDLCloseJoystick(int nr)
2653 {
2654   if (nr < 0 || nr >= MAX_PLAYERS)
2655     return;
2656
2657 #if DEBUG_JOYSTICKS
2658   Error(ERR_DEBUG, "closing joystick %d", nr);
2659 #endif
2660
2661 #if defined(TARGET_SDL2)
2662   if (sdl_is_controller[nr])
2663     SDL_GameControllerClose(sdl_joystick[nr]);
2664   else
2665     SDL_JoystickClose(sdl_joystick[nr]);
2666 #else
2667   SDL_JoystickClose(sdl_joystick[nr]);
2668 #endif
2669
2670   sdl_joystick[nr] = NULL;
2671 }
2672
2673 boolean SDLCheckJoystickOpened(int nr)
2674 {
2675   if (nr < 0 || nr >= MAX_PLAYERS)
2676     return FALSE;
2677
2678 #if defined(TARGET_SDL2)
2679   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2680 #else
2681   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2682 #endif
2683 }
2684
2685 static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
2686 {
2687 #if defined(TARGET_SDL2)
2688   int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX ||
2689                  axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 :
2690                  axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY ||
2691                  axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1);
2692 #else
2693   int axis_id = axis_id_raw % 2;
2694 #endif
2695
2696   if (nr < 0 || nr >= MAX_PLAYERS)
2697     return;
2698
2699   if (axis_id == -1)
2700     return;
2701
2702   // prevent (slightly jittering, but centered) axis A from resetting axis B
2703   if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 &&
2704       axis_id_raw != sdl_js_axis_raw[nr][axis_id])
2705     return;
2706
2707   sdl_js_axis[nr][axis_id] = axis_value;
2708   sdl_js_axis_raw[nr][axis_id] = axis_id_raw;
2709 }
2710
2711 static void setJoystickButton(int nr, int button_id_raw, int button_state)
2712 {
2713 #if defined(TARGET_SDL2)
2714   int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A ||
2715                    button_id_raw == SDL_CONTROLLER_BUTTON_X ||
2716                    button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER ||
2717                    button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK ||
2718                    button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 :
2719                    button_id_raw == SDL_CONTROLLER_BUTTON_B ||
2720                    button_id_raw == SDL_CONTROLLER_BUTTON_Y ||
2721                    button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 :
2722                    -1);
2723
2724   if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
2725     sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT;
2726   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
2727     sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT;
2728   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP)
2729     sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER;
2730   else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2731     sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER;
2732
2733   if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
2734       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT ||
2735       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP ||
2736       button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
2737     sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1;
2738 #else
2739   int button_id = button_id_raw % 2;
2740 #endif
2741
2742   if (nr < 0 || nr >= MAX_PLAYERS)
2743     return;
2744
2745   if (button_id == -1)
2746     return;
2747
2748   sdl_js_button[nr][button_id] = button_state;
2749 }
2750
2751 void HandleJoystickEvent(Event *event)
2752 {
2753   switch(event->type)
2754   {
2755 #if defined(TARGET_SDL2)
2756     case SDL_CONTROLLERDEVICEADDED:
2757 #if DEBUG_JOYSTICKS
2758       Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
2759             event->cdevice.which);
2760 #endif
2761       InitJoysticks();
2762       break;
2763
2764     case SDL_CONTROLLERDEVICEREMOVED:
2765 #if DEBUG_JOYSTICKS
2766       Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
2767             event->cdevice.which);
2768 #endif
2769       InitJoysticks();
2770       break;
2771
2772     case SDL_CONTROLLERAXISMOTION:
2773 #if DEBUG_JOYSTICKS
2774       Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
2775             event->caxis.which, event->caxis.axis, event->caxis.value);
2776 #endif
2777       setJoystickAxis(event->caxis.which,
2778                       event->caxis.axis,
2779                       event->caxis.value);
2780       break;
2781
2782     case SDL_CONTROLLERBUTTONDOWN:
2783 #if DEBUG_JOYSTICKS
2784       Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
2785             event->cbutton.which, event->cbutton.button);
2786 #endif
2787       setJoystickButton(event->cbutton.which,
2788                         event->cbutton.button,
2789                         TRUE);
2790       break;
2791
2792     case SDL_CONTROLLERBUTTONUP:
2793 #if DEBUG_JOYSTICKS
2794       Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
2795             event->cbutton.which, event->cbutton.button);
2796 #endif
2797       setJoystickButton(event->cbutton.which,
2798                         event->cbutton.button,
2799                         FALSE);
2800       break;
2801 #endif
2802
2803     case SDL_JOYAXISMOTION:
2804       if (sdl_is_controller[event->jaxis.which])
2805         break;
2806
2807 #if DEBUG_JOYSTICKS
2808       Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
2809             event->jaxis.which, event->jaxis.axis, event->jaxis.value);
2810 #endif
2811       if (event->jaxis.axis < 4)
2812         setJoystickAxis(event->jaxis.which,
2813                         event->jaxis.axis,
2814                         event->jaxis.value);
2815       break;
2816
2817     case SDL_JOYBUTTONDOWN:
2818       if (sdl_is_controller[event->jaxis.which])
2819         break;
2820
2821 #if DEBUG_JOYSTICKS
2822       Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
2823             event->jbutton.which, event->jbutton.button);
2824 #endif
2825       if (event->jbutton.button < 4)
2826         setJoystickButton(event->jbutton.which,
2827                           event->jbutton.button,
2828                           TRUE);
2829       break;
2830
2831     case SDL_JOYBUTTONUP:
2832       if (sdl_is_controller[event->jaxis.which])
2833         break;
2834
2835 #if DEBUG_JOYSTICKS
2836       Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
2837             event->jbutton.which, event->jbutton.button);
2838 #endif
2839       if (event->jbutton.button < 4)
2840         setJoystickButton(event->jbutton.which,
2841                           event->jbutton.button,
2842                           FALSE);
2843       break;
2844
2845     default:
2846       break;
2847   }
2848 }
2849
2850 void SDLInitJoysticks()
2851 {
2852   static boolean sdl_joystick_subsystem_initialized = FALSE;
2853   boolean print_warning = !sdl_joystick_subsystem_initialized;
2854 #if defined(TARGET_SDL2)
2855   char *mappings_file_base = getPath2(options.conf_directory,
2856                                       GAMECONTROLLER_BASENAME);
2857   char *mappings_file_user = getPath2(getUserGameDataDir(),
2858                                       GAMECONTROLLER_BASENAME);
2859   int num_mappings;
2860 #endif
2861   int i;
2862
2863   if (!sdl_joystick_subsystem_initialized)
2864   {
2865     sdl_joystick_subsystem_initialized = TRUE;
2866
2867 #if defined(TARGET_SDL2)
2868     SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
2869
2870     if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0)
2871 #else
2872     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2873 #endif
2874     {
2875       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2876       return;
2877     }
2878
2879 #if defined(TARGET_SDL2)
2880     num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
2881
2882     /* the included game controller base mappings should always be found */
2883     if (num_mappings == -1)
2884       Error(ERR_WARN, "no game controller base mappings found");
2885 #if DEBUG_JOYSTICKS
2886     else
2887       Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
2888 #endif
2889
2890     num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
2891
2892 #if DEBUG_JOYSTICKS
2893     /* the personal game controller user mappings may or may not be found */
2894     if (num_mappings == -1)
2895       Error(ERR_WARN, "no game controller user mappings found");
2896     else
2897       Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
2898
2899     Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
2900 #endif
2901
2902     checked_free(mappings_file_base);
2903     checked_free(mappings_file_user);
2904
2905 #if DEBUG_JOYSTICKS
2906     for (i = 0; i < SDL_NumJoysticks(); i++)
2907     {
2908       const char *name, *type;
2909
2910       if (SDL_IsGameController(i))
2911       {
2912         name = SDL_GameControllerNameForIndex(i);
2913         type = "game controller";
2914       }
2915       else
2916       {
2917         name = SDL_JoystickNameForIndex(i);
2918         type = "joystick";
2919       }
2920
2921       Error(ERR_INFO, "- joystick %d (%s): '%s'",
2922             i, type, (name ? name : "(Unknown)"));
2923     }
2924 #endif
2925 #endif
2926   }
2927
2928   /* assign joysticks from configured to connected joystick for all players */
2929   for (i = 0; i < MAX_PLAYERS; i++)
2930   {
2931     /* get configured joystick for this player */
2932     char *device_name = setup.input[i].joy.device_name;
2933     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2934
2935     if (joystick_nr >= SDL_NumJoysticks())
2936     {
2937       if (setup.input[i].use_joystick && print_warning)
2938         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2939
2940       joystick_nr = -1;
2941     }
2942
2943     /* store configured joystick number for each player */
2944     joystick.nr[i] = joystick_nr;
2945   }
2946
2947   /* now open all connected joysticks (regardless if configured or not) */
2948   for (i = 0; i < SDL_NumJoysticks(); i++)
2949   {
2950     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2951     if (SDLCheckJoystickOpened(i))
2952       SDLCloseJoystick(i);
2953
2954     if (SDLOpenJoystick(i))
2955       joystick.status = JOYSTICK_ACTIVATED;
2956     else if (print_warning)
2957       Error(ERR_WARN, "cannot open joystick %d", i);
2958   }
2959
2960   SDLClearJoystickState();
2961 }
2962
2963 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2964 {
2965   if (nr < 0 || nr >= MAX_PLAYERS)
2966     return FALSE;
2967
2968   if (x != NULL)
2969     *x = sdl_js_axis[nr][0];
2970   if (y != NULL)
2971     *y = sdl_js_axis[nr][1];
2972
2973   if (b1 != NULL)
2974     *b1 = sdl_js_button[nr][0];
2975   if (b2 != NULL)
2976     *b2 = sdl_js_button[nr][1];
2977
2978   return TRUE;
2979 }
2980
2981
2982 /* ========================================================================= */
2983 /* touch input overlay functions                                             */
2984 /* ========================================================================= */
2985
2986 #if defined(USE_TOUCH_INPUT_OVERLAY)
2987 static void DrawTouchInputOverlay_ShowGrid(int alpha)
2988 {
2989   SDL_Rect rect;
2990   int grid_xsize = overlay.grid_xsize;
2991   int grid_ysize = overlay.grid_ysize;
2992   int x, y;
2993
2994   SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
2995   SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
2996
2997   for (x = 0; x < grid_xsize; x++)
2998   {
2999     rect.x = (x + 0) * video.screen_width / grid_xsize;
3000     rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3001
3002     for (y = 0; y < grid_ysize; y++)
3003     {
3004       rect.y = (y + 0) * video.screen_height / grid_ysize;
3005       rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3006
3007       if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
3008         SDL_RenderDrawRect(sdl_renderer, &rect);
3009     }
3010   }
3011
3012   SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3013 }
3014
3015 static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
3016 {
3017   static int alpha_max = SDL_ALPHA_OPAQUE / 2;
3018   static int alpha_step = 5;
3019   static int alpha_direction = 0;
3020   static int alpha_highlight = 0;
3021   SDL_Rect rect;
3022   int grid_xsize = overlay.grid_xsize;
3023   int grid_ysize = overlay.grid_ysize;
3024   int x, y;
3025
3026   if (alpha == alpha_max)
3027   {
3028     if (alpha_direction < 0)
3029     {
3030       alpha_highlight = MAX(0, alpha_highlight - alpha_step);
3031
3032       if (alpha_highlight == 0)
3033         alpha_direction = 1;
3034     }
3035     else
3036     {
3037       alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
3038
3039       if (alpha_highlight == alpha_max)
3040         alpha_direction = -1;
3041     }
3042   }
3043   else
3044   {
3045     alpha_direction = 1;
3046     alpha_highlight = alpha;
3047   }
3048
3049   SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
3050
3051   for (x = 0; x < grid_xsize; x++)
3052   {
3053     rect.x = (x + 0) * video.screen_width / grid_xsize;
3054     rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
3055
3056     for (y = 0; y < grid_ysize; y++)
3057     {
3058       rect.y = (y + 0) * video.screen_height / grid_ysize;
3059       rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
3060
3061       if (overlay.grid_button[x][y] == overlay.grid_button_highlight)
3062         SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_highlight);
3063       else
3064         SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
3065
3066       if (overlay.grid_button[x][y] != CHAR_GRID_BUTTON_NONE)
3067         SDL_RenderFillRect(sdl_renderer, &rect);
3068     }
3069   }
3070
3071   SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
3072 }
3073
3074 static void DrawTouchInputOverlay()
3075 {
3076   static SDL_Texture *texture = NULL;
3077   static boolean initialized = FALSE;
3078   static boolean deactivated = TRUE;
3079   static boolean show_grid = FALSE;
3080   static int width = 0, height = 0;
3081   static int alpha_max = SDL_ALPHA_OPAQUE / 2;
3082   static int alpha_step = 5;
3083   static int alpha_last = -1;
3084   static int alpha = 0;
3085   boolean active = (overlay.enabled && overlay.active);
3086
3087   if (!active && deactivated)
3088     return;
3089
3090   if (active)
3091   {
3092     if (alpha < alpha_max)
3093       alpha = MIN(alpha + alpha_step, alpha_max);
3094
3095     deactivated = FALSE;
3096   }
3097   else
3098   {
3099     alpha = MAX(0, alpha - alpha_step);
3100
3101     if (alpha == 0)
3102       deactivated = TRUE;
3103   }
3104
3105   if (overlay.show_grid)
3106     show_grid = TRUE;
3107   else if (deactivated)
3108     show_grid = FALSE;
3109
3110   if (show_grid)
3111     DrawTouchInputOverlay_ShowGrid(alpha);
3112
3113   DrawTouchInputOverlay_ShowGridButtons(alpha);
3114
3115   return;
3116
3117
3118   // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
3119
3120   if (!initialized)
3121   {
3122     char *basename = "overlay/VirtualButtons.png";
3123     char *filename = getCustomImageFilename(basename);
3124
3125     if (filename == NULL)
3126       Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
3127
3128     SDL_Surface *surface;
3129
3130     if ((surface = IMG_Load(filename)) == NULL)
3131       Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
3132
3133     width  = surface->w;
3134     height = surface->h;
3135
3136     /* set black pixel to transparent if no alpha channel / transparent color */
3137     if (!SDLHasAlpha(surface) &&
3138         !SDLHasColorKey(surface))
3139       SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
3140                       SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
3141
3142     if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
3143       Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
3144
3145     SDL_FreeSurface(surface);
3146
3147     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
3148
3149     initialized = TRUE;
3150   }
3151
3152   if (alpha != alpha_last)
3153     SDL_SetTextureAlphaMod(texture, alpha);
3154
3155   alpha_last = alpha;
3156
3157   float ratio_overlay = (float) width / height;
3158   float ratio_screen = (float) video.screen_width / video.screen_height;
3159   int width_scaled, height_scaled;
3160   int xpos, ypos;
3161
3162   if (ratio_overlay > ratio_screen)
3163   {
3164     width_scaled = video.screen_width;
3165     height_scaled = video.screen_height * ratio_screen / ratio_overlay;
3166     xpos = 0;
3167     ypos = video.screen_height - height_scaled;
3168   }
3169   else
3170   {
3171     width_scaled = video.screen_width * ratio_overlay / ratio_screen;
3172     height_scaled = video.screen_height;
3173     xpos = (video.screen_width - width_scaled) / 2;
3174     ypos = 0;
3175   }
3176
3177   SDL_Rect src_rect = { 0, 0, width, height };
3178   SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
3179
3180   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
3181 }
3182 #endif