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