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