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