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