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