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