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