rnd-20030312-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   putenv("SDL_VIDEO_CENTERED=1");
44
45   /* initialize SDL video */
46   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
47     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
48
49   /* set default SDL depth */
50   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
51 }
52
53 inline void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
54                                boolean fullscreen)
55 {
56 #ifdef FULLSCREEN_BUG
57   int i;
58   static int screen_xy[][2] =
59   {
60     {  640, 480 },
61     {  800, 600 },
62     { 1024, 768 },
63     {   -1,  -1 }
64   };
65 #endif
66
67   /* default: normal game window size */
68   fullscreen_width = video.width;
69   fullscreen_height = video.height;
70   fullscreen_xoffset = 0;
71   fullscreen_yoffset = 0;
72
73 #ifdef FULLSCREEN_BUG
74   for (i=0; screen_xy[i][0] != -1; i++)
75   {
76     if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
77     {
78       fullscreen_width = screen_xy[i][0];
79       fullscreen_height = screen_xy[i][1];
80       break;
81     }
82   }
83
84   fullscreen_xoffset = (fullscreen_width - video.width) / 2;
85   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
86 #endif
87
88   /* open SDL video output device (window or fullscreen mode) */
89   if (!SDLSetVideoMode(backbuffer, fullscreen))
90     Error(ERR_EXIT, "setting video mode failed");
91
92   /* set window and icon title */
93   SDL_WM_SetCaption(program.window_title, program.window_title);
94
95   /* SDL cannot directly draw to the visible video framebuffer like X11,
96      but always uses a backbuffer, which is then blitted to the visible
97      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
98      visible video framebuffer with 'SDL_Flip', if the hardware supports
99      this). Therefore do not use an additional backbuffer for drawing, but
100      use a symbolic buffer (distinguishable from the SDL backbuffer) called
101      'window', which indicates that the SDL backbuffer should be updated to
102      the visible video framebuffer when attempting to blit to it.
103
104      For convenience, it seems to be a good idea to create this symbolic
105      buffer 'window' at the same size as the SDL backbuffer. Although it
106      should never be drawn to directly, it would do no harm nevertheless. */
107
108   /* create additional (symbolic) buffer for double-buffering */
109   *window = CreateBitmap(video.width, video.height, video.depth);
110 }
111
112 inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
113 {
114   boolean success = TRUE;
115   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
116   int surface_flags_window = SURFACE_FLAGS;
117   SDL_Surface *new_surface = NULL;
118
119   if (*backbuffer == NULL)
120     *backbuffer = CreateBitmapStruct();
121
122   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
123   {
124     video_xoffset = fullscreen_xoffset;
125     video_yoffset = fullscreen_yoffset;
126
127     /* switch display to fullscreen mode, if available */
128     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
129                                         video.depth, surface_flags_fullscreen))
130         == NULL)
131     {
132       /* switching display to fullscreen mode failed */
133       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
134
135       /* do not try it again */
136       video.fullscreen_available = FALSE;
137       success = FALSE;
138     }
139     else
140     {
141       (*backbuffer)->surface = new_surface;
142
143       video.fullscreen_enabled = TRUE;
144       success = TRUE;
145     }
146   }
147
148   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
149   {
150     video_xoffset = 0;
151     video_yoffset = 0;
152
153     /* switch display to window mode */
154     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
155                                         video.depth, surface_flags_window))
156         == NULL)
157     {
158       /* switching display to window mode failed -- should not happen */
159       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
160
161       success = FALSE;
162     }
163     else
164     {
165       (*backbuffer)->surface = new_surface;
166
167       video.fullscreen_enabled = FALSE;
168       success = TRUE;
169     }
170   }
171
172   return success;
173 }
174
175 inline void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
176                         int src_x, int src_y,
177                         int width, int height,
178                         int dst_x, int dst_y, int copy_mode)
179 {
180   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
181   SDL_Rect src_rect, dst_rect;
182
183 #ifdef FULLSCREEN_BUG
184   if (src_bitmap == backbuffer)
185   {
186     src_x += video_xoffset;
187     src_y += video_yoffset;
188   }
189 #endif
190
191   src_rect.x = src_x;
192   src_rect.y = src_y;
193   src_rect.w = width;
194   src_rect.h = height;
195
196 #ifdef FULLSCREEN_BUG
197   if (dst_bitmap == backbuffer || dst_bitmap == window)
198   {
199     dst_x += video_xoffset;
200     dst_y += video_yoffset;
201   }
202 #endif
203
204   dst_rect.x = dst_x;
205   dst_rect.y = dst_y;
206   dst_rect.w = width;
207   dst_rect.h = height;
208
209   if (src_bitmap != backbuffer || dst_bitmap != window)
210     SDL_BlitSurface((copy_mode == SDLCOPYAREA_MASKED ?
211                      src_bitmap->surface_masked : src_bitmap->surface),
212                     &src_rect, real_dst_bitmap->surface, &dst_rect);
213
214   if (dst_bitmap == window)
215     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
216 }
217
218 inline void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
219                              int width, int height, unsigned int color)
220 {
221   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
222   SDL_Rect rect;
223   unsigned int color_r = (color >> 16) && 0xff;
224   unsigned int color_g = (color >>  8) && 0xff;
225   unsigned int color_b = (color >>  0) && 0xff;
226
227 #ifdef FULLSCREEN_BUG
228   if (dst_bitmap == backbuffer || dst_bitmap == window)
229   {
230     x += video_xoffset;
231     y += video_yoffset;
232   }
233 #endif
234
235   rect.x = x;
236   rect.y = y;
237   rect.w = width;
238   rect.h = height;
239
240   SDL_FillRect(real_dst_bitmap->surface, &rect,
241                SDL_MapRGB(real_dst_bitmap->surface->format,
242                           color_r, color_g, color_b));
243
244   if (dst_bitmap == window)
245     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
246 }
247
248 inline void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
249                               int to_x, int to_y, unsigned int color)
250 {
251   SDL_Surface *surface = dst_bitmap->surface;
252   SDL_Rect rect;
253   unsigned int color_r = (color >> 16) & 0xff;
254   unsigned int color_g = (color >>  8) & 0xff;
255   unsigned int color_b = (color >>  0) & 0xff;
256
257   if (from_x > to_x)
258     swap_numbers(&from_x, &to_x);
259
260   if (from_y > to_y)
261     swap_numbers(&from_y, &to_y);
262
263   rect.x = from_x;
264   rect.y = from_y;
265   rect.w = (to_x - from_x + 1);
266   rect.h = (to_y - from_y + 1);
267
268 #ifdef FULLSCREEN_BUG
269   if (dst_bitmap == backbuffer || dst_bitmap == window)
270   {
271     rect.x += video_xoffset;
272     rect.y += video_yoffset;
273   }
274 #endif
275
276   SDL_FillRect(surface, &rect,
277                SDL_MapRGB(surface->format, color_r, color_g, color_b));
278 }
279
280 inline void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
281                         int to_x, int to_y, Uint32 color)
282 {
283 #ifdef FULLSCREEN_BUG
284   if (dst_bitmap == backbuffer || dst_bitmap == window)
285   {
286     from_x += video_xoffset;
287     from_y += video_yoffset;
288     to_x += video_xoffset;
289     to_y += video_yoffset;
290   }
291 #endif
292
293   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
294 }
295
296 #if 0
297 inline void SDLDrawLines(SDL_Surface *surface, struct XY *points,
298                          int num_points, Uint32 color)
299 {
300   int i, x, y;
301   int line_width = 4;
302
303   for (i=0; i<num_points - 1; i++)
304   {
305     for (x=0; x<line_width; x++)
306     {
307       for (y=0; y<line_width; y++)
308       {
309         int dx = x - line_width / 2;
310         int dy = y - line_width / 2;
311
312         if ((x == 0 && y == 0) ||
313             (x == 0 && y == line_width - 1) ||
314             (x == line_width - 1 && y == 0) ||
315             (x == line_width - 1 && y == line_width - 1))
316           continue;
317
318         sge_Line(surface, points[i].x + dx, points[i].y + dy,
319                  points[i+1].x + dx, points[i+1].y + dy, color);
320       }
321     }
322   }
323 }
324 #endif
325
326 inline Pixel SDLGetPixel(Bitmap *dst_bitmap, int x, int y)
327 {
328   SDL_Surface *surface = dst_bitmap->surface;
329
330 #ifdef FULLSCREEN_BUG
331   if (dst_bitmap == backbuffer || dst_bitmap == window)
332   {
333     x += video_xoffset;
334     y += video_yoffset;
335   }
336 #endif
337
338   switch (surface->format->BytesPerPixel)
339   {
340     case 1:             /* assuming 8-bpp */
341     {
342       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
343     }
344     break;
345
346     case 2:             /* probably 15-bpp or 16-bpp */
347     {
348       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
349     }
350     break;
351
352   case 3:               /* slow 24-bpp mode; usually not used */
353     {
354       /* does this work? */
355       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
356       Uint32 color = 0;
357       int shift;
358
359       shift = surface->format->Rshift;
360       color |= *(pix + shift / 8) >> shift;
361       shift = surface->format->Gshift;
362       color |= *(pix + shift / 8) >> shift;
363       shift = surface->format->Bshift;
364       color |= *(pix + shift / 8) >> shift;
365
366       return color;
367     }
368     break;
369
370   case 4:               /* probably 32-bpp */
371     {
372       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
373     }
374     break;
375   }
376
377   return 0;
378 }
379
380
381 /* ========================================================================= */
382 /* The following functions were taken from the SGE library                   */
383 /* (SDL Graphics Extension Library) by Anders Lindström                      */
384 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
385 /* ========================================================================= */
386
387 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
388 {
389   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
390   {
391     switch (surface->format->BytesPerPixel)
392     {
393       case 1:
394       {
395         /* Assuming 8-bpp */
396         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
397       }
398       break;
399
400       case 2:
401       {
402         /* Probably 15-bpp or 16-bpp */
403         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
404       }
405       break;
406
407       case 3:
408       {
409         /* Slow 24-bpp mode, usually not used */
410         Uint8 *pix;
411         int shift;
412
413         /* Gack - slow, but endian correct */
414         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
415         shift = surface->format->Rshift;
416         *(pix+shift/8) = color>>shift;
417         shift = surface->format->Gshift;
418         *(pix+shift/8) = color>>shift;
419         shift = surface->format->Bshift;
420         *(pix+shift/8) = color>>shift;
421       }
422       break;
423
424       case 4:
425       {
426         /* Probably 32-bpp */
427         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
428       }
429       break;
430     }
431   }
432 }
433
434 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
435                   Uint8 R, Uint8 G, Uint8 B)
436 {
437   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
438 }
439
440 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
441 {
442   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
443 }
444
445 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
446 {
447   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
448 }
449
450 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
451 {
452   Uint8 *pix;
453   int shift;
454
455   /* Gack - slow, but endian correct */
456   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
457   shift = surface->format->Rshift;
458   *(pix+shift/8) = color>>shift;
459   shift = surface->format->Gshift;
460   *(pix+shift/8) = color>>shift;
461   shift = surface->format->Bshift;
462   *(pix+shift/8) = color>>shift;
463 }
464
465 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
466 {
467   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
468 }
469
470 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
471 {
472   switch (dest->format->BytesPerPixel)
473   {
474     case 1:
475       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
476       break;
477
478     case 2:
479       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
480       break;
481
482     case 3:
483       _PutPixel24(dest,x,y,color);
484       break;
485
486     case 4:
487       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
488       break;
489   }
490 }
491
492 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
493 {
494   if (SDL_MUSTLOCK(surface))
495   {
496     if (SDL_LockSurface(surface) < 0)
497     {
498       return;
499     }
500   }
501
502   _PutPixel(surface, x, y, color);
503
504   if (SDL_MUSTLOCK(surface))
505   {
506     SDL_UnlockSurface(surface);
507   }
508 }
509
510 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
511                   Uint8 R, Uint8 G, Uint8 B)
512 {
513   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
514 }
515
516 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
517 {
518   if (y >= 0 && y <= dest->h - 1)
519   {
520     switch (dest->format->BytesPerPixel)
521     {
522       case 1:
523         return y*dest->pitch;
524         break;
525
526       case 2:
527         return y*dest->pitch/2;
528         break;
529
530       case 3:
531         return y*dest->pitch;
532         break;
533
534       case 4:
535         return y*dest->pitch/4;
536         break;
537     }
538   }
539
540   return -1;
541 }
542
543 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
544 {
545   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
546   {
547     switch (surface->format->BytesPerPixel)
548     {
549       case 1:
550       {
551         /* Assuming 8-bpp */
552         *((Uint8 *)surface->pixels + ypitch + x) = color;
553       }
554       break;
555
556       case 2:
557       {
558         /* Probably 15-bpp or 16-bpp */
559         *((Uint16 *)surface->pixels + ypitch + x) = color;
560       }
561       break;
562
563       case 3:
564       {
565         /* Slow 24-bpp mode, usually not used */
566         Uint8 *pix;
567         int shift;
568
569         /* Gack - slow, but endian correct */
570         pix = (Uint8 *)surface->pixels + ypitch + x*3;
571         shift = surface->format->Rshift;
572         *(pix+shift/8) = color>>shift;
573         shift = surface->format->Gshift;
574         *(pix+shift/8) = color>>shift;
575         shift = surface->format->Bshift;
576         *(pix+shift/8) = color>>shift;
577       }
578       break;
579
580       case 4:
581       {
582         /* Probably 32-bpp */
583         *((Uint32 *)surface->pixels + ypitch + x) = color;
584       }
585       break;
586     }
587   }
588 }
589
590 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
591                Uint32 Color)
592 {
593   SDL_Rect l;
594
595   if (SDL_MUSTLOCK(Surface))
596   {
597     if (SDL_LockSurface(Surface) < 0)
598     {
599       return;
600     }
601   }
602
603   if (x1 > x2)
604   {
605     Sint16 tmp = x1;
606     x1 = x2;
607     x2 = tmp;
608   }
609
610   /* Do the clipping */
611   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
612     return;
613   if (x1 < 0)
614     x1 = 0;
615   if (x2 > Surface->w - 1)
616     x2 = Surface->w - 1;
617
618   l.x = x1;
619   l.y = y;
620   l.w = x2 - x1 + 1;
621   l.h = 1;
622
623   SDL_FillRect(Surface, &l, Color);
624
625   if (SDL_MUSTLOCK(Surface))
626   {
627     SDL_UnlockSurface(Surface);
628   }
629 }
630
631 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
632                   Uint8 R, Uint8 G, Uint8 B)
633 {
634   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
635 }
636
637 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
638 {
639   SDL_Rect l;
640
641   if (x1 > x2)
642   {
643     Sint16 tmp = x1;
644     x1 = x2;
645     x2 = tmp;
646   }
647
648   /* Do the clipping */
649   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
650     return;
651   if (x1 < 0)
652     x1 = 0;
653   if (x2 > Surface->w - 1)
654     x2 = Surface->w - 1;
655
656   l.x = x1;
657   l.y = y;
658   l.w = x2 - x1 + 1;
659   l.h = 1;
660
661   SDL_FillRect(Surface, &l, Color);
662 }
663
664 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
665                Uint32 Color)
666 {
667   SDL_Rect l;
668
669   if (SDL_MUSTLOCK(Surface))
670   {
671     if (SDL_LockSurface(Surface) < 0)
672     {
673       return;
674     }
675   }
676
677   if (y1 > y2)
678   {
679     Sint16 tmp = y1;
680     y1 = y2;
681     y2 = tmp;
682   }
683
684   /* Do the clipping */
685   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
686     return;
687   if (y1 < 0)
688     y1 = 0;
689   if (y2 > Surface->h - 1)
690     y2 = Surface->h - 1;
691
692   l.x = x;
693   l.y = y1;
694   l.w = 1;
695   l.h = y2 - y1 + 1;
696
697   SDL_FillRect(Surface, &l, Color);
698
699   if (SDL_MUSTLOCK(Surface))
700   {
701     SDL_UnlockSurface(Surface);
702   }
703 }
704
705 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
706                   Uint8 R, Uint8 G, Uint8 B)
707 {
708   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
709 }
710
711 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
712 {
713   SDL_Rect l;
714
715   if (y1 > y2)
716   {
717     Sint16 tmp = y1;
718     y1 = y2;
719     y2 = tmp;
720   }
721
722   /* Do the clipping */
723   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
724     return;
725   if (y1 < 0)
726     y1 = 0;
727   if (y2 > Surface->h - 1)
728     y2 = Surface->h - 1;
729
730   l.x = x;
731   l.y = y1;
732   l.w = 1;
733   l.h = y2 - y1 + 1;
734
735   SDL_FillRect(Surface, &l, Color);
736 }
737
738 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
739                 Sint16 x2, Sint16 y2, Uint32 Color,
740                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
741                               Uint32 Color))
742 {
743   Sint16 dx, dy, sdx, sdy, x, y, px, py;
744
745   dx = x2 - x1;
746   dy = y2 - y1;
747
748   sdx = (dx < 0) ? -1 : 1;
749   sdy = (dy < 0) ? -1 : 1;
750
751   dx = sdx * dx + 1;
752   dy = sdy * dy + 1;
753
754   x = y = 0;
755
756   px = x1;
757   py = y1;
758
759   if (dx >= dy)
760   {
761     for (x = 0; x < dx; x++)
762     {
763       Callback(Surface, px, py, Color);
764
765       y += dy;
766       if (y >= dx)
767       {
768         y -= dx;
769         py += sdy;
770       }
771
772       px += sdx;
773     }
774   }
775   else
776   {
777     for (y = 0; y < dy; y++)
778     {
779       Callback(Surface, px, py, Color);
780
781       x += dx;
782       if (x >= dy)
783       {
784         x -= dy;
785         px += sdx;
786       }
787
788       py += sdy;
789     }
790   }
791 }
792
793 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
794                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
795                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
796                                  Uint32 Color))
797 {
798   sge_DoLine(Surface, X1, Y1, X2, Y2,
799              SDL_MapRGB(Surface->format, R, G, B), Callback);
800 }
801
802 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
803               Uint32 Color)
804 {
805   if (SDL_MUSTLOCK(Surface))
806   {
807     if (SDL_LockSurface(Surface) < 0)
808       return;
809    }
810
811    /* Draw the line */
812    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
813
814    /* unlock the display */
815    if (SDL_MUSTLOCK(Surface))
816    {
817       SDL_UnlockSurface(Surface);
818    }
819 }
820
821 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
822                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
823 {
824   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
825 }
826
827
828 /* ========================================================================= */
829 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
830 /* (Rotozoomer) by Andreas Schiffler                                         */
831 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
832 /* ========================================================================= */
833
834 /*
835   -----------------------------------------------------------------------------
836   32 bit zoomer
837
838   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
839   -----------------------------------------------------------------------------
840 */
841
842 typedef struct
843 {
844   Uint8 r;
845   Uint8 g;
846   Uint8 b;
847   Uint8 a;
848 } tColorRGBA;
849
850 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
851 {
852   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
853   tColorRGBA *sp, *csp, *dp;
854   int sgap, dgap;
855
856   /* variable setup */
857   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
858   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
859
860   /* allocate memory for row increments */
861   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
862   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
863
864   /* precalculate row increments */
865   csx = 0;
866   csax = sax;
867   for (x = 0; x <= dst->w; x++)
868   {
869     *csax = csx;
870     csax++;
871     csx &= 0xffff;
872     csx += sx;
873   }
874
875   csy = 0;
876   csay = say;
877   for (y = 0; y <= dst->h; y++)
878   {
879     *csay = csy;
880     csay++;
881     csy &= 0xffff;
882     csy += sy;
883   }
884
885   /* pointer setup */
886   sp = csp = (tColorRGBA *) src->pixels;
887   dp = (tColorRGBA *) dst->pixels;
888   sgap = src->pitch - src->w * 4;
889   dgap = dst->pitch - dst->w * 4;
890
891   csay = say;
892   for (y = 0; y < dst->h; y++)
893   {
894     sp = csp;
895     csax = sax;
896
897     for (x = 0; x < dst->w; x++)
898     {
899       /* draw */
900       *dp = *sp;
901
902       /* advance source pointers */
903       csax++;
904       sp += (*csax >> 16);
905
906       /* advance destination pointer */
907       dp++;
908     }
909
910     /* advance source pointer */
911     csay++;
912     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
913
914     /* advance destination pointers */
915     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
916   }
917
918   free(sax);
919   free(say);
920
921   return 0;
922 }
923
924 /*
925   -----------------------------------------------------------------------------
926   8 bit zoomer
927
928   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
929   -----------------------------------------------------------------------------
930 */
931
932 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
933 {
934   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
935   Uint8 *sp, *dp, *csp;
936   int dgap;
937
938   /* variable setup */
939   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
940   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
941
942   /* allocate memory for row increments */
943   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
944   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
945
946   /* precalculate row increments */
947   csx = 0;
948   csax = sax;
949   for (x = 0; x < dst->w; x++)
950   {
951     csx += sx;
952     *csax = (csx >> 16);
953     csx &= 0xffff;
954     csax++;
955   }
956
957   csy = 0;
958   csay = say;
959   for (y = 0; y < dst->h; y++)
960   {
961     csy += sy;
962     *csay = (csy >> 16);
963     csy &= 0xffff;
964     csay++;
965   }
966
967   csx = 0;
968   csax = sax;
969   for (x = 0; x < dst->w; x++)
970   {
971     csx += (*csax);
972     csax++;
973   }
974
975   csy = 0;
976   csay = say;
977   for (y = 0; y < dst->h; y++)
978   {
979     csy += (*csay);
980     csay++;
981   }
982
983   /* pointer setup */
984   sp = csp = (Uint8 *) src->pixels;
985   dp = (Uint8 *) dst->pixels;
986   dgap = dst->pitch - dst->w;
987
988   /* draw */
989   csay = say;
990   for (y = 0; y < dst->h; y++)
991   {
992     csax = sax;
993     sp = csp;
994     for (x = 0; x < dst->w; x++)
995     {
996       /* draw */
997       *dp = *sp;
998
999       /* advance source pointers */
1000       sp += (*csax);
1001       csax++;
1002
1003       /* advance destination pointer */
1004       dp++;
1005     }
1006
1007     /* advance source pointer (for row) */
1008     csp += ((*csay) * src->pitch);
1009     csay++;
1010
1011     /* advance destination pointers */
1012     dp += dgap;
1013   }
1014
1015   free(sax);
1016   free(say);
1017
1018   return 0;
1019 }
1020
1021 /*
1022   -----------------------------------------------------------------------------
1023   zoomSurface()
1024
1025   Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1026   'zoomx' and 'zoomy' are scaling factors for width and height.
1027   If 'smooth' is 1 then the destination 32bit surface is anti-aliased.
1028   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1029   into a 32bit RGBA format on the fly.
1030   -----------------------------------------------------------------------------
1031 */
1032
1033 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1034 {
1035   SDL_Surface *zoom_src = NULL;
1036   SDL_Surface *zoom_dst = NULL;
1037   boolean is_converted = FALSE;
1038   boolean is_32bit;
1039   int i;
1040
1041   if (src == NULL)
1042     return NULL;
1043
1044   /* determine if source surface is 32 bit or 8 bit */
1045   is_32bit = (src->format->BitsPerPixel == 32);
1046
1047   if (is_32bit || src->format->BitsPerPixel == 8)
1048   {
1049     /* use source surface 'as is' */
1050     zoom_src = src;
1051   }
1052   else
1053   {
1054     /* new source surface is 32 bit with a defined RGB ordering */
1055     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1056                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1057     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1058     is_32bit = TRUE;
1059     is_converted = TRUE;
1060   }
1061
1062   /* allocate surface to completely contain the zoomed surface */
1063   if (is_32bit)
1064   {
1065     /* target surface is 32 bit with source RGBA/ABGR ordering */
1066     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1067                                     zoom_src->format->Rmask,
1068                                     zoom_src->format->Gmask,
1069                                     zoom_src->format->Bmask, 0);
1070   }
1071   else
1072   {
1073     /* target surface is 8 bit */
1074     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1075                                     0, 0, 0, 0);
1076   }
1077
1078   /* lock source surface */
1079   SDL_LockSurface(zoom_src);
1080
1081   /* check which kind of surface we have */
1082   if (is_32bit)
1083   {
1084     /* call the 32 bit transformation routine to do the zooming */
1085     zoomSurfaceRGBA(zoom_src, zoom_dst);
1086   }
1087   else
1088   {
1089     /* copy palette */
1090     for (i=0; i < zoom_src->format->palette->ncolors; i++)
1091       zoom_dst->format->palette->colors[i] =
1092         zoom_src->format->palette->colors[i];
1093     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1094
1095     /* call the 8 bit transformation routine to do the zooming */
1096     zoomSurfaceY(zoom_src, zoom_dst);
1097   }
1098
1099   /* unlock source surface */
1100   SDL_UnlockSurface(zoom_src);
1101
1102   /* free temporary surface */
1103   if (is_converted)
1104     SDL_FreeSurface(zoom_src);
1105
1106   /* return destination surface */
1107   return zoom_dst;
1108 }
1109
1110 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1111 {
1112   SDL_Surface *sdl_surface_tmp;
1113   int dst_width = dst_bitmap->width;
1114   int dst_height = dst_bitmap->height;
1115
1116   /* throw away old destination surface */
1117   SDL_FreeSurface(dst_bitmap->surface);
1118
1119   /* create zoomed temporary surface from source surface */
1120   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1121
1122   /* create native format destination surface from zoomed temporary surface */
1123   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1124
1125   /* free temporary surface */
1126   SDL_FreeSurface(sdl_surface_tmp);
1127 }
1128
1129
1130 /* ========================================================================= */
1131 /* load image to bitmap                                                      */
1132 /* ========================================================================= */
1133
1134 Bitmap *SDLLoadImage(char *filename)
1135 {
1136   Bitmap *new_bitmap = CreateBitmapStruct();
1137   SDL_Surface *sdl_image_tmp;
1138
1139   /* load image to temporary surface */
1140   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1141   {
1142     SetError("IMG_Load(): %s", SDL_GetError());
1143     return NULL;
1144   }
1145
1146   /* create native non-transparent surface for current image */
1147   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1148   {
1149     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1150     return NULL;
1151   }
1152
1153   /* create native transparent surface for current image */
1154   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1155                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1156   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1157   {
1158     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1159     return NULL;
1160   }
1161
1162   /* free temporary surface */
1163   SDL_FreeSurface(sdl_image_tmp);
1164
1165   new_bitmap->width = new_bitmap->surface->w;
1166   new_bitmap->height = new_bitmap->surface->h;
1167
1168   return new_bitmap;
1169 }
1170
1171
1172 /* ========================================================================= */
1173 /* audio functions                                                           */
1174 /* ========================================================================= */
1175
1176 inline void SDLOpenAudio(void)
1177 {
1178   if (strcmp(setup.system.sdl_audiodriver, ARG_DEFAULT) != 0)
1179     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1180
1181   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1182   {
1183     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1184     return;
1185   }
1186
1187   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1188                     AUDIO_NUM_CHANNELS_STEREO,
1189                     setup.system.audio_fragment_size) < 0)
1190   {
1191     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1192     return;
1193   }
1194
1195   audio.sound_available = TRUE;
1196   audio.music_available = TRUE;
1197   audio.loops_available = TRUE;
1198   audio.sound_enabled = TRUE;
1199
1200   /* set number of available mixer channels */
1201   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1202   audio.music_channel = MUSIC_CHANNEL;
1203   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1204
1205   Mixer_InitChannels();
1206 }
1207
1208 inline void SDLCloseAudio(void)
1209 {
1210   Mix_HaltMusic();
1211   Mix_HaltChannel(-1);
1212
1213   Mix_CloseAudio();
1214   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1215 }
1216
1217
1218 /* ========================================================================= */
1219 /* event functions                                                           */
1220 /* ========================================================================= */
1221
1222 inline void SDLNextEvent(Event *event)
1223 {
1224   SDL_WaitEvent(event);
1225
1226 #ifdef FULLSCREEN_BUG
1227   if (event->type == EVENT_BUTTONPRESS ||
1228       event->type == EVENT_BUTTONRELEASE)
1229   {
1230     if (((ButtonEvent *)event)->x > video_xoffset)
1231       ((ButtonEvent *)event)->x -= video_xoffset;
1232     else
1233       ((ButtonEvent *)event)->x = 0;
1234     if (((ButtonEvent *)event)->y > video_yoffset)
1235       ((ButtonEvent *)event)->y -= video_yoffset;
1236     else
1237       ((ButtonEvent *)event)->y = 0;
1238   }
1239   else if (event->type == EVENT_MOTIONNOTIFY)
1240   {
1241     if (((ButtonEvent *)event)->x > video_xoffset)
1242       ((ButtonEvent *)event)->x -= video_xoffset;
1243     else
1244       ((ButtonEvent *)event)->x = 0;
1245     if (((ButtonEvent *)event)->y > video_yoffset)
1246       ((ButtonEvent *)event)->y -= video_yoffset;
1247     else
1248       ((ButtonEvent *)event)->y = 0;
1249   }
1250 #endif
1251 }
1252
1253
1254 /* ========================================================================= */
1255 /* joystick functions                                                        */
1256 /* ========================================================================= */
1257
1258 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1259 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1260 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1261
1262 static boolean SDLOpenJoystick(int nr)
1263 {
1264   if (nr < 0 || nr > MAX_PLAYERS)
1265     return FALSE;
1266
1267   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1268 }
1269
1270 static void SDLCloseJoystick(int nr)
1271 {
1272   if (nr < 0 || nr > MAX_PLAYERS)
1273     return;
1274
1275   SDL_JoystickClose(sdl_joystick[nr]);
1276 }
1277
1278 static boolean SDLCheckJoystickOpened(int nr)
1279 {
1280   if (nr < 0 || nr > MAX_PLAYERS)
1281     return FALSE;
1282
1283   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1284 }
1285
1286 void HandleJoystickEvent(Event *event)
1287 {
1288   switch(event->type)
1289   {
1290     case SDL_JOYAXISMOTION:
1291       if (event->jaxis.axis < 2)
1292         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1293       break;
1294
1295     case SDL_JOYBUTTONDOWN:
1296       if (event->jbutton.button < 2)
1297         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1298       break;
1299
1300     case SDL_JOYBUTTONUP:
1301       if (event->jbutton.button < 2)
1302         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1303       break;
1304
1305     default:
1306       break;
1307   }
1308 }
1309
1310 void SDLInitJoysticks()
1311 {
1312   static boolean sdl_joystick_subsystem_initialized = FALSE;
1313   int i;
1314
1315   if (!sdl_joystick_subsystem_initialized)
1316   {
1317     sdl_joystick_subsystem_initialized = TRUE;
1318
1319     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1320     {
1321       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1322       return;
1323     }
1324   }
1325
1326   for (i=0; i<MAX_PLAYERS; i++)
1327   {
1328     char *device_name = setup.input[i].joy.device_name;
1329     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1330
1331     if (joystick_nr >= SDL_NumJoysticks())
1332       joystick_nr = -1;
1333
1334     /* misuse joystick file descriptor variable to store joystick number */
1335     joystick.fd[i] = joystick_nr;
1336
1337     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1338     if (SDLCheckJoystickOpened(joystick_nr))
1339       SDLCloseJoystick(joystick_nr);
1340
1341     if (!setup.input[i].use_joystick)
1342       continue;
1343
1344     if (!SDLOpenJoystick(joystick_nr))
1345     {
1346       Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1347       continue;
1348     }
1349
1350     joystick.status = JOYSTICK_ACTIVATED;
1351   }
1352 }
1353
1354 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1355 {
1356   if (nr < 0 || nr >= MAX_PLAYERS)
1357     return FALSE;
1358
1359   if (x != NULL)
1360     *x = sdl_js_axis[nr][0];
1361   if (y != NULL)
1362     *y = sdl_js_axis[nr][1];
1363
1364   if (b1 != NULL)
1365     *b1 = sdl_js_button[nr][0];
1366   if (b2 != NULL)
1367     *b2 = sdl_js_button[nr][1];
1368
1369   return TRUE;
1370 }
1371
1372 #endif /* TARGET_SDL */