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