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