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