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