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