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