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