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