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