rnd-20020315-1-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 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 "misc.h"
17
18
19 #if defined(TARGET_SDL)
20
21 /* ========================================================================= */
22 /* video functions                                                           */
23 /* ========================================================================= */
24
25 /* functions from SGE library */
26 inline void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
27
28 #ifdef PLATFORM_WIN32
29 #define FULLSCREEN_BUG
30 #endif
31
32 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
33 static int fullscreen_width;
34 static int fullscreen_height;
35 static int fullscreen_xoffset;
36 static int fullscreen_yoffset;
37 static int video_xoffset;
38 static int video_yoffset;
39
40 inline void SDLInitVideoDisplay(void)
41 {
42   /* initialize SDL video */
43   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
44     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
45
46   /* set default SDL depth */
47   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
48 }
49
50 inline void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
51                                boolean fullscreen)
52 {
53 #ifdef FULLSCREEN_BUG
54   int i;
55   static int screen_xy[][2] =
56   {
57     {  640, 480 },
58     {  800, 600 },
59     { 1024, 768 },
60     {   -1,  -1 }
61   };
62 #endif
63
64   /* default: normal game window size */
65   fullscreen_width = video.width;
66   fullscreen_height = video.height;
67   fullscreen_xoffset = 0;
68   fullscreen_yoffset = 0;
69
70 #ifdef FULLSCREEN_BUG
71   for (i=0; screen_xy[i][0] != -1; i++)
72   {
73     if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
74     {
75       fullscreen_width = screen_xy[i][0];
76       fullscreen_height = screen_xy[i][1];
77       break;
78     }
79   }
80
81   fullscreen_xoffset = (fullscreen_width - video.width) / 2;
82   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
83 #endif
84
85   /* open SDL video output device (window or fullscreen mode) */
86   if (!SDLSetVideoMode(backbuffer, fullscreen))
87     Error(ERR_EXIT, "setting video mode failed");
88
89   /* set window and icon title */
90   SDL_WM_SetCaption(program.window_title, program.window_title);
91
92   /* SDL cannot directly draw to the visible video framebuffer like X11,
93      but always uses a backbuffer, which is then blitted to the visible
94      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
95      visible video framebuffer with 'SDL_Flip', if the hardware supports
96      this). Therefore do not use an additional backbuffer for drawing, but
97      use a symbolic buffer (distinguishable from the SDL backbuffer) called
98      'window', which indicates that the SDL backbuffer should be updated to
99      the visible video framebuffer when attempting to blit to it.
100
101      For convenience, it seems to be a good idea to create this symbolic
102      buffer 'window' at the same size as the SDL backbuffer. Although it
103      should never be drawn to directly, it would do no harm nevertheless. */
104
105   /* create additional (symbolic) buffer for double-buffering */
106   *window = CreateBitmap(video.width, video.height, video.depth);
107 }
108
109 inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
110 {
111   boolean success = TRUE;
112   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
113   int surface_flags_window = SURFACE_FLAGS;
114   SDL_Surface *new_surface = NULL;
115
116   if (*backbuffer == NULL)
117     *backbuffer = CreateBitmapStruct();
118
119   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
120   {
121     video_xoffset = fullscreen_xoffset;
122     video_yoffset = fullscreen_yoffset;
123
124     /* switch display to fullscreen mode, if available */
125     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
126                                         video.depth, surface_flags_fullscreen))
127         == NULL)
128     {
129       /* switching display to fullscreen mode failed */
130       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
131
132       /* do not try it again */
133       video.fullscreen_available = FALSE;
134       success = FALSE;
135     }
136     else
137     {
138       (*backbuffer)->surface = new_surface;
139
140       video.fullscreen_enabled = TRUE;
141       success = TRUE;
142     }
143   }
144
145   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
146   {
147     video_xoffset = 0;
148     video_yoffset = 0;
149
150     /* switch display to window mode */
151     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
152                                         video.depth, surface_flags_window))
153         == NULL)
154     {
155       /* switching display to window mode failed -- should not happen */
156       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
157
158       success = FALSE;
159     }
160     else
161     {
162       (*backbuffer)->surface = new_surface;
163
164       video.fullscreen_enabled = FALSE;
165       success = TRUE;
166     }
167   }
168
169   return success;
170 }
171
172 inline void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
173                         int src_x, int src_y,
174                         int width, int height,
175                         int dst_x, int dst_y, int copy_mode)
176 {
177   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
178   SDL_Rect src_rect, dst_rect;
179
180 #ifdef FULLSCREEN_BUG
181   if (src_bitmap == backbuffer)
182   {
183     src_x += video_xoffset;
184     src_y += video_yoffset;
185   }
186 #endif
187
188   src_rect.x = src_x;
189   src_rect.y = src_y;
190   src_rect.w = width;
191   src_rect.h = height;
192
193 #ifdef FULLSCREEN_BUG
194   if (dst_bitmap == backbuffer || dst_bitmap == window)
195   {
196     dst_x += video_xoffset;
197     dst_y += video_yoffset;
198   }
199 #endif
200
201   dst_rect.x = dst_x;
202   dst_rect.y = dst_y;
203   dst_rect.w = width;
204   dst_rect.h = height;
205
206   if (src_bitmap != backbuffer || dst_bitmap != window)
207     SDL_BlitSurface((copy_mode == SDLCOPYAREA_MASKED ?
208                      src_bitmap->surface_masked : src_bitmap->surface),
209                     &src_rect, real_dst_bitmap->surface, &dst_rect);
210
211   if (dst_bitmap == window)
212     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
213 }
214
215 inline void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
216                              int width, int height, unsigned int color)
217 {
218   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
219   SDL_Rect rect;
220   unsigned int color_r = (color >> 16) && 0xff;
221   unsigned int color_g = (color >>  8) && 0xff;
222   unsigned int color_b = (color >>  0) && 0xff;
223
224 #ifdef FULLSCREEN_BUG
225   if (dst_bitmap == backbuffer || dst_bitmap == window)
226   {
227     x += video_xoffset;
228     y += video_yoffset;
229   }
230 #endif
231
232   rect.x = x;
233   rect.y = y;
234   rect.w = width;
235   rect.h = height;
236
237   SDL_FillRect(real_dst_bitmap->surface, &rect,
238                SDL_MapRGB(real_dst_bitmap->surface->format,
239                           color_r, color_g, color_b));
240
241   if (dst_bitmap == window)
242     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
243 }
244
245 inline void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
246                               int to_x, int to_y, unsigned int color)
247 {
248   SDL_Surface *surface = dst_bitmap->surface;
249   SDL_Rect rect;
250   unsigned int color_r = (color >> 16) & 0xff;
251   unsigned int color_g = (color >>  8) & 0xff;
252   unsigned int color_b = (color >>  0) & 0xff;
253
254   if (from_x > to_x)
255     swap_numbers(&from_x, &to_x);
256
257   if (from_y > to_y)
258     swap_numbers(&from_y, &to_y);
259
260   rect.x = from_x;
261   rect.y = from_y;
262   rect.w = (to_x - from_x + 1);
263   rect.h = (to_y - from_y + 1);
264
265 #ifdef FULLSCREEN_BUG
266   if (dst_bitmap == backbuffer || dst_bitmap == window)
267   {
268     rect.x += video_xoffset;
269     rect.y += video_yoffset;
270   }
271 #endif
272
273   SDL_FillRect(surface, &rect,
274                SDL_MapRGB(surface->format, color_r, color_g, color_b));
275 }
276
277 inline void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
278                         int to_x, int to_y, Uint32 color)
279 {
280 #ifdef FULLSCREEN_BUG
281   if (dst_bitmap == backbuffer || dst_bitmap == window)
282   {
283     from_x += video_xoffset;
284     from_y += video_yoffset;
285     to_x += video_xoffset;
286     to_y += video_yoffset;
287   }
288 #endif
289
290   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
291 }
292
293 #if 0
294 inline void SDLDrawLines(SDL_Surface *surface, struct XY *points,
295                          int num_points, Uint32 color)
296 {
297   int i, x, y;
298   int line_width = 4;
299
300   for (i=0; i<num_points - 1; i++)
301   {
302     for (x=0; x<line_width; x++)
303     {
304       for (y=0; y<line_width; y++)
305       {
306         int dx = x - line_width / 2;
307         int dy = y - line_width / 2;
308
309         if ((x == 0 && y == 0) ||
310             (x == 0 && y == line_width - 1) ||
311             (x == line_width - 1 && y == 0) ||
312             (x == line_width - 1 && y == line_width - 1))
313           continue;
314
315         sge_Line(surface, points[i].x + dx, points[i].y + dy,
316                  points[i+1].x + dx, points[i+1].y + dy, color);
317       }
318     }
319   }
320 }
321 #endif
322
323
324 /* ========================================================================= */
325 /* The following functions have been taken from the SGE library              */
326 /* (SDL Graphics Extension Library) by Anders Lindström                      */
327 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
328 /* ========================================================================= */
329
330 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
331 {
332   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
333   {
334     switch (surface->format->BytesPerPixel)
335     {
336       case 1:
337       {
338         /* Assuming 8-bpp */
339         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
340       }
341       break;
342
343       case 2:
344       {
345         /* Probably 15-bpp or 16-bpp */
346         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
347       }
348       break;
349
350       case 3:
351       {
352         /* Slow 24-bpp mode, usually not used */
353         Uint8 *pix;
354         int shift;
355
356         /* Gack - slow, but endian correct */
357         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
358         shift = surface->format->Rshift;
359         *(pix+shift/8) = color>>shift;
360         shift = surface->format->Gshift;
361         *(pix+shift/8) = color>>shift;
362         shift = surface->format->Bshift;
363         *(pix+shift/8) = color>>shift;
364       }
365       break;
366
367       case 4:
368       {
369         /* Probably 32-bpp */
370         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
371       }
372       break;
373     }
374   }
375 }
376
377 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
378                   Uint8 R, Uint8 G, Uint8 B)
379 {
380   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
381 }
382
383 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
384 {
385   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
386 }
387
388 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
389 {
390   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
391 }
392
393 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
394 {
395   Uint8 *pix;
396   int shift;
397
398   /* Gack - slow, but endian correct */
399   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
400   shift = surface->format->Rshift;
401   *(pix+shift/8) = color>>shift;
402   shift = surface->format->Gshift;
403   *(pix+shift/8) = color>>shift;
404   shift = surface->format->Bshift;
405   *(pix+shift/8) = color>>shift;
406 }
407
408 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
409 {
410   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
411 }
412
413 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
414 {
415   switch (dest->format->BytesPerPixel)
416   {
417     case 1:
418       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
419       break;
420
421     case 2:
422       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
423       break;
424
425     case 3:
426       _PutPixel24(dest,x,y,color);
427       break;
428
429     case 4:
430       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
431       break;
432   }
433 }
434
435 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
436 {
437   if (SDL_MUSTLOCK(surface))
438   {
439     if (SDL_LockSurface(surface) < 0)
440     {
441       return;
442     }
443   }
444
445   _PutPixel(surface, x, y, color);
446
447   if (SDL_MUSTLOCK(surface))
448   {
449     SDL_UnlockSurface(surface);
450   }
451 }
452
453 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
454                   Uint8 R, Uint8 G, Uint8 B)
455 {
456   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
457 }
458
459 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
460 {
461   if (y >= 0 && y <= dest->h - 1)
462   {
463     switch (dest->format->BytesPerPixel)
464     {
465       case 1:
466         return y*dest->pitch;
467         break;
468
469       case 2:
470         return y*dest->pitch/2;
471         break;
472
473       case 3:
474         return y*dest->pitch;
475         break;
476
477       case 4:
478         return y*dest->pitch/4;
479         break;
480     }
481   }
482
483   return -1;
484 }
485
486 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
487 {
488   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
489   {
490     switch (surface->format->BytesPerPixel)
491     {
492       case 1:
493       {
494         /* Assuming 8-bpp */
495         *((Uint8 *)surface->pixels + ypitch + x) = color;
496       }
497       break;
498
499       case 2:
500       {
501         /* Probably 15-bpp or 16-bpp */
502         *((Uint16 *)surface->pixels + ypitch + x) = color;
503       }
504       break;
505
506       case 3:
507       {
508         /* Slow 24-bpp mode, usually not used */
509         Uint8 *pix;
510         int shift;
511
512         /* Gack - slow, but endian correct */
513         pix = (Uint8 *)surface->pixels + ypitch + x*3;
514         shift = surface->format->Rshift;
515         *(pix+shift/8) = color>>shift;
516         shift = surface->format->Gshift;
517         *(pix+shift/8) = color>>shift;
518         shift = surface->format->Bshift;
519         *(pix+shift/8) = color>>shift;
520       }
521       break;
522
523       case 4:
524       {
525         /* Probably 32-bpp */
526         *((Uint32 *)surface->pixels + ypitch + x) = color;
527       }
528       break;
529     }
530   }
531 }
532
533 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
534                Uint32 Color)
535 {
536   SDL_Rect l;
537
538   if (SDL_MUSTLOCK(Surface))
539   {
540     if (SDL_LockSurface(Surface) < 0)
541     {
542       return;
543     }
544   }
545
546   if (x1 > x2)
547   {
548     Sint16 tmp = x1;
549     x1 = x2;
550     x2 = tmp;
551   }
552
553   /* Do the clipping */
554   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
555     return;
556   if (x1 < 0)
557     x1 = 0;
558   if (x2 > Surface->w - 1)
559     x2 = Surface->w - 1;
560
561   l.x = x1;
562   l.y = y;
563   l.w = x2 - x1 + 1;
564   l.h = 1;
565
566   SDL_FillRect(Surface, &l, Color);
567
568   if (SDL_MUSTLOCK(Surface))
569   {
570     SDL_UnlockSurface(Surface);
571   }
572 }
573
574 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
575                   Uint8 R, Uint8 G, Uint8 B)
576 {
577   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
578 }
579
580 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
581 {
582   SDL_Rect l;
583
584   if (x1 > x2)
585   {
586     Sint16 tmp = x1;
587     x1 = x2;
588     x2 = tmp;
589   }
590
591   /* Do the clipping */
592   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
593     return;
594   if (x1 < 0)
595     x1 = 0;
596   if (x2 > Surface->w - 1)
597     x2 = Surface->w - 1;
598
599   l.x = x1;
600   l.y = y;
601   l.w = x2 - x1 + 1;
602   l.h = 1;
603
604   SDL_FillRect(Surface, &l, Color);
605 }
606
607 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
608                Uint32 Color)
609 {
610   SDL_Rect l;
611
612   if (SDL_MUSTLOCK(Surface))
613   {
614     if (SDL_LockSurface(Surface) < 0)
615     {
616       return;
617     }
618   }
619
620   if (y1 > y2)
621   {
622     Sint16 tmp = y1;
623     y1 = y2;
624     y2 = tmp;
625   }
626
627   /* Do the clipping */
628   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
629     return;
630   if (y1 < 0)
631     y1 = 0;
632   if (y2 > Surface->h - 1)
633     y2 = Surface->h - 1;
634
635   l.x = x;
636   l.y = y1;
637   l.w = 1;
638   l.h = y2 - y1 + 1;
639
640   SDL_FillRect(Surface, &l, Color);
641
642   if (SDL_MUSTLOCK(Surface))
643   {
644     SDL_UnlockSurface(Surface);
645   }
646 }
647
648 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
649                   Uint8 R, Uint8 G, Uint8 B)
650 {
651   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
652 }
653
654 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
655 {
656   SDL_Rect l;
657
658   if (y1 > y2)
659   {
660     Sint16 tmp = y1;
661     y1 = y2;
662     y2 = tmp;
663   }
664
665   /* Do the clipping */
666   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
667     return;
668   if (y1 < 0)
669     y1 = 0;
670   if (y2 > Surface->h - 1)
671     y2 = Surface->h - 1;
672
673   l.x = x;
674   l.y = y1;
675   l.w = 1;
676   l.h = y2 - y1 + 1;
677
678   SDL_FillRect(Surface, &l, Color);
679 }
680
681 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
682                 Sint16 x2, Sint16 y2, Uint32 Color,
683                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
684                               Uint32 Color))
685 {
686   Sint16 dx, dy, sdx, sdy, x, y, px, py;
687
688   dx = x2 - x1;
689   dy = y2 - y1;
690
691   sdx = (dx < 0) ? -1 : 1;
692   sdy = (dy < 0) ? -1 : 1;
693
694   dx = sdx * dx + 1;
695   dy = sdy * dy + 1;
696
697   x = y = 0;
698
699   px = x1;
700   py = y1;
701
702   if (dx >= dy)
703   {
704     for (x = 0; x < dx; x++)
705     {
706       Callback(Surface, px, py, Color);
707
708       y += dy;
709       if (y >= dx)
710       {
711         y -= dx;
712         py += sdy;
713       }
714
715       px += sdx;
716     }
717   }
718   else
719   {
720     for (y = 0; y < dy; y++)
721     {
722       Callback(Surface, px, py, Color);
723
724       x += dx;
725       if (x >= dy)
726       {
727         x -= dy;
728         px += sdx;
729       }
730
731       py += sdy;
732     }
733   }
734 }
735
736 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
737                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
738                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
739                                  Uint32 Color))
740 {
741   sge_DoLine(Surface, X1, Y1, X2, Y2,
742              SDL_MapRGB(Surface->format, R, G, B), Callback);
743 }
744
745 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
746               Uint32 Color)
747 {
748   if (SDL_MUSTLOCK(Surface))
749   {
750     if (SDL_LockSurface(Surface) < 0)
751       return;
752    }
753
754    /* Draw the line */
755    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
756
757    /* unlock the display */
758    if (SDL_MUSTLOCK(Surface))
759    {
760       SDL_UnlockSurface(Surface);
761    }
762 }
763
764 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
765                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
766 {
767   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
768 }
769
770 Bitmap *SDLLoadImage(char *filename)
771 {
772   Bitmap *new_bitmap = CreateBitmapStruct();
773   SDL_Surface *sdl_image_tmp;
774
775   /* load image to temporary surface */
776   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
777     Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
778
779   /* create native non-transparent surface for current image */
780   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
781     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
782
783   /* create native transparent surface for current image */
784   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
785                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
786   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
787     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
788
789   /* free temporary surface */
790   SDL_FreeSurface(sdl_image_tmp);
791
792   return new_bitmap;
793 }
794
795
796 /* ========================================================================= */
797 /* audio functions                                                           */
798 /* ========================================================================= */
799
800 inline void SDLOpenAudio(void)
801 {
802   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
803   {
804     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
805     return;
806   }
807
808   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, AUDIO_S16,
809                     AUDIO_STEREO_CHANNELS,
810                     DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
811   {
812     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
813     return;
814   }
815
816   audio.sound_available = TRUE;
817   audio.music_available = TRUE;
818   audio.loops_available = TRUE;
819   audio.sound_enabled = TRUE;
820
821   /* determine number of available channels */
822   audio.channels = Mix_AllocateChannels(MIX_CHANNELS);
823
824   if (!audio.mods_available)    /* reserve first channel for music loops */
825   {
826     if (Mix_ReserveChannels(1) == 1)
827       audio.music_channel = 0;
828     else
829       audio.music_available = FALSE;
830   }
831
832   Mix_Volume(-1, SOUND_MAX_VOLUME);
833   Mix_VolumeMusic(SOUND_MAX_VOLUME);
834 }
835
836 inline void SDLCloseAudio(void)
837 {
838   Mix_HaltMusic();
839   Mix_HaltChannel(-1);
840
841   Mix_CloseAudio();
842   SDL_QuitSubSystem(SDL_INIT_AUDIO);
843 }
844
845
846 /* ========================================================================= */
847 /* event functions                                                           */
848 /* ========================================================================= */
849
850 inline void SDLNextEvent(Event *event)
851 {
852   SDL_WaitEvent(event);
853
854 #ifdef FULLSCREEN_BUG
855   if (event->type == EVENT_BUTTONPRESS ||
856       event->type == EVENT_BUTTONRELEASE)
857   {
858     if (((ButtonEvent *)event)->x > video_xoffset)
859       ((ButtonEvent *)event)->x -= video_xoffset;
860     else
861       ((ButtonEvent *)event)->x = 0;
862     if (((ButtonEvent *)event)->y > video_yoffset)
863       ((ButtonEvent *)event)->y -= video_yoffset;
864     else
865       ((ButtonEvent *)event)->y = 0;
866   }
867   else if (event->type == EVENT_MOTIONNOTIFY)
868   {
869     if (((ButtonEvent *)event)->x > video_xoffset)
870       ((ButtonEvent *)event)->x -= video_xoffset;
871     else
872       ((ButtonEvent *)event)->x = 0;
873     if (((ButtonEvent *)event)->y > video_yoffset)
874       ((ButtonEvent *)event)->y -= video_yoffset;
875     else
876       ((ButtonEvent *)event)->y = 0;
877   }
878 #endif
879 }
880
881 #endif /* TARGET_SDL */