rnd-20060502-2-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(SDL_Surface *src, SDL_Surface *dst)
1050 {
1051   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1052   tColorRGBA *sp, *csp, *dp;
1053   int sgap, dgap;
1054
1055   /* variable setup */
1056   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1057   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1058
1059   /* allocate memory for row increments */
1060   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1061   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1062
1063   /* precalculate row increments */
1064   csx = 0;
1065   csax = sax;
1066   for (x = 0; x <= dst->w; x++)
1067   {
1068     *csax = csx;
1069     csax++;
1070     csx &= 0xffff;
1071     csx += sx;
1072   }
1073
1074   csy = 0;
1075   csay = say;
1076   for (y = 0; y <= dst->h; y++)
1077   {
1078     *csay = csy;
1079     csay++;
1080     csy &= 0xffff;
1081     csy += sy;
1082   }
1083
1084   /* pointer setup */
1085   sp = csp = (tColorRGBA *) src->pixels;
1086   dp = (tColorRGBA *) dst->pixels;
1087   sgap = src->pitch - src->w * 4;
1088   dgap = dst->pitch - dst->w * 4;
1089
1090   csay = say;
1091   for (y = 0; y < dst->h; y++)
1092   {
1093     sp = csp;
1094     csax = sax;
1095
1096     for (x = 0; x < dst->w; x++)
1097     {
1098       /* draw */
1099       *dp = *sp;
1100
1101       /* advance source pointers */
1102       csax++;
1103       sp += (*csax >> 16);
1104
1105       /* advance destination pointer */
1106       dp++;
1107     }
1108
1109     /* advance source pointer */
1110     csay++;
1111     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1112
1113     /* advance destination pointers */
1114     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1115   }
1116
1117   free(sax);
1118   free(say);
1119
1120   return 0;
1121 }
1122
1123 /*
1124   -----------------------------------------------------------------------------
1125   8 bit zoomer
1126
1127   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1128   -----------------------------------------------------------------------------
1129 */
1130
1131 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1132 {
1133   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1134   Uint8 *sp, *dp, *csp;
1135   int dgap;
1136
1137   /* variable setup */
1138   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1139   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1140
1141   /* allocate memory for row increments */
1142   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1143   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1144
1145   /* precalculate row increments */
1146   csx = 0;
1147   csax = sax;
1148   for (x = 0; x < dst->w; x++)
1149   {
1150     csx += sx;
1151     *csax = (csx >> 16);
1152     csx &= 0xffff;
1153     csax++;
1154   }
1155
1156   csy = 0;
1157   csay = say;
1158   for (y = 0; y < dst->h; y++)
1159   {
1160     csy += sy;
1161     *csay = (csy >> 16);
1162     csy &= 0xffff;
1163     csay++;
1164   }
1165
1166   csx = 0;
1167   csax = sax;
1168   for (x = 0; x < dst->w; x++)
1169   {
1170     csx += (*csax);
1171     csax++;
1172   }
1173
1174   csy = 0;
1175   csay = say;
1176   for (y = 0; y < dst->h; y++)
1177   {
1178     csy += (*csay);
1179     csay++;
1180   }
1181
1182   /* pointer setup */
1183   sp = csp = (Uint8 *) src->pixels;
1184   dp = (Uint8 *) dst->pixels;
1185   dgap = dst->pitch - dst->w;
1186
1187   /* draw */
1188   csay = say;
1189   for (y = 0; y < dst->h; y++)
1190   {
1191     csax = sax;
1192     sp = csp;
1193     for (x = 0; x < dst->w; x++)
1194     {
1195       /* draw */
1196       *dp = *sp;
1197
1198       /* advance source pointers */
1199       sp += (*csax);
1200       csax++;
1201
1202       /* advance destination pointer */
1203       dp++;
1204     }
1205
1206     /* advance source pointer (for row) */
1207     csp += ((*csay) * src->pitch);
1208     csay++;
1209
1210     /* advance destination pointers */
1211     dp += dgap;
1212   }
1213
1214   free(sax);
1215   free(say);
1216
1217   return 0;
1218 }
1219
1220 /*
1221   -----------------------------------------------------------------------------
1222   zoomSurface()
1223
1224   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1225   'zoomx' and 'zoomy' are scaling factors for width and height.
1226   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1227   into a 32bit RGBA format on the fly.
1228   -----------------------------------------------------------------------------
1229 */
1230
1231 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1232 {
1233   SDL_Surface *zoom_src = NULL;
1234   SDL_Surface *zoom_dst = NULL;
1235   boolean is_converted = FALSE;
1236   boolean is_32bit;
1237   int i;
1238
1239   if (src == NULL)
1240     return NULL;
1241
1242   /* determine if source surface is 32 bit or 8 bit */
1243   is_32bit = (src->format->BitsPerPixel == 32);
1244
1245   if (is_32bit || src->format->BitsPerPixel == 8)
1246   {
1247     /* use source surface 'as is' */
1248     zoom_src = src;
1249   }
1250   else
1251   {
1252     /* new source surface is 32 bit with a defined RGB ordering */
1253     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1254                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1255     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1256     is_32bit = TRUE;
1257     is_converted = TRUE;
1258   }
1259
1260   /* allocate surface to completely contain the zoomed surface */
1261   if (is_32bit)
1262   {
1263     /* target surface is 32 bit with source RGBA/ABGR ordering */
1264     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1265                                     zoom_src->format->Rmask,
1266                                     zoom_src->format->Gmask,
1267                                     zoom_src->format->Bmask, 0);
1268   }
1269   else
1270   {
1271     /* target surface is 8 bit */
1272     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1273                                     0, 0, 0, 0);
1274   }
1275
1276   /* lock source surface */
1277   SDL_LockSurface(zoom_src);
1278
1279   /* check which kind of surface we have */
1280   if (is_32bit)
1281   {
1282     /* call the 32 bit transformation routine to do the zooming */
1283     zoomSurfaceRGBA(zoom_src, zoom_dst);
1284   }
1285   else
1286   {
1287     /* copy palette */
1288     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1289       zoom_dst->format->palette->colors[i] =
1290         zoom_src->format->palette->colors[i];
1291     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1292
1293     /* call the 8 bit transformation routine to do the zooming */
1294     zoomSurfaceY(zoom_src, zoom_dst);
1295   }
1296
1297   /* unlock source surface */
1298   SDL_UnlockSurface(zoom_src);
1299
1300   /* free temporary surface */
1301   if (is_converted)
1302     SDL_FreeSurface(zoom_src);
1303
1304   /* return destination surface */
1305   return zoom_dst;
1306 }
1307
1308 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1309 {
1310   SDL_Surface *sdl_surface_tmp;
1311   int dst_width = dst_bitmap->width;
1312   int dst_height = dst_bitmap->height;
1313
1314   /* throw away old destination surface */
1315   SDL_FreeSurface(dst_bitmap->surface);
1316
1317   /* create zoomed temporary surface from source surface */
1318   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1319
1320   /* create native format destination surface from zoomed temporary surface */
1321   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1322
1323   /* free temporary surface */
1324   SDL_FreeSurface(sdl_surface_tmp);
1325 }
1326
1327
1328 /* ========================================================================= */
1329 /* load image to bitmap                                                      */
1330 /* ========================================================================= */
1331
1332 Bitmap *SDLLoadImage(char *filename)
1333 {
1334   Bitmap *new_bitmap = CreateBitmapStruct();
1335   SDL_Surface *sdl_image_tmp;
1336
1337   /* load image to temporary surface */
1338   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1339   {
1340     SetError("IMG_Load(): %s", SDL_GetError());
1341     return NULL;
1342   }
1343
1344   /* create native non-transparent surface for current image */
1345   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1346   {
1347     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1348     return NULL;
1349   }
1350
1351   /* create native transparent surface for current image */
1352   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1353                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1354   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1355   {
1356     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1357     return NULL;
1358   }
1359
1360   /* free temporary surface */
1361   SDL_FreeSurface(sdl_image_tmp);
1362
1363   new_bitmap->width = new_bitmap->surface->w;
1364   new_bitmap->height = new_bitmap->surface->h;
1365
1366   return new_bitmap;
1367 }
1368
1369
1370 /* ------------------------------------------------------------------------- */
1371 /* custom cursor fuctions                                                    */
1372 /* ------------------------------------------------------------------------- */
1373
1374 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1375 {
1376   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1377                           cursor_info->width, cursor_info->height,
1378                           cursor_info->hot_x, cursor_info->hot_y);
1379 }
1380
1381 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1382 {
1383   static struct MouseCursorInfo *last_cursor_info = NULL;
1384   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1385   static SDL_Cursor *cursor_default = NULL;
1386   static SDL_Cursor *cursor_current = NULL;
1387
1388   /* if invoked for the first time, store the SDL default cursor */
1389   if (cursor_default == NULL)
1390     cursor_default = SDL_GetCursor();
1391
1392   /* only create new cursor if cursor info (custom only) has changed */
1393   if (cursor_info != NULL && cursor_info != last_cursor_info)
1394   {
1395     cursor_current = create_cursor(cursor_info);
1396     last_cursor_info = cursor_info;
1397   }
1398
1399   /* only set new cursor if cursor info (custom or NULL) has changed */
1400   if (cursor_info != last_cursor_info2)
1401     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1402
1403   last_cursor_info2 = cursor_info;
1404 }
1405
1406
1407 /* ========================================================================= */
1408 /* audio functions                                                           */
1409 /* ========================================================================= */
1410
1411 void SDLOpenAudio(void)
1412 {
1413   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1414     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1415
1416   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1417   {
1418     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1419     return;
1420   }
1421
1422   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1423                     AUDIO_NUM_CHANNELS_STEREO,
1424                     setup.system.audio_fragment_size) < 0)
1425   {
1426     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1427     return;
1428   }
1429
1430   audio.sound_available = TRUE;
1431   audio.music_available = TRUE;
1432   audio.loops_available = TRUE;
1433   audio.sound_enabled = TRUE;
1434
1435   /* set number of available mixer channels */
1436   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1437   audio.music_channel = MUSIC_CHANNEL;
1438   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1439
1440   Mixer_InitChannels();
1441 }
1442
1443 void SDLCloseAudio(void)
1444 {
1445   Mix_HaltMusic();
1446   Mix_HaltChannel(-1);
1447
1448   Mix_CloseAudio();
1449   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1450 }
1451
1452
1453 /* ========================================================================= */
1454 /* event functions                                                           */
1455 /* ========================================================================= */
1456
1457 void SDLNextEvent(Event *event)
1458 {
1459   SDL_WaitEvent(event);
1460
1461 #ifdef FULLSCREEN_BUG
1462   if (event->type == EVENT_BUTTONPRESS ||
1463       event->type == EVENT_BUTTONRELEASE)
1464   {
1465     if (((ButtonEvent *)event)->x > video_xoffset)
1466       ((ButtonEvent *)event)->x -= video_xoffset;
1467     else
1468       ((ButtonEvent *)event)->x = 0;
1469     if (((ButtonEvent *)event)->y > video_yoffset)
1470       ((ButtonEvent *)event)->y -= video_yoffset;
1471     else
1472       ((ButtonEvent *)event)->y = 0;
1473   }
1474   else if (event->type == EVENT_MOTIONNOTIFY)
1475   {
1476     if (((MotionEvent *)event)->x > video_xoffset)
1477       ((MotionEvent *)event)->x -= video_xoffset;
1478     else
1479       ((MotionEvent *)event)->x = 0;
1480     if (((MotionEvent *)event)->y > video_yoffset)
1481       ((MotionEvent *)event)->y -= video_yoffset;
1482     else
1483       ((MotionEvent *)event)->y = 0;
1484   }
1485 #endif
1486 }
1487
1488
1489 /* ========================================================================= */
1490 /* joystick functions                                                        */
1491 /* ========================================================================= */
1492
1493 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1494 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1495 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1496
1497 static boolean SDLOpenJoystick(int nr)
1498 {
1499   if (nr < 0 || nr > MAX_PLAYERS)
1500     return FALSE;
1501
1502   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1503 }
1504
1505 static void SDLCloseJoystick(int nr)
1506 {
1507   if (nr < 0 || nr > MAX_PLAYERS)
1508     return;
1509
1510   SDL_JoystickClose(sdl_joystick[nr]);
1511 }
1512
1513 static boolean SDLCheckJoystickOpened(int nr)
1514 {
1515   if (nr < 0 || nr > MAX_PLAYERS)
1516     return FALSE;
1517
1518   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1519 }
1520
1521 void HandleJoystickEvent(Event *event)
1522 {
1523   switch(event->type)
1524   {
1525     case SDL_JOYAXISMOTION:
1526       if (event->jaxis.axis < 2)
1527         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1528       break;
1529
1530     case SDL_JOYBUTTONDOWN:
1531       if (event->jbutton.button < 2)
1532         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1533       break;
1534
1535     case SDL_JOYBUTTONUP:
1536       if (event->jbutton.button < 2)
1537         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1538       break;
1539
1540     default:
1541       break;
1542   }
1543 }
1544
1545 void SDLInitJoysticks()
1546 {
1547   static boolean sdl_joystick_subsystem_initialized = FALSE;
1548   boolean print_warning = !sdl_joystick_subsystem_initialized;
1549   int i;
1550
1551   if (!sdl_joystick_subsystem_initialized)
1552   {
1553     sdl_joystick_subsystem_initialized = TRUE;
1554
1555     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1556     {
1557       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1558       return;
1559     }
1560   }
1561
1562   for (i = 0; i < MAX_PLAYERS; i++)
1563   {
1564     /* get configured joystick for this player */
1565     char *device_name = setup.input[i].joy.device_name;
1566     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1567
1568     if (joystick_nr >= SDL_NumJoysticks())
1569     {
1570       if (setup.input[i].use_joystick && print_warning)
1571         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1572
1573       joystick_nr = -1;
1574     }
1575
1576     /* misuse joystick file descriptor variable to store joystick number */
1577     joystick.fd[i] = joystick_nr;
1578
1579     if (joystick_nr == -1)
1580       continue;
1581
1582     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1583     if (SDLCheckJoystickOpened(joystick_nr))
1584       SDLCloseJoystick(joystick_nr);
1585
1586     if (!setup.input[i].use_joystick)
1587       continue;
1588
1589     if (!SDLOpenJoystick(joystick_nr))
1590     {
1591       if (print_warning)
1592         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1593
1594       continue;
1595     }
1596
1597     joystick.status = JOYSTICK_ACTIVATED;
1598   }
1599 }
1600
1601 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1602 {
1603   if (nr < 0 || nr >= MAX_PLAYERS)
1604     return FALSE;
1605
1606   if (x != NULL)
1607     *x = sdl_js_axis[nr][0];
1608   if (y != NULL)
1609     *y = sdl_js_axis[nr][1];
1610
1611   if (b1 != NULL)
1612     *b1 = sdl_js_button[nr][0];
1613   if (b2 != NULL)
1614     *b2 = sdl_js_button[nr][1];
1615
1616   return TRUE;
1617 }
1618
1619 #endif /* TARGET_SDL */