rnd-20030126-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 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 were 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
826 /* ========================================================================= */
827 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
828 /* (Rotozoomer) by Andreas Schiffler                                         */
829 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
830 /* ========================================================================= */
831
832 /*
833   -----------------------------------------------------------------------------
834   32 bit zoomer
835
836   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
837   -----------------------------------------------------------------------------
838 */
839
840 typedef struct
841 {
842   Uint8 r;
843   Uint8 g;
844   Uint8 b;
845   Uint8 a;
846 } tColorRGBA;
847
848 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
849 {
850   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
851   tColorRGBA *sp, *csp, *dp;
852   int sgap, dgap;
853
854   /* variable setup */
855   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
856   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
857
858   /* allocate memory for row increments */
859   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
860   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
861
862   /* precalculate row increments */
863   csx = 0;
864   csax = sax;
865   for (x = 0; x <= dst->w; x++)
866   {
867     *csax = csx;
868     csax++;
869     csx &= 0xffff;
870     csx += sx;
871   }
872
873   csy = 0;
874   csay = say;
875   for (y = 0; y <= dst->h; y++)
876   {
877     *csay = csy;
878     csay++;
879     csy &= 0xffff;
880     csy += sy;
881   }
882
883   /* pointer setup */
884   sp = csp = (tColorRGBA *) src->pixels;
885   dp = (tColorRGBA *) dst->pixels;
886   sgap = src->pitch - src->w * 4;
887   dgap = dst->pitch - dst->w * 4;
888
889   csay = say;
890   for (y = 0; y < dst->h; y++)
891   {
892     sp = csp;
893     csax = sax;
894
895     for (x = 0; x < dst->w; x++)
896     {
897       /* draw */
898       *dp = *sp;
899
900       /* advance source pointers */
901       csax++;
902       sp += (*csax >> 16);
903
904       /* advance destination pointer */
905       dp++;
906     }
907
908     /* advance source pointer */
909     csay++;
910     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
911
912     /* advance destination pointers */
913     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
914   }
915
916   free(sax);
917   free(say);
918
919   return 0;
920 }
921
922 /*
923   -----------------------------------------------------------------------------
924   8 bit zoomer
925
926   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
927   -----------------------------------------------------------------------------
928 */
929
930 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
931 {
932   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
933   Uint8 *sp, *dp, *csp;
934   int dgap;
935
936   /* variable setup */
937   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
938   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
939
940   /* allocate memory for row increments */
941   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
942   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
943
944   /* precalculate row increments */
945   csx = 0;
946   csax = sax;
947   for (x = 0; x < dst->w; x++)
948   {
949     csx += sx;
950     *csax = (csx >> 16);
951     csx &= 0xffff;
952     csax++;
953   }
954
955   csy = 0;
956   csay = say;
957   for (y = 0; y < dst->h; y++)
958   {
959     csy += sy;
960     *csay = (csy >> 16);
961     csy &= 0xffff;
962     csay++;
963   }
964
965   csx = 0;
966   csax = sax;
967   for (x = 0; x < dst->w; x++)
968   {
969     csx += (*csax);
970     csax++;
971   }
972
973   csy = 0;
974   csay = say;
975   for (y = 0; y < dst->h; y++)
976   {
977     csy += (*csay);
978     csay++;
979   }
980
981   /* pointer setup */
982   sp = csp = (Uint8 *) src->pixels;
983   dp = (Uint8 *) dst->pixels;
984   dgap = dst->pitch - dst->w;
985
986   /* draw */
987   csay = say;
988   for (y = 0; y < dst->h; y++)
989   {
990     csax = sax;
991     sp = csp;
992     for (x = 0; x < dst->w; x++)
993     {
994       /* draw */
995       *dp = *sp;
996
997       /* advance source pointers */
998       sp += (*csax);
999       csax++;
1000
1001       /* advance destination pointer */
1002       dp++;
1003     }
1004
1005     /* advance source pointer (for row) */
1006     csp += ((*csay) * src->pitch);
1007     csay++;
1008
1009     /* advance destination pointers */
1010     dp += dgap;
1011   }
1012
1013   free(sax);
1014   free(say);
1015
1016   return 0;
1017 }
1018
1019 /*
1020   -----------------------------------------------------------------------------
1021   zoomSurface()
1022
1023   Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1024   'zoomx' and 'zoomy' are scaling factors for width and height.
1025   If 'smooth' is 1 then the destination 32bit surface is anti-aliased.
1026   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1027   into a 32bit RGBA format on the fly.
1028   -----------------------------------------------------------------------------
1029 */
1030
1031 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1032 {
1033   SDL_Surface *zoom_src = NULL;
1034   SDL_Surface *zoom_dst = NULL;
1035   boolean is_converted = FALSE;
1036   boolean is_32bit;
1037   int i;
1038
1039   if (src == NULL)
1040     return NULL;
1041
1042   /* determine if source surface is 32 bit or 8 bit */
1043   is_32bit = (src->format->BitsPerPixel == 32);
1044
1045   if (is_32bit || src->format->BitsPerPixel == 8)
1046   {
1047     /* use source surface 'as is' */
1048     zoom_src = src;
1049   }
1050   else
1051   {
1052     /* new source surface is 32 bit with a defined RGB ordering */
1053     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1054                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1055     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1056     is_32bit = TRUE;
1057     is_converted = TRUE;
1058   }
1059
1060   /* allocate surface to completely contain the zoomed surface */
1061   if (is_32bit)
1062   {
1063     /* target surface is 32 bit with source RGBA/ABGR ordering */
1064     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1065                                     zoom_src->format->Rmask,
1066                                     zoom_src->format->Gmask,
1067                                     zoom_src->format->Bmask, 0);
1068   }
1069   else
1070   {
1071     /* target surface is 8 bit */
1072     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1073                                     0, 0, 0, 0);
1074   }
1075
1076   /* lock source surface */
1077   SDL_LockSurface(zoom_src);
1078
1079   /* check which kind of surface we have */
1080   if (is_32bit)
1081   {
1082     /* call the 32 bit transformation routine to do the zooming */
1083     zoomSurfaceRGBA(zoom_src, zoom_dst);
1084   }
1085   else
1086   {
1087     /* copy palette */
1088     for (i=0; i < zoom_src->format->palette->ncolors; i++)
1089       zoom_dst->format->palette->colors[i] =
1090         zoom_src->format->palette->colors[i];
1091     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1092
1093     /* call the 8 bit transformation routine to do the zooming */
1094     zoomSurfaceY(zoom_src, zoom_dst);
1095   }
1096
1097   /* unlock source surface */
1098   SDL_UnlockSurface(zoom_src);
1099
1100   /* free temporary surface */
1101   if (is_converted)
1102     SDL_FreeSurface(zoom_src);
1103
1104   /* return destination surface */
1105   return zoom_dst;
1106 }
1107
1108 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1109 {
1110   int dst_width = dst_bitmap->width;
1111   int dst_height = dst_bitmap->height;
1112
1113   SDL_FreeSurface(dst_bitmap->surface);
1114
1115   dst_bitmap->surface = zoomSurface(src_bitmap->surface, dst_width,dst_height);
1116 }
1117
1118
1119 /* ========================================================================= */
1120 /* load image to bitmap                                                      */
1121 /* ========================================================================= */
1122
1123 Bitmap *SDLLoadImage(char *filename)
1124 {
1125   Bitmap *new_bitmap = CreateBitmapStruct();
1126   SDL_Surface *sdl_image_tmp;
1127
1128   /* load image to temporary surface */
1129   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1130   {
1131     SetError("IMG_Load(): %s", SDL_GetError());
1132     return NULL;
1133   }
1134
1135   /* create native non-transparent surface for current image */
1136   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1137   {
1138     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1139     return NULL;
1140   }
1141
1142   /* create native transparent surface for current image */
1143   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1144                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1145   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1146   {
1147     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1148     return NULL;
1149   }
1150
1151   /* free temporary surface */
1152   SDL_FreeSurface(sdl_image_tmp);
1153
1154   new_bitmap->width = new_bitmap->surface->w;
1155   new_bitmap->height = new_bitmap->surface->h;
1156
1157   return new_bitmap;
1158 }
1159
1160
1161 /* ========================================================================= */
1162 /* audio functions                                                           */
1163 /* ========================================================================= */
1164
1165 inline void SDLOpenAudio(void)
1166 {
1167   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1168   {
1169     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1170     return;
1171   }
1172
1173   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1174                     AUDIO_NUM_CHANNELS_STEREO,
1175                     DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
1176   {
1177     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1178     return;
1179   }
1180
1181   audio.sound_available = TRUE;
1182   audio.music_available = TRUE;
1183   audio.loops_available = TRUE;
1184   audio.sound_enabled = TRUE;
1185
1186   /* set number of available mixer channels */
1187   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1188   audio.music_channel = MUSIC_CHANNEL;
1189   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1190
1191   Mixer_InitChannels();
1192 }
1193
1194 inline void SDLCloseAudio(void)
1195 {
1196   Mix_HaltMusic();
1197   Mix_HaltChannel(-1);
1198
1199   Mix_CloseAudio();
1200   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1201 }
1202
1203
1204 /* ========================================================================= */
1205 /* event functions                                                           */
1206 /* ========================================================================= */
1207
1208 inline void SDLNextEvent(Event *event)
1209 {
1210   SDL_WaitEvent(event);
1211
1212 #ifdef FULLSCREEN_BUG
1213   if (event->type == EVENT_BUTTONPRESS ||
1214       event->type == EVENT_BUTTONRELEASE)
1215   {
1216     if (((ButtonEvent *)event)->x > video_xoffset)
1217       ((ButtonEvent *)event)->x -= video_xoffset;
1218     else
1219       ((ButtonEvent *)event)->x = 0;
1220     if (((ButtonEvent *)event)->y > video_yoffset)
1221       ((ButtonEvent *)event)->y -= video_yoffset;
1222     else
1223       ((ButtonEvent *)event)->y = 0;
1224   }
1225   else if (event->type == EVENT_MOTIONNOTIFY)
1226   {
1227     if (((ButtonEvent *)event)->x > video_xoffset)
1228       ((ButtonEvent *)event)->x -= video_xoffset;
1229     else
1230       ((ButtonEvent *)event)->x = 0;
1231     if (((ButtonEvent *)event)->y > video_yoffset)
1232       ((ButtonEvent *)event)->y -= video_yoffset;
1233     else
1234       ((ButtonEvent *)event)->y = 0;
1235   }
1236 #endif
1237 }
1238
1239
1240 /* ========================================================================= */
1241 /* joystick functions                                                        */
1242 /* ========================================================================= */
1243
1244 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1245 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1246 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1247
1248 static boolean SDLOpenJoystick(int nr)
1249 {
1250   if (nr < 0 || nr > MAX_PLAYERS)
1251     return FALSE;
1252
1253   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1254 }
1255
1256 static void SDLCloseJoystick(int nr)
1257 {
1258   if (nr < 0 || nr > MAX_PLAYERS)
1259     return;
1260
1261   SDL_JoystickClose(sdl_joystick[nr]);
1262 }
1263
1264 static boolean SDLCheckJoystickOpened(int nr)
1265 {
1266   if (nr < 0 || nr > MAX_PLAYERS)
1267     return FALSE;
1268
1269   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1270 }
1271
1272 void HandleJoystickEvent(Event *event)
1273 {
1274   switch(event->type)
1275   {
1276     case SDL_JOYAXISMOTION:
1277       if (event->jaxis.axis < 2)
1278         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1279       break;
1280
1281     case SDL_JOYBUTTONDOWN:
1282       if (event->jbutton.button < 2)
1283         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1284       break;
1285
1286     case SDL_JOYBUTTONUP:
1287       if (event->jbutton.button < 2)
1288         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1289       break;
1290
1291     default:
1292       break;
1293   }
1294 }
1295
1296 void SDLInitJoysticks()
1297 {
1298   static boolean sdl_joystick_subsystem_initialized = FALSE;
1299   int i;
1300
1301   if (!sdl_joystick_subsystem_initialized)
1302   {
1303     sdl_joystick_subsystem_initialized = TRUE;
1304
1305     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1306     {
1307       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1308       return;
1309     }
1310   }
1311
1312   for (i=0; i<MAX_PLAYERS; i++)
1313   {
1314     char *device_name = setup.input[i].joy.device_name;
1315     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1316
1317     if (joystick_nr >= SDL_NumJoysticks())
1318       joystick_nr = -1;
1319
1320     /* misuse joystick file descriptor variable to store joystick number */
1321     joystick.fd[i] = joystick_nr;
1322
1323     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1324     if (SDLCheckJoystickOpened(joystick_nr))
1325       SDLCloseJoystick(joystick_nr);
1326
1327     if (!setup.input[i].use_joystick)
1328       continue;
1329
1330     if (!SDLOpenJoystick(joystick_nr))
1331     {
1332       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1333       continue;
1334     }
1335
1336     joystick.status = JOYSTICK_ACTIVATED;
1337   }
1338 }
1339
1340 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1341 {
1342   if (nr < 0 || nr >= MAX_PLAYERS)
1343     return FALSE;
1344
1345   if (x != NULL)
1346     *x = sdl_js_axis[nr][0];
1347   if (y != NULL)
1348     *y = sdl_js_axis[nr][1];
1349
1350   if (b1 != NULL)
1351     *b1 = sdl_js_button[nr][0];
1352   if (b2 != NULL)
1353     *b2 = sdl_js_button[nr][1];
1354
1355   return TRUE;
1356 }
1357
1358 #endif /* TARGET_SDL */