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