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