rnd-20060430-3-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   boolean fade_reverse;                 /* initialized later */
279   unsigned int time_last, time_current;
280   float alpha;
281   int alpha_final;
282
283   if (initialization_needed)
284   {
285     unsigned int flags = SDL_SRCALPHA;
286
287     /* use same surface type as screen surface */
288     if ((surface_screen->flags & SDL_HWSURFACE))
289       flags |= SDL_HWSURFACE;
290     else
291       flags |= SDL_SWSURFACE;
292
293     /* create surface for temporary copy of screen buffer */
294     if ((surface_screen_copy =
295          SDL_CreateRGBSurface(flags,
296                               surface_screen->w,
297                               surface_screen->h,
298                               surface_screen->format->BitsPerPixel,
299                               surface_screen->format->Rmask,
300                               surface_screen->format->Gmask,
301                               surface_screen->format->Bmask,
302                               surface_screen->format->Amask)) == NULL)
303       Error(ERR_EXIT, "SDL_CreateRGBSurface(    ) failed: %s", SDL_GetError());
304
305     /* create black surface for fading from/to black */
306     if ((surface_black =
307          SDL_CreateRGBSurface(flags,
308                               surface_screen->w,
309                               surface_screen->h,
310                               surface_screen->format->BitsPerPixel,
311                               surface_screen->format->Rmask,
312                               surface_screen->format->Gmask,
313                               surface_screen->format->Bmask,
314                               surface_screen->format->Amask)) == NULL)
315       Error(ERR_EXIT, "SDL_CreateRGBSurface(    ) failed: %s", SDL_GetError());
316
317     /* completely fill the surface with black color pixels */
318     SDL_FillRect(surface_black, NULL,
319                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
320
321     initialization_needed = FALSE;
322   }
323
324   /* copy the current screen backbuffer to the temporary screen copy buffer */
325   SDL_BlitSurface(surface_screen, NULL, surface_screen_copy, NULL);
326
327   fade_reverse = (fade_mode == FADE_MODE_FADE_IN ? TRUE : FALSE);
328   surface_cross = (fade_mode == FADE_MODE_CROSSFADE ? bitmap_cross->surface :
329                    surface_black);
330
331   time_current = SDL_GetTicks();
332
333   for (alpha = 0.0; alpha < 255.0;)
334   {
335     time_last = time_current;
336     time_current = SDL_GetTicks();
337     alpha += 255 * ((float)(time_current - time_last) / fade_delay);
338     alpha_final = (int)(fade_reverse ? 255.0 - alpha : alpha);
339     alpha_final = MIN(MAX(0, alpha_final), 255);
340
341     /* draw existing image to screen buffer */
342     SDL_BlitSurface(surface_screen_copy, NULL, surface_screen, NULL);
343
344     /* draw new image to screen buffer using alpha blending */
345     SDL_SetAlpha(surface_cross, SDL_SRCALPHA, alpha_final);
346     SDL_BlitSurface(surface_cross, NULL, surface_screen, NULL);
347
348     /* draw screen buffer to visible display */
349     SDL_Flip(surface_screen);
350   }
351
352   Delay(post_delay);
353 }
354
355 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
356                        int to_x, int to_y, Uint32 color)
357 {
358   SDL_Surface *surface = dst_bitmap->surface;
359   SDL_Rect rect;
360
361   if (from_x > to_x)
362     swap_numbers(&from_x, &to_x);
363
364   if (from_y > to_y)
365     swap_numbers(&from_y, &to_y);
366
367   rect.x = from_x;
368   rect.y = from_y;
369   rect.w = (to_x - from_x + 1);
370   rect.h = (to_y - from_y + 1);
371
372 #ifdef FULLSCREEN_BUG
373   if (dst_bitmap == backbuffer || dst_bitmap == window)
374   {
375     rect.x += video_xoffset;
376     rect.y += video_yoffset;
377   }
378 #endif
379
380   SDL_FillRect(surface, &rect, color);
381 }
382
383 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
384                  int to_x, int to_y, Uint32 color)
385 {
386 #ifdef FULLSCREEN_BUG
387   if (dst_bitmap == backbuffer || dst_bitmap == window)
388   {
389     from_x += video_xoffset;
390     from_y += video_yoffset;
391     to_x += video_xoffset;
392     to_y += video_yoffset;
393   }
394 #endif
395
396   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
397 }
398
399 #if 0
400 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
401                   int num_points, Uint32 color)
402 {
403   int i, x, y;
404   int line_width = 4;
405
406   for (i = 0; i < num_points - 1; i++)
407   {
408     for (x = 0; x < line_width; x++)
409     {
410       for (y = 0; y < line_width; y++)
411       {
412         int dx = x - line_width / 2;
413         int dy = y - line_width / 2;
414
415         if ((x == 0 && y == 0) ||
416             (x == 0 && y == line_width - 1) ||
417             (x == line_width - 1 && y == 0) ||
418             (x == line_width - 1 && y == line_width - 1))
419           continue;
420
421         sge_Line(surface, points[i].x + dx, points[i].y + dy,
422                  points[i+1].x + dx, points[i+1].y + dy, color);
423       }
424     }
425   }
426 }
427 #endif
428
429 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
430 {
431   SDL_Surface *surface = src_bitmap->surface;
432
433 #ifdef FULLSCREEN_BUG
434   if (src_bitmap == backbuffer || src_bitmap == window)
435   {
436     x += video_xoffset;
437     y += video_yoffset;
438   }
439 #endif
440
441   switch (surface->format->BytesPerPixel)
442   {
443     case 1:             /* assuming 8-bpp */
444     {
445       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
446     }
447     break;
448
449     case 2:             /* probably 15-bpp or 16-bpp */
450     {
451       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
452     }
453     break;
454
455   case 3:               /* slow 24-bpp mode; usually not used */
456     {
457       /* does this work? */
458       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
459       Uint32 color = 0;
460       int shift;
461
462       shift = surface->format->Rshift;
463       color |= *(pix + shift / 8) >> shift;
464       shift = surface->format->Gshift;
465       color |= *(pix + shift / 8) >> shift;
466       shift = surface->format->Bshift;
467       color |= *(pix + shift / 8) >> shift;
468
469       return color;
470     }
471     break;
472
473   case 4:               /* probably 32-bpp */
474     {
475       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
476     }
477     break;
478   }
479
480   return 0;
481 }
482
483
484 /* ========================================================================= */
485 /* The following functions were taken from the SGE library                   */
486 /* (SDL Graphics Extension Library) by Anders Lindström                      */
487 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
488 /* ========================================================================= */
489
490 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
491 {
492   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
493   {
494     switch (surface->format->BytesPerPixel)
495     {
496       case 1:
497       {
498         /* Assuming 8-bpp */
499         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
500       }
501       break;
502
503       case 2:
504       {
505         /* Probably 15-bpp or 16-bpp */
506         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
507       }
508       break;
509
510       case 3:
511       {
512         /* Slow 24-bpp mode, usually not used */
513         Uint8 *pix;
514         int shift;
515
516         /* Gack - slow, but endian correct */
517         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
518         shift = surface->format->Rshift;
519         *(pix+shift/8) = color>>shift;
520         shift = surface->format->Gshift;
521         *(pix+shift/8) = color>>shift;
522         shift = surface->format->Bshift;
523         *(pix+shift/8) = color>>shift;
524       }
525       break;
526
527       case 4:
528       {
529         /* Probably 32-bpp */
530         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
531       }
532       break;
533     }
534   }
535 }
536
537 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
538                   Uint8 R, Uint8 G, Uint8 B)
539 {
540   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
541 }
542
543 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
544 {
545   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
546 }
547
548 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
549 {
550   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
551 }
552
553 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
554 {
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
568 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
569 {
570   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
571 }
572
573 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
574 {
575   switch (dest->format->BytesPerPixel)
576   {
577     case 1:
578       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
579       break;
580
581     case 2:
582       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
583       break;
584
585     case 3:
586       _PutPixel24(dest,x,y,color);
587       break;
588
589     case 4:
590       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
591       break;
592   }
593 }
594
595 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
596 {
597   if (SDL_MUSTLOCK(surface))
598   {
599     if (SDL_LockSurface(surface) < 0)
600     {
601       return;
602     }
603   }
604
605   _PutPixel(surface, x, y, color);
606
607   if (SDL_MUSTLOCK(surface))
608   {
609     SDL_UnlockSurface(surface);
610   }
611 }
612
613 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
614                   Uint8 r, Uint8 g, Uint8 b)
615 {
616   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
617 }
618
619 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
620 {
621   if (y >= 0 && y <= dest->h - 1)
622   {
623     switch (dest->format->BytesPerPixel)
624     {
625       case 1:
626         return y*dest->pitch;
627         break;
628
629       case 2:
630         return y*dest->pitch/2;
631         break;
632
633       case 3:
634         return y*dest->pitch;
635         break;
636
637       case 4:
638         return y*dest->pitch/4;
639         break;
640     }
641   }
642
643   return -1;
644 }
645
646 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
647 {
648   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
649   {
650     switch (surface->format->BytesPerPixel)
651     {
652       case 1:
653       {
654         /* Assuming 8-bpp */
655         *((Uint8 *)surface->pixels + ypitch + x) = color;
656       }
657       break;
658
659       case 2:
660       {
661         /* Probably 15-bpp or 16-bpp */
662         *((Uint16 *)surface->pixels + ypitch + x) = color;
663       }
664       break;
665
666       case 3:
667       {
668         /* Slow 24-bpp mode, usually not used */
669         Uint8 *pix;
670         int shift;
671
672         /* Gack - slow, but endian correct */
673         pix = (Uint8 *)surface->pixels + ypitch + x*3;
674         shift = surface->format->Rshift;
675         *(pix+shift/8) = color>>shift;
676         shift = surface->format->Gshift;
677         *(pix+shift/8) = color>>shift;
678         shift = surface->format->Bshift;
679         *(pix+shift/8) = color>>shift;
680       }
681       break;
682
683       case 4:
684       {
685         /* Probably 32-bpp */
686         *((Uint32 *)surface->pixels + ypitch + x) = color;
687       }
688       break;
689     }
690   }
691 }
692
693 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
694                Uint32 Color)
695 {
696   SDL_Rect l;
697
698   if (SDL_MUSTLOCK(Surface))
699   {
700     if (SDL_LockSurface(Surface) < 0)
701     {
702       return;
703     }
704   }
705
706   if (x1 > x2)
707   {
708     Sint16 tmp = x1;
709     x1 = x2;
710     x2 = tmp;
711   }
712
713   /* Do the clipping */
714   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
715     return;
716   if (x1 < 0)
717     x1 = 0;
718   if (x2 > Surface->w - 1)
719     x2 = Surface->w - 1;
720
721   l.x = x1;
722   l.y = y;
723   l.w = x2 - x1 + 1;
724   l.h = 1;
725
726   SDL_FillRect(Surface, &l, Color);
727
728   if (SDL_MUSTLOCK(Surface))
729   {
730     SDL_UnlockSurface(Surface);
731   }
732 }
733
734 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
735                   Uint8 R, Uint8 G, Uint8 B)
736 {
737   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
738 }
739
740 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
741 {
742   SDL_Rect l;
743
744   if (x1 > x2)
745   {
746     Sint16 tmp = x1;
747     x1 = x2;
748     x2 = tmp;
749   }
750
751   /* Do the clipping */
752   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
753     return;
754   if (x1 < 0)
755     x1 = 0;
756   if (x2 > Surface->w - 1)
757     x2 = Surface->w - 1;
758
759   l.x = x1;
760   l.y = y;
761   l.w = x2 - x1 + 1;
762   l.h = 1;
763
764   SDL_FillRect(Surface, &l, Color);
765 }
766
767 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
768                Uint32 Color)
769 {
770   SDL_Rect l;
771
772   if (SDL_MUSTLOCK(Surface))
773   {
774     if (SDL_LockSurface(Surface) < 0)
775     {
776       return;
777     }
778   }
779
780   if (y1 > y2)
781   {
782     Sint16 tmp = y1;
783     y1 = y2;
784     y2 = tmp;
785   }
786
787   /* Do the clipping */
788   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
789     return;
790   if (y1 < 0)
791     y1 = 0;
792   if (y2 > Surface->h - 1)
793     y2 = Surface->h - 1;
794
795   l.x = x;
796   l.y = y1;
797   l.w = 1;
798   l.h = y2 - y1 + 1;
799
800   SDL_FillRect(Surface, &l, Color);
801
802   if (SDL_MUSTLOCK(Surface))
803   {
804     SDL_UnlockSurface(Surface);
805   }
806 }
807
808 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
809                   Uint8 R, Uint8 G, Uint8 B)
810 {
811   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
812 }
813
814 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
815 {
816   SDL_Rect l;
817
818   if (y1 > y2)
819   {
820     Sint16 tmp = y1;
821     y1 = y2;
822     y2 = tmp;
823   }
824
825   /* Do the clipping */
826   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
827     return;
828   if (y1 < 0)
829     y1 = 0;
830   if (y2 > Surface->h - 1)
831     y2 = Surface->h - 1;
832
833   l.x = x;
834   l.y = y1;
835   l.w = 1;
836   l.h = y2 - y1 + 1;
837
838   SDL_FillRect(Surface, &l, Color);
839 }
840
841 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
842                 Sint16 x2, Sint16 y2, Uint32 Color,
843                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
844                               Uint32 Color))
845 {
846   Sint16 dx, dy, sdx, sdy, x, y, px, py;
847
848   dx = x2 - x1;
849   dy = y2 - y1;
850
851   sdx = (dx < 0) ? -1 : 1;
852   sdy = (dy < 0) ? -1 : 1;
853
854   dx = sdx * dx + 1;
855   dy = sdy * dy + 1;
856
857   x = y = 0;
858
859   px = x1;
860   py = y1;
861
862   if (dx >= dy)
863   {
864     for (x = 0; x < dx; x++)
865     {
866       Callback(Surface, px, py, Color);
867
868       y += dy;
869       if (y >= dx)
870       {
871         y -= dx;
872         py += sdy;
873       }
874
875       px += sdx;
876     }
877   }
878   else
879   {
880     for (y = 0; y < dy; y++)
881     {
882       Callback(Surface, px, py, Color);
883
884       x += dx;
885       if (x >= dy)
886       {
887         x -= dy;
888         px += sdx;
889       }
890
891       py += sdy;
892     }
893   }
894 }
895
896 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
897                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
898                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
899                                  Uint32 Color))
900 {
901   sge_DoLine(Surface, X1, Y1, X2, Y2,
902              SDL_MapRGB(Surface->format, R, G, B), Callback);
903 }
904
905 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
906               Uint32 Color)
907 {
908   if (SDL_MUSTLOCK(Surface))
909   {
910     if (SDL_LockSurface(Surface) < 0)
911       return;
912    }
913
914    /* Draw the line */
915    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
916
917    /* unlock the display */
918    if (SDL_MUSTLOCK(Surface))
919    {
920       SDL_UnlockSurface(Surface);
921    }
922 }
923
924 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
925                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
926 {
927   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
928 }
929
930 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
931 {
932 #ifdef FULLSCREEN_BUG
933   if (dst_bitmap == backbuffer || dst_bitmap == window)
934   {
935     x += video_xoffset;
936     y += video_yoffset;
937   }
938 #endif
939
940   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
941 }
942
943
944 /*
945   -----------------------------------------------------------------------------
946   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
947   -----------------------------------------------------------------------------
948 */
949
950 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
951                    int width, int height, Uint32 color)
952 {
953   int x, y;
954
955   for (y = src_y; y < src_y + height; y++)
956   {
957     for (x = src_x; x < src_x + width; x++)
958     {
959       Uint32 pixel = SDLGetPixel(bitmap, x, y);
960
961       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
962     }
963   }
964 }
965
966 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
967                           int src_x, int src_y, int width, int height,
968                           int dst_x, int dst_y)
969 {
970   int x, y;
971
972   for (y = 0; y < height; y++)
973   {
974     for (x = 0; x < width; x++)
975     {
976       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
977
978       if (pixel != BLACK_PIXEL)
979         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
980     }
981   }
982 }
983
984
985 /* ========================================================================= */
986 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
987 /* (Rotozoomer) by Andreas Schiffler                                         */
988 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
989 /* ========================================================================= */
990
991 /*
992   -----------------------------------------------------------------------------
993   32 bit zoomer
994
995   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
996   -----------------------------------------------------------------------------
997 */
998
999 typedef struct
1000 {
1001   Uint8 r;
1002   Uint8 g;
1003   Uint8 b;
1004   Uint8 a;
1005 } tColorRGBA;
1006
1007 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1008 {
1009   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1010   tColorRGBA *sp, *csp, *dp;
1011   int sgap, dgap;
1012
1013   /* variable setup */
1014   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1015   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1016
1017   /* allocate memory for row increments */
1018   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1019   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1020
1021   /* precalculate row increments */
1022   csx = 0;
1023   csax = sax;
1024   for (x = 0; x <= dst->w; x++)
1025   {
1026     *csax = csx;
1027     csax++;
1028     csx &= 0xffff;
1029     csx += sx;
1030   }
1031
1032   csy = 0;
1033   csay = say;
1034   for (y = 0; y <= dst->h; y++)
1035   {
1036     *csay = csy;
1037     csay++;
1038     csy &= 0xffff;
1039     csy += sy;
1040   }
1041
1042   /* pointer setup */
1043   sp = csp = (tColorRGBA *) src->pixels;
1044   dp = (tColorRGBA *) dst->pixels;
1045   sgap = src->pitch - src->w * 4;
1046   dgap = dst->pitch - dst->w * 4;
1047
1048   csay = say;
1049   for (y = 0; y < dst->h; y++)
1050   {
1051     sp = csp;
1052     csax = sax;
1053
1054     for (x = 0; x < dst->w; x++)
1055     {
1056       /* draw */
1057       *dp = *sp;
1058
1059       /* advance source pointers */
1060       csax++;
1061       sp += (*csax >> 16);
1062
1063       /* advance destination pointer */
1064       dp++;
1065     }
1066
1067     /* advance source pointer */
1068     csay++;
1069     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1070
1071     /* advance destination pointers */
1072     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1073   }
1074
1075   free(sax);
1076   free(say);
1077
1078   return 0;
1079 }
1080
1081 /*
1082   -----------------------------------------------------------------------------
1083   8 bit zoomer
1084
1085   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1086   -----------------------------------------------------------------------------
1087 */
1088
1089 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1090 {
1091   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1092   Uint8 *sp, *dp, *csp;
1093   int dgap;
1094
1095   /* variable setup */
1096   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1097   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1098
1099   /* allocate memory for row increments */
1100   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1101   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1102
1103   /* precalculate row increments */
1104   csx = 0;
1105   csax = sax;
1106   for (x = 0; x < dst->w; x++)
1107   {
1108     csx += sx;
1109     *csax = (csx >> 16);
1110     csx &= 0xffff;
1111     csax++;
1112   }
1113
1114   csy = 0;
1115   csay = say;
1116   for (y = 0; y < dst->h; y++)
1117   {
1118     csy += sy;
1119     *csay = (csy >> 16);
1120     csy &= 0xffff;
1121     csay++;
1122   }
1123
1124   csx = 0;
1125   csax = sax;
1126   for (x = 0; x < dst->w; x++)
1127   {
1128     csx += (*csax);
1129     csax++;
1130   }
1131
1132   csy = 0;
1133   csay = say;
1134   for (y = 0; y < dst->h; y++)
1135   {
1136     csy += (*csay);
1137     csay++;
1138   }
1139
1140   /* pointer setup */
1141   sp = csp = (Uint8 *) src->pixels;
1142   dp = (Uint8 *) dst->pixels;
1143   dgap = dst->pitch - dst->w;
1144
1145   /* draw */
1146   csay = say;
1147   for (y = 0; y < dst->h; y++)
1148   {
1149     csax = sax;
1150     sp = csp;
1151     for (x = 0; x < dst->w; x++)
1152     {
1153       /* draw */
1154       *dp = *sp;
1155
1156       /* advance source pointers */
1157       sp += (*csax);
1158       csax++;
1159
1160       /* advance destination pointer */
1161       dp++;
1162     }
1163
1164     /* advance source pointer (for row) */
1165     csp += ((*csay) * src->pitch);
1166     csay++;
1167
1168     /* advance destination pointers */
1169     dp += dgap;
1170   }
1171
1172   free(sax);
1173   free(say);
1174
1175   return 0;
1176 }
1177
1178 /*
1179   -----------------------------------------------------------------------------
1180   zoomSurface()
1181
1182   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1183   'zoomx' and 'zoomy' are scaling factors for width and height.
1184   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1185   into a 32bit RGBA format on the fly.
1186   -----------------------------------------------------------------------------
1187 */
1188
1189 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1190 {
1191   SDL_Surface *zoom_src = NULL;
1192   SDL_Surface *zoom_dst = NULL;
1193   boolean is_converted = FALSE;
1194   boolean is_32bit;
1195   int i;
1196
1197   if (src == NULL)
1198     return NULL;
1199
1200   /* determine if source surface is 32 bit or 8 bit */
1201   is_32bit = (src->format->BitsPerPixel == 32);
1202
1203   if (is_32bit || src->format->BitsPerPixel == 8)
1204   {
1205     /* use source surface 'as is' */
1206     zoom_src = src;
1207   }
1208   else
1209   {
1210     /* new source surface is 32 bit with a defined RGB ordering */
1211     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1212                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1213     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1214     is_32bit = TRUE;
1215     is_converted = TRUE;
1216   }
1217
1218   /* allocate surface to completely contain the zoomed surface */
1219   if (is_32bit)
1220   {
1221     /* target surface is 32 bit with source RGBA/ABGR ordering */
1222     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1223                                     zoom_src->format->Rmask,
1224                                     zoom_src->format->Gmask,
1225                                     zoom_src->format->Bmask, 0);
1226   }
1227   else
1228   {
1229     /* target surface is 8 bit */
1230     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1231                                     0, 0, 0, 0);
1232   }
1233
1234   /* lock source surface */
1235   SDL_LockSurface(zoom_src);
1236
1237   /* check which kind of surface we have */
1238   if (is_32bit)
1239   {
1240     /* call the 32 bit transformation routine to do the zooming */
1241     zoomSurfaceRGBA(zoom_src, zoom_dst);
1242   }
1243   else
1244   {
1245     /* copy palette */
1246     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1247       zoom_dst->format->palette->colors[i] =
1248         zoom_src->format->palette->colors[i];
1249     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1250
1251     /* call the 8 bit transformation routine to do the zooming */
1252     zoomSurfaceY(zoom_src, zoom_dst);
1253   }
1254
1255   /* unlock source surface */
1256   SDL_UnlockSurface(zoom_src);
1257
1258   /* free temporary surface */
1259   if (is_converted)
1260     SDL_FreeSurface(zoom_src);
1261
1262   /* return destination surface */
1263   return zoom_dst;
1264 }
1265
1266 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1267 {
1268   SDL_Surface *sdl_surface_tmp;
1269   int dst_width = dst_bitmap->width;
1270   int dst_height = dst_bitmap->height;
1271
1272   /* throw away old destination surface */
1273   SDL_FreeSurface(dst_bitmap->surface);
1274
1275   /* create zoomed temporary surface from source surface */
1276   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1277
1278   /* create native format destination surface from zoomed temporary surface */
1279   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1280
1281   /* free temporary surface */
1282   SDL_FreeSurface(sdl_surface_tmp);
1283 }
1284
1285
1286 /* ========================================================================= */
1287 /* load image to bitmap                                                      */
1288 /* ========================================================================= */
1289
1290 Bitmap *SDLLoadImage(char *filename)
1291 {
1292   Bitmap *new_bitmap = CreateBitmapStruct();
1293   SDL_Surface *sdl_image_tmp;
1294
1295   /* load image to temporary surface */
1296   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1297   {
1298     SetError("IMG_Load(): %s", SDL_GetError());
1299     return NULL;
1300   }
1301
1302   /* create native non-transparent surface for current image */
1303   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1304   {
1305     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1306     return NULL;
1307   }
1308
1309   /* create native transparent surface for current image */
1310   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1311                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1312   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1313   {
1314     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1315     return NULL;
1316   }
1317
1318   /* free temporary surface */
1319   SDL_FreeSurface(sdl_image_tmp);
1320
1321   new_bitmap->width = new_bitmap->surface->w;
1322   new_bitmap->height = new_bitmap->surface->h;
1323
1324   return new_bitmap;
1325 }
1326
1327
1328 /* ------------------------------------------------------------------------- */
1329 /* custom cursor fuctions                                                    */
1330 /* ------------------------------------------------------------------------- */
1331
1332 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1333 {
1334   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1335                           cursor_info->width, cursor_info->height,
1336                           cursor_info->hot_x, cursor_info->hot_y);
1337 }
1338
1339 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1340 {
1341   static struct MouseCursorInfo *last_cursor_info = NULL;
1342   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1343   static SDL_Cursor *cursor_default = NULL;
1344   static SDL_Cursor *cursor_current = NULL;
1345
1346   /* if invoked for the first time, store the SDL default cursor */
1347   if (cursor_default == NULL)
1348     cursor_default = SDL_GetCursor();
1349
1350   /* only create new cursor if cursor info (custom only) has changed */
1351   if (cursor_info != NULL && cursor_info != last_cursor_info)
1352   {
1353     cursor_current = create_cursor(cursor_info);
1354     last_cursor_info = cursor_info;
1355   }
1356
1357   /* only set new cursor if cursor info (custom or NULL) has changed */
1358   if (cursor_info != last_cursor_info2)
1359     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1360
1361   last_cursor_info2 = cursor_info;
1362 }
1363
1364
1365 /* ========================================================================= */
1366 /* audio functions                                                           */
1367 /* ========================================================================= */
1368
1369 void SDLOpenAudio(void)
1370 {
1371   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1372     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1373
1374   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1375   {
1376     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1377     return;
1378   }
1379
1380   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1381                     AUDIO_NUM_CHANNELS_STEREO,
1382                     setup.system.audio_fragment_size) < 0)
1383   {
1384     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1385     return;
1386   }
1387
1388   audio.sound_available = TRUE;
1389   audio.music_available = TRUE;
1390   audio.loops_available = TRUE;
1391   audio.sound_enabled = TRUE;
1392
1393   /* set number of available mixer channels */
1394   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1395   audio.music_channel = MUSIC_CHANNEL;
1396   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1397
1398   Mixer_InitChannels();
1399 }
1400
1401 void SDLCloseAudio(void)
1402 {
1403   Mix_HaltMusic();
1404   Mix_HaltChannel(-1);
1405
1406   Mix_CloseAudio();
1407   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1408 }
1409
1410
1411 /* ========================================================================= */
1412 /* event functions                                                           */
1413 /* ========================================================================= */
1414
1415 void SDLNextEvent(Event *event)
1416 {
1417   SDL_WaitEvent(event);
1418
1419 #ifdef FULLSCREEN_BUG
1420   if (event->type == EVENT_BUTTONPRESS ||
1421       event->type == EVENT_BUTTONRELEASE)
1422   {
1423     if (((ButtonEvent *)event)->x > video_xoffset)
1424       ((ButtonEvent *)event)->x -= video_xoffset;
1425     else
1426       ((ButtonEvent *)event)->x = 0;
1427     if (((ButtonEvent *)event)->y > video_yoffset)
1428       ((ButtonEvent *)event)->y -= video_yoffset;
1429     else
1430       ((ButtonEvent *)event)->y = 0;
1431   }
1432   else if (event->type == EVENT_MOTIONNOTIFY)
1433   {
1434     if (((MotionEvent *)event)->x > video_xoffset)
1435       ((MotionEvent *)event)->x -= video_xoffset;
1436     else
1437       ((MotionEvent *)event)->x = 0;
1438     if (((MotionEvent *)event)->y > video_yoffset)
1439       ((MotionEvent *)event)->y -= video_yoffset;
1440     else
1441       ((MotionEvent *)event)->y = 0;
1442   }
1443 #endif
1444 }
1445
1446
1447 /* ========================================================================= */
1448 /* joystick functions                                                        */
1449 /* ========================================================================= */
1450
1451 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1452 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1453 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1454
1455 static boolean SDLOpenJoystick(int nr)
1456 {
1457   if (nr < 0 || nr > MAX_PLAYERS)
1458     return FALSE;
1459
1460   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1461 }
1462
1463 static void SDLCloseJoystick(int nr)
1464 {
1465   if (nr < 0 || nr > MAX_PLAYERS)
1466     return;
1467
1468   SDL_JoystickClose(sdl_joystick[nr]);
1469 }
1470
1471 static boolean SDLCheckJoystickOpened(int nr)
1472 {
1473   if (nr < 0 || nr > MAX_PLAYERS)
1474     return FALSE;
1475
1476   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1477 }
1478
1479 void HandleJoystickEvent(Event *event)
1480 {
1481   switch(event->type)
1482   {
1483     case SDL_JOYAXISMOTION:
1484       if (event->jaxis.axis < 2)
1485         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1486       break;
1487
1488     case SDL_JOYBUTTONDOWN:
1489       if (event->jbutton.button < 2)
1490         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1491       break;
1492
1493     case SDL_JOYBUTTONUP:
1494       if (event->jbutton.button < 2)
1495         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1496       break;
1497
1498     default:
1499       break;
1500   }
1501 }
1502
1503 void SDLInitJoysticks()
1504 {
1505   static boolean sdl_joystick_subsystem_initialized = FALSE;
1506   boolean print_warning = !sdl_joystick_subsystem_initialized;
1507   int i;
1508
1509   if (!sdl_joystick_subsystem_initialized)
1510   {
1511     sdl_joystick_subsystem_initialized = TRUE;
1512
1513     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1514     {
1515       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1516       return;
1517     }
1518   }
1519
1520   for (i = 0; i < MAX_PLAYERS; i++)
1521   {
1522     /* get configured joystick for this player */
1523     char *device_name = setup.input[i].joy.device_name;
1524     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1525
1526     if (joystick_nr >= SDL_NumJoysticks())
1527     {
1528       if (setup.input[i].use_joystick && print_warning)
1529         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1530
1531       joystick_nr = -1;
1532     }
1533
1534     /* misuse joystick file descriptor variable to store joystick number */
1535     joystick.fd[i] = joystick_nr;
1536
1537     if (joystick_nr == -1)
1538       continue;
1539
1540     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1541     if (SDLCheckJoystickOpened(joystick_nr))
1542       SDLCloseJoystick(joystick_nr);
1543
1544     if (!setup.input[i].use_joystick)
1545       continue;
1546
1547     if (!SDLOpenJoystick(joystick_nr))
1548     {
1549       if (print_warning)
1550         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1551
1552       continue;
1553     }
1554
1555     joystick.status = JOYSTICK_ACTIVATED;
1556   }
1557 }
1558
1559 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1560 {
1561   if (nr < 0 || nr >= MAX_PLAYERS)
1562     return FALSE;
1563
1564   if (x != NULL)
1565     *x = sdl_js_axis[nr][0];
1566   if (y != NULL)
1567     *y = sdl_js_axis[nr][1];
1568
1569   if (b1 != NULL)
1570     *b1 = sdl_js_button[nr][0];
1571   if (b2 != NULL)
1572     *b2 = sdl_js_button[nr][1];
1573
1574   return TRUE;
1575 }
1576
1577 #endif /* TARGET_SDL */