rnd-20060816-4-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 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 "joystick.h"
17 #include "misc.h"
18 #include "setup.h"
19
20
21 #if defined(TARGET_SDL)
22
23 /* ========================================================================= */
24 /* video functions                                                           */
25 /* ========================================================================= */
26
27 /* functions from SGE library */
28 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
29
30 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
31 static int fullscreen_width;
32 static int fullscreen_height;
33 static int fullscreen_xoffset;
34 static int fullscreen_yoffset;
35 static int video_xoffset;
36 static int video_yoffset;
37
38 static void setFullscreenParameters()
39 {
40   struct ScreenModeInfo *fullscreen_mode;
41   int i;
42
43   fullscreen_mode = get_screen_mode_from_string(setup.fullscreen_mode);
44
45   if (fullscreen_mode == NULL)
46     return;
47
48   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
49   {
50     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
51         fullscreen_mode->height == video.fullscreen_modes[i].height)
52     {
53       fullscreen_width  = fullscreen_mode->width;
54       fullscreen_height = fullscreen_mode->height;
55
56       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
57       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
58
59       break;
60     }
61   }
62 }
63
64 static void SDLSetWindowIcon(char *basename)
65 {
66   char *filename = getCustomImageFilename(basename);
67   SDL_Surface *surface;
68
69   if (filename == NULL)
70   {
71     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
72
73     return;
74   }
75
76   if ((surface = IMG_Load(filename)) == NULL)
77   {
78     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
79
80     return;
81   }
82
83   /* set transparent color */
84   SDL_SetColorKey(surface, SDL_SRCCOLORKEY,
85                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
86
87   SDL_WM_SetIcon(surface, NULL);
88 }
89
90 void SDLInitVideoDisplay(void)
91 {
92   putenv("SDL_VIDEO_CENTERED=1");
93
94   /* initialize SDL video */
95   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
96     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
97
98   /* set default SDL depth */
99   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
100 }
101
102 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
103                         boolean fullscreen)
104 {
105   static int screen_xy[][2] =
106   {
107     {  640, 480 },
108     {  800, 600 },
109     { 1024, 768 },
110     {   -1,  -1 }
111   };
112   SDL_Rect **modes;
113   int i, j;
114
115   /* default: normal game window size */
116   fullscreen_width = video.width;
117   fullscreen_height = video.height;
118   fullscreen_xoffset = 0;
119   fullscreen_yoffset = 0;
120
121   for (i = 0; screen_xy[i][0] != -1; i++)
122   {
123     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
124     {
125       fullscreen_width  = screen_xy[i][0];
126       fullscreen_height = screen_xy[i][1];
127
128       break;
129     }
130   }
131
132   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
133   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
134
135   /* get available hardware supported fullscreen modes */
136   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
137
138   if (modes == NULL)
139   {
140     /* no screen modes available => no fullscreen mode support */
141     video.fullscreen_available = FALSE;
142   }
143   else if (modes == (SDL_Rect **)-1)
144   {
145     /* fullscreen resolution is not restricted -- all resolutions available */
146     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
147
148     /* use native video buffer size for fullscreen mode */
149     video.fullscreen_modes[0].width  = video.width;
150     video.fullscreen_modes[0].height = video.height;
151
152     video.fullscreen_modes[1].width  = -1;
153     video.fullscreen_modes[1].height = -1;
154   }
155   else
156   {
157     /* in this case, a certain number of screen modes is available */
158     int num_modes = 0;
159
160     for(i = 0; modes[i] != NULL; i++)
161     {
162       boolean found_mode = FALSE;
163
164       /* screen mode is smaller than video buffer size -- skip it */
165       if (modes[i]->w < video.width || modes[i]->h < video.height)
166         continue;
167
168       if (video.fullscreen_modes != NULL)
169         for (j = 0; video.fullscreen_modes[j].width != -1; j++)
170           if (modes[i]->w == video.fullscreen_modes[j].width &&
171               modes[i]->h == video.fullscreen_modes[j].height)
172             found_mode = TRUE;
173
174       if (found_mode)           /* screen mode already stored -- skip it */
175         continue;
176
177       /* new mode found; add it to list of available fullscreen modes */
178
179       num_modes++;
180
181       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
182                                                (num_modes + 1) *
183                                                sizeof(struct ScreenModeInfo));
184
185       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
186       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
187
188       video.fullscreen_modes[num_modes].width  = -1;
189       video.fullscreen_modes[num_modes].height = -1;
190     }
191
192     if (num_modes == 0)
193     {
194       /* no appropriate screen modes available => no fullscreen mode support */
195       video.fullscreen_available = FALSE;
196     }
197   }
198
199   /* set window icon */
200   SDLSetWindowIcon(program.sdl_icon_filename);
201
202   /* open SDL video output device (window or fullscreen mode) */
203   if (!SDLSetVideoMode(backbuffer, fullscreen))
204     Error(ERR_EXIT, "setting video mode failed");
205
206   /* set window and icon title */
207   SDL_WM_SetCaption(program.window_title, program.window_title);
208
209   /* SDL cannot directly draw to the visible video framebuffer like X11,
210      but always uses a backbuffer, which is then blitted to the visible
211      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
212      visible video framebuffer with 'SDL_Flip', if the hardware supports
213      this). Therefore do not use an additional backbuffer for drawing, but
214      use a symbolic buffer (distinguishable from the SDL backbuffer) called
215      'window', which indicates that the SDL backbuffer should be updated to
216      the visible video framebuffer when attempting to blit to it.
217
218      For convenience, it seems to be a good idea to create this symbolic
219      buffer 'window' at the same size as the SDL backbuffer. Although it
220      should never be drawn to directly, it would do no harm nevertheless. */
221
222   /* create additional (symbolic) buffer for double-buffering */
223   *window = CreateBitmap(video.width, video.height, video.depth);
224 }
225
226 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
227 {
228   boolean success = TRUE;
229   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
230   int surface_flags_window = SURFACE_FLAGS;
231   SDL_Surface *new_surface = NULL;
232
233   if (*backbuffer == NULL)
234     *backbuffer = CreateBitmapStruct();
235
236   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
237   {
238     setFullscreenParameters();
239
240     video_xoffset = fullscreen_xoffset;
241     video_yoffset = fullscreen_yoffset;
242
243     /* switch display to fullscreen mode, if available */
244     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
245                                         video.depth, surface_flags_fullscreen))
246         == NULL)
247     {
248       /* switching display to fullscreen mode failed */
249       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
250
251       /* do not try it again */
252       video.fullscreen_available = FALSE;
253       success = FALSE;
254     }
255     else
256     {
257       (*backbuffer)->surface = new_surface;
258
259       video.fullscreen_enabled = TRUE;
260       success = TRUE;
261     }
262   }
263
264   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
265   {
266     video_xoffset = 0;
267     video_yoffset = 0;
268
269     /* switch display to window mode */
270     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
271                                         video.depth, surface_flags_window))
272         == NULL)
273     {
274       /* switching display to window mode failed -- should not happen */
275       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
276
277       success = FALSE;
278     }
279     else
280     {
281       (*backbuffer)->surface = new_surface;
282
283       video.fullscreen_enabled = FALSE;
284       success = TRUE;
285     }
286   }
287
288   return success;
289 }
290
291 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
292                             int depth)
293 {
294   SDL_Surface *surface_tmp, *surface_native;
295
296   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
297                                           0, 0, 0, 0))
298       == NULL)
299     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
300
301   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
302     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
303
304   SDL_FreeSurface(surface_tmp);
305
306   new_bitmap->surface = surface_native;
307 }
308
309 void SDLFreeBitmapPointers(Bitmap *bitmap)
310 {
311   if (bitmap->surface)
312     SDL_FreeSurface(bitmap->surface);
313   if (bitmap->surface_masked)
314     SDL_FreeSurface(bitmap->surface_masked);
315   bitmap->surface = NULL;
316   bitmap->surface_masked = NULL;
317 }
318
319 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
320                  int src_x, int src_y, int width, int height,
321                  int dst_x, int dst_y, int mask_mode)
322 {
323   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
324   SDL_Rect src_rect, dst_rect;
325
326   if (src_bitmap == backbuffer)
327   {
328     src_x += video_xoffset;
329     src_y += video_yoffset;
330   }
331
332   src_rect.x = src_x;
333   src_rect.y = src_y;
334   src_rect.w = width;
335   src_rect.h = height;
336
337   if (dst_bitmap == backbuffer || dst_bitmap == window)
338   {
339     dst_x += video_xoffset;
340     dst_y += video_yoffset;
341   }
342
343   dst_rect.x = dst_x;
344   dst_rect.y = dst_y;
345   dst_rect.w = width;
346   dst_rect.h = height;
347
348   if (src_bitmap != backbuffer || dst_bitmap != window)
349     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
350                      src_bitmap->surface_masked : src_bitmap->surface),
351                     &src_rect, real_dst_bitmap->surface, &dst_rect);
352
353   if (dst_bitmap == window)
354     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
355 }
356
357 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
358                       Uint32 color)
359 {
360   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
361   SDL_Rect rect;
362
363   if (dst_bitmap == backbuffer || dst_bitmap == window)
364   {
365     x += video_xoffset;
366     y += video_yoffset;
367   }
368
369   rect.x = x;
370   rect.y = y;
371   rect.w = width;
372   rect.h = height;
373
374   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
375
376   if (dst_bitmap == window)
377     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
378 }
379
380 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
381                       int fade_mode, int fade_delay, int post_delay)
382 {
383   static boolean initialization_needed = TRUE;
384   static SDL_Surface *surface_source = NULL;
385   static SDL_Surface *surface_target = NULL;
386   static SDL_Surface *surface_black = NULL;
387   SDL_Surface *surface_screen = backbuffer->surface;
388   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
389   SDL_Rect src_rect, dst_rect;
390   int src_x = x, src_y = y;
391   int dst_x = x, dst_y = y;
392   unsigned int time_last, time_current;
393   float alpha;
394   int alpha_final;
395
396   src_rect.x = src_x;
397   src_rect.y = src_y;
398   src_rect.w = width;
399   src_rect.h = height;
400
401   dst_x += video_xoffset;
402   dst_y += video_yoffset;
403
404   dst_rect.x = dst_x;
405   dst_rect.y = dst_y;
406   dst_rect.w = width;
407   dst_rect.h = height;
408
409   if (initialization_needed)
410   {
411     unsigned int flags = SDL_SRCALPHA;
412
413     /* use same surface type as screen surface */
414     if ((surface_screen->flags & SDL_HWSURFACE))
415       flags |= SDL_HWSURFACE;
416     else
417       flags |= SDL_SWSURFACE;
418
419     /* create surface for temporary copy of screen buffer (source) */
420     if ((surface_source =
421          SDL_CreateRGBSurface(flags,
422                               video.width,
423                               video.height,
424                               surface_screen->format->BitsPerPixel,
425                               surface_screen->format->Rmask,
426                               surface_screen->format->Gmask,
427                               surface_screen->format->Bmask,
428                               surface_screen->format->Amask)) == NULL)
429       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
430
431     /* create surface for cross-fading screen buffer (target) */
432     if ((surface_target =
433          SDL_CreateRGBSurface(flags,
434                               video.width,
435                               video.height,
436                               surface_screen->format->BitsPerPixel,
437                               surface_screen->format->Rmask,
438                               surface_screen->format->Gmask,
439                               surface_screen->format->Bmask,
440                               surface_screen->format->Amask)) == NULL)
441       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
442
443     /* create black surface for fading from/to black */
444     if ((surface_black =
445          SDL_CreateRGBSurface(flags,
446                               video.width,
447                               video.height,
448                               surface_screen->format->BitsPerPixel,
449                               surface_screen->format->Rmask,
450                               surface_screen->format->Gmask,
451                               surface_screen->format->Bmask,
452                               surface_screen->format->Amask)) == NULL)
453       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
454
455     /* completely fill the surface with black color pixels */
456     SDL_FillRect(surface_black, NULL,
457                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
458
459     initialization_needed = FALSE;
460   }
461
462   /* copy source and target surfaces to temporary surfaces for fading */
463   if (fade_mode == FADE_MODE_CROSSFADE)
464   {
465     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
466     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
467   }
468   else if (fade_mode == FADE_MODE_FADE_IN)
469   {
470     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
471     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
472   }
473   else          /* FADE_MODE_FADE_OUT */
474   {
475     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
476     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
477   }
478
479   time_current = SDL_GetTicks();
480
481   for (alpha = 0.0; alpha < 255.0;)
482   {
483     time_last = time_current;
484     time_current = SDL_GetTicks();
485     alpha += 255 * ((float)(time_current - time_last) / fade_delay);
486     alpha_final = MIN(MAX(0, alpha), 255);
487
488     /* draw existing (source) image to screen buffer */
489     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
490
491     /* draw new (target) image to screen buffer using alpha blending */
492     SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
493     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
494
495 #if 1
496     /* only update the region of the screen that is affected from fading */
497     SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
498 #else
499     SDL_Flip(surface_screen);
500 #endif
501   }
502
503   Delay(post_delay);
504 }
505
506 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
507                        int to_x, int to_y, Uint32 color)
508 {
509   SDL_Surface *surface = dst_bitmap->surface;
510   SDL_Rect rect;
511
512   if (from_x > to_x)
513     swap_numbers(&from_x, &to_x);
514
515   if (from_y > to_y)
516     swap_numbers(&from_y, &to_y);
517
518   rect.x = from_x;
519   rect.y = from_y;
520   rect.w = (to_x - from_x + 1);
521   rect.h = (to_y - from_y + 1);
522
523   if (dst_bitmap == backbuffer || dst_bitmap == window)
524   {
525     rect.x += video_xoffset;
526     rect.y += video_yoffset;
527   }
528
529   SDL_FillRect(surface, &rect, color);
530 }
531
532 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
533                  int to_x, int to_y, Uint32 color)
534 {
535   if (dst_bitmap == backbuffer || dst_bitmap == window)
536   {
537     from_x += video_xoffset;
538     from_y += video_yoffset;
539     to_x += video_xoffset;
540     to_y += video_yoffset;
541   }
542
543   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
544 }
545
546 #if 0
547 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
548                   int num_points, Uint32 color)
549 {
550   int i, x, y;
551   int line_width = 4;
552
553   for (i = 0; i < num_points - 1; i++)
554   {
555     for (x = 0; x < line_width; x++)
556     {
557       for (y = 0; y < line_width; y++)
558       {
559         int dx = x - line_width / 2;
560         int dy = y - line_width / 2;
561
562         if ((x == 0 && y == 0) ||
563             (x == 0 && y == line_width - 1) ||
564             (x == line_width - 1 && y == 0) ||
565             (x == line_width - 1 && y == line_width - 1))
566           continue;
567
568         sge_Line(surface, points[i].x + dx, points[i].y + dy,
569                  points[i+1].x + dx, points[i+1].y + dy, color);
570       }
571     }
572   }
573 }
574 #endif
575
576 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
577 {
578   SDL_Surface *surface = src_bitmap->surface;
579
580   if (src_bitmap == backbuffer || src_bitmap == window)
581   {
582     x += video_xoffset;
583     y += video_yoffset;
584   }
585
586   switch (surface->format->BytesPerPixel)
587   {
588     case 1:             /* assuming 8-bpp */
589     {
590       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
591     }
592     break;
593
594     case 2:             /* probably 15-bpp or 16-bpp */
595     {
596       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
597     }
598     break;
599
600   case 3:               /* slow 24-bpp mode; usually not used */
601     {
602       /* does this work? */
603       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
604       Uint32 color = 0;
605       int shift;
606
607       shift = surface->format->Rshift;
608       color |= *(pix + shift / 8) >> shift;
609       shift = surface->format->Gshift;
610       color |= *(pix + shift / 8) >> shift;
611       shift = surface->format->Bshift;
612       color |= *(pix + shift / 8) >> shift;
613
614       return color;
615     }
616     break;
617
618   case 4:               /* probably 32-bpp */
619     {
620       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
621     }
622     break;
623   }
624
625   return 0;
626 }
627
628
629 /* ========================================================================= */
630 /* The following functions were taken from the SGE library                   */
631 /* (SDL Graphics Extension Library) by Anders Lindström                      */
632 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
633 /* ========================================================================= */
634
635 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
636 {
637   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
638   {
639     switch (surface->format->BytesPerPixel)
640     {
641       case 1:
642       {
643         /* Assuming 8-bpp */
644         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
645       }
646       break;
647
648       case 2:
649       {
650         /* Probably 15-bpp or 16-bpp */
651         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
652       }
653       break;
654
655       case 3:
656       {
657         /* Slow 24-bpp mode, usually not used */
658         Uint8 *pix;
659         int shift;
660
661         /* Gack - slow, but endian correct */
662         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
663         shift = surface->format->Rshift;
664         *(pix+shift/8) = color>>shift;
665         shift = surface->format->Gshift;
666         *(pix+shift/8) = color>>shift;
667         shift = surface->format->Bshift;
668         *(pix+shift/8) = color>>shift;
669       }
670       break;
671
672       case 4:
673       {
674         /* Probably 32-bpp */
675         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
676       }
677       break;
678     }
679   }
680 }
681
682 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
683                   Uint8 R, Uint8 G, Uint8 B)
684 {
685   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
686 }
687
688 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
689 {
690   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
691 }
692
693 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
694 {
695   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
696 }
697
698 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
699 {
700   Uint8 *pix;
701   int shift;
702
703   /* Gack - slow, but endian correct */
704   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
705   shift = surface->format->Rshift;
706   *(pix+shift/8) = color>>shift;
707   shift = surface->format->Gshift;
708   *(pix+shift/8) = color>>shift;
709   shift = surface->format->Bshift;
710   *(pix+shift/8) = color>>shift;
711 }
712
713 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
714 {
715   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
716 }
717
718 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
719 {
720   switch (dest->format->BytesPerPixel)
721   {
722     case 1:
723       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
724       break;
725
726     case 2:
727       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
728       break;
729
730     case 3:
731       _PutPixel24(dest,x,y,color);
732       break;
733
734     case 4:
735       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
736       break;
737   }
738 }
739
740 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
741 {
742   if (SDL_MUSTLOCK(surface))
743   {
744     if (SDL_LockSurface(surface) < 0)
745     {
746       return;
747     }
748   }
749
750   _PutPixel(surface, x, y, color);
751
752   if (SDL_MUSTLOCK(surface))
753   {
754     SDL_UnlockSurface(surface);
755   }
756 }
757
758 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
759                   Uint8 r, Uint8 g, Uint8 b)
760 {
761   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
762 }
763
764 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
765 {
766   if (y >= 0 && y <= dest->h - 1)
767   {
768     switch (dest->format->BytesPerPixel)
769     {
770       case 1:
771         return y*dest->pitch;
772         break;
773
774       case 2:
775         return y*dest->pitch/2;
776         break;
777
778       case 3:
779         return y*dest->pitch;
780         break;
781
782       case 4:
783         return y*dest->pitch/4;
784         break;
785     }
786   }
787
788   return -1;
789 }
790
791 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
792 {
793   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
794   {
795     switch (surface->format->BytesPerPixel)
796     {
797       case 1:
798       {
799         /* Assuming 8-bpp */
800         *((Uint8 *)surface->pixels + ypitch + x) = color;
801       }
802       break;
803
804       case 2:
805       {
806         /* Probably 15-bpp or 16-bpp */
807         *((Uint16 *)surface->pixels + ypitch + x) = color;
808       }
809       break;
810
811       case 3:
812       {
813         /* Slow 24-bpp mode, usually not used */
814         Uint8 *pix;
815         int shift;
816
817         /* Gack - slow, but endian correct */
818         pix = (Uint8 *)surface->pixels + ypitch + x*3;
819         shift = surface->format->Rshift;
820         *(pix+shift/8) = color>>shift;
821         shift = surface->format->Gshift;
822         *(pix+shift/8) = color>>shift;
823         shift = surface->format->Bshift;
824         *(pix+shift/8) = color>>shift;
825       }
826       break;
827
828       case 4:
829       {
830         /* Probably 32-bpp */
831         *((Uint32 *)surface->pixels + ypitch + x) = color;
832       }
833       break;
834     }
835   }
836 }
837
838 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
839                Uint32 Color)
840 {
841   SDL_Rect l;
842
843   if (SDL_MUSTLOCK(Surface))
844   {
845     if (SDL_LockSurface(Surface) < 0)
846     {
847       return;
848     }
849   }
850
851   if (x1 > x2)
852   {
853     Sint16 tmp = x1;
854     x1 = x2;
855     x2 = tmp;
856   }
857
858   /* Do the clipping */
859   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
860     return;
861   if (x1 < 0)
862     x1 = 0;
863   if (x2 > Surface->w - 1)
864     x2 = Surface->w - 1;
865
866   l.x = x1;
867   l.y = y;
868   l.w = x2 - x1 + 1;
869   l.h = 1;
870
871   SDL_FillRect(Surface, &l, Color);
872
873   if (SDL_MUSTLOCK(Surface))
874   {
875     SDL_UnlockSurface(Surface);
876   }
877 }
878
879 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
880                   Uint8 R, Uint8 G, Uint8 B)
881 {
882   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
883 }
884
885 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
886 {
887   SDL_Rect l;
888
889   if (x1 > x2)
890   {
891     Sint16 tmp = x1;
892     x1 = x2;
893     x2 = tmp;
894   }
895
896   /* Do the clipping */
897   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
898     return;
899   if (x1 < 0)
900     x1 = 0;
901   if (x2 > Surface->w - 1)
902     x2 = Surface->w - 1;
903
904   l.x = x1;
905   l.y = y;
906   l.w = x2 - x1 + 1;
907   l.h = 1;
908
909   SDL_FillRect(Surface, &l, Color);
910 }
911
912 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
913                Uint32 Color)
914 {
915   SDL_Rect l;
916
917   if (SDL_MUSTLOCK(Surface))
918   {
919     if (SDL_LockSurface(Surface) < 0)
920     {
921       return;
922     }
923   }
924
925   if (y1 > y2)
926   {
927     Sint16 tmp = y1;
928     y1 = y2;
929     y2 = tmp;
930   }
931
932   /* Do the clipping */
933   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
934     return;
935   if (y1 < 0)
936     y1 = 0;
937   if (y2 > Surface->h - 1)
938     y2 = Surface->h - 1;
939
940   l.x = x;
941   l.y = y1;
942   l.w = 1;
943   l.h = y2 - y1 + 1;
944
945   SDL_FillRect(Surface, &l, Color);
946
947   if (SDL_MUSTLOCK(Surface))
948   {
949     SDL_UnlockSurface(Surface);
950   }
951 }
952
953 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
954                   Uint8 R, Uint8 G, Uint8 B)
955 {
956   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
957 }
958
959 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
960 {
961   SDL_Rect l;
962
963   if (y1 > y2)
964   {
965     Sint16 tmp = y1;
966     y1 = y2;
967     y2 = tmp;
968   }
969
970   /* Do the clipping */
971   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
972     return;
973   if (y1 < 0)
974     y1 = 0;
975   if (y2 > Surface->h - 1)
976     y2 = Surface->h - 1;
977
978   l.x = x;
979   l.y = y1;
980   l.w = 1;
981   l.h = y2 - y1 + 1;
982
983   SDL_FillRect(Surface, &l, Color);
984 }
985
986 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
987                 Sint16 x2, Sint16 y2, Uint32 Color,
988                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
989                               Uint32 Color))
990 {
991   Sint16 dx, dy, sdx, sdy, x, y, px, py;
992
993   dx = x2 - x1;
994   dy = y2 - y1;
995
996   sdx = (dx < 0) ? -1 : 1;
997   sdy = (dy < 0) ? -1 : 1;
998
999   dx = sdx * dx + 1;
1000   dy = sdy * dy + 1;
1001
1002   x = y = 0;
1003
1004   px = x1;
1005   py = y1;
1006
1007   if (dx >= dy)
1008   {
1009     for (x = 0; x < dx; x++)
1010     {
1011       Callback(Surface, px, py, Color);
1012
1013       y += dy;
1014       if (y >= dx)
1015       {
1016         y -= dx;
1017         py += sdy;
1018       }
1019
1020       px += sdx;
1021     }
1022   }
1023   else
1024   {
1025     for (y = 0; y < dy; y++)
1026     {
1027       Callback(Surface, px, py, Color);
1028
1029       x += dx;
1030       if (x >= dy)
1031       {
1032         x -= dy;
1033         px += sdx;
1034       }
1035
1036       py += sdy;
1037     }
1038   }
1039 }
1040
1041 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1042                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1043                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1044                                  Uint32 Color))
1045 {
1046   sge_DoLine(Surface, X1, Y1, X2, Y2,
1047              SDL_MapRGB(Surface->format, R, G, B), Callback);
1048 }
1049
1050 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1051               Uint32 Color)
1052 {
1053   if (SDL_MUSTLOCK(Surface))
1054   {
1055     if (SDL_LockSurface(Surface) < 0)
1056       return;
1057    }
1058
1059    /* Draw the line */
1060    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1061
1062    /* unlock the display */
1063    if (SDL_MUSTLOCK(Surface))
1064    {
1065       SDL_UnlockSurface(Surface);
1066    }
1067 }
1068
1069 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1070                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1071 {
1072   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1073 }
1074
1075 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1076 {
1077   if (dst_bitmap == backbuffer || dst_bitmap == window)
1078   {
1079     x += video_xoffset;
1080     y += video_yoffset;
1081   }
1082
1083   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1084 }
1085
1086
1087 /*
1088   -----------------------------------------------------------------------------
1089   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1090   -----------------------------------------------------------------------------
1091 */
1092
1093 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1094                    int width, int height, Uint32 color)
1095 {
1096   int x, y;
1097
1098   for (y = src_y; y < src_y + height; y++)
1099   {
1100     for (x = src_x; x < src_x + width; x++)
1101     {
1102       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1103
1104       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1105     }
1106   }
1107 }
1108
1109 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1110                           int src_x, int src_y, int width, int height,
1111                           int dst_x, int dst_y)
1112 {
1113   int x, y;
1114
1115   for (y = 0; y < height; y++)
1116   {
1117     for (x = 0; x < width; x++)
1118     {
1119       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1120
1121       if (pixel != BLACK_PIXEL)
1122         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1123     }
1124   }
1125 }
1126
1127
1128 /* ========================================================================= */
1129 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1130 /* (Rotozoomer) by Andreas Schiffler                                         */
1131 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1132 /* ========================================================================= */
1133
1134 /*
1135   -----------------------------------------------------------------------------
1136   32 bit zoomer
1137
1138   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1139   -----------------------------------------------------------------------------
1140 */
1141
1142 typedef struct
1143 {
1144   Uint8 r;
1145   Uint8 g;
1146   Uint8 b;
1147   Uint8 a;
1148 } tColorRGBA;
1149
1150 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1151 {
1152   int x, y;
1153   tColorRGBA *sp, *csp, *dp;
1154   int sgap, dgap;
1155
1156   /* pointer setup */
1157   sp = csp = (tColorRGBA *) src->pixels;
1158   dp = (tColorRGBA *) dst->pixels;
1159   sgap = src->pitch - src->w * 4;
1160   dgap = dst->pitch - dst->w * 4;
1161
1162   for (y = 0; y < dst->h; y++)
1163   {
1164     sp = csp;
1165
1166     for (x = 0; x < dst->w; x++)
1167     {
1168       tColorRGBA *sp0 = sp;
1169       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1170       tColorRGBA *sp00 = &sp0[0];
1171       tColorRGBA *sp01 = &sp0[1];
1172       tColorRGBA *sp10 = &sp1[0];
1173       tColorRGBA *sp11 = &sp1[1];
1174       tColorRGBA new;
1175
1176       /* create new color pixel from all four source color pixels */
1177       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1178       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1179       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1180       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1181
1182       /* draw */
1183       *dp = new;
1184
1185       /* advance source pointers */
1186       sp += 2;
1187
1188       /* advance destination pointer */
1189       dp++;
1190     }
1191
1192     /* advance source pointer */
1193     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1194
1195     /* advance destination pointers */
1196     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1197   }
1198
1199   return 0;
1200 }
1201
1202 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1203 {
1204   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1205   tColorRGBA *sp, *csp, *dp;
1206   int sgap, dgap;
1207
1208   /* use specialized zoom function when scaling down to exactly half size */
1209   if (src->w == 2 * dst->w &&
1210       src->h == 2 * dst->h)
1211     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1212
1213   /* variable setup */
1214   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1215   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1216
1217   /* allocate memory for row increments */
1218   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1219   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1220
1221   /* precalculate row increments */
1222   csx = 0;
1223   csax = sax;
1224   for (x = 0; x <= dst->w; x++)
1225   {
1226     *csax = csx;
1227     csax++;
1228     csx &= 0xffff;
1229     csx += sx;
1230   }
1231
1232   csy = 0;
1233   csay = say;
1234   for (y = 0; y <= dst->h; y++)
1235   {
1236     *csay = csy;
1237     csay++;
1238     csy &= 0xffff;
1239     csy += sy;
1240   }
1241
1242   /* pointer setup */
1243   sp = csp = (tColorRGBA *) src->pixels;
1244   dp = (tColorRGBA *) dst->pixels;
1245   sgap = src->pitch - src->w * 4;
1246   dgap = dst->pitch - dst->w * 4;
1247
1248   csay = say;
1249   for (y = 0; y < dst->h; y++)
1250   {
1251     sp = csp;
1252     csax = sax;
1253
1254     for (x = 0; x < dst->w; x++)
1255     {
1256       /* draw */
1257       *dp = *sp;
1258
1259       /* advance source pointers */
1260       csax++;
1261       sp += (*csax >> 16);
1262
1263       /* advance destination pointer */
1264       dp++;
1265     }
1266
1267     /* advance source pointer */
1268     csay++;
1269     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1270
1271     /* advance destination pointers */
1272     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1273   }
1274
1275   free(sax);
1276   free(say);
1277
1278   return 0;
1279 }
1280
1281 /*
1282   -----------------------------------------------------------------------------
1283   8 bit zoomer
1284
1285   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1286   -----------------------------------------------------------------------------
1287 */
1288
1289 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1290 {
1291   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1292   Uint8 *sp, *dp, *csp;
1293   int dgap;
1294
1295   /* variable setup */
1296   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1297   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1298
1299   /* allocate memory for row increments */
1300   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1301   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1302
1303   /* precalculate row increments */
1304   csx = 0;
1305   csax = sax;
1306   for (x = 0; x < dst->w; x++)
1307   {
1308     csx += sx;
1309     *csax = (csx >> 16);
1310     csx &= 0xffff;
1311     csax++;
1312   }
1313
1314   csy = 0;
1315   csay = say;
1316   for (y = 0; y < dst->h; y++)
1317   {
1318     csy += sy;
1319     *csay = (csy >> 16);
1320     csy &= 0xffff;
1321     csay++;
1322   }
1323
1324   csx = 0;
1325   csax = sax;
1326   for (x = 0; x < dst->w; x++)
1327   {
1328     csx += (*csax);
1329     csax++;
1330   }
1331
1332   csy = 0;
1333   csay = say;
1334   for (y = 0; y < dst->h; y++)
1335   {
1336     csy += (*csay);
1337     csay++;
1338   }
1339
1340   /* pointer setup */
1341   sp = csp = (Uint8 *) src->pixels;
1342   dp = (Uint8 *) dst->pixels;
1343   dgap = dst->pitch - dst->w;
1344
1345   /* draw */
1346   csay = say;
1347   for (y = 0; y < dst->h; y++)
1348   {
1349     csax = sax;
1350     sp = csp;
1351     for (x = 0; x < dst->w; x++)
1352     {
1353       /* draw */
1354       *dp = *sp;
1355
1356       /* advance source pointers */
1357       sp += (*csax);
1358       csax++;
1359
1360       /* advance destination pointer */
1361       dp++;
1362     }
1363
1364     /* advance source pointer (for row) */
1365     csp += ((*csay) * src->pitch);
1366     csay++;
1367
1368     /* advance destination pointers */
1369     dp += dgap;
1370   }
1371
1372   free(sax);
1373   free(say);
1374
1375   return 0;
1376 }
1377
1378 /*
1379   -----------------------------------------------------------------------------
1380   zoomSurface()
1381
1382   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1383   'zoomx' and 'zoomy' are scaling factors for width and height.
1384   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1385   into a 32bit RGBA format on the fly.
1386   -----------------------------------------------------------------------------
1387 */
1388
1389 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1390 {
1391   SDL_Surface *zoom_src = NULL;
1392   SDL_Surface *zoom_dst = NULL;
1393   boolean is_converted = FALSE;
1394   boolean is_32bit;
1395   int i;
1396
1397   if (src == NULL)
1398     return NULL;
1399
1400   /* determine if source surface is 32 bit or 8 bit */
1401   is_32bit = (src->format->BitsPerPixel == 32);
1402
1403   if (is_32bit || src->format->BitsPerPixel == 8)
1404   {
1405     /* use source surface 'as is' */
1406     zoom_src = src;
1407   }
1408   else
1409   {
1410     /* new source surface is 32 bit with a defined RGB ordering */
1411     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1412                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1413     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1414     is_32bit = TRUE;
1415     is_converted = TRUE;
1416   }
1417
1418   /* allocate surface to completely contain the zoomed surface */
1419   if (is_32bit)
1420   {
1421     /* target surface is 32 bit with source RGBA/ABGR ordering */
1422     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1423                                     zoom_src->format->Rmask,
1424                                     zoom_src->format->Gmask,
1425                                     zoom_src->format->Bmask, 0);
1426   }
1427   else
1428   {
1429     /* target surface is 8 bit */
1430     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1431                                     0, 0, 0, 0);
1432   }
1433
1434   /* lock source surface */
1435   SDL_LockSurface(zoom_src);
1436
1437   /* check which kind of surface we have */
1438   if (is_32bit)
1439   {
1440     /* call the 32 bit transformation routine to do the zooming */
1441     zoomSurfaceRGBA(zoom_src, zoom_dst);
1442   }
1443   else
1444   {
1445     /* copy palette */
1446     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1447       zoom_dst->format->palette->colors[i] =
1448         zoom_src->format->palette->colors[i];
1449     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1450
1451     /* call the 8 bit transformation routine to do the zooming */
1452     zoomSurfaceY(zoom_src, zoom_dst);
1453   }
1454
1455   /* unlock source surface */
1456   SDL_UnlockSurface(zoom_src);
1457
1458   /* free temporary surface */
1459   if (is_converted)
1460     SDL_FreeSurface(zoom_src);
1461
1462   /* return destination surface */
1463   return zoom_dst;
1464 }
1465
1466 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1467 {
1468   SDL_Surface *sdl_surface_tmp;
1469   int dst_width = dst_bitmap->width;
1470   int dst_height = dst_bitmap->height;
1471
1472   /* throw away old destination surface */
1473   SDL_FreeSurface(dst_bitmap->surface);
1474
1475   /* create zoomed temporary surface from source surface */
1476   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1477
1478   /* create native format destination surface from zoomed temporary surface */
1479   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1480
1481   /* free temporary surface */
1482   SDL_FreeSurface(sdl_surface_tmp);
1483 }
1484
1485
1486 /* ========================================================================= */
1487 /* load image to bitmap                                                      */
1488 /* ========================================================================= */
1489
1490 Bitmap *SDLLoadImage(char *filename)
1491 {
1492   Bitmap *new_bitmap = CreateBitmapStruct();
1493   SDL_Surface *sdl_image_tmp;
1494
1495   /* load image to temporary surface */
1496   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1497   {
1498     SetError("IMG_Load(): %s", SDL_GetError());
1499
1500     return NULL;
1501   }
1502
1503   /* create native non-transparent surface for current image */
1504   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1505   {
1506     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1507
1508     return NULL;
1509   }
1510
1511   /* create native transparent surface for current image */
1512   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1513                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1514   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1515   {
1516     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1517
1518     return NULL;
1519   }
1520
1521   /* free temporary surface */
1522   SDL_FreeSurface(sdl_image_tmp);
1523
1524   new_bitmap->width = new_bitmap->surface->w;
1525   new_bitmap->height = new_bitmap->surface->h;
1526
1527   return new_bitmap;
1528 }
1529
1530
1531 /* ------------------------------------------------------------------------- */
1532 /* custom cursor fuctions                                                    */
1533 /* ------------------------------------------------------------------------- */
1534
1535 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1536 {
1537   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1538                           cursor_info->width, cursor_info->height,
1539                           cursor_info->hot_x, cursor_info->hot_y);
1540 }
1541
1542 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1543 {
1544   static struct MouseCursorInfo *last_cursor_info = NULL;
1545   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1546   static SDL_Cursor *cursor_default = NULL;
1547   static SDL_Cursor *cursor_current = NULL;
1548
1549   /* if invoked for the first time, store the SDL default cursor */
1550   if (cursor_default == NULL)
1551     cursor_default = SDL_GetCursor();
1552
1553   /* only create new cursor if cursor info (custom only) has changed */
1554   if (cursor_info != NULL && cursor_info != last_cursor_info)
1555   {
1556     cursor_current = create_cursor(cursor_info);
1557     last_cursor_info = cursor_info;
1558   }
1559
1560   /* only set new cursor if cursor info (custom or NULL) has changed */
1561   if (cursor_info != last_cursor_info2)
1562     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1563
1564   last_cursor_info2 = cursor_info;
1565 }
1566
1567
1568 /* ========================================================================= */
1569 /* audio functions                                                           */
1570 /* ========================================================================= */
1571
1572 void SDLOpenAudio(void)
1573 {
1574   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1575     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1576
1577   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1578   {
1579     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1580     return;
1581   }
1582
1583   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1584                     AUDIO_NUM_CHANNELS_STEREO,
1585                     setup.system.audio_fragment_size) < 0)
1586   {
1587     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1588     return;
1589   }
1590
1591   audio.sound_available = TRUE;
1592   audio.music_available = TRUE;
1593   audio.loops_available = TRUE;
1594   audio.sound_enabled = TRUE;
1595
1596   /* set number of available mixer channels */
1597   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1598   audio.music_channel = MUSIC_CHANNEL;
1599   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1600
1601   Mixer_InitChannels();
1602 }
1603
1604 void SDLCloseAudio(void)
1605 {
1606   Mix_HaltMusic();
1607   Mix_HaltChannel(-1);
1608
1609   Mix_CloseAudio();
1610   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1611 }
1612
1613
1614 /* ========================================================================= */
1615 /* event functions                                                           */
1616 /* ========================================================================= */
1617
1618 void SDLNextEvent(Event *event)
1619 {
1620   SDL_WaitEvent(event);
1621
1622   if (event->type == EVENT_BUTTONPRESS ||
1623       event->type == EVENT_BUTTONRELEASE)
1624   {
1625     if (((ButtonEvent *)event)->x > video_xoffset)
1626       ((ButtonEvent *)event)->x -= video_xoffset;
1627     else
1628       ((ButtonEvent *)event)->x = 0;
1629     if (((ButtonEvent *)event)->y > video_yoffset)
1630       ((ButtonEvent *)event)->y -= video_yoffset;
1631     else
1632       ((ButtonEvent *)event)->y = 0;
1633   }
1634   else if (event->type == EVENT_MOTIONNOTIFY)
1635   {
1636     if (((MotionEvent *)event)->x > video_xoffset)
1637       ((MotionEvent *)event)->x -= video_xoffset;
1638     else
1639       ((MotionEvent *)event)->x = 0;
1640     if (((MotionEvent *)event)->y > video_yoffset)
1641       ((MotionEvent *)event)->y -= video_yoffset;
1642     else
1643       ((MotionEvent *)event)->y = 0;
1644   }
1645 }
1646
1647
1648 /* ========================================================================= */
1649 /* joystick functions                                                        */
1650 /* ========================================================================= */
1651
1652 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1653 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1654 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1655
1656 static boolean SDLOpenJoystick(int nr)
1657 {
1658   if (nr < 0 || nr > MAX_PLAYERS)
1659     return FALSE;
1660
1661   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1662 }
1663
1664 static void SDLCloseJoystick(int nr)
1665 {
1666   if (nr < 0 || nr > MAX_PLAYERS)
1667     return;
1668
1669   SDL_JoystickClose(sdl_joystick[nr]);
1670 }
1671
1672 static boolean SDLCheckJoystickOpened(int nr)
1673 {
1674   if (nr < 0 || nr > MAX_PLAYERS)
1675     return FALSE;
1676
1677   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1678 }
1679
1680 void HandleJoystickEvent(Event *event)
1681 {
1682   switch(event->type)
1683   {
1684     case SDL_JOYAXISMOTION:
1685       if (event->jaxis.axis < 2)
1686         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1687       break;
1688
1689     case SDL_JOYBUTTONDOWN:
1690       if (event->jbutton.button < 2)
1691         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1692       break;
1693
1694     case SDL_JOYBUTTONUP:
1695       if (event->jbutton.button < 2)
1696         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1697       break;
1698
1699     default:
1700       break;
1701   }
1702 }
1703
1704 void SDLInitJoysticks()
1705 {
1706   static boolean sdl_joystick_subsystem_initialized = FALSE;
1707   boolean print_warning = !sdl_joystick_subsystem_initialized;
1708   int i;
1709
1710   if (!sdl_joystick_subsystem_initialized)
1711   {
1712     sdl_joystick_subsystem_initialized = TRUE;
1713
1714     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1715     {
1716       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1717       return;
1718     }
1719   }
1720
1721   for (i = 0; i < MAX_PLAYERS; i++)
1722   {
1723     /* get configured joystick for this player */
1724     char *device_name = setup.input[i].joy.device_name;
1725     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1726
1727     if (joystick_nr >= SDL_NumJoysticks())
1728     {
1729       if (setup.input[i].use_joystick && print_warning)
1730         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1731
1732       joystick_nr = -1;
1733     }
1734
1735     /* misuse joystick file descriptor variable to store joystick number */
1736     joystick.fd[i] = joystick_nr;
1737
1738     if (joystick_nr == -1)
1739       continue;
1740
1741     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1742     if (SDLCheckJoystickOpened(joystick_nr))
1743       SDLCloseJoystick(joystick_nr);
1744
1745     if (!setup.input[i].use_joystick)
1746       continue;
1747
1748     if (!SDLOpenJoystick(joystick_nr))
1749     {
1750       if (print_warning)
1751         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1752
1753       continue;
1754     }
1755
1756     joystick.status = JOYSTICK_ACTIVATED;
1757   }
1758 }
1759
1760 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1761 {
1762   if (nr < 0 || nr >= MAX_PLAYERS)
1763     return FALSE;
1764
1765   if (x != NULL)
1766     *x = sdl_js_axis[nr][0];
1767   if (y != NULL)
1768     *y = sdl_js_axis[nr][1];
1769
1770   if (b1 != NULL)
1771     *b1 = sdl_js_button[nr][0];
1772   if (b2 != NULL)
1773     *b2 = sdl_js_button[nr][1];
1774
1775   return TRUE;
1776 }
1777
1778 #endif /* TARGET_SDL */