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