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