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