rnd-20030127-3-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * sdl.c                                                    *
12 ***********************************************************/
13
14 #include "system.h"
15 #include "sound.h"
16 #include "joystick.h"
17 #include "misc.h"
18
19
20 #if defined(TARGET_SDL)
21
22 /* ========================================================================= */
23 /* video functions                                                           */
24 /* ========================================================================= */
25
26 /* functions from SGE library */
27 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   SDL_Surface *sdl_surface_tmp;
1111   int dst_width = dst_bitmap->width;
1112   int dst_height = dst_bitmap->height;
1113
1114   /* throw away old destination surface */
1115   SDL_FreeSurface(dst_bitmap->surface);
1116
1117   /* create zoomed temporary surface from source surface */
1118   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1119
1120   /* create native format destination surface from zoomed temporary surface */
1121   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1122
1123   /* free temporary surface */
1124   SDL_FreeSurface(sdl_surface_tmp);
1125 }
1126
1127
1128 /* ========================================================================= */
1129 /* load image to bitmap                                                      */
1130 /* ========================================================================= */
1131
1132 Bitmap *SDLLoadImage(char *filename)
1133 {
1134   Bitmap *new_bitmap = CreateBitmapStruct();
1135   SDL_Surface *sdl_image_tmp;
1136
1137   /* load image to temporary surface */
1138   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1139   {
1140     SetError("IMG_Load(): %s", SDL_GetError());
1141     return NULL;
1142   }
1143
1144   /* create native non-transparent surface for current image */
1145   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1146   {
1147     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1148     return NULL;
1149   }
1150
1151   /* create native transparent surface for current image */
1152   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1153                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1154   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1155   {
1156     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1157     return NULL;
1158   }
1159
1160   /* free temporary surface */
1161   SDL_FreeSurface(sdl_image_tmp);
1162
1163   new_bitmap->width = new_bitmap->surface->w;
1164   new_bitmap->height = new_bitmap->surface->h;
1165
1166   return new_bitmap;
1167 }
1168
1169
1170 /* ========================================================================= */
1171 /* audio functions                                                           */
1172 /* ========================================================================= */
1173
1174 inline void SDLOpenAudio(void)
1175 {
1176   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1177   {
1178     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1179     return;
1180   }
1181
1182   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1183                     AUDIO_NUM_CHANNELS_STEREO,
1184                     DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
1185   {
1186     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1187     return;
1188   }
1189
1190   audio.sound_available = TRUE;
1191   audio.music_available = TRUE;
1192   audio.loops_available = TRUE;
1193   audio.sound_enabled = TRUE;
1194
1195   /* set number of available mixer channels */
1196   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1197   audio.music_channel = MUSIC_CHANNEL;
1198   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1199
1200   Mixer_InitChannels();
1201 }
1202
1203 inline void SDLCloseAudio(void)
1204 {
1205   Mix_HaltMusic();
1206   Mix_HaltChannel(-1);
1207
1208   Mix_CloseAudio();
1209   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1210 }
1211
1212
1213 /* ========================================================================= */
1214 /* event functions                                                           */
1215 /* ========================================================================= */
1216
1217 inline void SDLNextEvent(Event *event)
1218 {
1219   SDL_WaitEvent(event);
1220
1221 #ifdef FULLSCREEN_BUG
1222   if (event->type == EVENT_BUTTONPRESS ||
1223       event->type == EVENT_BUTTONRELEASE)
1224   {
1225     if (((ButtonEvent *)event)->x > video_xoffset)
1226       ((ButtonEvent *)event)->x -= video_xoffset;
1227     else
1228       ((ButtonEvent *)event)->x = 0;
1229     if (((ButtonEvent *)event)->y > video_yoffset)
1230       ((ButtonEvent *)event)->y -= video_yoffset;
1231     else
1232       ((ButtonEvent *)event)->y = 0;
1233   }
1234   else if (event->type == EVENT_MOTIONNOTIFY)
1235   {
1236     if (((ButtonEvent *)event)->x > video_xoffset)
1237       ((ButtonEvent *)event)->x -= video_xoffset;
1238     else
1239       ((ButtonEvent *)event)->x = 0;
1240     if (((ButtonEvent *)event)->y > video_yoffset)
1241       ((ButtonEvent *)event)->y -= video_yoffset;
1242     else
1243       ((ButtonEvent *)event)->y = 0;
1244   }
1245 #endif
1246 }
1247
1248
1249 /* ========================================================================= */
1250 /* joystick functions                                                        */
1251 /* ========================================================================= */
1252
1253 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1254 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1255 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1256
1257 static boolean SDLOpenJoystick(int nr)
1258 {
1259   if (nr < 0 || nr > MAX_PLAYERS)
1260     return FALSE;
1261
1262   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1263 }
1264
1265 static void SDLCloseJoystick(int nr)
1266 {
1267   if (nr < 0 || nr > MAX_PLAYERS)
1268     return;
1269
1270   SDL_JoystickClose(sdl_joystick[nr]);
1271 }
1272
1273 static boolean SDLCheckJoystickOpened(int nr)
1274 {
1275   if (nr < 0 || nr > MAX_PLAYERS)
1276     return FALSE;
1277
1278   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1279 }
1280
1281 void HandleJoystickEvent(Event *event)
1282 {
1283   switch(event->type)
1284   {
1285     case SDL_JOYAXISMOTION:
1286       if (event->jaxis.axis < 2)
1287         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1288       break;
1289
1290     case SDL_JOYBUTTONDOWN:
1291       if (event->jbutton.button < 2)
1292         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1293       break;
1294
1295     case SDL_JOYBUTTONUP:
1296       if (event->jbutton.button < 2)
1297         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1298       break;
1299
1300     default:
1301       break;
1302   }
1303 }
1304
1305 void SDLInitJoysticks()
1306 {
1307   static boolean sdl_joystick_subsystem_initialized = FALSE;
1308   int i;
1309
1310   if (!sdl_joystick_subsystem_initialized)
1311   {
1312     sdl_joystick_subsystem_initialized = TRUE;
1313
1314     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1315     {
1316       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1317       return;
1318     }
1319   }
1320
1321   for (i=0; i<MAX_PLAYERS; i++)
1322   {
1323     char *device_name = setup.input[i].joy.device_name;
1324     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1325
1326     if (joystick_nr >= SDL_NumJoysticks())
1327       joystick_nr = -1;
1328
1329     /* misuse joystick file descriptor variable to store joystick number */
1330     joystick.fd[i] = joystick_nr;
1331
1332     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1333     if (SDLCheckJoystickOpened(joystick_nr))
1334       SDLCloseJoystick(joystick_nr);
1335
1336     if (!setup.input[i].use_joystick)
1337       continue;
1338
1339     if (!SDLOpenJoystick(joystick_nr))
1340     {
1341       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1342       continue;
1343     }
1344
1345     joystick.status = JOYSTICK_ACTIVATED;
1346   }
1347 }
1348
1349 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1350 {
1351   if (nr < 0 || nr >= MAX_PLAYERS)
1352     return FALSE;
1353
1354   if (x != NULL)
1355     *x = sdl_js_axis[nr][0];
1356   if (y != NULL)
1357     *y = sdl_js_axis[nr][1];
1358
1359   if (b1 != NULL)
1360     *b1 = sdl_js_button[nr][0];
1361   if (b2 != NULL)
1362     *b2 = sdl_js_button[nr][1];
1363
1364   return TRUE;
1365 }
1366
1367 #endif /* TARGET_SDL */