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