rnd-20020424-1-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 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 inline 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 inline void SDLInitVideoDisplay(void)
42 {
43   /* initialize SDL video */
44   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
45     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
46
47   /* set default SDL depth */
48   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
49 }
50
51 inline void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
52                                boolean fullscreen)
53 {
54 #ifdef FULLSCREEN_BUG
55   int i;
56   static int screen_xy[][2] =
57   {
58     {  640, 480 },
59     {  800, 600 },
60     { 1024, 768 },
61     {   -1,  -1 }
62   };
63 #endif
64
65   /* default: normal game window size */
66   fullscreen_width = video.width;
67   fullscreen_height = video.height;
68   fullscreen_xoffset = 0;
69   fullscreen_yoffset = 0;
70
71 #ifdef FULLSCREEN_BUG
72   for (i=0; screen_xy[i][0] != -1; i++)
73   {
74     if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
75     {
76       fullscreen_width = screen_xy[i][0];
77       fullscreen_height = screen_xy[i][1];
78       break;
79     }
80   }
81
82   fullscreen_xoffset = (fullscreen_width - video.width) / 2;
83   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
84 #endif
85
86   /* open SDL video output device (window or fullscreen mode) */
87   if (!SDLSetVideoMode(backbuffer, fullscreen))
88     Error(ERR_EXIT, "setting video mode failed");
89
90   /* set window and icon title */
91   SDL_WM_SetCaption(program.window_title, program.window_title);
92
93   /* SDL cannot directly draw to the visible video framebuffer like X11,
94      but always uses a backbuffer, which is then blitted to the visible
95      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
96      visible video framebuffer with 'SDL_Flip', if the hardware supports
97      this). Therefore do not use an additional backbuffer for drawing, but
98      use a symbolic buffer (distinguishable from the SDL backbuffer) called
99      'window', which indicates that the SDL backbuffer should be updated to
100      the visible video framebuffer when attempting to blit to it.
101
102      For convenience, it seems to be a good idea to create this symbolic
103      buffer 'window' at the same size as the SDL backbuffer. Although it
104      should never be drawn to directly, it would do no harm nevertheless. */
105
106   /* create additional (symbolic) buffer for double-buffering */
107   *window = CreateBitmap(video.width, video.height, video.depth);
108 }
109
110 inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
111 {
112   boolean success = TRUE;
113   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
114   int surface_flags_window = SURFACE_FLAGS;
115   SDL_Surface *new_surface = NULL;
116
117   if (*backbuffer == NULL)
118     *backbuffer = CreateBitmapStruct();
119
120   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
121   {
122     video_xoffset = fullscreen_xoffset;
123     video_yoffset = fullscreen_yoffset;
124
125     /* switch display to fullscreen mode, if available */
126     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
127                                         video.depth, surface_flags_fullscreen))
128         == NULL)
129     {
130       /* switching display to fullscreen mode failed */
131       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
132
133       /* do not try it again */
134       video.fullscreen_available = FALSE;
135       success = FALSE;
136     }
137     else
138     {
139       (*backbuffer)->surface = new_surface;
140
141       video.fullscreen_enabled = TRUE;
142       success = TRUE;
143     }
144   }
145
146   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
147   {
148     video_xoffset = 0;
149     video_yoffset = 0;
150
151     /* switch display to window mode */
152     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
153                                         video.depth, surface_flags_window))
154         == NULL)
155     {
156       /* switching display to window mode failed -- should not happen */
157       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
158
159       success = FALSE;
160     }
161     else
162     {
163       (*backbuffer)->surface = new_surface;
164
165       video.fullscreen_enabled = FALSE;
166       success = TRUE;
167     }
168   }
169
170   return success;
171 }
172
173 inline void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
174                         int src_x, int src_y,
175                         int width, int height,
176                         int dst_x, int dst_y, int copy_mode)
177 {
178   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
179   SDL_Rect src_rect, dst_rect;
180
181 #ifdef FULLSCREEN_BUG
182   if (src_bitmap == backbuffer)
183   {
184     src_x += video_xoffset;
185     src_y += video_yoffset;
186   }
187 #endif
188
189   src_rect.x = src_x;
190   src_rect.y = src_y;
191   src_rect.w = width;
192   src_rect.h = height;
193
194 #ifdef FULLSCREEN_BUG
195   if (dst_bitmap == backbuffer || dst_bitmap == window)
196   {
197     dst_x += video_xoffset;
198     dst_y += video_yoffset;
199   }
200 #endif
201
202   dst_rect.x = dst_x;
203   dst_rect.y = dst_y;
204   dst_rect.w = width;
205   dst_rect.h = height;
206
207   if (src_bitmap != backbuffer || dst_bitmap != window)
208     SDL_BlitSurface((copy_mode == SDLCOPYAREA_MASKED ?
209                      src_bitmap->surface_masked : src_bitmap->surface),
210                     &src_rect, real_dst_bitmap->surface, &dst_rect);
211
212   if (dst_bitmap == window)
213     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
214 }
215
216 inline void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
217                              int width, int height, unsigned int color)
218 {
219   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
220   SDL_Rect rect;
221   unsigned int color_r = (color >> 16) && 0xff;
222   unsigned int color_g = (color >>  8) && 0xff;
223   unsigned int color_b = (color >>  0) && 0xff;
224
225 #ifdef FULLSCREEN_BUG
226   if (dst_bitmap == backbuffer || dst_bitmap == window)
227   {
228     x += video_xoffset;
229     y += video_yoffset;
230   }
231 #endif
232
233   rect.x = x;
234   rect.y = y;
235   rect.w = width;
236   rect.h = height;
237
238   SDL_FillRect(real_dst_bitmap->surface, &rect,
239                SDL_MapRGB(real_dst_bitmap->surface->format,
240                           color_r, color_g, color_b));
241
242   if (dst_bitmap == window)
243     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
244 }
245
246 inline void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
247                               int to_x, int to_y, unsigned int color)
248 {
249   SDL_Surface *surface = dst_bitmap->surface;
250   SDL_Rect rect;
251   unsigned int color_r = (color >> 16) & 0xff;
252   unsigned int color_g = (color >>  8) & 0xff;
253   unsigned int color_b = (color >>  0) & 0xff;
254
255   if (from_x > to_x)
256     swap_numbers(&from_x, &to_x);
257
258   if (from_y > to_y)
259     swap_numbers(&from_y, &to_y);
260
261   rect.x = from_x;
262   rect.y = from_y;
263   rect.w = (to_x - from_x + 1);
264   rect.h = (to_y - from_y + 1);
265
266 #ifdef FULLSCREEN_BUG
267   if (dst_bitmap == backbuffer || dst_bitmap == window)
268   {
269     rect.x += video_xoffset;
270     rect.y += video_yoffset;
271   }
272 #endif
273
274   SDL_FillRect(surface, &rect,
275                SDL_MapRGB(surface->format, color_r, color_g, color_b));
276 }
277
278 inline void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
279                         int to_x, int to_y, Uint32 color)
280 {
281 #ifdef FULLSCREEN_BUG
282   if (dst_bitmap == backbuffer || dst_bitmap == window)
283   {
284     from_x += video_xoffset;
285     from_y += video_yoffset;
286     to_x += video_xoffset;
287     to_y += video_yoffset;
288   }
289 #endif
290
291   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
292 }
293
294 #if 0
295 inline void SDLDrawLines(SDL_Surface *surface, struct XY *points,
296                          int num_points, Uint32 color)
297 {
298   int i, x, y;
299   int line_width = 4;
300
301   for (i=0; i<num_points - 1; i++)
302   {
303     for (x=0; x<line_width; x++)
304     {
305       for (y=0; y<line_width; y++)
306       {
307         int dx = x - line_width / 2;
308         int dy = y - line_width / 2;
309
310         if ((x == 0 && y == 0) ||
311             (x == 0 && y == line_width - 1) ||
312             (x == line_width - 1 && y == 0) ||
313             (x == line_width - 1 && y == line_width - 1))
314           continue;
315
316         sge_Line(surface, points[i].x + dx, points[i].y + dy,
317                  points[i+1].x + dx, points[i+1].y + dy, color);
318       }
319     }
320   }
321 }
322 #endif
323
324 inline Pixel SDLGetPixel(Bitmap *dst_bitmap, int x, int y)
325 {
326   SDL_Surface *surface = dst_bitmap->surface;
327
328 #ifdef FULLSCREEN_BUG
329   if (dst_bitmap == backbuffer || dst_bitmap == window)
330   {
331     x += video_xoffset;
332     y += video_yoffset;
333   }
334 #endif
335
336   switch (surface->format->BytesPerPixel)
337   {
338     case 1:             /* assuming 8-bpp */
339     {
340       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
341     }
342     break;
343
344     case 2:             /* probably 15-bpp or 16-bpp */
345     {
346       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
347     }
348     break;
349
350   case 3:               /* slow 24-bpp mode; usually not used */
351     {
352       /* does this work? */
353       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
354       Uint32 color = 0;
355       int shift;
356
357       shift = surface->format->Rshift;
358       color |= *(pix + shift / 8) >> shift;
359       shift = surface->format->Gshift;
360       color |= *(pix + shift / 8) >> shift;
361       shift = surface->format->Bshift;
362       color |= *(pix + shift / 8) >> shift;
363
364       return color;
365     }
366     break;
367
368   case 4:               /* probably 32-bpp */
369     {
370       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
371     }
372     break;
373   }
374
375   return 0;
376 }
377
378
379 /* ========================================================================= */
380 /* The following functions have been taken from the SGE library              */
381 /* (SDL Graphics Extension Library) by Anders Lindström                      */
382 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
383 /* ========================================================================= */
384
385 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
386 {
387   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
388   {
389     switch (surface->format->BytesPerPixel)
390     {
391       case 1:
392       {
393         /* Assuming 8-bpp */
394         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
395       }
396       break;
397
398       case 2:
399       {
400         /* Probably 15-bpp or 16-bpp */
401         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
402       }
403       break;
404
405       case 3:
406       {
407         /* Slow 24-bpp mode, usually not used */
408         Uint8 *pix;
409         int shift;
410
411         /* Gack - slow, but endian correct */
412         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
413         shift = surface->format->Rshift;
414         *(pix+shift/8) = color>>shift;
415         shift = surface->format->Gshift;
416         *(pix+shift/8) = color>>shift;
417         shift = surface->format->Bshift;
418         *(pix+shift/8) = color>>shift;
419       }
420       break;
421
422       case 4:
423       {
424         /* Probably 32-bpp */
425         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
426       }
427       break;
428     }
429   }
430 }
431
432 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
433                   Uint8 R, Uint8 G, Uint8 B)
434 {
435   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
436 }
437
438 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
439 {
440   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
441 }
442
443 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
444 {
445   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
446 }
447
448 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
449 {
450   Uint8 *pix;
451   int shift;
452
453   /* Gack - slow, but endian correct */
454   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
455   shift = surface->format->Rshift;
456   *(pix+shift/8) = color>>shift;
457   shift = surface->format->Gshift;
458   *(pix+shift/8) = color>>shift;
459   shift = surface->format->Bshift;
460   *(pix+shift/8) = color>>shift;
461 }
462
463 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
464 {
465   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
466 }
467
468 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
469 {
470   switch (dest->format->BytesPerPixel)
471   {
472     case 1:
473       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
474       break;
475
476     case 2:
477       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
478       break;
479
480     case 3:
481       _PutPixel24(dest,x,y,color);
482       break;
483
484     case 4:
485       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
486       break;
487   }
488 }
489
490 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
491 {
492   if (SDL_MUSTLOCK(surface))
493   {
494     if (SDL_LockSurface(surface) < 0)
495     {
496       return;
497     }
498   }
499
500   _PutPixel(surface, x, y, color);
501
502   if (SDL_MUSTLOCK(surface))
503   {
504     SDL_UnlockSurface(surface);
505   }
506 }
507
508 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
509                   Uint8 R, Uint8 G, Uint8 B)
510 {
511   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
512 }
513
514 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
515 {
516   if (y >= 0 && y <= dest->h - 1)
517   {
518     switch (dest->format->BytesPerPixel)
519     {
520       case 1:
521         return y*dest->pitch;
522         break;
523
524       case 2:
525         return y*dest->pitch/2;
526         break;
527
528       case 3:
529         return y*dest->pitch;
530         break;
531
532       case 4:
533         return y*dest->pitch/4;
534         break;
535     }
536   }
537
538   return -1;
539 }
540
541 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
542 {
543   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
544   {
545     switch (surface->format->BytesPerPixel)
546     {
547       case 1:
548       {
549         /* Assuming 8-bpp */
550         *((Uint8 *)surface->pixels + ypitch + x) = color;
551       }
552       break;
553
554       case 2:
555       {
556         /* Probably 15-bpp or 16-bpp */
557         *((Uint16 *)surface->pixels + ypitch + x) = color;
558       }
559       break;
560
561       case 3:
562       {
563         /* Slow 24-bpp mode, usually not used */
564         Uint8 *pix;
565         int shift;
566
567         /* Gack - slow, but endian correct */
568         pix = (Uint8 *)surface->pixels + ypitch + x*3;
569         shift = surface->format->Rshift;
570         *(pix+shift/8) = color>>shift;
571         shift = surface->format->Gshift;
572         *(pix+shift/8) = color>>shift;
573         shift = surface->format->Bshift;
574         *(pix+shift/8) = color>>shift;
575       }
576       break;
577
578       case 4:
579       {
580         /* Probably 32-bpp */
581         *((Uint32 *)surface->pixels + ypitch + x) = color;
582       }
583       break;
584     }
585   }
586 }
587
588 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
589                Uint32 Color)
590 {
591   SDL_Rect l;
592
593   if (SDL_MUSTLOCK(Surface))
594   {
595     if (SDL_LockSurface(Surface) < 0)
596     {
597       return;
598     }
599   }
600
601   if (x1 > x2)
602   {
603     Sint16 tmp = x1;
604     x1 = x2;
605     x2 = tmp;
606   }
607
608   /* Do the clipping */
609   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
610     return;
611   if (x1 < 0)
612     x1 = 0;
613   if (x2 > Surface->w - 1)
614     x2 = Surface->w - 1;
615
616   l.x = x1;
617   l.y = y;
618   l.w = x2 - x1 + 1;
619   l.h = 1;
620
621   SDL_FillRect(Surface, &l, Color);
622
623   if (SDL_MUSTLOCK(Surface))
624   {
625     SDL_UnlockSurface(Surface);
626   }
627 }
628
629 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
630                   Uint8 R, Uint8 G, Uint8 B)
631 {
632   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
633 }
634
635 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
636 {
637   SDL_Rect l;
638
639   if (x1 > x2)
640   {
641     Sint16 tmp = x1;
642     x1 = x2;
643     x2 = tmp;
644   }
645
646   /* Do the clipping */
647   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
648     return;
649   if (x1 < 0)
650     x1 = 0;
651   if (x2 > Surface->w - 1)
652     x2 = Surface->w - 1;
653
654   l.x = x1;
655   l.y = y;
656   l.w = x2 - x1 + 1;
657   l.h = 1;
658
659   SDL_FillRect(Surface, &l, Color);
660 }
661
662 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
663                Uint32 Color)
664 {
665   SDL_Rect l;
666
667   if (SDL_MUSTLOCK(Surface))
668   {
669     if (SDL_LockSurface(Surface) < 0)
670     {
671       return;
672     }
673   }
674
675   if (y1 > y2)
676   {
677     Sint16 tmp = y1;
678     y1 = y2;
679     y2 = tmp;
680   }
681
682   /* Do the clipping */
683   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
684     return;
685   if (y1 < 0)
686     y1 = 0;
687   if (y2 > Surface->h - 1)
688     y2 = Surface->h - 1;
689
690   l.x = x;
691   l.y = y1;
692   l.w = 1;
693   l.h = y2 - y1 + 1;
694
695   SDL_FillRect(Surface, &l, Color);
696
697   if (SDL_MUSTLOCK(Surface))
698   {
699     SDL_UnlockSurface(Surface);
700   }
701 }
702
703 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
704                   Uint8 R, Uint8 G, Uint8 B)
705 {
706   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
707 }
708
709 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
710 {
711   SDL_Rect l;
712
713   if (y1 > y2)
714   {
715     Sint16 tmp = y1;
716     y1 = y2;
717     y2 = tmp;
718   }
719
720   /* Do the clipping */
721   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
722     return;
723   if (y1 < 0)
724     y1 = 0;
725   if (y2 > Surface->h - 1)
726     y2 = Surface->h - 1;
727
728   l.x = x;
729   l.y = y1;
730   l.w = 1;
731   l.h = y2 - y1 + 1;
732
733   SDL_FillRect(Surface, &l, Color);
734 }
735
736 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
737                 Sint16 x2, Sint16 y2, Uint32 Color,
738                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
739                               Uint32 Color))
740 {
741   Sint16 dx, dy, sdx, sdy, x, y, px, py;
742
743   dx = x2 - x1;
744   dy = y2 - y1;
745
746   sdx = (dx < 0) ? -1 : 1;
747   sdy = (dy < 0) ? -1 : 1;
748
749   dx = sdx * dx + 1;
750   dy = sdy * dy + 1;
751
752   x = y = 0;
753
754   px = x1;
755   py = y1;
756
757   if (dx >= dy)
758   {
759     for (x = 0; x < dx; x++)
760     {
761       Callback(Surface, px, py, Color);
762
763       y += dy;
764       if (y >= dx)
765       {
766         y -= dx;
767         py += sdy;
768       }
769
770       px += sdx;
771     }
772   }
773   else
774   {
775     for (y = 0; y < dy; y++)
776     {
777       Callback(Surface, px, py, Color);
778
779       x += dx;
780       if (x >= dy)
781       {
782         x -= dy;
783         px += sdx;
784       }
785
786       py += sdy;
787     }
788   }
789 }
790
791 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
792                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
793                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
794                                  Uint32 Color))
795 {
796   sge_DoLine(Surface, X1, Y1, X2, Y2,
797              SDL_MapRGB(Surface->format, R, G, B), Callback);
798 }
799
800 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
801               Uint32 Color)
802 {
803   if (SDL_MUSTLOCK(Surface))
804   {
805     if (SDL_LockSurface(Surface) < 0)
806       return;
807    }
808
809    /* Draw the line */
810    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
811
812    /* unlock the display */
813    if (SDL_MUSTLOCK(Surface))
814    {
815       SDL_UnlockSurface(Surface);
816    }
817 }
818
819 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
820                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
821 {
822   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
823 }
824
825 Bitmap *SDLLoadImage(char *filename)
826 {
827   Bitmap *new_bitmap = CreateBitmapStruct();
828   SDL_Surface *sdl_image_tmp;
829
830   /* load image to temporary surface */
831   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
832   {
833     SetError("IMG_Load(): %s", SDL_GetError());
834     return NULL;
835   }
836
837   /* create native non-transparent surface for current image */
838   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
839   {
840     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
841     return NULL;
842   }
843
844   /* create native transparent surface for current image */
845   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
846                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
847   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
848   {
849     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
850     return NULL;
851   }
852
853   /* free temporary surface */
854   SDL_FreeSurface(sdl_image_tmp);
855
856   new_bitmap->width = new_bitmap->surface->w;
857   new_bitmap->height = new_bitmap->surface->h;
858
859   return new_bitmap;
860 }
861
862
863 /* ========================================================================= */
864 /* audio functions                                                           */
865 /* ========================================================================= */
866
867 inline void SDLOpenAudio(void)
868 {
869   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
870   {
871     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
872     return;
873   }
874
875   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, AUDIO_S16,
876                     AUDIO_STEREO_CHANNELS,
877                     DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
878   {
879     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
880     return;
881   }
882
883   audio.sound_available = TRUE;
884   audio.music_available = TRUE;
885   audio.loops_available = TRUE;
886   audio.sound_enabled = TRUE;
887
888   /* determine number of available channels */
889   audio.channels = Mix_AllocateChannels(MIX_CHANNELS);
890
891   /* reserve first channel for music loops */
892   if (Mix_ReserveChannels(1) == 1)
893     audio.music_channel = 0;
894   else
895     audio.music_available = FALSE;
896
897   Mix_Volume(-1, SOUND_MAX_VOLUME);
898   Mix_VolumeMusic(SOUND_MAX_VOLUME);
899 }
900
901 inline void SDLCloseAudio(void)
902 {
903   Mix_HaltMusic();
904   Mix_HaltChannel(-1);
905
906   Mix_CloseAudio();
907   SDL_QuitSubSystem(SDL_INIT_AUDIO);
908 }
909
910
911 /* ========================================================================= */
912 /* event functions                                                           */
913 /* ========================================================================= */
914
915 inline void SDLNextEvent(Event *event)
916 {
917   SDL_WaitEvent(event);
918
919 #ifdef FULLSCREEN_BUG
920   if (event->type == EVENT_BUTTONPRESS ||
921       event->type == EVENT_BUTTONRELEASE)
922   {
923     if (((ButtonEvent *)event)->x > video_xoffset)
924       ((ButtonEvent *)event)->x -= video_xoffset;
925     else
926       ((ButtonEvent *)event)->x = 0;
927     if (((ButtonEvent *)event)->y > video_yoffset)
928       ((ButtonEvent *)event)->y -= video_yoffset;
929     else
930       ((ButtonEvent *)event)->y = 0;
931   }
932   else if (event->type == EVENT_MOTIONNOTIFY)
933   {
934     if (((ButtonEvent *)event)->x > video_xoffset)
935       ((ButtonEvent *)event)->x -= video_xoffset;
936     else
937       ((ButtonEvent *)event)->x = 0;
938     if (((ButtonEvent *)event)->y > video_yoffset)
939       ((ButtonEvent *)event)->y -= video_yoffset;
940     else
941       ((ButtonEvent *)event)->y = 0;
942   }
943 #endif
944 }
945
946
947 /* ========================================================================= */
948 /* joystick functions                                                        */
949 /* ========================================================================= */
950
951 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
952 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
953 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
954
955 static boolean SDLOpenJoystick(int nr)
956 {
957   if (nr < 0 || nr > MAX_PLAYERS)
958     return FALSE;
959
960   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
961 }
962
963 static void SDLCloseJoystick(int nr)
964 {
965   if (nr < 0 || nr > MAX_PLAYERS)
966     return;
967
968   SDL_JoystickClose(sdl_joystick[nr]);
969 }
970
971 static boolean SDLCheckJoystickOpened(int nr)
972 {
973   if (nr < 0 || nr > MAX_PLAYERS)
974     return FALSE;
975
976   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
977 }
978
979 void HandleJoystickEvent(Event *event)
980 {
981   switch(event->type)
982   {
983     case SDL_JOYAXISMOTION:
984       if (event->jaxis.axis < 2)
985         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
986       break;
987
988     case SDL_JOYBUTTONDOWN:
989       if (event->jbutton.button < 2)
990         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
991       break;
992
993     case SDL_JOYBUTTONUP:
994       if (event->jbutton.button < 2)
995         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
996       break;
997
998     default:
999       break;
1000   }
1001 }
1002
1003 void SDLInitJoysticks()
1004 {
1005   static boolean sdl_joystick_subsystem_initialized = FALSE;
1006   int i;
1007
1008   if (!sdl_joystick_subsystem_initialized)
1009   {
1010     sdl_joystick_subsystem_initialized = TRUE;
1011
1012     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1013     {
1014       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1015       return;
1016     }
1017   }
1018
1019   for (i=0; i<MAX_PLAYERS; i++)
1020   {
1021     char *device_name = setup.input[i].joy.device_name;
1022     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1023
1024     if (joystick_nr >= SDL_NumJoysticks())
1025       joystick_nr = -1;
1026
1027     /* misuse joystick file descriptor variable to store joystick number */
1028     joystick.fd[i] = joystick_nr;
1029
1030     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1031     if (SDLCheckJoystickOpened(joystick_nr))
1032       SDLCloseJoystick(joystick_nr);
1033
1034     if (!setup.input[i].use_joystick)
1035       continue;
1036
1037     if (!SDLOpenJoystick(joystick_nr))
1038     {
1039       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1040       continue;
1041     }
1042
1043     joystick.status = JOYSTICK_ACTIVATED;
1044   }
1045 }
1046
1047 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1048 {
1049   if (nr < 0 || nr >= MAX_PLAYERS)
1050     return FALSE;
1051
1052   if (x != NULL)
1053     *x = sdl_js_axis[nr][0];
1054   if (y != NULL)
1055     *y = sdl_js_axis[nr][1];
1056
1057   if (b1 != NULL)
1058     *b1 = sdl_js_button[nr][0];
1059   if (b2 != NULL)
1060     *b2 = sdl_js_button[nr][1];
1061
1062   return TRUE;
1063 }
1064
1065 #endif /* TARGET_SDL */