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