rnd-20030126-1-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 typedef struct
833 {
834   Uint8 r;
835   Uint8 g;
836   Uint8 b;
837   Uint8 a;
838 } tColorRGBA;
839
840 /*
841   -----------------------------------------------------------------------------
842   32 bit zoomer
843
844   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
845   -----------------------------------------------------------------------------
846 */
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 void zoomSurfaceSize(int width, int height, float zoom_x, float zoom_y,
1032                      int *dst_width, int *dst_height)
1033 {
1034   const float value_limit = 0.001;
1035
1036   /* sanity check zoom factors */
1037   if (zoom_x < value_limit)
1038     zoom_x = value_limit;
1039   if (zoom_y < value_limit)
1040     zoom_y = value_limit;
1041
1042   /* calculate target size */
1043   *dst_width  = (int) ((float) width  * zoom_x);
1044   *dst_height = (int) ((float) height * zoom_y);
1045
1046   if (*dst_width < 1)
1047     *dst_width = 1;
1048   if (*dst_height < 1)
1049     *dst_height = 1;
1050 }
1051
1052 SDL_Surface *zoomSurface(SDL_Surface *src, float zoom_x, float zoom_y)
1053 {
1054   SDL_Surface *zoom_src = NULL;
1055   SDL_Surface *zoom_dst = NULL;
1056   int dst_width, dst_height;
1057   boolean is_converted = FALSE;
1058   boolean is_32bit;
1059   int i;
1060
1061   if (src == NULL)
1062     return NULL;
1063
1064   /* determine if source surface is 32 bit or 8 bit */
1065   is_32bit = (src->format->BitsPerPixel == 32);
1066
1067   if (is_32bit || src->format->BitsPerPixel == 8)
1068   {
1069     /* use source surface 'as is' */
1070     zoom_src = src;
1071   }
1072   else
1073   {
1074     /* new source surface is 32 bit with a defined RGB ordering */
1075     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1076                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1077     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1078     is_32bit = TRUE;
1079     is_converted = TRUE;
1080   }
1081
1082   /* get size of destination surface */
1083   zoomSurfaceSize(zoom_src->w, zoom_src->h, zoom_x, zoom_y,
1084                   &dst_width, &dst_height);
1085
1086   /* allocate surface to completely contain the zoomed surface */
1087   if (is_32bit)
1088   {
1089     /* target surface is 32 bit with source RGBA/ABGR ordering */
1090     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1091                                     zoom_src->format->Rmask,
1092                                     zoom_src->format->Gmask,
1093                                     zoom_src->format->Bmask, 0);
1094   }
1095   else
1096   {
1097     /* target surface is 8 bit */
1098     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1099                                     0, 0, 0, 0);
1100   }
1101
1102   /* lock source surface */
1103   SDL_LockSurface(zoom_src);
1104
1105   /* check which kind of surface we have */
1106   if (is_32bit)
1107   {
1108     /* call the 32 bit transformation routine to do the zooming */
1109     zoomSurfaceRGBA(zoom_src, zoom_dst);
1110   }
1111   else
1112   {
1113     /* copy palette */
1114     for (i=0; i < zoom_src->format->palette->ncolors; i++)
1115       zoom_dst->format->palette->colors[i] =
1116         zoom_src->format->palette->colors[i];
1117     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1118
1119     /* call the 8 bit transformation routine to do the zooming */
1120     zoomSurfaceY(zoom_src, zoom_dst);
1121   }
1122
1123   /* unlock source surface */
1124   SDL_UnlockSurface(zoom_src);
1125
1126   /* free temporary surface */
1127   if (is_converted)
1128     SDL_FreeSurface(zoom_src);
1129
1130   /* return destination surface */
1131   return zoom_dst;
1132 }
1133
1134 SDL_Surface *SDLZoomSurface(SDL_Surface *src, float zoom_x, float zoom_y)
1135 {
1136   return zoomSurface(src, zoom_x, zoom_y);
1137 }
1138
1139
1140 /* ========================================================================= */
1141 /* load image to bitmap                                                      */
1142 /* ========================================================================= */
1143
1144 Bitmap *SDLLoadImage(char *filename)
1145 {
1146   Bitmap *new_bitmap = CreateBitmapStruct();
1147   SDL_Surface *sdl_image_tmp;
1148
1149   /* load image to temporary surface */
1150   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1151   {
1152     SetError("IMG_Load(): %s", SDL_GetError());
1153     return NULL;
1154   }
1155
1156   /* create native non-transparent surface for current image */
1157   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1158   {
1159     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1160     return NULL;
1161   }
1162
1163   /* create native transparent surface for current image */
1164   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1165                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1166   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1167   {
1168     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1169     return NULL;
1170   }
1171
1172   /* free temporary surface */
1173   SDL_FreeSurface(sdl_image_tmp);
1174
1175   new_bitmap->width = new_bitmap->surface->w;
1176   new_bitmap->height = new_bitmap->surface->h;
1177
1178   return new_bitmap;
1179 }
1180
1181
1182 /* ========================================================================= */
1183 /* audio functions                                                           */
1184 /* ========================================================================= */
1185
1186 inline void SDLOpenAudio(void)
1187 {
1188   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1189   {
1190     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1191     return;
1192   }
1193
1194   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1195                     AUDIO_NUM_CHANNELS_STEREO,
1196                     DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
1197   {
1198     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1199     return;
1200   }
1201
1202   audio.sound_available = TRUE;
1203   audio.music_available = TRUE;
1204   audio.loops_available = TRUE;
1205   audio.sound_enabled = TRUE;
1206
1207   /* set number of available mixer channels */
1208   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1209   audio.music_channel = MUSIC_CHANNEL;
1210   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1211
1212   Mixer_InitChannels();
1213 }
1214
1215 inline void SDLCloseAudio(void)
1216 {
1217   Mix_HaltMusic();
1218   Mix_HaltChannel(-1);
1219
1220   Mix_CloseAudio();
1221   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1222 }
1223
1224
1225 /* ========================================================================= */
1226 /* event functions                                                           */
1227 /* ========================================================================= */
1228
1229 inline void SDLNextEvent(Event *event)
1230 {
1231   SDL_WaitEvent(event);
1232
1233 #ifdef FULLSCREEN_BUG
1234   if (event->type == EVENT_BUTTONPRESS ||
1235       event->type == EVENT_BUTTONRELEASE)
1236   {
1237     if (((ButtonEvent *)event)->x > video_xoffset)
1238       ((ButtonEvent *)event)->x -= video_xoffset;
1239     else
1240       ((ButtonEvent *)event)->x = 0;
1241     if (((ButtonEvent *)event)->y > video_yoffset)
1242       ((ButtonEvent *)event)->y -= video_yoffset;
1243     else
1244       ((ButtonEvent *)event)->y = 0;
1245   }
1246   else if (event->type == EVENT_MOTIONNOTIFY)
1247   {
1248     if (((ButtonEvent *)event)->x > video_xoffset)
1249       ((ButtonEvent *)event)->x -= video_xoffset;
1250     else
1251       ((ButtonEvent *)event)->x = 0;
1252     if (((ButtonEvent *)event)->y > video_yoffset)
1253       ((ButtonEvent *)event)->y -= video_yoffset;
1254     else
1255       ((ButtonEvent *)event)->y = 0;
1256   }
1257 #endif
1258 }
1259
1260
1261 /* ========================================================================= */
1262 /* joystick functions                                                        */
1263 /* ========================================================================= */
1264
1265 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1266 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1267 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1268
1269 static boolean SDLOpenJoystick(int nr)
1270 {
1271   if (nr < 0 || nr > MAX_PLAYERS)
1272     return FALSE;
1273
1274   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1275 }
1276
1277 static void SDLCloseJoystick(int nr)
1278 {
1279   if (nr < 0 || nr > MAX_PLAYERS)
1280     return;
1281
1282   SDL_JoystickClose(sdl_joystick[nr]);
1283 }
1284
1285 static boolean SDLCheckJoystickOpened(int nr)
1286 {
1287   if (nr < 0 || nr > MAX_PLAYERS)
1288     return FALSE;
1289
1290   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1291 }
1292
1293 void HandleJoystickEvent(Event *event)
1294 {
1295   switch(event->type)
1296   {
1297     case SDL_JOYAXISMOTION:
1298       if (event->jaxis.axis < 2)
1299         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1300       break;
1301
1302     case SDL_JOYBUTTONDOWN:
1303       if (event->jbutton.button < 2)
1304         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1305       break;
1306
1307     case SDL_JOYBUTTONUP:
1308       if (event->jbutton.button < 2)
1309         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1310       break;
1311
1312     default:
1313       break;
1314   }
1315 }
1316
1317 void SDLInitJoysticks()
1318 {
1319   static boolean sdl_joystick_subsystem_initialized = FALSE;
1320   int i;
1321
1322   if (!sdl_joystick_subsystem_initialized)
1323   {
1324     sdl_joystick_subsystem_initialized = TRUE;
1325
1326     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1327     {
1328       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1329       return;
1330     }
1331   }
1332
1333   for (i=0; i<MAX_PLAYERS; i++)
1334   {
1335     char *device_name = setup.input[i].joy.device_name;
1336     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1337
1338     if (joystick_nr >= SDL_NumJoysticks())
1339       joystick_nr = -1;
1340
1341     /* misuse joystick file descriptor variable to store joystick number */
1342     joystick.fd[i] = joystick_nr;
1343
1344     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1345     if (SDLCheckJoystickOpened(joystick_nr))
1346       SDLCloseJoystick(joystick_nr);
1347
1348     if (!setup.input[i].use_joystick)
1349       continue;
1350
1351     if (!SDLOpenJoystick(joystick_nr))
1352     {
1353       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1354       continue;
1355     }
1356
1357     joystick.status = JOYSTICK_ACTIVATED;
1358   }
1359 }
1360
1361 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1362 {
1363   if (nr < 0 || nr >= MAX_PLAYERS)
1364     return FALSE;
1365
1366   if (x != NULL)
1367     *x = sdl_js_axis[nr][0];
1368   if (y != NULL)
1369     *y = sdl_js_axis[nr][1];
1370
1371   if (b1 != NULL)
1372     *b1 = sdl_js_button[nr][0];
1373   if (b2 != NULL)
1374     *b2 = sdl_js_button[nr][1];
1375
1376   return TRUE;
1377 }
1378
1379 #endif /* TARGET_SDL */