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