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